1 /* 2 * Copyright (C) 2014 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.qs; 18 19 import static android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS; 20 import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; 21 22 import android.content.Context; 23 import android.content.res.Configuration; 24 import android.graphics.Point; 25 import android.util.AttributeSet; 26 import android.view.View; 27 import android.widget.FrameLayout; 28 29 import com.android.systemui.R; 30 import com.android.systemui.qs.customize.QSCustomizer; 31 32 /** 33 * Wrapper view with background which contains {@link QSPanel} and {@link BaseStatusBarHeader} 34 */ 35 public class QSContainerImpl extends FrameLayout { 36 37 private final Point mSizePoint = new Point(); 38 39 private int mHeightOverride = -1; 40 private QSPanel mQSPanel; 41 private View mQSDetail; 42 private QuickStatusBarHeader mHeader; 43 private float mQsExpansion; 44 private QSCustomizer mQSCustomizer; 45 private View mQSFooter; 46 47 private View mBackground; 48 private View mBackgroundGradient; 49 private View mStatusBarBackground; 50 51 private int mSideMargins; 52 private boolean mQsDisabled; 53 QSContainerImpl(Context context, AttributeSet attrs)54 public QSContainerImpl(Context context, AttributeSet attrs) { 55 super(context, attrs); 56 } 57 58 @Override onFinishInflate()59 protected void onFinishInflate() { 60 super.onFinishInflate(); 61 mQSPanel = findViewById(R.id.quick_settings_panel); 62 mQSDetail = findViewById(R.id.qs_detail); 63 mHeader = findViewById(R.id.header); 64 mQSCustomizer = findViewById(R.id.qs_customize); 65 mQSFooter = findViewById(R.id.qs_footer); 66 mBackground = findViewById(R.id.quick_settings_background); 67 mStatusBarBackground = findViewById(R.id.quick_settings_status_bar_background); 68 mBackgroundGradient = findViewById(R.id.quick_settings_gradient_view); 69 mSideMargins = getResources().getDimensionPixelSize(R.dimen.notification_side_paddings); 70 71 setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO); 72 setMargins(); 73 } 74 75 @Override onConfigurationChanged(Configuration newConfig)76 protected void onConfigurationChanged(Configuration newConfig) { 77 super.onConfigurationChanged(newConfig); 78 setBackgroundGradientVisibility(newConfig); 79 updateResources(); 80 mSizePoint.set(0, 0); // Will be retrieved on next measure pass. 81 } 82 83 @Override performClick()84 public boolean performClick() { 85 // Want to receive clicks so missing QQS tiles doesn't cause collapse, but 86 // don't want to do anything with them. 87 return true; 88 } 89 90 @Override onMeasure(int widthMeasureSpec, int heightMeasureSpec)91 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 92 // QSPanel will show as many rows as it can (up to TileLayout.MAX_ROWS) such that the 93 // bottom and footer are inside the screen. 94 Configuration config = getResources().getConfiguration(); 95 boolean navBelow = config.smallestScreenWidthDp >= 600 96 || config.orientation != Configuration.ORIENTATION_LANDSCAPE; 97 MarginLayoutParams layoutParams = (MarginLayoutParams) mQSPanel.getLayoutParams(); 98 99 // The footer is pinned to the bottom of QSPanel (same bottoms), therefore we don't need to 100 // subtract its height. We do not care if the collapsed notifications fit in the screen. 101 int maxQs = getDisplayHeight() - layoutParams.topMargin - layoutParams.bottomMargin 102 - getPaddingBottom(); 103 if (navBelow) { 104 maxQs -= getResources().getDimensionPixelSize(R.dimen.navigation_bar_height); 105 } 106 // Measure with EXACTLY. That way, PagedTileLayout will only use excess height and will be 107 // measured last, after other views and padding is accounted for. 108 mQSPanel.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(maxQs, MeasureSpec.EXACTLY)); 109 int width = mQSPanel.getMeasuredWidth(); 110 int height = layoutParams.topMargin + layoutParams.bottomMargin 111 + mQSPanel.getMeasuredHeight() + getPaddingBottom(); 112 super.onMeasure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), 113 MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)); 114 115 // QSCustomizer will always be the height of the screen, but do this after 116 // other measuring to avoid changing the height of the QS. 117 mQSCustomizer.measure(widthMeasureSpec, 118 MeasureSpec.makeMeasureSpec(getDisplayHeight(), MeasureSpec.EXACTLY)); 119 } 120 121 @Override onLayout(boolean changed, int left, int top, int right, int bottom)122 protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 123 super.onLayout(changed, left, top, right, bottom); 124 updateExpansion(); 125 } 126 disable(int state1, int state2, boolean animate)127 public void disable(int state1, int state2, boolean animate) { 128 final boolean disabled = (state2 & DISABLE2_QUICK_SETTINGS) != 0; 129 if (disabled == mQsDisabled) return; 130 mQsDisabled = disabled; 131 setBackgroundGradientVisibility(getResources().getConfiguration()); 132 mBackground.setVisibility(mQsDisabled ? View.GONE : View.VISIBLE); 133 } 134 updateResources()135 private void updateResources() { 136 LayoutParams layoutParams = (LayoutParams) mQSPanel.getLayoutParams(); 137 layoutParams.topMargin = mContext.getResources().getDimensionPixelSize( 138 com.android.internal.R.dimen.quick_qs_offset_height); 139 140 mQSPanel.setLayoutParams(layoutParams); 141 } 142 143 /** 144 * Overrides the height of this view (post-layout), so that the content is clipped to that 145 * height and the background is set to that height. 146 * 147 * @param heightOverride the overridden height 148 */ setHeightOverride(int heightOverride)149 public void setHeightOverride(int heightOverride) { 150 mHeightOverride = heightOverride; 151 updateExpansion(); 152 } 153 updateExpansion()154 public void updateExpansion() { 155 int height = calculateContainerHeight(); 156 setBottom(getTop() + height); 157 mQSDetail.setBottom(getTop() + height); 158 // Pin QS Footer to the bottom of the panel. 159 mQSFooter.setTranslationY(height - mQSFooter.getHeight()); 160 mBackground.setTop(mQSPanel.getTop()); 161 mBackground.setBottom(height); 162 } 163 calculateContainerHeight()164 protected int calculateContainerHeight() { 165 int heightOverride = mHeightOverride != -1 ? mHeightOverride : getMeasuredHeight(); 166 return mQSCustomizer.isCustomizing() ? mQSCustomizer.getHeight() 167 : Math.round(mQsExpansion * (heightOverride - mHeader.getHeight())) 168 + mHeader.getHeight(); 169 } 170 setBackgroundGradientVisibility(Configuration newConfig)171 private void setBackgroundGradientVisibility(Configuration newConfig) { 172 if (newConfig.orientation == ORIENTATION_LANDSCAPE) { 173 mBackgroundGradient.setVisibility(View.INVISIBLE); 174 mStatusBarBackground.setVisibility(View.INVISIBLE); 175 } else { 176 mBackgroundGradient.setVisibility(mQsDisabled ? View.INVISIBLE : View.VISIBLE); 177 mStatusBarBackground.setVisibility(View.VISIBLE); 178 } 179 } 180 setExpansion(float expansion)181 public void setExpansion(float expansion) { 182 mQsExpansion = expansion; 183 updateExpansion(); 184 } 185 setMargins()186 private void setMargins() { 187 setMargins(mQSDetail); 188 setMargins(mBackground); 189 setMargins(mQSFooter); 190 mQSPanel.setMargins(mSideMargins); 191 mHeader.setMargins(mSideMargins); 192 } 193 setMargins(View view)194 private void setMargins(View view) { 195 FrameLayout.LayoutParams lp = (LayoutParams) view.getLayoutParams(); 196 lp.rightMargin = mSideMargins; 197 lp.leftMargin = mSideMargins; 198 } 199 getDisplayHeight()200 private int getDisplayHeight() { 201 if (mSizePoint.y == 0) { 202 getDisplay().getRealSize(mSizePoint); 203 } 204 return mSizePoint.y; 205 } 206 } 207