1 /* 2 * Copyright (C) 2021 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.Spanned; 22 import android.text.TextUtils; 23 import android.text.style.AbsoluteSizeSpan; 24 import android.util.AttributeSet; 25 import android.view.View; 26 import android.widget.FrameLayout; 27 import android.widget.ImageView; 28 import android.widget.ProgressBar; 29 import android.widget.TextView; 30 31 import androidx.preference.Preference; 32 import androidx.preference.PreferenceViewHolder; 33 34 import java.util.regex.Matcher; 35 import java.util.regex.Pattern; 36 37 /** 38 * Progres bar preference with a usage summary and a total summary. 39 * This preference shows number in usage summary with enlarged font size. 40 */ 41 public class UsageProgressBarPreference extends Preference { 42 43 private final Pattern mNumberPattern = Pattern.compile("[\\d]*[\\٫.,]?[\\d]+"); 44 45 private CharSequence mUsageSummary; 46 private CharSequence mTotalSummary; 47 private CharSequence mBottomSummary; 48 private ImageView mCustomImageView; 49 private int mPercent = -1; 50 51 /** 52 * Perform inflation from XML and apply a class-specific base style. 53 * 54 * @param context The {@link Context} this is associated with, through which it can 55 * access the current theme, resources, {@link SharedPreferences}, etc. 56 * @param attrs The attributes of the XML tag that is inflating the preference 57 * @param defStyle An attribute in the current theme that contains a reference to a style 58 * resource that supplies default values for the view. Can be 0 to not 59 * look for defaults. 60 */ UsageProgressBarPreference(Context context, AttributeSet attrs, int defStyle)61 public UsageProgressBarPreference(Context context, AttributeSet attrs, int defStyle) { 62 super(context, attrs, defStyle); 63 setLayoutResource(R.layout.preference_usage_progress_bar); 64 } 65 66 /** 67 * Perform inflation from XML and apply a class-specific base style. 68 * 69 * @param context The {@link Context} this is associated with, through which it can 70 * access the current theme, resources, {@link SharedPreferences}, etc. 71 * @param attrs The attributes of the XML tag that is inflating the preference 72 */ UsageProgressBarPreference(Context context, AttributeSet attrs)73 public UsageProgressBarPreference(Context context, AttributeSet attrs) { 74 super(context, attrs); 75 setLayoutResource(R.layout.preference_usage_progress_bar); 76 } 77 78 /** 79 * Constructor to create a preference. 80 * 81 * @param context The Context this is associated with. 82 */ UsageProgressBarPreference(Context context)83 public UsageProgressBarPreference(Context context) { 84 this(context, null); 85 } 86 87 /** Set usage summary, number in the summary will show with enlarged font size. */ setUsageSummary(CharSequence usageSummary)88 public void setUsageSummary(CharSequence usageSummary) { 89 if (TextUtils.equals(mUsageSummary, usageSummary)) { 90 return; 91 } 92 mUsageSummary = usageSummary; 93 notifyChanged(); 94 } 95 96 /** Set total summary. */ setTotalSummary(CharSequence totalSummary)97 public void setTotalSummary(CharSequence totalSummary) { 98 if (TextUtils.equals(mTotalSummary, totalSummary)) { 99 return; 100 } 101 mTotalSummary = totalSummary; 102 notifyChanged(); 103 } 104 105 /** Set bottom summary. */ setBottomSummary(CharSequence bottomSummary)106 public void setBottomSummary(CharSequence bottomSummary) { 107 if (TextUtils.equals(mBottomSummary, bottomSummary)) { 108 return; 109 } 110 mBottomSummary = bottomSummary; 111 notifyChanged(); 112 } 113 114 /** Set percentage of the progress bar. */ setPercent(long usage, long total)115 public void setPercent(long usage, long total) { 116 if (usage > total) { 117 return; 118 } 119 if (total == 0L) { 120 if (mPercent != 0) { 121 mPercent = 0; 122 notifyChanged(); 123 } 124 return; 125 } 126 final int percent = (int) (usage / (double) total * 100); 127 if (mPercent == percent) { 128 return; 129 } 130 mPercent = percent; 131 notifyChanged(); 132 } 133 134 /** Set custom ImageView to the right side of total summary. */ setCustomContent(T imageView)135 public <T extends ImageView> void setCustomContent(T imageView) { 136 if (imageView == mCustomImageView) { 137 return; 138 } 139 mCustomImageView = imageView; 140 notifyChanged(); 141 } 142 143 /** 144 * Binds the created View to the data for this preference. 145 * 146 * <p>This is a good place to grab references to custom Views in the layout and set 147 * properties on them. 148 * 149 * <p>Make sure to call through to the superclass's implementation. 150 * 151 * @param holder The ViewHolder that provides references to the views to fill in. These views 152 * will be recycled, so you should not hold a reference to them after this method 153 * returns. 154 */ 155 @Override onBindViewHolder(PreferenceViewHolder holder)156 public void onBindViewHolder(PreferenceViewHolder holder) { 157 super.onBindViewHolder(holder); 158 159 holder.setDividerAllowedAbove(false); 160 holder.setDividerAllowedBelow(false); 161 162 final TextView usageSummary = (TextView) holder.findViewById(R.id.usage_summary); 163 usageSummary.setText(enlargeFontOfNumber(mUsageSummary)); 164 165 final TextView totalSummary = (TextView) holder.findViewById(R.id.total_summary); 166 if (mTotalSummary != null) { 167 totalSummary.setText(mTotalSummary); 168 } 169 170 final TextView bottomSummary = (TextView) holder.findViewById(R.id.bottom_summary); 171 if (TextUtils.isEmpty(mBottomSummary)) { 172 bottomSummary.setVisibility(View.GONE); 173 } else { 174 bottomSummary.setVisibility(View.VISIBLE); 175 bottomSummary.setText(mBottomSummary); 176 } 177 178 final ProgressBar progressBar = (ProgressBar) holder.findViewById(android.R.id.progress); 179 if (mPercent < 0) { 180 progressBar.setIndeterminate(true); 181 } else { 182 progressBar.setIndeterminate(false); 183 progressBar.setProgress(mPercent); 184 } 185 186 final FrameLayout customLayout = (FrameLayout) holder.findViewById(R.id.custom_content); 187 if (mCustomImageView == null) { 188 customLayout.removeAllViews(); 189 customLayout.setVisibility(View.GONE); 190 } else { 191 customLayout.removeAllViews(); 192 customLayout.addView(mCustomImageView); 193 customLayout.setVisibility(View.VISIBLE); 194 } 195 } 196 enlargeFontOfNumber(CharSequence summary)197 private CharSequence enlargeFontOfNumber(CharSequence summary) { 198 if (TextUtils.isEmpty(summary)) { 199 return ""; 200 } 201 202 final Matcher matcher = mNumberPattern.matcher(summary); 203 if (matcher.find()) { 204 final SpannableString spannableSummary = new SpannableString(summary); 205 spannableSummary.setSpan(new AbsoluteSizeSpan(64, true /* dip */), matcher.start(), 206 matcher.end(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); 207 return spannableSummary; 208 } 209 return summary; 210 } 211 } 212