1 /* 2 * Copyright (C) 2019 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.settings.widget; 18 19 import android.content.Context; 20 import android.content.res.TypedArray; 21 import android.graphics.drawable.Drawable; 22 import android.util.AttributeSet; 23 import android.view.View; 24 import android.view.ViewGroup; 25 import android.widget.ImageView; 26 import android.widget.SeekBar; 27 import android.widget.TextView; 28 29 import androidx.core.content.res.TypedArrayUtils; 30 import androidx.preference.PreferenceViewHolder; 31 32 import com.android.internal.util.Preconditions; 33 import com.android.settings.R; 34 import com.android.settings.Utils; 35 36 /** 37 * A labeled {@link SeekBarPreference} with left and right text label, icon label, or both. 38 * 39 * <p> 40 * The component provides the attribute usage below. 41 * <attr name="textStart" format="reference" /> 42 * <attr name="textEnd" format="reference" /> 43 * <attr name="tickMark" format="reference" /> 44 * <attr name="iconStart" format="reference" /> 45 * <attr name="iconEnd" format="reference" /> 46 * <attr name="iconStartContentDescription" format="reference" /> 47 * <attr name="iconEndContentDescription" format="reference" /> 48 * </p> 49 * 50 * <p> If you set the attribute values {@code iconStartContentDescription} or {@code 51 * iconEndContentDescription} from XML, you must also set the corresponding attributes {@code 52 * iconStart} or {@code iconEnd}, otherwise throws an {@link IllegalArgumentException}.</p> 53 */ 54 public class LabeledSeekBarPreference extends SeekBarPreference { 55 56 private final int mTextStartId; 57 private final int mTextEndId; 58 private final int mTickMarkId; 59 private final int mIconStartId; 60 private final int mIconEndId; 61 private final int mIconStartContentDescriptionId; 62 private final int mIconEndContentDescriptionId; 63 private OnPreferenceChangeListener mStopListener; 64 private SeekBar.OnSeekBarChangeListener mSeekBarChangeListener; 65 66 private SeekBar mSeekBar; 67 LabeledSeekBarPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)68 public LabeledSeekBarPreference(Context context, AttributeSet attrs, int defStyleAttr, 69 int defStyleRes) { 70 71 super(context, attrs, defStyleAttr, defStyleRes); 72 setLayoutResource(R.layout.preference_labeled_slider); 73 74 final TypedArray styledAttrs = context.obtainStyledAttributes(attrs, 75 R.styleable.LabeledSeekBarPreference); 76 mTextStartId = styledAttrs.getResourceId( 77 R.styleable.LabeledSeekBarPreference_textStart, /* defValue= */ 0); 78 mTextEndId = styledAttrs.getResourceId( 79 R.styleable.LabeledSeekBarPreference_textEnd, /* defValue= */ 0); 80 mTickMarkId = styledAttrs.getResourceId( 81 R.styleable.LabeledSeekBarPreference_tickMark, /* defValue= */ 0); 82 mIconStartId = styledAttrs.getResourceId( 83 R.styleable.LabeledSeekBarPreference_iconStart, /* defValue= */ 0); 84 mIconEndId = styledAttrs.getResourceId( 85 R.styleable.LabeledSeekBarPreference_iconEnd, /* defValue= */ 0); 86 87 mIconStartContentDescriptionId = styledAttrs.getResourceId( 88 R.styleable.LabeledSeekBarPreference_iconStartContentDescription, 89 /* defValue= */ 0); 90 Preconditions.checkArgument(!(mIconStartContentDescriptionId != 0 && mIconStartId == 0), 91 "The resource of the iconStart attribute may be invalid or not set, " 92 + "you should set the iconStart attribute and have the valid resource."); 93 94 mIconEndContentDescriptionId = styledAttrs.getResourceId( 95 R.styleable.LabeledSeekBarPreference_iconEndContentDescription, 96 /* defValue= */ 0); 97 Preconditions.checkArgument(!(mIconEndContentDescriptionId != 0 && mIconEndId == 0), 98 "The resource of the iconEnd attribute may be invalid or not set, " 99 + "you should set the iconEnd attribute and have the valid resource."); 100 styledAttrs.recycle(); 101 } 102 LabeledSeekBarPreference(Context context, AttributeSet attrs)103 public LabeledSeekBarPreference(Context context, AttributeSet attrs) { 104 this(context, attrs, TypedArrayUtils.getAttr(context, 105 androidx.preference.R.attr.seekBarPreferenceStyle, 106 com.android.internal.R.attr.seekBarPreferenceStyle), 0); 107 } 108 getSeekbar()109 public SeekBar getSeekbar() { 110 return mSeekBar; 111 } 112 113 @Override onBindViewHolder(PreferenceViewHolder holder)114 public void onBindViewHolder(PreferenceViewHolder holder) { 115 super.onBindViewHolder(holder); 116 117 final TextView summaryView = (TextView) holder.findViewById(android.R.id.summary); 118 boolean isSummaryVisible = false; 119 if (summaryView != null) { 120 isSummaryVisible = (summaryView.getVisibility() == View.VISIBLE); 121 } 122 final TextView titleView = (TextView) holder.findViewById(android.R.id.title); 123 if (titleView != null && !isSelectable() && isEnabled() && isSummaryVisible) { 124 titleView.setTextColor( 125 Utils.getColorAttr(getContext(), android.R.attr.textColorPrimary)); 126 } 127 128 final TextView startText = (TextView) holder.findViewById(android.R.id.text1); 129 if (mTextStartId > 0) { 130 startText.setText(mTextStartId); 131 } 132 133 final TextView endText = (TextView) holder.findViewById(android.R.id.text2); 134 if (mTextEndId > 0) { 135 endText.setText(mTextEndId); 136 } 137 138 final View labelFrame = holder.findViewById(R.id.label_frame); 139 final boolean isValidTextResIdExist = mTextStartId > 0 || mTextEndId > 0; 140 labelFrame.setVisibility(isValidTextResIdExist ? View.VISIBLE : View.GONE); 141 142 mSeekBar = (SeekBar) holder.findViewById(com.android.internal.R.id.seekbar); 143 if (mTickMarkId != 0) { 144 final Drawable tickMark = getContext().getDrawable(mTickMarkId); 145 mSeekBar.setTickMark(tickMark); 146 } 147 148 final ViewGroup iconStartFrame = (ViewGroup) holder.findViewById(R.id.icon_start_frame); 149 final ImageView iconStartView = (ImageView) holder.findViewById(R.id.icon_start); 150 updateIconStartIfNeeded(iconStartFrame, iconStartView, mSeekBar); 151 152 final ViewGroup iconEndFrame = (ViewGroup) holder.findViewById(R.id.icon_end_frame); 153 final ImageView iconEndView = (ImageView) holder.findViewById(R.id.icon_end); 154 updateIconEndIfNeeded(iconEndFrame, iconEndView, mSeekBar); 155 } 156 setOnPreferenceChangeStopListener(OnPreferenceChangeListener listener)157 public void setOnPreferenceChangeStopListener(OnPreferenceChangeListener listener) { 158 mStopListener = listener; 159 } 160 161 @Override onStartTrackingTouch(SeekBar seekBar)162 public void onStartTrackingTouch(SeekBar seekBar) { 163 super.onStartTrackingTouch(seekBar); 164 165 if (mSeekBarChangeListener != null) { 166 mSeekBarChangeListener.onStartTrackingTouch(seekBar); 167 } 168 } 169 170 @Override onProgressChanged(SeekBar seekBar, int progress, boolean fromUser)171 public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { 172 super.onProgressChanged(seekBar, progress, fromUser); 173 174 if (mSeekBarChangeListener != null) { 175 mSeekBarChangeListener.onProgressChanged(seekBar, progress, fromUser); 176 } 177 } 178 179 @Override onStopTrackingTouch(SeekBar seekBar)180 public void onStopTrackingTouch(SeekBar seekBar) { 181 super.onStopTrackingTouch(seekBar); 182 183 if (mSeekBarChangeListener != null) { 184 mSeekBarChangeListener.onStopTrackingTouch(seekBar); 185 } 186 187 if (mStopListener != null) { 188 mStopListener.onPreferenceChange(this, seekBar.getProgress()); 189 } 190 191 // Need to update the icon enabled status 192 notifyChanged(); 193 } 194 setOnSeekBarChangeListener(SeekBar.OnSeekBarChangeListener seekBarChangeListener)195 public void setOnSeekBarChangeListener(SeekBar.OnSeekBarChangeListener seekBarChangeListener) { 196 mSeekBarChangeListener = seekBarChangeListener; 197 } 198 updateIconStartIfNeeded(ViewGroup iconFrame, ImageView iconStart, SeekBar seekBar)199 private void updateIconStartIfNeeded(ViewGroup iconFrame, ImageView iconStart, 200 SeekBar seekBar) { 201 if (mIconStartId == 0) { 202 return; 203 } 204 205 if (iconStart.getDrawable() == null) { 206 iconStart.setImageResource(mIconStartId); 207 } 208 209 if (mIconStartContentDescriptionId != 0) { 210 final String contentDescription = 211 iconFrame.getContext().getString(mIconStartContentDescriptionId); 212 iconFrame.setContentDescription(contentDescription); 213 } 214 215 iconFrame.setOnClickListener((view) -> { 216 final int progress = getProgress(); 217 if (progress > 0) { 218 setProgress(progress - 1); 219 } 220 }); 221 222 iconFrame.setVisibility(View.VISIBLE); 223 setIconViewAndFrameEnabled(iconStart, seekBar.getProgress() > 0); 224 } 225 updateIconEndIfNeeded(ViewGroup iconFrame, ImageView iconEnd, SeekBar seekBar)226 private void updateIconEndIfNeeded(ViewGroup iconFrame, ImageView iconEnd, SeekBar seekBar) { 227 if (mIconEndId == 0) { 228 return; 229 } 230 231 if (iconEnd.getDrawable() == null) { 232 iconEnd.setImageResource(mIconEndId); 233 } 234 235 if (mIconEndContentDescriptionId != 0) { 236 final String contentDescription = 237 iconFrame.getContext().getString(mIconEndContentDescriptionId); 238 iconFrame.setContentDescription(contentDescription); 239 } 240 241 iconFrame.setOnClickListener((view) -> { 242 final int progress = getProgress(); 243 if (progress < getMax()) { 244 setProgress(progress + 1); 245 } 246 }); 247 248 iconFrame.setVisibility(View.VISIBLE); 249 setIconViewAndFrameEnabled(iconEnd, seekBar.getProgress() < seekBar.getMax()); 250 } 251 setIconViewAndFrameEnabled(View iconView, boolean enabled)252 private static void setIconViewAndFrameEnabled(View iconView, boolean enabled) { 253 iconView.setEnabled(enabled); 254 final ViewGroup iconFrame = (ViewGroup) iconView.getParent(); 255 iconFrame.setEnabled(enabled); 256 } 257 } 258 259