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 import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
19 
20 import android.content.Context;
21 import android.content.res.Configuration;
22 import android.content.res.Resources;
23 import android.graphics.Color;
24 import android.graphics.Rect;
25 import android.util.AttributeSet;
26 import android.util.Pair;
27 import android.view.DisplayCutout;
28 import android.view.View;
29 import android.view.ViewGroup;
30 import android.view.WindowInsets;
31 import android.widget.FrameLayout;
32 import android.widget.LinearLayout;
33 import android.widget.Space;
34 
35 import androidx.annotation.NonNull;
36 
37 import com.android.internal.policy.SystemBarUtils;
38 import com.android.settingslib.Utils;
39 import com.android.systemui.R;
40 import com.android.systemui.battery.BatteryMeterView;
41 import com.android.systemui.qs.QSDetail.Callback;
42 import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider;
43 import com.android.systemui.statusbar.phone.StatusBarIconController.TintedIconManager;
44 import com.android.systemui.statusbar.phone.StatusIconContainer;
45 import com.android.systemui.statusbar.policy.Clock;
46 import com.android.systemui.statusbar.policy.VariableDateView;
47 
48 import java.util.List;
49 
50 /**
51  * View that contains the top-most bits of the QS panel (primarily the status bar with date, time,
52  * battery, carrier info and privacy icons) and also contains the {@link QuickQSPanel}.
53  */
54 public class QuickStatusBarHeader extends FrameLayout {
55 
56     private boolean mExpanded;
57     private boolean mQsDisabled;
58 
59     private TouchAnimator mAlphaAnimator;
60     private TouchAnimator mTranslationAnimator;
61     private TouchAnimator mIconsAlphaAnimator;
62     private TouchAnimator mIconsAlphaAnimatorFixed;
63 
64     protected QuickQSPanel mHeaderQsPanel;
65     private View mDatePrivacyView;
66     private View mDateView;
67     // DateView next to clock. Visible on QQS
68     private VariableDateView mClockDateView;
69     private View mSecurityHeaderView;
70     private View mStatusIconsView;
71     private View mContainer;
72 
73     private View mQSCarriers;
74     private ViewGroup mClockContainer;
75     private Clock mClockView;
76     private Space mDatePrivacySeparator;
77     private View mClockIconsSeparator;
78     private boolean mShowClockIconsSeparator;
79     private View mRightLayout;
80     private View mDateContainer;
81     private View mPrivacyContainer;
82 
83     private BatteryMeterView mBatteryRemainingIcon;
84     private StatusIconContainer mIconContainer;
85     private View mPrivacyChip;
86 
87     private TintedIconManager mTintedIconManager;
88     private QSExpansionPathInterpolator mQSExpansionPathInterpolator;
89     private StatusBarContentInsetsProvider mInsetsProvider;
90 
91     private int mRoundedCornerPadding = 0;
92     private int mWaterfallTopInset;
93     private int mCutOutPaddingLeft;
94     private int mCutOutPaddingRight;
95     private float mKeyguardExpansionFraction;
96     private int mTextColorPrimary = Color.TRANSPARENT;
97     private int mTopViewMeasureHeight;
98 
99     @NonNull
100     private List<String> mRssiIgnoredSlots;
101     private boolean mIsSingleCarrier;
102 
103     private boolean mHasCenterCutout;
104     private boolean mConfigShowBatteryEstimate;
105 
106     private boolean mUseCombinedQSHeader;
107 
QuickStatusBarHeader(Context context, AttributeSet attrs)108     public QuickStatusBarHeader(Context context, AttributeSet attrs) {
109         super(context, attrs);
110     }
111 
112     /**
113      * How much the view containing the clock and QQS will translate down when QS is fully expanded.
114      *
115      * This matches the measured height of the view containing the date and privacy icons.
116      */
getOffsetTranslation()117     public int getOffsetTranslation() {
118         return mTopViewMeasureHeight;
119     }
120 
121     @Override
onFinishInflate()122     protected void onFinishInflate() {
123         super.onFinishInflate();
124 
125         mHeaderQsPanel = findViewById(R.id.quick_qs_panel);
126         mDatePrivacyView = findViewById(R.id.quick_status_bar_date_privacy);
127         mStatusIconsView = findViewById(R.id.quick_qs_status_icons);
128         mQSCarriers = findViewById(R.id.carrier_group);
129         mContainer = findViewById(R.id.qs_container);
130         mIconContainer = findViewById(R.id.statusIcons);
131         mPrivacyChip = findViewById(R.id.privacy_chip);
132         mDateView = findViewById(R.id.date);
133         mClockDateView = findViewById(R.id.date_clock);
134         mSecurityHeaderView = findViewById(R.id.header_text_container);
135         mClockIconsSeparator = findViewById(R.id.separator);
136         mRightLayout = findViewById(R.id.rightLayout);
137         mDateContainer = findViewById(R.id.date_container);
138         mPrivacyContainer = findViewById(R.id.privacy_container);
139 
140         mClockContainer = findViewById(R.id.clock_container);
141         mClockView = findViewById(R.id.clock);
142         mDatePrivacySeparator = findViewById(R.id.space);
143         // Tint for the battery icons are handled in setupHost()
144         mBatteryRemainingIcon = findViewById(R.id.batteryRemainingIcon);
145 
146         updateResources();
147         Configuration config = mContext.getResources().getConfiguration();
148         setDatePrivacyContainersWidth(config.orientation == Configuration.ORIENTATION_LANDSCAPE);
149         setSecurityHeaderContainerVisibility(
150                 config.orientation == Configuration.ORIENTATION_LANDSCAPE);
151 
152         // QS will always show the estimate, and BatteryMeterView handles the case where
153         // it's unavailable or charging
154         mBatteryRemainingIcon.setPercentShowMode(BatteryMeterView.MODE_ESTIMATE);
155 
156         mIconsAlphaAnimatorFixed = new TouchAnimator.Builder()
157                 .addFloat(mIconContainer, "alpha", 0, 1)
158                 .addFloat(mBatteryRemainingIcon, "alpha", 0, 1)
159                 .build();
160     }
161 
onAttach(TintedIconManager iconManager, QSExpansionPathInterpolator qsExpansionPathInterpolator, List<String> rssiIgnoredSlots, boolean useCombinedQSHeader, StatusBarContentInsetsProvider insetsProvider)162     void onAttach(TintedIconManager iconManager,
163             QSExpansionPathInterpolator qsExpansionPathInterpolator,
164             List<String> rssiIgnoredSlots,
165             boolean useCombinedQSHeader,
166             StatusBarContentInsetsProvider insetsProvider) {
167         mUseCombinedQSHeader = useCombinedQSHeader;
168         mTintedIconManager = iconManager;
169         mRssiIgnoredSlots = rssiIgnoredSlots;
170         mInsetsProvider = insetsProvider;
171         int fillColor = Utils.getColorAttrDefaultColor(getContext(),
172                 android.R.attr.textColorPrimary);
173 
174         // Set the correct tint for the status icons so they contrast
175         iconManager.setTint(fillColor);
176 
177         mQSExpansionPathInterpolator = qsExpansionPathInterpolator;
178         updateAnimators();
179     }
180 
setIsSingleCarrier(boolean isSingleCarrier)181     void setIsSingleCarrier(boolean isSingleCarrier) {
182         mIsSingleCarrier = isSingleCarrier;
183         updateAlphaAnimator();
184     }
185 
getHeaderQsPanel()186     public QuickQSPanel getHeaderQsPanel() {
187         return mHeaderQsPanel;
188     }
189 
190     @Override
onMeasure(int widthMeasureSpec, int heightMeasureSpec)191     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
192         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
193         if (mDatePrivacyView.getMeasuredHeight() != mTopViewMeasureHeight) {
194             mTopViewMeasureHeight = mDatePrivacyView.getMeasuredHeight();
195             post(this::updateAnimators);
196         }
197     }
198 
199     @Override
onConfigurationChanged(Configuration newConfig)200     protected void onConfigurationChanged(Configuration newConfig) {
201         super.onConfigurationChanged(newConfig);
202         updateResources();
203         setDatePrivacyContainersWidth(newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE);
204         setSecurityHeaderContainerVisibility(
205                 newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE);
206     }
207 
208     @Override
onRtlPropertiesChanged(int layoutDirection)209     public void onRtlPropertiesChanged(int layoutDirection) {
210         super.onRtlPropertiesChanged(layoutDirection);
211         updateResources();
212     }
213 
setDatePrivacyContainersWidth(boolean landscape)214     private void setDatePrivacyContainersWidth(boolean landscape) {
215         LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) mDateContainer.getLayoutParams();
216         lp.width = landscape ? WRAP_CONTENT : 0;
217         lp.weight = landscape ? 0f : 1f;
218         mDateContainer.setLayoutParams(lp);
219 
220         lp = (LinearLayout.LayoutParams) mPrivacyContainer.getLayoutParams();
221         lp.width = landscape ? WRAP_CONTENT : 0;
222         lp.weight = landscape ? 0f : 1f;
223         mPrivacyContainer.setLayoutParams(lp);
224     }
225 
setSecurityHeaderContainerVisibility(boolean landscape)226     private void setSecurityHeaderContainerVisibility(boolean landscape) {
227         mSecurityHeaderView.setVisibility(landscape ? VISIBLE : GONE);
228     }
229 
updateBatteryMode()230     private void updateBatteryMode() {
231         if (mConfigShowBatteryEstimate && !mHasCenterCutout) {
232             mBatteryRemainingIcon.setPercentShowMode(BatteryMeterView.MODE_ESTIMATE);
233         } else {
234             mBatteryRemainingIcon.setPercentShowMode(BatteryMeterView.MODE_ON);
235         }
236     }
237 
updateResources()238     void updateResources() {
239         Resources resources = mContext.getResources();
240         // status bar is already displayed out of QS in split shade
241         boolean shouldUseSplitShade =
242                 resources.getBoolean(R.bool.config_use_split_notification_shade);
243 
244         boolean gone = shouldUseSplitShade || mUseCombinedQSHeader || mQsDisabled;
245         mStatusIconsView.setVisibility(gone ? View.GONE : View.VISIBLE);
246         mDatePrivacyView.setVisibility(gone ? View.GONE : View.VISIBLE);
247 
248         mConfigShowBatteryEstimate = resources.getBoolean(R.bool.config_showBatteryEstimateQSBH);
249 
250         mRoundedCornerPadding = resources.getDimensionPixelSize(
251                 R.dimen.rounded_corner_content_padding);
252 
253         int qsOffsetHeight = SystemBarUtils.getQuickQsOffsetHeight(mContext);
254 
255         mDatePrivacyView.getLayoutParams().height =
256                 Math.max(qsOffsetHeight, mDatePrivacyView.getMinimumHeight());
257         mDatePrivacyView.setLayoutParams(mDatePrivacyView.getLayoutParams());
258 
259         mStatusIconsView.getLayoutParams().height =
260                 Math.max(qsOffsetHeight, mStatusIconsView.getMinimumHeight());
261         mStatusIconsView.setLayoutParams(mStatusIconsView.getLayoutParams());
262 
263         ViewGroup.LayoutParams lp = getLayoutParams();
264         if (mQsDisabled) {
265             lp.height = mStatusIconsView.getLayoutParams().height;
266         } else {
267             lp.height = WRAP_CONTENT;
268         }
269         setLayoutParams(lp);
270 
271         int textColor = Utils.getColorAttrDefaultColor(mContext, android.R.attr.textColorPrimary);
272         if (textColor != mTextColorPrimary) {
273             int textColorSecondary = Utils.getColorAttrDefaultColor(mContext,
274                     android.R.attr.textColorSecondary);
275             mTextColorPrimary = textColor;
276             mClockView.setTextColor(textColor);
277             if (mTintedIconManager != null) {
278                 mTintedIconManager.setTint(textColor);
279             }
280             mBatteryRemainingIcon.updateColors(mTextColorPrimary, textColorSecondary,
281                     mTextColorPrimary);
282         }
283 
284         MarginLayoutParams qqsLP = (MarginLayoutParams) mHeaderQsPanel.getLayoutParams();
285         qqsLP.topMargin = shouldUseSplitShade || !mUseCombinedQSHeader ? mContext.getResources()
286                 .getDimensionPixelSize(R.dimen.qqs_layout_margin_top) : qsOffsetHeight;
287         mHeaderQsPanel.setLayoutParams(qqsLP);
288 
289         updateBatteryMode();
290         updateHeadersPadding();
291         updateAnimators();
292 
293         updateClockDatePadding();
294     }
295 
updateClockDatePadding()296     private void updateClockDatePadding() {
297         int startPadding = mContext.getResources()
298                 .getDimensionPixelSize(R.dimen.status_bar_left_clock_starting_padding);
299         int endPadding = mContext.getResources()
300                 .getDimensionPixelSize(R.dimen.status_bar_left_clock_end_padding);
301         mClockView.setPaddingRelative(
302                 startPadding,
303                 mClockView.getPaddingTop(),
304                 endPadding,
305                 mClockView.getPaddingBottom()
306         );
307 
308         MarginLayoutParams lp = (MarginLayoutParams) mClockDateView.getLayoutParams();
309         lp.setMarginStart(endPadding);
310         mClockDateView.setLayoutParams(lp);
311     }
312 
updateAnimators()313     private void updateAnimators() {
314         if (mUseCombinedQSHeader) {
315             mTranslationAnimator = null;
316             return;
317         }
318         updateAlphaAnimator();
319         int offset = mTopViewMeasureHeight;
320 
321         mTranslationAnimator = new TouchAnimator.Builder()
322                 .addFloat(mContainer, "translationY", 0, offset)
323                 .setInterpolator(mQSExpansionPathInterpolator != null
324                         ? mQSExpansionPathInterpolator.getYInterpolator()
325                         : null)
326                 .build();
327     }
328 
updateAlphaAnimator()329     private void updateAlphaAnimator() {
330         if (mUseCombinedQSHeader) {
331             mAlphaAnimator = null;
332             return;
333         }
334         TouchAnimator.Builder builder = new TouchAnimator.Builder()
335                 .addFloat(mSecurityHeaderView, "alpha", 0, 1)
336                 // These views appear on expanding down
337                 .addFloat(mDateView, "alpha", 0, 0, 1)
338                 .addFloat(mClockDateView, "alpha", 1, 0, 0)
339                 .addFloat(mQSCarriers, "alpha", 0, 1)
340                 .setListener(new TouchAnimator.ListenerAdapter() {
341                     @Override
342                     public void onAnimationAtEnd() {
343                         super.onAnimationAtEnd();
344                         if (!mIsSingleCarrier) {
345                             mIconContainer.addIgnoredSlots(mRssiIgnoredSlots);
346                         }
347                         // Make it gone so there's enough room for carrier names
348                         mClockDateView.setVisibility(View.GONE);
349                     }
350 
351                     @Override
352                     public void onAnimationStarted() {
353                         mClockDateView.setVisibility(View.VISIBLE);
354                         mClockDateView.setFreezeSwitching(true);
355                         setSeparatorVisibility(false);
356                         if (!mIsSingleCarrier) {
357                             mIconContainer.addIgnoredSlots(mRssiIgnoredSlots);
358                         }
359                     }
360 
361                     @Override
362                     public void onAnimationAtStart() {
363                         super.onAnimationAtStart();
364                         mClockDateView.setFreezeSwitching(false);
365                         mClockDateView.setVisibility(View.VISIBLE);
366                         setSeparatorVisibility(mShowClockIconsSeparator);
367                         // In QQS we never ignore RSSI.
368                         mIconContainer.removeIgnoredSlots(mRssiIgnoredSlots);
369                     }
370                 });
371         mAlphaAnimator = builder.build();
372     }
373 
setChipVisibility(boolean visibility)374     void setChipVisibility(boolean visibility) {
375         if (visibility) {
376             // Animates the icons and battery indicator from alpha 0 to 1, when the chip is visible
377             mIconsAlphaAnimator = mIconsAlphaAnimatorFixed;
378             mIconsAlphaAnimator.setPosition(mKeyguardExpansionFraction);
379         } else {
380             mIconsAlphaAnimator = null;
381             mIconContainer.setAlpha(1);
382             mBatteryRemainingIcon.setAlpha(1);
383         }
384 
385     }
386 
387     /** */
setExpanded(boolean expanded, QuickQSPanelController quickQSPanelController)388     public void setExpanded(boolean expanded, QuickQSPanelController quickQSPanelController) {
389         if (mExpanded == expanded) return;
390         mExpanded = expanded;
391         quickQSPanelController.setExpanded(expanded);
392         updateEverything();
393     }
394 
395     /**
396      * Animates the inner contents based on the given expansion details.
397      *
398      * @param forceExpanded whether we should show the state expanded forcibly
399      * @param expansionFraction how much the QS panel is expanded/pulled out (up to 1f)
400      * @param panelTranslationY how much the panel has physically moved down vertically (required
401      *                          for keyguard animations only)
402      */
setExpansion(boolean forceExpanded, float expansionFraction, float panelTranslationY)403     public void setExpansion(boolean forceExpanded, float expansionFraction,
404                              float panelTranslationY) {
405         final float keyguardExpansionFraction = forceExpanded ? 1f : expansionFraction;
406 
407         if (mAlphaAnimator != null) {
408             mAlphaAnimator.setPosition(keyguardExpansionFraction);
409         }
410         if (mTranslationAnimator != null) {
411             mTranslationAnimator.setPosition(keyguardExpansionFraction);
412         }
413         if (mIconsAlphaAnimator != null) {
414             mIconsAlphaAnimator.setPosition(keyguardExpansionFraction);
415         }
416         // If forceExpanded (we are opening QS from lockscreen), the animators have been set to
417         // position = 1f.
418         if (forceExpanded) {
419             setTranslationY(panelTranslationY);
420         } else {
421             setTranslationY(0);
422         }
423 
424         mKeyguardExpansionFraction = keyguardExpansionFraction;
425     }
426 
disable(int state1, int state2, boolean animate)427     public void disable(int state1, int state2, boolean animate) {
428         final boolean disabled = (state2 & DISABLE2_QUICK_SETTINGS) != 0;
429         if (disabled == mQsDisabled) return;
430         mQsDisabled = disabled;
431         mHeaderQsPanel.setDisabledByPolicy(disabled);
432         mStatusIconsView.setVisibility(mQsDisabled ? View.GONE : View.VISIBLE);
433         updateResources();
434     }
435 
436     @Override
onApplyWindowInsets(WindowInsets insets)437     public WindowInsets onApplyWindowInsets(WindowInsets insets) {
438         // Handle padding of the views
439         DisplayCutout cutout = insets.getDisplayCutout();
440 
441         Pair<Integer, Integer> sbInsets = mInsetsProvider
442                 .getStatusBarContentInsetsForCurrentRotation();
443         boolean hasCornerCutout = mInsetsProvider.currentRotationHasCornerCutout();
444 
445         mDatePrivacyView.setPadding(sbInsets.first, 0, sbInsets.second, 0);
446         mStatusIconsView.setPadding(sbInsets.first, 0, sbInsets.second, 0);
447         LinearLayout.LayoutParams datePrivacySeparatorLayoutParams =
448                 (LinearLayout.LayoutParams) mDatePrivacySeparator.getLayoutParams();
449         LinearLayout.LayoutParams mClockIconsSeparatorLayoutParams =
450                 (LinearLayout.LayoutParams) mClockIconsSeparator.getLayoutParams();
451         if (cutout != null) {
452             Rect topCutout = cutout.getBoundingRectTop();
453             if (topCutout.isEmpty() || hasCornerCutout) {
454                 datePrivacySeparatorLayoutParams.width = 0;
455                 mDatePrivacySeparator.setVisibility(View.GONE);
456                 mClockIconsSeparatorLayoutParams.width = 0;
457                 setSeparatorVisibility(false);
458                 mShowClockIconsSeparator = false;
459                 mHasCenterCutout = false;
460             } else {
461                 datePrivacySeparatorLayoutParams.width = topCutout.width();
462                 mDatePrivacySeparator.setVisibility(View.VISIBLE);
463                 mClockIconsSeparatorLayoutParams.width = topCutout.width();
464                 mShowClockIconsSeparator = true;
465                 setSeparatorVisibility(mKeyguardExpansionFraction == 0f);
466                 mHasCenterCutout = true;
467             }
468         }
469         mDatePrivacySeparator.setLayoutParams(datePrivacySeparatorLayoutParams);
470         mClockIconsSeparator.setLayoutParams(mClockIconsSeparatorLayoutParams);
471         mCutOutPaddingLeft = sbInsets.first;
472         mCutOutPaddingRight = sbInsets.second;
473         mWaterfallTopInset = cutout == null ? 0 : cutout.getWaterfallInsets().top;
474 
475         updateBatteryMode();
476         updateHeadersPadding();
477         return super.onApplyWindowInsets(insets);
478     }
479 
480     /**
481      * Sets the visibility of the separator between clock and icons.
482      *
483      * This separator is "visible" when there is a center cutout, to block that space. In that
484      * case, the clock and the layout on the right (containing the icons and the battery meter) are
485      * set to weight 1 to take the available space.
486      * @param visible whether the separator between clock and icons should be visible.
487      */
setSeparatorVisibility(boolean visible)488     private void setSeparatorVisibility(boolean visible) {
489         int newVisibility = visible ? View.VISIBLE : View.GONE;
490         if (mClockIconsSeparator.getVisibility() == newVisibility) return;
491 
492         mClockIconsSeparator.setVisibility(visible ? View.VISIBLE : View.GONE);
493         mQSCarriers.setVisibility(visible ? View.GONE : View.VISIBLE);
494 
495         LinearLayout.LayoutParams lp =
496                 (LinearLayout.LayoutParams) mClockContainer.getLayoutParams();
497         lp.width = visible ? 0 : WRAP_CONTENT;
498         lp.weight = visible ? 1f : 0f;
499         mClockContainer.setLayoutParams(lp);
500 
501         lp = (LinearLayout.LayoutParams) mRightLayout.getLayoutParams();
502         lp.width = visible ? 0 : WRAP_CONTENT;
503         lp.weight = visible ? 1f : 0f;
504         mRightLayout.setLayoutParams(lp);
505     }
506 
updateHeadersPadding()507     private void updateHeadersPadding() {
508         setContentMargins(mDatePrivacyView, 0, 0);
509         setContentMargins(mStatusIconsView, 0, 0);
510         int paddingLeft = 0;
511         int paddingRight = 0;
512 
513         FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams();
514         int leftMargin = lp.leftMargin;
515         int rightMargin = lp.rightMargin;
516 
517         // The clock might collide with cutouts, let's shift it out of the way.
518         // We only do that if the inset is bigger than our own padding, since it's nicer to
519         // align with
520         if (mCutOutPaddingLeft > 0) {
521             // if there's a cutout, let's use at least the rounded corner inset
522             int cutoutPadding = Math.max(mCutOutPaddingLeft, mRoundedCornerPadding);
523             paddingLeft = Math.max(cutoutPadding - leftMargin, 0);
524         }
525         if (mCutOutPaddingRight > 0) {
526             // if there's a cutout, let's use at least the rounded corner inset
527             int cutoutPadding = Math.max(mCutOutPaddingRight, mRoundedCornerPadding);
528             paddingRight = Math.max(cutoutPadding - rightMargin, 0);
529         }
530 
531         mDatePrivacyView.setPadding(paddingLeft,
532                 mWaterfallTopInset,
533                 paddingRight,
534                 0);
535         mStatusIconsView.setPadding(paddingLeft,
536                 mWaterfallTopInset,
537                 paddingRight,
538                 0);
539     }
540 
updateEverything()541     public void updateEverything() {
542         post(() -> setClickable(!mExpanded));
543     }
544 
setCallback(Callback qsPanelCallback)545     public void setCallback(Callback qsPanelCallback) {
546         mHeaderQsPanel.setCallback(qsPanelCallback);
547     }
548 
setContentMargins(View view, int marginStart, int marginEnd)549     private void setContentMargins(View view, int marginStart, int marginEnd) {
550         MarginLayoutParams lp = (MarginLayoutParams) view.getLayoutParams();
551         lp.setMarginStart(marginStart);
552         lp.setMarginEnd(marginEnd);
553         view.setLayoutParams(lp);
554     }
555 
556     /**
557      * Scroll the headers away.
558      *
559      * @param scrollY the scroll of the QSPanel container
560      */
setExpandedScrollAmount(int scrollY)561     public void setExpandedScrollAmount(int scrollY) {
562         mStatusIconsView.setScrollY(scrollY);
563         mDatePrivacyView.setScrollY(scrollY);
564     }
565 }
566