• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 int mIconStartId;
60     private int mIconEndId;
61     private int mIconStartContentDescriptionId;
62     private 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     /** Set the start icon of the Seekbar. */
setIconStart(int iconStartId)114     public void setIconStart(int iconStartId) {
115         if (mIconStartId != iconStartId) {
116             mIconStartId = iconStartId;
117             notifyChanged();
118         }
119     }
120 
121     /** Set the description resource id of the start icon. */
setIconStartContentDescription(int iconStartContentDescriptionId)122     public void setIconStartContentDescription(int iconStartContentDescriptionId) {
123         if (mIconStartContentDescriptionId != iconStartContentDescriptionId) {
124             mIconStartContentDescriptionId = iconStartContentDescriptionId;
125             notifyChanged();
126         }
127     }
128 
129     /** Set the end icon of the Seekbar. */
setIconEnd(int iconEndId)130     public void setIconEnd(int iconEndId) {
131         if (mIconEndId != iconEndId) {
132             mIconEndId = iconEndId;
133             notifyChanged();
134         }
135     }
136 
137     /** Set the description resource id of the end icon. */
setIconEndContentDescription(int iconEndContentDescriptionId)138     public void setIconEndContentDescription(int iconEndContentDescriptionId) {
139         if (mIconEndContentDescriptionId != iconEndContentDescriptionId) {
140             mIconEndContentDescriptionId = iconEndContentDescriptionId;
141             notifyChanged();
142         }
143     }
144 
145     @Override
onBindViewHolder(PreferenceViewHolder holder)146     public void onBindViewHolder(PreferenceViewHolder holder) {
147         super.onBindViewHolder(holder);
148 
149         final TextView summaryView = (TextView) holder.findViewById(android.R.id.summary);
150         boolean isSummaryVisible = false;
151         if (summaryView != null) {
152             isSummaryVisible = (summaryView.getVisibility() == View.VISIBLE);
153         }
154         final TextView titleView = (TextView) holder.findViewById(android.R.id.title);
155         if (titleView != null && !isSelectable() && isEnabled() && isSummaryVisible) {
156             titleView.setTextColor(
157                     Utils.getColorAttr(getContext(), android.R.attr.textColorPrimary));
158         }
159 
160         final TextView startText = (TextView) holder.findViewById(android.R.id.text1);
161         if (mTextStartId > 0) {
162             startText.setText(mTextStartId);
163         }
164 
165         final TextView endText = (TextView) holder.findViewById(android.R.id.text2);
166         if (mTextEndId > 0) {
167             endText.setText(mTextEndId);
168         }
169 
170         final View labelFrame = holder.findViewById(R.id.label_frame);
171         final boolean isValidTextResIdExist = mTextStartId > 0 || mTextEndId > 0;
172         labelFrame.setVisibility(isValidTextResIdExist ? View.VISIBLE : View.GONE);
173 
174         mSeekBar = (SeekBar) holder.findViewById(com.android.internal.R.id.seekbar);
175         if (mTickMarkId != 0) {
176             final Drawable tickMark = getContext().getDrawable(mTickMarkId);
177             mSeekBar.setTickMark(tickMark);
178         }
179 
180         final ViewGroup iconStartFrame = (ViewGroup) holder.findViewById(R.id.icon_start_frame);
181         final ImageView iconStartView = (ImageView) holder.findViewById(R.id.icon_start);
182         updateIconStartIfNeeded(iconStartFrame, iconStartView, mSeekBar);
183 
184         final ViewGroup iconEndFrame = (ViewGroup) holder.findViewById(R.id.icon_end_frame);
185         final ImageView iconEndView = (ImageView) holder.findViewById(R.id.icon_end);
186         updateIconEndIfNeeded(iconEndFrame, iconEndView, mSeekBar);
187     }
188 
setOnPreferenceChangeStopListener(OnPreferenceChangeListener listener)189     public void setOnPreferenceChangeStopListener(OnPreferenceChangeListener listener) {
190         mStopListener = listener;
191     }
192 
193     @Override
onStartTrackingTouch(SeekBar seekBar)194     public void onStartTrackingTouch(SeekBar seekBar) {
195         super.onStartTrackingTouch(seekBar);
196 
197         if (mSeekBarChangeListener != null) {
198             mSeekBarChangeListener.onStartTrackingTouch(seekBar);
199         }
200     }
201 
202     @Override
onProgressChanged(SeekBar seekBar, int progress, boolean fromUser)203     public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
204         super.onProgressChanged(seekBar, progress, fromUser);
205 
206         if (mSeekBarChangeListener != null) {
207             mSeekBarChangeListener.onProgressChanged(seekBar, progress, fromUser);
208         }
209     }
210 
211     @Override
onStopTrackingTouch(SeekBar seekBar)212     public void onStopTrackingTouch(SeekBar seekBar) {
213         super.onStopTrackingTouch(seekBar);
214 
215         if (mSeekBarChangeListener != null) {
216             mSeekBarChangeListener.onStopTrackingTouch(seekBar);
217         }
218 
219         if (mStopListener != null) {
220             mStopListener.onPreferenceChange(this, seekBar.getProgress());
221         }
222 
223         // Need to update the icon enabled status
224         notifyChanged();
225     }
226 
setOnSeekBarChangeListener(SeekBar.OnSeekBarChangeListener seekBarChangeListener)227     public void setOnSeekBarChangeListener(SeekBar.OnSeekBarChangeListener seekBarChangeListener) {
228         mSeekBarChangeListener = seekBarChangeListener;
229     }
230 
updateIconStartIfNeeded(ViewGroup iconFrame, ImageView iconStart, SeekBar seekBar)231     private void updateIconStartIfNeeded(ViewGroup iconFrame, ImageView iconStart,
232             SeekBar seekBar) {
233         if (mIconStartId == 0) {
234             return;
235         }
236 
237         if (iconStart.getDrawable() == null) {
238             iconStart.setImageResource(mIconStartId);
239         }
240 
241         if (mIconStartContentDescriptionId != 0) {
242             final String contentDescription =
243                     iconFrame.getContext().getString(mIconStartContentDescriptionId);
244             iconFrame.setContentDescription(contentDescription);
245         }
246 
247         iconFrame.setOnClickListener((view) -> {
248             final int progress = getProgress();
249             if (progress > 0) {
250                 setProgress(progress - 1);
251             }
252         });
253 
254         iconFrame.setVisibility(View.VISIBLE);
255         setIconViewAndFrameEnabled(iconStart, seekBar.getProgress() > 0);
256     }
257 
updateIconEndIfNeeded(ViewGroup iconFrame, ImageView iconEnd, SeekBar seekBar)258     private void updateIconEndIfNeeded(ViewGroup iconFrame, ImageView iconEnd, SeekBar seekBar) {
259         if (mIconEndId == 0) {
260             return;
261         }
262 
263         if (iconEnd.getDrawable() == null) {
264             iconEnd.setImageResource(mIconEndId);
265         }
266 
267         if (mIconEndContentDescriptionId != 0) {
268             final String contentDescription =
269                     iconFrame.getContext().getString(mIconEndContentDescriptionId);
270             iconFrame.setContentDescription(contentDescription);
271         }
272 
273         iconFrame.setOnClickListener((view) -> {
274             final int progress = getProgress();
275             if (progress < getMax()) {
276                 setProgress(progress + 1);
277             }
278         });
279 
280         iconFrame.setVisibility(View.VISIBLE);
281         setIconViewAndFrameEnabled(iconEnd, seekBar.getProgress() < seekBar.getMax());
282     }
283 
setIconViewAndFrameEnabled(View iconView, boolean enabled)284     private static void setIconViewAndFrameEnabled(View iconView, boolean enabled) {
285         iconView.setEnabled(enabled);
286         final ViewGroup iconFrame = (ViewGroup) iconView.getParent();
287         iconFrame.setEnabled(enabled);
288     }
289 }
290 
291