• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 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.TypedArray;
21 import android.graphics.Color;
22 import android.graphics.drawable.Drawable;
23 import android.util.AttributeSet;
24 import android.view.Gravity;
25 import android.view.View;
26 import android.view.ViewGroup.LayoutParams;
27 import android.widget.ImageView;
28 import android.widget.LinearLayout;
29 import android.widget.TextView;
30 import androidx.annotation.ColorInt;
31 import androidx.annotation.Nullable;
32 import com.google.android.setupcompat.partnerconfig.PartnerConfigHelper;
33 import com.google.android.setupdesign.R;
34 import com.google.android.setupdesign.span.LinkSpan;
35 import com.google.android.setupdesign.util.ItemStyler;
36 import com.google.android.setupdesign.util.LayoutStyler;
37 import com.google.android.setupdesign.view.RichTextView;
38 
39 /**
40  * Definition of an item in an {@link ItemHierarchy}. An item is usually defined in XML and inflated
41  * using {@link ItemInflater}.
42  */
43 public class Item extends AbstractItem implements LinkSpan.OnLinkClickListener {
44 
45   /**
46    * Listener that is invoked when a link span is clicked in summary RichTextView.
47    * If the containing view of this span implements this interface, this will be invoked when the
48    * link is clicked.
49    * @apiNote Make sure to use RichTextView for the textViews wherever Linking of text is expected.
50    * This OnLinkClickListener can be extended to Title TextViews based on use case.
51    */
52   public interface OnItemTextLinkClickListener {
53 
54     /**
55      * Called when a link has been clicked.
56      *
57      * @param span The span that was clicked.
58      * @return True if the click was handled, stopping further propagation of the click event.
59      */
onItemTextLinkClicked(LinkSpan span)60     boolean onItemTextLinkClicked(LinkSpan span);
61   }
62 
63   private boolean enabled = true;
64   @Nullable private Drawable icon;
65   private int layoutRes;
66   @Nullable private CharSequence summary;
67   @Nullable private CharSequence title;
68   @Nullable private CharSequence contentDescription;
69   @Nullable private Boolean isClickable;
70   @Nullable private OnItemTextLinkClickListener itemTextLinkClickListener;
71   private boolean visible = true;
72   @ColorInt private int iconTint = Color.TRANSPARENT;
73   private int iconGravity = Gravity.CENTER_VERTICAL;
74 
Item()75   public Item() {
76     super();
77     layoutRes = getDefaultLayoutResource();
78   }
79 
Item(Context context, AttributeSet attrs)80   public Item(Context context, AttributeSet attrs) {
81     super(context, attrs);
82     TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SudItem);
83     enabled = a.getBoolean(R.styleable.SudItem_android_enabled, true);
84     icon = a.getDrawable(R.styleable.SudItem_android_icon);
85     title = a.getText(R.styleable.SudItem_android_title);
86     summary = a.getText(R.styleable.SudItem_android_summary);
87     contentDescription = a.getText(R.styleable.SudItem_android_contentDescription);
88     layoutRes = a.getResourceId(R.styleable.SudItem_android_layout, getDefaultLayoutResource());
89     visible = a.getBoolean(R.styleable.SudItem_android_visible, true);
90     iconTint = a.getColor(R.styleable.SudItem_sudIconTint, Color.TRANSPARENT);
91     iconGravity = a.getInt(R.styleable.SudItem_sudIconGravity, Gravity.CENTER_VERTICAL);
92 
93     a.recycle();
94   }
95 
getDefaultLayoutResource()96   protected int getDefaultLayoutResource() {
97     return R.layout.sud_items_default;
98   }
99 
setEnabled(boolean enabled)100   public void setEnabled(boolean enabled) {
101     if (this.enabled == enabled) {
102       return;
103     }
104     this.enabled = enabled;
105     notifyItemChanged();
106   }
107 
108   @Override
getCount()109   public int getCount() {
110     return isVisible() ? 1 : 0;
111   }
112 
113   @Override
isEnabled()114   public boolean isEnabled() {
115     return enabled;
116   }
117 
setIcon(@ullable Drawable icon)118   public void setIcon(@Nullable Drawable icon) {
119     this.icon = icon;
120     notifyItemChanged();
121   }
122 
123   @Nullable
getIcon()124   public Drawable getIcon() {
125     return icon;
126   }
127 
setIconTint(@olorInt int iconTint)128   public void setIconTint(@ColorInt int iconTint) {
129     this.iconTint = iconTint;
130   }
131 
132   @ColorInt
getIconTint()133   public int getIconTint() {
134     return iconTint;
135   }
136 
setIconGravity(int iconGravity)137   public void setIconGravity(int iconGravity) {
138     this.iconGravity = iconGravity;
139   }
140 
getClickable()141   public Boolean getClickable() {
142     return isClickable;
143   }
144 
setClickable(Boolean isClickable)145   public void setClickable(Boolean isClickable) {
146     this.isClickable = isClickable;
147   }
148 
getIconGravity()149   public int getIconGravity() {
150     return iconGravity;
151   }
152 
setLayoutResource(int layoutResource)153   public void setLayoutResource(int layoutResource) {
154     layoutRes = layoutResource;
155     notifyItemChanged();
156   }
157 
158   @Override
getLayoutResource()159   public int getLayoutResource() {
160     return layoutRes;
161   }
162 
setSummary(@ullable CharSequence summary)163   public void setSummary(@Nullable CharSequence summary) {
164     this.summary = summary;
165     notifyItemChanged();
166   }
167 
168   @Nullable
getSummary()169   public CharSequence getSummary() {
170     return summary;
171   }
172 
setOnItemTextLinkClickListener(@ullable OnItemTextLinkClickListener itemTextLinkClickListener)173   public void setOnItemTextLinkClickListener(@Nullable OnItemTextLinkClickListener itemTextLinkClickListener) {
174     this.itemTextLinkClickListener = itemTextLinkClickListener;
175   }
176 
setTitle(@ullable CharSequence title)177   public void setTitle(@Nullable CharSequence title) {
178     this.title = title;
179     notifyItemChanged();
180   }
181 
182   @Nullable
getTitle()183   public CharSequence getTitle() {
184     return title;
185   }
186 
187   @Nullable
getContentDescription()188   public CharSequence getContentDescription() {
189     return contentDescription;
190   }
191 
setContentDescription(@ullable CharSequence contentDescription)192   public void setContentDescription(@Nullable CharSequence contentDescription) {
193     this.contentDescription = contentDescription;
194     notifyItemChanged();
195   }
196 
setVisible(boolean visible)197   public void setVisible(boolean visible) {
198     if (this.visible == visible) {
199       return;
200     }
201     this.visible = visible;
202     if (!visible) {
203       notifyItemRangeRemoved(0, 1);
204     } else {
205       notifyItemRangeInserted(0, 1);
206     }
207   }
208 
isVisible()209   public boolean isVisible() {
210     return visible;
211   }
212 
hasSummary(CharSequence summary)213   private boolean hasSummary(CharSequence summary) {
214     return summary != null && summary.length() > 0;
215   }
216 
217   @Override
getViewId()218   public int getViewId() {
219     return getId();
220   }
221 
222   @Override
onBindView(View view)223   public void onBindView(View view) {
224     TextView label = (TextView) view.findViewById(R.id.sud_items_title);
225     label.setText(getTitle());
226     if (isClickable != null) {
227       view.setClickable(isClickable);
228     }
229     TextView summaryView = (TextView) view.findViewById(R.id.sud_items_summary);
230     CharSequence summary = getSummary();
231     if (hasSummary(summary)) {
232       summaryView.setText(summary);
233       if (summaryView instanceof RichTextView tv) {
234         tv.setOnLinkClickListener(this);
235       }
236       summaryView.setVisibility(View.VISIBLE);
237     } else {
238       summaryView.setVisibility(View.GONE);
239     }
240 
241     view.setContentDescription(getContentDescription());
242 
243     final View iconContainer = view.findViewById(R.id.sud_items_icon_container);
244     final Drawable icon = getIcon();
245     if (icon != null) {
246       final ImageView iconView = (ImageView) view.findViewById(R.id.sud_items_icon);
247       // Set the image drawable to null before setting the state and level to avoid affecting
248       // any recycled drawable in the ImageView
249       iconView.setImageDrawable(null);
250       onMergeIconStateAndLevels(iconView, icon);
251       iconView.setImageDrawable(icon);
252       if (iconTint != Color.TRANSPARENT) {
253         iconView.setColorFilter(iconTint);
254       } else {
255         iconView.clearColorFilter();
256       }
257       LayoutParams layoutParams = iconContainer.getLayoutParams();
258       if (layoutParams instanceof LinearLayout.LayoutParams) {
259         ((LinearLayout.LayoutParams) layoutParams).gravity = iconGravity;
260       }
261       iconContainer.setVisibility(View.VISIBLE);
262     } else {
263       iconContainer.setVisibility(View.GONE);
264     }
265 
266     view.setId(getViewId());
267 
268     // ExpandableSwitchItem uses its child view to apply the style SudItemContainer. It is not
269     // possible to directly adjust the padding start/end of the item's layout here. It needs to
270     // get its child view to adjust it first, so skip the Layout padding adjustment.
271     // If the item view is a header layout, it doesn't need to adjust the layout padding start/end
272     // here. It will be adjusted by HeaderMixin.
273     // TODO: Add partner resource enable check
274     if (!(this instanceof ExpandableSwitchItem) && view.getId() != R.id.sud_layout_header) {
275       if (!PartnerConfigHelper.isGlifExpressiveEnabled(view.getContext())) {
276         LayoutStyler.applyPartnerCustomizationLayoutPaddingStyle(view);
277       }
278     }
279     ItemStyler.applyPartnerCustomizationItemStyle(view);
280   }
281 
282   /**
283    * Copies state and level information from {@link #getIcon()} to the currently bound view's
284    * ImageView. Subclasses can override this method to change whats being copied from the icon to
285    * the ImageView.
286    */
onMergeIconStateAndLevels(ImageView iconView, Drawable icon)287   protected void onMergeIconStateAndLevels(ImageView iconView, Drawable icon) {
288     iconView.setImageState(icon.getState(), false /* merge */);
289     iconView.setImageLevel(icon.getLevel());
290   }
291 
292   @Override
onLinkClick(LinkSpan span)293   public boolean onLinkClick(LinkSpan span) {
294     if (itemTextLinkClickListener != null) {
295       return itemTextLinkClickListener.onItemTextLinkClicked(span);
296     }
297     return false;
298   }
299 
300 }
301