• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
5  * except in compliance with the License. You may obtain a copy of the License at
6  *
7  *      http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software distributed under the
10  * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
11  * KIND, either express or implied. See the License for the specific language governing
12  * permissions and limitations under the License.
13  */
14 
15 package com.android.systemui.qs;
16 
17 import static android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS;
18 
19 import android.animation.Animator;
20 import android.animation.AnimatorListenerAdapter;
21 import android.annotation.ColorInt;
22 import android.app.ActivityManager;
23 import android.app.AlarmManager;
24 import android.content.BroadcastReceiver;
25 import android.content.Context;
26 import android.content.Intent;
27 import android.content.IntentFilter;
28 import android.content.res.Configuration;
29 import android.content.res.Resources;
30 import android.graphics.Color;
31 import android.graphics.Rect;
32 import android.media.AudioManager;
33 import android.os.Handler;
34 import android.provider.AlarmClock;
35 import android.service.notification.ZenModeConfig;
36 import android.support.annotation.VisibleForTesting;
37 import android.text.format.DateUtils;
38 import android.util.AttributeSet;
39 import android.util.Log;
40 import android.util.Pair;
41 import android.view.View;
42 import android.view.WindowInsets;
43 import android.widget.ImageView;
44 import android.widget.LinearLayout;
45 import android.widget.RelativeLayout;
46 import android.widget.TextView;
47 
48 import com.android.settingslib.Utils;
49 import com.android.systemui.BatteryMeterView;
50 import com.android.systemui.Dependency;
51 import com.android.systemui.Prefs;
52 import com.android.systemui.R;
53 import com.android.systemui.plugins.ActivityStarter;
54 import com.android.systemui.qs.QSDetail.Callback;
55 import com.android.systemui.statusbar.phone.PhoneStatusBarView;
56 import com.android.systemui.statusbar.phone.StatusBarIconController;
57 import com.android.systemui.statusbar.phone.StatusBarIconController.TintedIconManager;
58 import com.android.systemui.statusbar.policy.Clock;
59 import com.android.systemui.statusbar.phone.StatusIconContainer;
60 import com.android.systemui.statusbar.policy.DarkIconDispatcher;
61 import com.android.systemui.statusbar.policy.DarkIconDispatcher.DarkReceiver;
62 import com.android.systemui.statusbar.policy.DateView;
63 import com.android.systemui.statusbar.policy.NextAlarmController;
64 import com.android.systemui.statusbar.policy.ZenModeController;
65 
66 import java.util.Locale;
67 import java.util.Objects;
68 
69 /**
70  * View that contains the top-most bits of the screen (primarily the status bar with date, time, and
71  * battery) and also contains the {@link QuickQSPanel} along with some of the panel's inner
72  * contents.
73  */
74 public class QuickStatusBarHeader extends RelativeLayout implements
75         View.OnClickListener, NextAlarmController.NextAlarmChangeCallback,
76         ZenModeController.Callback {
77     private static final String TAG = "QuickStatusBarHeader";
78     private static final boolean DEBUG = false;
79 
80     /** Delay for auto fading out the long press tooltip after it's fully visible (in ms). */
81     private static final long AUTO_FADE_OUT_DELAY_MS = DateUtils.SECOND_IN_MILLIS * 6;
82     private static final int FADE_ANIMATION_DURATION_MS = 300;
83     private static final int TOOLTIP_NOT_YET_SHOWN_COUNT = 0;
84     public static final int MAX_TOOLTIP_SHOWN_COUNT = 2;
85 
86     private final Handler mHandler = new Handler();
87 
88     private QSPanel mQsPanel;
89 
90     private boolean mExpanded;
91     private boolean mListening;
92     private boolean mQsDisabled;
93 
94     protected QuickQSPanel mHeaderQsPanel;
95     protected QSTileHost mHost;
96     private TintedIconManager mIconManager;
97     private TouchAnimator mStatusIconsAlphaAnimator;
98     private TouchAnimator mHeaderTextContainerAlphaAnimator;
99 
100     private View mSystemIconsView;
101     private View mQuickQsStatusIcons;
102     private View mHeaderTextContainerView;
103     /** View containing the next alarm and ringer mode info. */
104     private View mStatusContainer;
105     /** Tooltip for educating users that they can long press on icons to see more details. */
106     private View mLongPressTooltipView;
107 
108     private int mRingerMode = AudioManager.RINGER_MODE_NORMAL;
109     private AlarmManager.AlarmClockInfo mNextAlarm;
110 
111     private ImageView mNextAlarmIcon;
112     /** {@link TextView} containing the actual text indicating when the next alarm will go off. */
113     private TextView mNextAlarmTextView;
114     private View mStatusSeparator;
115     private ImageView mRingerModeIcon;
116     private TextView mRingerModeTextView;
117     private BatteryMeterView mBatteryMeterView;
118     private Clock mClockView;
119     private DateView mDateView;
120 
121     private NextAlarmController mAlarmController;
122     private ZenModeController mZenController;
123     /** Counts how many times the long press tooltip has been shown to the user. */
124     private int mShownCount;
125 
126     private final BroadcastReceiver mRingerReceiver = new BroadcastReceiver() {
127         @Override
128         public void onReceive(Context context, Intent intent) {
129             mRingerMode = intent.getIntExtra(AudioManager.EXTRA_RINGER_MODE, -1);
130             updateStatusText();
131         }
132     };
133 
134     /**
135      * Runnable for automatically fading out the long press tooltip (as if it were animating away).
136      */
137     private final Runnable mAutoFadeOutTooltipRunnable = () -> hideLongPressTooltip(false);
138 
QuickStatusBarHeader(Context context, AttributeSet attrs)139     public QuickStatusBarHeader(Context context, AttributeSet attrs) {
140         super(context, attrs);
141         mAlarmController = Dependency.get(NextAlarmController.class);
142         mZenController = Dependency.get(ZenModeController.class);
143         mShownCount = getStoredShownCount();
144     }
145 
146     @Override
onFinishInflate()147     protected void onFinishInflate() {
148         super.onFinishInflate();
149 
150         mHeaderQsPanel = findViewById(R.id.quick_qs_panel);
151         mSystemIconsView = findViewById(R.id.quick_status_bar_system_icons);
152         mQuickQsStatusIcons = findViewById(R.id.quick_qs_status_icons);
153         StatusIconContainer iconContainer = findViewById(R.id.statusIcons);
154         iconContainer.setShouldRestrictIcons(false);
155         mIconManager = new TintedIconManager(iconContainer);
156 
157         // Views corresponding to the header info section (e.g. tooltip and next alarm).
158         mHeaderTextContainerView = findViewById(R.id.header_text_container);
159         mLongPressTooltipView = findViewById(R.id.long_press_tooltip);
160         mStatusContainer = findViewById(R.id.status_container);
161         mStatusSeparator = findViewById(R.id.status_separator);
162         mNextAlarmIcon = findViewById(R.id.next_alarm_icon);
163         mNextAlarmTextView = findViewById(R.id.next_alarm_text);
164         mRingerModeIcon = findViewById(R.id.ringer_mode_icon);
165         mRingerModeTextView = findViewById(R.id.ringer_mode_text);
166 
167         updateResources();
168 
169         Rect tintArea = new Rect(0, 0, 0, 0);
170         int colorForeground = Utils.getColorAttr(getContext(), android.R.attr.colorForeground);
171         float intensity = getColorIntensity(colorForeground);
172         int fillColor = fillColorForIntensity(intensity, getContext());
173 
174         // Set light text on the header icons because they will always be on a black background
175         applyDarkness(R.id.clock, tintArea, 0, DarkIconDispatcher.DEFAULT_ICON_TINT);
176 
177         // Set the correct tint for the status icons so they contrast
178         mIconManager.setTint(fillColor);
179 
180         mBatteryMeterView = findViewById(R.id.battery);
181         mBatteryMeterView.setForceShowPercent(true);
182         mBatteryMeterView.setOnClickListener(this);
183         mClockView = findViewById(R.id.clock);
184         mClockView.setOnClickListener(this);
185         mDateView = findViewById(R.id.date);
186     }
187 
updateStatusText()188     private void updateStatusText() {
189         boolean changed = updateRingerStatus() || updateAlarmStatus();
190 
191         if (changed) {
192             boolean alarmVisible = mNextAlarmTextView.getVisibility() == View.VISIBLE;
193             boolean ringerVisible = mRingerModeTextView.getVisibility() == View.VISIBLE;
194             mStatusSeparator.setVisibility(alarmVisible && ringerVisible ? View.VISIBLE
195                     : View.GONE);
196             updateTooltipShow();
197         }
198     }
199 
updateRingerStatus()200     private boolean updateRingerStatus() {
201         boolean isOriginalVisible = mRingerModeTextView.getVisibility() == View.VISIBLE;
202         CharSequence originalRingerText = mRingerModeTextView.getText();
203 
204         boolean ringerVisible = false;
205         if (!ZenModeConfig.isZenOverridingRinger(mZenController.getZen(),
206                 mZenController.getConfig())) {
207             if (mRingerMode == AudioManager.RINGER_MODE_VIBRATE) {
208                 mRingerModeIcon.setImageResource(R.drawable.stat_sys_ringer_vibrate);
209                 mRingerModeTextView.setText(R.string.qs_status_phone_vibrate);
210                 ringerVisible = true;
211             } else if (mRingerMode == AudioManager.RINGER_MODE_SILENT) {
212                 mRingerModeIcon.setImageResource(R.drawable.stat_sys_ringer_silent);
213                 mRingerModeTextView.setText(R.string.qs_status_phone_muted);
214                 ringerVisible = true;
215             }
216         }
217         mRingerModeIcon.setVisibility(ringerVisible ? View.VISIBLE : View.GONE);
218         mRingerModeTextView.setVisibility(ringerVisible ? View.VISIBLE : View.GONE);
219 
220         return isOriginalVisible != ringerVisible ||
221                 !Objects.equals(originalRingerText, mRingerModeTextView.getText());
222     }
223 
updateAlarmStatus()224     private boolean updateAlarmStatus() {
225         boolean isOriginalVisible = mNextAlarmTextView.getVisibility() == View.VISIBLE;
226         CharSequence originalAlarmText = mNextAlarmTextView.getText();
227 
228         boolean alarmVisible = false;
229         if (mNextAlarm != null) {
230             alarmVisible = true;
231             mNextAlarmTextView.setText(formatNextAlarm(mNextAlarm));
232         }
233         mNextAlarmIcon.setVisibility(alarmVisible ? View.VISIBLE : View.GONE);
234         mNextAlarmTextView.setVisibility(alarmVisible ? View.VISIBLE : View.GONE);
235 
236         return isOriginalVisible != alarmVisible ||
237                 !Objects.equals(originalAlarmText, mNextAlarmTextView.getText());
238     }
239 
applyDarkness(int id, Rect tintArea, float intensity, int color)240     private void applyDarkness(int id, Rect tintArea, float intensity, int color) {
241         View v = findViewById(id);
242         if (v instanceof DarkReceiver) {
243             ((DarkReceiver) v).onDarkChanged(tintArea, intensity, color);
244         }
245     }
246 
fillColorForIntensity(float intensity, Context context)247     private int fillColorForIntensity(float intensity, Context context) {
248         if (intensity == 0) {
249             return context.getColor(R.color.light_mode_icon_color_single_tone);
250         }
251         return context.getColor(R.color.dark_mode_icon_color_single_tone);
252     }
253 
254     @Override
onConfigurationChanged(Configuration newConfig)255     protected void onConfigurationChanged(Configuration newConfig) {
256         super.onConfigurationChanged(newConfig);
257         updateResources();
258 
259         // Update color schemes in landscape to use wallpaperTextColor
260         boolean shouldUseWallpaperTextColor =
261                 newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE;
262         mBatteryMeterView.useWallpaperTextColor(shouldUseWallpaperTextColor);
263         mClockView.useWallpaperTextColor(shouldUseWallpaperTextColor);
264     }
265 
266     @Override
onRtlPropertiesChanged(int layoutDirection)267     public void onRtlPropertiesChanged(int layoutDirection) {
268         super.onRtlPropertiesChanged(layoutDirection);
269         updateResources();
270     }
271 
updateResources()272     private void updateResources() {
273         Resources resources = mContext.getResources();
274 
275         // Update height for a few views, especially due to landscape mode restricting space.
276         mHeaderTextContainerView.getLayoutParams().height =
277                 resources.getDimensionPixelSize(R.dimen.qs_header_tooltip_height);
278         mHeaderTextContainerView.setLayoutParams(mHeaderTextContainerView.getLayoutParams());
279 
280         mSystemIconsView.getLayoutParams().height = resources.getDimensionPixelSize(
281                 com.android.internal.R.dimen.quick_qs_offset_height);
282         mSystemIconsView.setLayoutParams(mSystemIconsView.getLayoutParams());
283 
284         getLayoutParams().height = resources.getDimensionPixelSize(mQsDisabled
285                 ? com.android.internal.R.dimen.quick_qs_offset_height
286                 : com.android.internal.R.dimen.quick_qs_total_height);
287         setLayoutParams(getLayoutParams());
288 
289         updateStatusIconAlphaAnimator();
290         updateHeaderTextContainerAlphaAnimator();
291     }
292 
updateStatusIconAlphaAnimator()293     private void updateStatusIconAlphaAnimator() {
294         mStatusIconsAlphaAnimator = new TouchAnimator.Builder()
295                 .addFloat(mQuickQsStatusIcons, "alpha", 1, 0)
296                 .build();
297     }
298 
updateHeaderTextContainerAlphaAnimator()299     private void updateHeaderTextContainerAlphaAnimator() {
300         mHeaderTextContainerAlphaAnimator = new TouchAnimator.Builder()
301                 .addFloat(mHeaderTextContainerView, "alpha", 0, 1)
302                 .setStartDelay(.5f)
303                 .build();
304     }
305 
setExpanded(boolean expanded)306     public void setExpanded(boolean expanded) {
307         if (mExpanded == expanded) return;
308         mExpanded = expanded;
309         mHeaderQsPanel.setExpanded(expanded);
310         updateEverything();
311     }
312 
313     /**
314      * Animates the inner contents based on the given expansion details.
315      *
316      * @param isKeyguardShowing whether or not we're showing the keyguard (a.k.a. lockscreen)
317      * @param expansionFraction how much the QS panel is expanded/pulled out (up to 1f)
318      * @param panelTranslationY how much the panel has physically moved down vertically (required
319      *                          for keyguard animations only)
320      */
setExpansion(boolean isKeyguardShowing, float expansionFraction, float panelTranslationY)321     public void setExpansion(boolean isKeyguardShowing, float expansionFraction,
322                              float panelTranslationY) {
323         final float keyguardExpansionFraction = isKeyguardShowing ? 1f : expansionFraction;
324         if (mStatusIconsAlphaAnimator != null) {
325             mStatusIconsAlphaAnimator.setPosition(keyguardExpansionFraction);
326         }
327 
328         if (isKeyguardShowing) {
329             // If the keyguard is showing, we want to offset the text so that it comes in at the
330             // same time as the panel as it slides down.
331             mHeaderTextContainerView.setTranslationY(panelTranslationY);
332         } else {
333             mHeaderTextContainerView.setTranslationY(0f);
334         }
335 
336         if (mHeaderTextContainerAlphaAnimator != null) {
337             mHeaderTextContainerAlphaAnimator.setPosition(keyguardExpansionFraction);
338         }
339 
340         // Check the original expansion fraction - we don't want to show the tooltip until the
341         // panel is pulled all the way out.
342         if (expansionFraction == 1f) {
343             // QS is fully expanded, bring in the tooltip.
344             showLongPressTooltip();
345         }
346     }
347 
348     /** Returns the latest stored tooltip shown count from SharedPreferences. */
getStoredShownCount()349     private int getStoredShownCount() {
350         return Prefs.getInt(
351                 mContext,
352                 Prefs.Key.QS_LONG_PRESS_TOOLTIP_SHOWN_COUNT,
353                 TOOLTIP_NOT_YET_SHOWN_COUNT);
354     }
355 
disable(int state1, int state2, boolean animate)356     public void disable(int state1, int state2, boolean animate) {
357         final boolean disabled = (state2 & DISABLE2_QUICK_SETTINGS) != 0;
358         if (disabled == mQsDisabled) return;
359         mQsDisabled = disabled;
360         mHeaderQsPanel.setDisabledByPolicy(disabled);
361         mHeaderTextContainerView.setVisibility(mQsDisabled ? View.GONE : View.VISIBLE);
362         mQuickQsStatusIcons.setVisibility(mQsDisabled ? View.GONE : View.VISIBLE);
363         updateResources();
364     }
365 
366     @Override
onAttachedToWindow()367     public void onAttachedToWindow() {
368         super.onAttachedToWindow();
369         Dependency.get(StatusBarIconController.class).addIconGroup(mIconManager);
370         requestApplyInsets();
371     }
372 
373     @Override
onApplyWindowInsets(WindowInsets insets)374     public WindowInsets onApplyWindowInsets(WindowInsets insets) {
375         Pair<Integer, Integer> padding = PhoneStatusBarView.cornerCutoutMargins(
376                 insets.getDisplayCutout(), getDisplay());
377         if (padding == null) {
378             mSystemIconsView.setPaddingRelative(
379                     getResources().getDimensionPixelSize(R.dimen.status_bar_padding_start), 0,
380                     getResources().getDimensionPixelSize(R.dimen.status_bar_padding_end), 0);
381         } else {
382             mSystemIconsView.setPadding(padding.first, 0, padding.second, 0);
383 
384         }
385         return super.onApplyWindowInsets(insets);
386     }
387 
388     @Override
389     @VisibleForTesting
onDetachedFromWindow()390     public void onDetachedFromWindow() {
391         setListening(false);
392         Dependency.get(StatusBarIconController.class).removeIconGroup(mIconManager);
393         super.onDetachedFromWindow();
394     }
395 
setListening(boolean listening)396     public void setListening(boolean listening) {
397         if (listening == mListening) {
398             return;
399         }
400         mHeaderQsPanel.setListening(listening);
401         mListening = listening;
402 
403         if (listening) {
404             mZenController.addCallback(this);
405             mAlarmController.addCallback(this);
406             mContext.registerReceiver(mRingerReceiver,
407                     new IntentFilter(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION));
408         } else {
409             mZenController.removeCallback(this);
410             mAlarmController.removeCallback(this);
411             mContext.unregisterReceiver(mRingerReceiver);
412         }
413     }
414 
415     @Override
onClick(View v)416     public void onClick(View v) {
417         if (v == mClockView) {
418             Dependency.get(ActivityStarter.class).postStartActivityDismissingKeyguard(new Intent(
419                     AlarmClock.ACTION_SHOW_ALARMS),0);
420         } else if (v == mBatteryMeterView) {
421             Dependency.get(ActivityStarter.class).postStartActivityDismissingKeyguard(new Intent(
422                     Intent.ACTION_POWER_USAGE_SUMMARY),0);
423         }
424     }
425 
426     @Override
onNextAlarmChanged(AlarmManager.AlarmClockInfo nextAlarm)427     public void onNextAlarmChanged(AlarmManager.AlarmClockInfo nextAlarm) {
428         mNextAlarm = nextAlarm;
429         updateStatusText();
430     }
431 
432     @Override
onZenChanged(int zen)433     public void onZenChanged(int zen) {
434         updateStatusText();
435 
436     }
437 
438     @Override
onConfigChanged(ZenModeConfig config)439     public void onConfigChanged(ZenModeConfig config) {
440         updateStatusText();
441     }
442 
updateTooltipShow()443     private void updateTooltipShow() {
444         if (hasStatusText()) {
445             hideLongPressTooltip(true /* shouldShowStatusText */);
446         } else {
447             hideStatusText();
448         }
449         updateHeaderTextContainerAlphaAnimator();
450     }
451 
hasStatusText()452     private boolean hasStatusText() {
453         return mNextAlarmTextView.getVisibility() == View.VISIBLE
454                 || mRingerModeTextView.getVisibility() == View.VISIBLE;
455     }
456 
457     /**
458      * Animates in the long press tooltip (as long as the next alarm text isn't currently occupying
459      * the space).
460      */
showLongPressTooltip()461     public void showLongPressTooltip() {
462         // If we have status text to show, don't bother fading in the tooltip.
463         if (hasStatusText()) {
464             return;
465         }
466 
467         if (mShownCount < MAX_TOOLTIP_SHOWN_COUNT) {
468             mLongPressTooltipView.animate().cancel();
469             mLongPressTooltipView.setVisibility(View.VISIBLE);
470             mLongPressTooltipView.animate()
471                     .alpha(1f)
472                     .setDuration(FADE_ANIMATION_DURATION_MS)
473                     .setListener(new AnimatorListenerAdapter() {
474                         @Override
475                         public void onAnimationEnd(Animator animation) {
476                             mHandler.postDelayed(
477                                     mAutoFadeOutTooltipRunnable, AUTO_FADE_OUT_DELAY_MS);
478                         }
479                     })
480                     .start();
481 
482             // Increment and drop the shown count in prefs for the next time we're deciding to
483             // fade in the tooltip. We first sanity check that the tooltip count hasn't changed yet
484             // in prefs (say, from a long press).
485             if (getStoredShownCount() <= mShownCount) {
486                 Prefs.putInt(mContext, Prefs.Key.QS_LONG_PRESS_TOOLTIP_SHOWN_COUNT, ++mShownCount);
487             }
488         }
489     }
490 
491     /**
492      * Fades out the long press tooltip if it's partially visible - short circuits any running
493      * animation. Additionally has the ability to fade in the status info text.
494      *
495      * @param shouldShowStatusText whether we should fade in the status text
496      */
hideLongPressTooltip(boolean shouldShowStatusText)497     private void hideLongPressTooltip(boolean shouldShowStatusText) {
498         mLongPressTooltipView.animate().cancel();
499         if (mLongPressTooltipView.getVisibility() == View.VISIBLE
500                 && mLongPressTooltipView.getAlpha() != 0f) {
501             mHandler.removeCallbacks(mAutoFadeOutTooltipRunnable);
502             mLongPressTooltipView.animate()
503                     .alpha(0f)
504                     .setDuration(FADE_ANIMATION_DURATION_MS)
505                     .setListener(new AnimatorListenerAdapter() {
506                         @Override
507                         public void onAnimationEnd(Animator animation) {
508                             if (DEBUG) Log.d(TAG, "hideLongPressTooltip: Hid long press tip");
509                             mLongPressTooltipView.setVisibility(View.INVISIBLE);
510 
511                             if (shouldShowStatusText) {
512                                 showStatus();
513                             }
514                         }
515                     })
516                     .start();
517         } else {
518             mLongPressTooltipView.setVisibility(View.INVISIBLE);
519             if (shouldShowStatusText) {
520                 showStatus();
521             }
522         }
523     }
524 
525     /**
526      * Fades in the updated status text. Note that if there's already a status showing, this will
527      * immediately hide it and fade in the updated status.
528      */
showStatus()529     private void showStatus() {
530         mStatusContainer.setAlpha(0f);
531         mStatusContainer.setVisibility(View.VISIBLE);
532 
533         // Animate the alarm back in. Make sure to clear the animator listener for the animation!
534         mStatusContainer.animate()
535                 .alpha(1f)
536                 .setDuration(FADE_ANIMATION_DURATION_MS)
537                 .setListener(null)
538                 .start();
539     }
540 
541     /** Fades out and hides the status text. */
hideStatusText()542     private void hideStatusText() {
543         if (mStatusContainer.getVisibility() == View.VISIBLE) {
544             mStatusContainer.animate()
545                     .alpha(0f)
546                     .setListener(new AnimatorListenerAdapter() {
547                         @Override
548                         public void onAnimationEnd(Animator animation) {
549                             if (DEBUG) Log.d(TAG, "hideAlarmText: Hid alarm text");
550 
551                             // Reset the alpha regardless of how the animation ends for the next
552                             // time we show this view/want to animate it.
553                             mStatusContainer.setVisibility(View.INVISIBLE);
554                             mStatusContainer.setAlpha(1f);
555                         }
556                     })
557                     .start();
558         }
559     }
560 
updateEverything()561     public void updateEverything() {
562         post(() -> setClickable(false));
563     }
564 
setQSPanel(final QSPanel qsPanel)565     public void setQSPanel(final QSPanel qsPanel) {
566         mQsPanel = qsPanel;
567         setupHost(qsPanel.getHost());
568     }
569 
setupHost(final QSTileHost host)570     public void setupHost(final QSTileHost host) {
571         mHost = host;
572         //host.setHeaderView(mExpandIndicator);
573         mHeaderQsPanel.setQSPanelAndHeader(mQsPanel, this);
574         mHeaderQsPanel.setHost(host, null /* No customization in header */);
575 
576         // Use SystemUI context to get battery meter colors, and let it use the default tint (white)
577         mBatteryMeterView.setColorsFromContext(mHost.getContext());
578         mBatteryMeterView.onDarkChanged(new Rect(), 0, DarkIconDispatcher.DEFAULT_ICON_TINT);
579     }
580 
setCallback(Callback qsPanelCallback)581     public void setCallback(Callback qsPanelCallback) {
582         mHeaderQsPanel.setCallback(qsPanelCallback);
583     }
584 
formatNextAlarm(AlarmManager.AlarmClockInfo info)585     private String formatNextAlarm(AlarmManager.AlarmClockInfo info) {
586         if (info == null) {
587             return "";
588         }
589         String skeleton = android.text.format.DateFormat
590                 .is24HourFormat(mContext, ActivityManager.getCurrentUser()) ? "EHm" : "Ehma";
591         String pattern = android.text.format.DateFormat
592                 .getBestDateTimePattern(Locale.getDefault(), skeleton);
593         return android.text.format.DateFormat.format(pattern, info.getTriggerTime()).toString();
594     }
595 
getColorIntensity(@olorInt int color)596     public static float getColorIntensity(@ColorInt int color) {
597         return color == Color.WHITE ? 0 : 1;
598     }
599 
setMargins(int sideMargins)600     public void setMargins(int sideMargins) {
601         for (int i = 0; i < getChildCount(); i++) {
602             View v = getChildAt(i);
603             if (v == mSystemIconsView || v == mQuickQsStatusIcons || v == mHeaderQsPanel) {
604                 continue;
605             }
606             RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) v.getLayoutParams();
607             lp.leftMargin = sideMargins;
608             lp.rightMargin = sideMargins;
609         }
610     }
611 }
612