1 /* 2 * Copyright (C) 2020 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.android.settingslib.widget; 18 19 import android.content.Context; 20 import android.text.SpannableString; 21 import android.text.TextUtils; 22 import android.text.style.URLSpan; 23 import android.util.AttributeSet; 24 import android.view.View; 25 import android.widget.TextView; 26 27 import androidx.annotation.NonNull; 28 import androidx.annotation.StringRes; 29 import androidx.annotation.VisibleForTesting; 30 import androidx.preference.Preference; 31 import androidx.preference.PreferenceViewHolder; 32 33 /** 34 * A custom preference acting as "footer" of a page. It has a field for icon and text. It is added 35 * to screen as the last preference. 36 */ 37 public class FooterPreference extends Preference { 38 39 public static final String KEY_FOOTER = "footer_preference"; 40 static final int ORDER_FOOTER = Integer.MAX_VALUE - 1; 41 @VisibleForTesting 42 View.OnClickListener mLearnMoreListener; 43 @VisibleForTesting 44 int mIconVisibility = View.VISIBLE; 45 private CharSequence mContentDescription; 46 private CharSequence mLearnMoreText; 47 private FooterLearnMoreSpan mLearnMoreSpan; 48 FooterPreference(Context context, AttributeSet attrs)49 public FooterPreference(Context context, AttributeSet attrs) { 50 super(context, attrs, R.attr.footerPreferenceStyle); 51 init(); 52 } 53 FooterPreference(Context context)54 public FooterPreference(Context context) { 55 this(context, null); 56 } 57 58 @Override onBindViewHolder(PreferenceViewHolder holder)59 public void onBindViewHolder(PreferenceViewHolder holder) { 60 super.onBindViewHolder(holder); 61 TextView title = holder.itemView.findViewById(android.R.id.title); 62 if (title != null && !TextUtils.isEmpty(mContentDescription)) { 63 title.setContentDescription(mContentDescription); 64 } 65 66 TextView learnMore = holder.itemView.findViewById(R.id.settingslib_learn_more); 67 if (learnMore != null) { 68 if (mLearnMoreListener != null) { 69 learnMore.setVisibility(View.VISIBLE); 70 if (TextUtils.isEmpty(mLearnMoreText)) { 71 mLearnMoreText = learnMore.getText(); 72 } else { 73 learnMore.setText(mLearnMoreText); 74 } 75 SpannableString learnMoreText = new SpannableString(mLearnMoreText); 76 if (mLearnMoreSpan != null) { 77 learnMoreText.removeSpan(mLearnMoreSpan); 78 } 79 mLearnMoreSpan = new FooterLearnMoreSpan(mLearnMoreListener); 80 learnMoreText.setSpan(mLearnMoreSpan, 0, 81 learnMoreText.length(), 0); 82 learnMore.setText(learnMoreText); 83 } else { 84 learnMore.setVisibility(View.GONE); 85 } 86 } 87 88 View icon = holder.itemView.findViewById(R.id.icon_frame); 89 if (icon != null) { 90 icon.setVisibility(mIconVisibility); 91 } 92 } 93 94 @Override setSummary(CharSequence summary)95 public void setSummary(CharSequence summary) { 96 setTitle(summary); 97 } 98 99 @Override setSummary(int summaryResId)100 public void setSummary(int summaryResId) { 101 setTitle(summaryResId); 102 } 103 104 @Override getSummary()105 public CharSequence getSummary() { 106 return getTitle(); 107 } 108 109 /** 110 * To set content description of the {@link FooterPreference}. This can use for talkback 111 * environment if developer wants to have a customization content. 112 * 113 * @param contentDescription The resource id of the content description. 114 */ setContentDescription(CharSequence contentDescription)115 public void setContentDescription(CharSequence contentDescription) { 116 if (!TextUtils.equals(mContentDescription, contentDescription)) { 117 mContentDescription = contentDescription; 118 notifyChanged(); 119 } 120 } 121 122 /** 123 * Return the content description of footer preference. 124 */ 125 @VisibleForTesting getContentDescription()126 CharSequence getContentDescription() { 127 return mContentDescription; 128 } 129 130 /** 131 * Sets the learn more text. 132 * 133 * @param learnMoreText The string of the learn more text. 134 */ setLearnMoreText(CharSequence learnMoreText)135 public void setLearnMoreText(CharSequence learnMoreText) { 136 if (!TextUtils.equals(mLearnMoreText, learnMoreText)) { 137 mLearnMoreText = learnMoreText; 138 notifyChanged(); 139 } 140 } 141 142 /** 143 * Assign an action for the learn more link. 144 */ setLearnMoreAction(View.OnClickListener listener)145 public void setLearnMoreAction(View.OnClickListener listener) { 146 if (mLearnMoreListener != listener) { 147 mLearnMoreListener = listener; 148 notifyChanged(); 149 } 150 } 151 152 /** 153 * Set visibility of footer icon. 154 */ setIconVisibility(int iconVisibility)155 public void setIconVisibility(int iconVisibility) { 156 if (mIconVisibility == iconVisibility) { 157 return; 158 } 159 mIconVisibility = iconVisibility; 160 notifyChanged(); 161 } 162 init()163 private void init() { 164 setLayoutResource(R.layout.preference_footer); 165 if (getIcon() == null) { 166 setIcon(R.drawable.settingslib_ic_info_outline_24); 167 } 168 setOrder(ORDER_FOOTER); 169 if (TextUtils.isEmpty(getKey())) { 170 setKey(KEY_FOOTER); 171 } 172 setSelectable(false); 173 } 174 175 /** 176 * The builder is convenient to creat a dynamic FooterPreference. 177 */ 178 public static class Builder { 179 private Context mContext; 180 private String mKey; 181 private CharSequence mTitle; 182 private CharSequence mContentDescription; 183 private CharSequence mLearnMoreText; 184 Builder(@onNull Context context)185 public Builder(@NonNull Context context) { 186 mContext = context; 187 } 188 189 /** 190 * To set the key value of the {@link FooterPreference}. 191 * 192 * @param key The key value. 193 */ setKey(@onNull String key)194 public Builder setKey(@NonNull String key) { 195 mKey = key; 196 return this; 197 } 198 199 /** 200 * To set the title of the {@link FooterPreference}. 201 * 202 * @param title The title. 203 */ setTitle(CharSequence title)204 public Builder setTitle(CharSequence title) { 205 mTitle = title; 206 return this; 207 } 208 209 /** 210 * To set the title of the {@link FooterPreference}. 211 * 212 * @param titleResId The resource id of the title. 213 */ setTitle(@tringRes int titleResId)214 public Builder setTitle(@StringRes int titleResId) { 215 mTitle = mContext.getText(titleResId); 216 return this; 217 } 218 219 /** 220 * To set content description of the {@link FooterPreference}. This can use for talkback 221 * environment if developer wants to have a customization content. 222 * 223 * @param contentDescription The resource id of the content description. 224 */ setContentDescription(CharSequence contentDescription)225 public Builder setContentDescription(CharSequence contentDescription) { 226 mContentDescription = contentDescription; 227 return this; 228 } 229 230 /** 231 * To set content description of the {@link FooterPreference}. This can use for talkback 232 * environment if developer wants to have a customization content. 233 * 234 * @param contentDescriptionResId The resource id of the content description. 235 */ setContentDescription(@tringRes int contentDescriptionResId)236 public Builder setContentDescription(@StringRes int contentDescriptionResId) { 237 mContentDescription = mContext.getText(contentDescriptionResId); 238 return this; 239 } 240 241 /** 242 * To set learn more string of the learn more text. This can use for talkback 243 * environment if developer wants to have a customization content. 244 * 245 * @param learnMoreText The resource id of the learn more string. 246 */ setLearnMoreText(CharSequence learnMoreText)247 public Builder setLearnMoreText(CharSequence learnMoreText) { 248 mLearnMoreText = learnMoreText; 249 return this; 250 } 251 252 /** 253 * To set learn more string of the {@link FooterPreference}. This can use for talkback 254 * environment if developer wants to have a customization content. 255 * 256 * @param learnMoreTextResId The resource id of the learn more string. 257 */ setLearnMoreText(@tringRes int learnMoreTextResId)258 public Builder setLearnMoreText(@StringRes int learnMoreTextResId) { 259 mLearnMoreText = mContext.getText(learnMoreTextResId); 260 return this; 261 } 262 263 264 /** 265 * To generate the {@link FooterPreference}. 266 */ build()267 public FooterPreference build() { 268 final FooterPreference footerPreference = new FooterPreference(mContext); 269 footerPreference.setSelectable(false); 270 if (TextUtils.isEmpty(mTitle)) { 271 throw new IllegalArgumentException("Footer title cannot be empty!"); 272 } 273 footerPreference.setTitle(mTitle); 274 if (!TextUtils.isEmpty(mKey)) { 275 footerPreference.setKey(mKey); 276 } 277 278 if (!TextUtils.isEmpty(mContentDescription)) { 279 footerPreference.setContentDescription(mContentDescription); 280 } 281 282 if (!TextUtils.isEmpty(mLearnMoreText)) { 283 footerPreference.setLearnMoreText(mLearnMoreText); 284 } 285 return footerPreference; 286 } 287 } 288 289 /** 290 * A {@link URLSpan} that opens a support page when clicked 291 */ 292 static class FooterLearnMoreSpan extends URLSpan { 293 294 private final View.OnClickListener mClickListener; 295 FooterLearnMoreSpan(View.OnClickListener clickListener)296 FooterLearnMoreSpan(View.OnClickListener clickListener) { 297 // sets the url to empty string so we can prevent any other span processing from 298 // clearing things we need in this string. 299 super(""); 300 mClickListener = clickListener; 301 } 302 303 @Override onClick(View widget)304 public void onClick(View widget) { 305 if (mClickListener != null) { 306 mClickListener.onClick(widget); 307 } 308 } 309 } 310 } 311