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.systemui.media.dialog;
18 
19 import static com.android.systemui.media.dialog.MediaOutputSeekbar.VOLUME_PERCENTAGE_SCALE_SIZE;
20 
21 import android.animation.Animator;
22 import android.animation.ValueAnimator;
23 import android.annotation.DrawableRes;
24 import android.app.WallpaperColors;
25 import android.content.Context;
26 import android.content.res.ColorStateList;
27 import android.graphics.Typeface;
28 import android.graphics.drawable.ClipDrawable;
29 import android.graphics.drawable.Drawable;
30 import android.graphics.drawable.GradientDrawable;
31 import android.graphics.drawable.Icon;
32 import android.graphics.drawable.LayerDrawable;
33 import android.text.TextUtils;
34 import android.view.LayoutInflater;
35 import android.view.View;
36 import android.view.ViewGroup;
37 import android.view.animation.LinearInterpolator;
38 import android.widget.CheckBox;
39 import android.widget.FrameLayout;
40 import android.widget.ImageView;
41 import android.widget.LinearLayout;
42 import android.widget.ProgressBar;
43 import android.widget.SeekBar;
44 import android.widget.TextView;
45 
46 import androidx.annotation.NonNull;
47 import androidx.annotation.VisibleForTesting;
48 import androidx.recyclerview.widget.RecyclerView;
49 
50 import com.android.settingslib.media.MediaDevice;
51 import com.android.settingslib.utils.ThreadUtils;
52 import com.android.systemui.res.R;
53 
54 import java.util.List;
55 
56 /**
57  * Base adapter for media output dialog.
58  */
59 public abstract class MediaOutputBaseAdapter extends
60         RecyclerView.Adapter<RecyclerView.ViewHolder> {
61 
62     static final int CUSTOMIZED_ITEM_PAIR_NEW = 1;
63     static final int CUSTOMIZED_ITEM_GROUP = 2;
64     static final int CUSTOMIZED_ITEM_DYNAMIC_GROUP = 3;
65 
66     protected final MediaOutputController mController;
67 
68     private static final int UNMUTE_DEFAULT_VOLUME = 2;
69 
70     Context mContext;
71     View mHolderView;
72     boolean mIsDragging;
73     int mCurrentActivePosition;
74     private boolean mIsInitVolumeFirstTime;
75 
MediaOutputBaseAdapter(MediaOutputController controller)76     public MediaOutputBaseAdapter(MediaOutputController controller) {
77         mController = controller;
78         mIsDragging = false;
79         mCurrentActivePosition = -1;
80         mIsInitVolumeFirstTime = true;
81     }
82 
83     /**
84      * Refresh current dataset
85      */
updateItems()86     public abstract void updateItems();
87 
88     @Override
onCreateViewHolder(@onNull ViewGroup viewGroup, int viewType)89     public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup,
90             int viewType) {
91         mContext = viewGroup.getContext();
92         mHolderView = LayoutInflater.from(mContext).inflate(MediaItem.getMediaLayoutId(viewType),
93                 viewGroup, false);
94 
95         return null;
96     }
97 
updateColorScheme(WallpaperColors wallpaperColors, boolean isDarkTheme)98     void updateColorScheme(WallpaperColors wallpaperColors, boolean isDarkTheme) {
99         mController.setCurrentColorScheme(wallpaperColors, isDarkTheme);
100     }
101 
getItemTitle(MediaDevice device)102     CharSequence getItemTitle(MediaDevice device) {
103         return device.getName();
104     }
105 
isCurrentlyConnected(MediaDevice device)106     boolean isCurrentlyConnected(MediaDevice device) {
107         return TextUtils.equals(device.getId(),
108                 mController.getCurrentConnectedMediaDevice().getId())
109                 || (mController.getSelectedMediaDevice().size() == 1
110                 && isDeviceIncluded(mController.getSelectedMediaDevice(), device));
111     }
112 
isDeviceIncluded(List<MediaDevice> deviceList, MediaDevice targetDevice)113     boolean isDeviceIncluded(List<MediaDevice> deviceList, MediaDevice targetDevice) {
114         for (MediaDevice device : deviceList) {
115             if (TextUtils.equals(device.getId(), targetDevice.getId())) {
116                 return true;
117             }
118         }
119         return false;
120     }
121 
isDragging()122     boolean isDragging() {
123         return mIsDragging;
124     }
125 
getCurrentActivePosition()126     int getCurrentActivePosition() {
127         return mCurrentActivePosition;
128     }
129 
getController()130     public MediaOutputController getController() {
131         return mController;
132     }
133 
134     /**
135      * ViewHolder for binding device view.
136      */
137     abstract class MediaDeviceBaseViewHolder extends RecyclerView.ViewHolder {
138 
139         private static final int ANIM_DURATION = 500;
140 
141         final ViewGroup mContainerLayout;
142         final FrameLayout mItemLayout;
143         final FrameLayout mIconAreaLayout;
144         final TextView mTitleText;
145         final TextView mTwoLineTitleText;
146         final TextView mSubTitleText;
147         final TextView mVolumeValueText;
148         final ImageView mTitleIcon;
149         final ProgressBar mProgressBar;
150         final LinearLayout mTwoLineLayout;
151         final ImageView mStatusIcon;
152         final CheckBox mCheckBox;
153         final ViewGroup mEndTouchArea;
154         final ImageView mEndClickIcon;
155         @VisibleForTesting
156         MediaOutputSeekbar mSeekBar;
157         private String mDeviceId;
158         private ValueAnimator mCornerAnimator;
159         private ValueAnimator mVolumeAnimator;
160         private int mLatestUpdateVolume = -1;
161 
MediaDeviceBaseViewHolder(View view)162         MediaDeviceBaseViewHolder(View view) {
163             super(view);
164             mContainerLayout = view.requireViewById(R.id.device_container);
165             mItemLayout = view.requireViewById(R.id.item_layout);
166             mTitleText = view.requireViewById(R.id.title);
167             mSubTitleText = view.requireViewById(R.id.subtitle);
168             mTwoLineLayout = view.requireViewById(R.id.two_line_layout);
169             mTwoLineTitleText = view.requireViewById(R.id.two_line_title);
170             mTitleIcon = view.requireViewById(R.id.title_icon);
171             mProgressBar = view.requireViewById(R.id.volume_indeterminate_progress);
172             mSeekBar = view.requireViewById(R.id.volume_seekbar);
173             mStatusIcon = view.requireViewById(R.id.media_output_item_status);
174             mCheckBox = view.requireViewById(R.id.check_box);
175             mEndTouchArea = view.requireViewById(R.id.end_action_area);
176             mEndClickIcon = view.requireViewById(R.id.media_output_item_end_click_icon);
177             mVolumeValueText = view.requireViewById(R.id.volume_value);
178             mIconAreaLayout = view.requireViewById(R.id.icon_area);
179             initAnimator();
180         }
181 
onBind(MediaDevice device, int position)182         void onBind(MediaDevice device, int position) {
183             mDeviceId = device.getId();
184             mCheckBox.setVisibility(View.GONE);
185             mStatusIcon.setVisibility(View.GONE);
186             mEndTouchArea.setVisibility(View.GONE);
187             mEndTouchArea.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
188             mContainerLayout.setOnClickListener(null);
189             mContainerLayout.setContentDescription(null);
190             mTitleText.setTextColor(mController.getColorItemContent());
191             mSubTitleText.setTextColor(mController.getColorItemContent());
192             mSubTitleText.setSelected(true);
193             mTwoLineTitleText.setTextColor(mController.getColorItemContent());
194             mVolumeValueText.setTextColor(mController.getColorItemContent());
195             mSeekBar.setProgressTintList(
196                     ColorStateList.valueOf(mController.getColorSeekbarProgress()));
197         }
198 
onBind(int customizedItem)199         abstract void onBind(int customizedItem);
200 
setSingleLineLayout(CharSequence title)201         void setSingleLineLayout(CharSequence title) {
202             setSingleLineLayout(title, false, false, false, false);
203         }
204 
setSingleLineLayout(CharSequence title, boolean showSeekBar, boolean showProgressBar, boolean showCheckBox, boolean showEndTouchArea)205         void setSingleLineLayout(CharSequence title, boolean showSeekBar,
206                 boolean showProgressBar, boolean showCheckBox, boolean showEndTouchArea) {
207             mTwoLineLayout.setVisibility(View.GONE);
208             boolean isActive = showSeekBar || showProgressBar;
209             if (!mCornerAnimator.isRunning()) {
210                 final Drawable backgroundDrawable =
211                         showSeekBar
212                                 ? mContext.getDrawable(
213                                         R.drawable.media_output_item_background_active)
214                                 .mutate() : mContext.getDrawable(
215                                         R.drawable.media_output_item_background)
216                                 .mutate();
217                 mItemLayout.setBackground(backgroundDrawable);
218                 if (showSeekBar) {
219                     updateSeekbarProgressBackground();
220                 }
221             }
222             mItemLayout.setBackgroundTintList(
223                     ColorStateList.valueOf(isActive ? mController.getColorConnectedItemBackground()
224                             : mController.getColorItemBackground()));
225             mIconAreaLayout.setBackgroundTintList(
226                     ColorStateList.valueOf(showSeekBar ? mController.getColorSeekbarProgress()
227                             : showProgressBar ? mController.getColorConnectedItemBackground()
228                                     : mController.getColorItemBackground()));
229             mProgressBar.setVisibility(showProgressBar ? View.VISIBLE : View.GONE);
230             mSeekBar.setAlpha(1);
231             mSeekBar.setVisibility(showSeekBar ? View.VISIBLE : View.GONE);
232             if (!showSeekBar) {
233                 mSeekBar.resetVolume();
234             }
235             mTitleText.setText(title);
236             mTitleText.setVisibility(View.VISIBLE);
237             mCheckBox.setVisibility(showCheckBox ? View.VISIBLE : View.GONE);
238             mEndTouchArea.setVisibility(showEndTouchArea ? View.VISIBLE : View.GONE);
239             ViewGroup.MarginLayoutParams params =
240                     (ViewGroup.MarginLayoutParams) mItemLayout.getLayoutParams();
241             params.rightMargin = showEndTouchArea ? mController.getItemMarginEndSelectable()
242                     : mController.getItemMarginEndDefault();
243             mTitleIcon.setBackgroundTintList(
244                     ColorStateList.valueOf(mController.getColorItemContent()));
245         }
246 
setTwoLineLayout(MediaDevice device, boolean bFocused, boolean showSeekBar, boolean showProgressBar, boolean showSubtitle, boolean showStatus, boolean isFakeActive)247         void setTwoLineLayout(MediaDevice device, boolean bFocused, boolean showSeekBar,
248                 boolean showProgressBar, boolean showSubtitle, boolean showStatus,
249                 boolean isFakeActive) {
250             setTwoLineLayout(device, null, bFocused, showSeekBar, showProgressBar, showSubtitle,
251                     showStatus, false, isFakeActive);
252         }
253 
setTwoLineLayout(MediaDevice device, CharSequence title, boolean bFocused, boolean showSeekBar, boolean showProgressBar, boolean showSubtitle, boolean showStatus , boolean showEndTouchArea, boolean isFakeActive)254         void setTwoLineLayout(MediaDevice device, CharSequence title, boolean bFocused,
255                 boolean showSeekBar, boolean showProgressBar, boolean showSubtitle,
256                 boolean showStatus , boolean showEndTouchArea, boolean isFakeActive) {
257             mTitleText.setVisibility(View.GONE);
258             mTwoLineLayout.setVisibility(View.VISIBLE);
259             mStatusIcon.setVisibility(showStatus ? View.VISIBLE : View.GONE);
260             mSeekBar.setAlpha(1);
261             mSeekBar.setVisibility(showSeekBar ? View.VISIBLE : View.GONE);
262             final Drawable backgroundDrawable;
263             backgroundDrawable = mContext.getDrawable(
264                     showSeekBar || isFakeActive ? R.drawable.media_output_item_background_active
265                             : R.drawable.media_output_item_background).mutate();
266             mItemLayout.setBackgroundTintList(ColorStateList.valueOf(
267                     showSeekBar || isFakeActive ? mController.getColorConnectedItemBackground()
268                             : mController.getColorItemBackground()
269             ));
270             mIconAreaLayout.setBackgroundTintList(
271                     ColorStateList.valueOf(showProgressBar || isFakeActive
272                             ? mController.getColorConnectedItemBackground()
273                             : showSeekBar ? mController.getColorSeekbarProgress()
274                                     : mController.getColorItemBackground()));
275             if (showSeekBar) {
276                 updateSeekbarProgressBackground();
277             }
278             //update end click area by isActive
279             mEndTouchArea.setVisibility(showEndTouchArea ? View.VISIBLE : View.GONE);
280             mEndClickIcon.setVisibility(showEndTouchArea ? View.VISIBLE : View.GONE);
281             ViewGroup.MarginLayoutParams params =
282                     (ViewGroup.MarginLayoutParams) mItemLayout.getLayoutParams();
283             params.rightMargin = showEndTouchArea ? mController.getItemMarginEndSelectable()
284                     : mController.getItemMarginEndDefault();
285             mItemLayout.setBackground(backgroundDrawable);
286             mProgressBar.setVisibility(showProgressBar ? View.VISIBLE : View.GONE);
287             mSubTitleText.setVisibility(showSubtitle ? View.VISIBLE : View.GONE);
288             mTwoLineTitleText.setTranslationY(0);
289             mTwoLineTitleText.setText(device == null ? title : getItemTitle(device));
290             mTwoLineTitleText.setTypeface(Typeface.create(mContext.getString(
291                             bFocused ? com.android.internal.R.string.config_headlineFontFamilyMedium
292                                     : com.android.internal.R.string.config_headlineFontFamily),
293                     Typeface.NORMAL));
294         }
295 
updateSeekbarProgressBackground()296         void updateSeekbarProgressBackground() {
297             final ClipDrawable clipDrawable =
298                     (ClipDrawable) ((LayerDrawable) mSeekBar.getProgressDrawable())
299                             .findDrawableByLayerId(android.R.id.progress);
300             final GradientDrawable progressDrawable =
301                     (GradientDrawable) clipDrawable.getDrawable();
302             progressDrawable.setCornerRadii(
303                     new float[]{0, 0, mController.getActiveRadius(),
304                             mController.getActiveRadius(),
305                             mController.getActiveRadius(),
306                             mController.getActiveRadius(), 0, 0});
307         }
308 
initSeekbar(MediaDevice device, boolean isCurrentSeekbarInvisible)309         void initSeekbar(MediaDevice device, boolean isCurrentSeekbarInvisible) {
310             if (!mController.isVolumeControlEnabled(device)) {
311                 disableSeekBar();
312             } else {
313                 enableSeekBar(device);
314             }
315             mSeekBar.setMaxVolume(device.getMaxVolume());
316             final int currentVolume = device.getCurrentVolume();
317             if (!mIsDragging) {
318                 if (mSeekBar.getVolume() != currentVolume && (mLatestUpdateVolume == -1
319                         || currentVolume == mLatestUpdateVolume)) {
320                     // Update only if volume of device and value of volume bar doesn't match.
321                     // Check if response volume match with the latest request, to ignore obsolete
322                     // response
323                     if (isCurrentSeekbarInvisible && !mIsInitVolumeFirstTime) {
324                         updateTitleIcon(currentVolume == 0 ? R.drawable.media_output_icon_volume_off
325                                         : R.drawable.media_output_icon_volume,
326                                 mController.getColorItemContent());
327                     } else {
328                         if (!mVolumeAnimator.isStarted()) {
329                             int percentage =
330                                     (int) ((double) currentVolume * VOLUME_PERCENTAGE_SCALE_SIZE
331                                             / (double) mSeekBar.getMax());
332                             if (percentage == 0) {
333                                 updateMutedVolumeIcon();
334                             } else {
335                                 updateUnmutedVolumeIcon();
336                             }
337                             mSeekBar.setVolume(currentVolume);
338                             mLatestUpdateVolume = -1;
339                         }
340                     }
341                 } else if (currentVolume == 0) {
342                     mSeekBar.resetVolume();
343                     updateMutedVolumeIcon();
344                 }
345                 if (currentVolume == mLatestUpdateVolume) {
346                     mLatestUpdateVolume = -1;
347                 }
348             }
349             if (mIsInitVolumeFirstTime) {
350                 mIsInitVolumeFirstTime = false;
351             }
352             mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
353                 boolean mStartFromMute = false;
354                 @Override
355                 public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
356                     if (device == null || !fromUser) {
357                         return;
358                     }
359                     int progressToVolume = MediaOutputSeekbar.scaleProgressToVolume(progress);
360                     int deviceVolume = device.getCurrentVolume();
361                     int percentage =
362                             (int) ((double) progressToVolume * VOLUME_PERCENTAGE_SCALE_SIZE
363                                     / (double) seekBar.getMax());
364                     mVolumeValueText.setText(mContext.getResources().getString(
365                             R.string.media_output_dialog_volume_percentage, percentage));
366                     mVolumeValueText.setVisibility(View.VISIBLE);
367                     if (mStartFromMute) {
368                         updateUnmutedVolumeIcon();
369                         mStartFromMute = false;
370                     }
371                     if (progressToVolume != deviceVolume) {
372                         mLatestUpdateVolume = progressToVolume;
373                         mController.adjustVolume(device, progressToVolume);
374                     }
375                 }
376 
377                 @Override
378                 public void onStartTrackingTouch(SeekBar seekBar) {
379                     mTitleIcon.setVisibility(View.INVISIBLE);
380                     mVolumeValueText.setVisibility(View.VISIBLE);
381                     int currentVolume = MediaOutputSeekbar.scaleProgressToVolume(
382                             seekBar.getProgress());
383                     mStartFromMute = (currentVolume == 0);
384                     mIsDragging = true;
385                 }
386 
387                 @Override
388                 public void onStopTrackingTouch(SeekBar seekBar) {
389                     int currentVolume = MediaOutputSeekbar.scaleProgressToVolume(
390                             seekBar.getProgress());
391                     if (currentVolume == 0) {
392                         seekBar.setProgress(0);
393                         updateMutedVolumeIcon();
394                     } else {
395                         updateUnmutedVolumeIcon();
396                     }
397                     mTitleIcon.setVisibility(View.VISIBLE);
398                     mVolumeValueText.setVisibility(View.GONE);
399                     mController.logInteractionAdjustVolume(device);
400                     mIsDragging = false;
401                 }
402             });
403         }
404 
updateMutedVolumeIcon()405         void updateMutedVolumeIcon() {
406             mIconAreaLayout.setBackground(
407                     mContext.getDrawable(R.drawable.media_output_item_background_active));
408             updateTitleIcon(R.drawable.media_output_icon_volume_off,
409                     mController.getColorItemContent());
410         }
411 
updateUnmutedVolumeIcon()412         void updateUnmutedVolumeIcon() {
413             mIconAreaLayout.setBackground(
414                     mContext.getDrawable(R.drawable.media_output_title_icon_area)
415             );
416             updateTitleIcon(R.drawable.media_output_icon_volume,
417                     mController.getColorItemContent());
418         }
419 
updateTitleIcon(@rawableRes int id, int color)420         void updateTitleIcon(@DrawableRes int id, int color) {
421             mTitleIcon.setImageDrawable(mContext.getDrawable(id));
422             mTitleIcon.setImageTintList(ColorStateList.valueOf(color));
423             mIconAreaLayout.setBackgroundTintList(
424                     ColorStateList.valueOf(mController.getColorSeekbarProgress()));
425         }
426 
updateIconAreaClickListener(View.OnClickListener listener)427         void updateIconAreaClickListener(View.OnClickListener listener) {
428             mIconAreaLayout.setOnClickListener(listener);
429         }
430 
initFakeActiveDevice()431         void initFakeActiveDevice() {
432             disableSeekBar();
433             updateTitleIcon(R.drawable.media_output_icon_volume,
434                     mController.getColorItemContent());
435             final Drawable backgroundDrawable = mContext.getDrawable(
436                                     R.drawable.media_output_item_background_active)
437                             .mutate();
438             mItemLayout.setBackground(backgroundDrawable);
439             mItemLayout.setBackgroundTintList(
440                     ColorStateList.valueOf(mController.getColorConnectedItemBackground()));
441             mIconAreaLayout.setBackgroundTintList(
442                     ColorStateList.valueOf(mController.getColorConnectedItemBackground()));
443         }
444 
animateCornerAndVolume(int fromProgress, int toProgress)445         private void animateCornerAndVolume(int fromProgress, int toProgress) {
446             final GradientDrawable layoutBackgroundDrawable =
447                     (GradientDrawable) mItemLayout.getBackground();
448             final ClipDrawable clipDrawable =
449                     (ClipDrawable) ((LayerDrawable) mSeekBar.getProgressDrawable())
450                             .findDrawableByLayerId(android.R.id.progress);
451             final GradientDrawable targetBackgroundDrawable =
452                     (GradientDrawable) (mIconAreaLayout.getBackground());
453             mCornerAnimator.addUpdateListener(animation -> {
454                 float value = (float) animation.getAnimatedValue();
455                 layoutBackgroundDrawable.setCornerRadius(value);
456                 if (toProgress == 0) {
457                     targetBackgroundDrawable.setCornerRadius(value);
458                 } else {
459                     targetBackgroundDrawable.setCornerRadii(new float[]{
460                             value,
461                             value,
462                             0, 0, 0, 0, value, value
463                     });
464                 }
465             });
466             mVolumeAnimator.setIntValues(fromProgress, toProgress);
467             mVolumeAnimator.start();
468             mCornerAnimator.start();
469         }
470 
initAnimator()471         private void initAnimator() {
472             mCornerAnimator = ValueAnimator.ofFloat(mController.getInactiveRadius(),
473                     mController.getActiveRadius());
474             mCornerAnimator.setDuration(ANIM_DURATION);
475             mCornerAnimator.setInterpolator(new LinearInterpolator());
476 
477             mVolumeAnimator = ValueAnimator.ofInt();
478             mVolumeAnimator.addUpdateListener(animation -> {
479                 int value = (int) animation.getAnimatedValue();
480                 mSeekBar.setProgress(value);
481             });
482             mVolumeAnimator.setDuration(ANIM_DURATION);
483             mVolumeAnimator.setInterpolator(new LinearInterpolator());
484             mVolumeAnimator.addListener(new Animator.AnimatorListener() {
485                 @Override
486                 public void onAnimationStart(Animator animation) {
487                     mSeekBar.setEnabled(false);
488                 }
489 
490                 @Override
491                 public void onAnimationEnd(Animator animation) {
492                     mSeekBar.setEnabled(true);
493                 }
494 
495                 @Override
496                 public void onAnimationCancel(Animator animation) {
497                     mSeekBar.setEnabled(true);
498                 }
499 
500                 @Override
501                 public void onAnimationRepeat(Animator animation) {
502 
503                 }
504             });
505         }
506 
disableSeekBar()507         protected void disableSeekBar() {
508             mSeekBar.setEnabled(false);
509             mSeekBar.setOnTouchListener((v, event) -> true);
510             updateIconAreaClickListener(null);
511         }
512 
enableSeekBar(MediaDevice device)513         private void enableSeekBar(MediaDevice device) {
514             mSeekBar.setEnabled(true);
515             mSeekBar.setOnTouchListener((v, event) -> false);
516             updateIconAreaClickListener((v) -> {
517                 if (device.getCurrentVolume() == 0) {
518                     mController.logInteractionUnmuteDevice(device);
519                     mSeekBar.setVolume(UNMUTE_DEFAULT_VOLUME);
520                     mController.adjustVolume(device, UNMUTE_DEFAULT_VOLUME);
521                     updateUnmutedVolumeIcon();
522                     mIconAreaLayout.setOnTouchListener(((iconV, event) -> false));
523                 } else {
524                     mController.logInteractionMuteDevice(device);
525                     mSeekBar.resetVolume();
526                     mController.adjustVolume(device, 0);
527                     updateMutedVolumeIcon();
528                     mIconAreaLayout.setOnTouchListener(((iconV, event) -> {
529                         mSeekBar.dispatchTouchEvent(event);
530                         return false;
531                     }));
532                 }
533             });
534         }
535 
setUpDeviceIcon(MediaDevice device)536         protected void setUpDeviceIcon(MediaDevice device) {
537             ThreadUtils.postOnBackgroundThread(() -> {
538                 Icon icon = mController.getDeviceIconCompat(device).toIcon(mContext);
539                 ThreadUtils.postOnMainThread(() -> {
540                     if (!TextUtils.equals(mDeviceId, device.getId())) {
541                         return;
542                     }
543                     mTitleIcon.setImageIcon(icon);
544                     mTitleIcon.setImageTintList(
545                             ColorStateList.valueOf(mController.getColorItemContent()));
546                 });
547             });
548         }
549     }
550 }
551