StatusIconContainer.java revision 264275e85824148d18ba0656de7826ea0e7ab2aa
1/* 2 * Copyright (C) 2017 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 17package com.android.systemui.statusbar.phone; 18 19import android.annotation.Nullable; 20import android.content.Context; 21import android.util.ArrayMap; 22import android.util.AttributeSet; 23 24import android.view.View; 25import com.android.keyguard.AlphaOptimizedLinearLayout; 26import com.android.systemui.R; 27import com.android.systemui.statusbar.StatusBarIconView; 28import com.android.systemui.statusbar.stack.ViewState; 29 30/** 31 * A container for Status bar system icons. Limits the number of system icons and handles overflow 32 * similar to NotificationIconController. Can be used to layout nested StatusIconContainers 33 * 34 * Children are expected to be of type StatusBarIconView. 35 */ 36public class StatusIconContainer extends AlphaOptimizedLinearLayout { 37 38 private static final String TAG = "StatusIconContainer"; 39 private static final boolean DEBUG = false; 40 private static final int MAX_ICONS = 5; 41 private static final int MAX_DOTS = 3; 42 43 public StatusIconContainer(Context context) { 44 this(context, null); 45 } 46 47 public StatusIconContainer(Context context, AttributeSet attrs) { 48 super(context, attrs); 49 } 50 51 @Override 52 protected void onLayout(boolean changed, int l, int t, int r, int b) { 53 float midY = getHeight() / 2.0f; 54 55 // Layout all child views so that we can move them around later 56 for (int i = 0; i < getChildCount(); i++) { 57 View child = getChildAt(i); 58 int width = child.getMeasuredWidth(); 59 int height = child.getMeasuredHeight(); 60 int top = (int) (midY - height / 2.0f); 61 child.layout(0, top, width, top + height); 62 } 63 64 resetViewStates(); 65 calculateIconTranslations(); 66 applyIconStates(); 67 } 68 69 @Override 70 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 71 super.onMeasure(widthMeasureSpec, heightMeasureSpec); 72 73 int width = MeasureSpec.getSize(widthMeasureSpec); 74 int widthSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.UNSPECIFIED); 75 76 final int count = getChildCount(); 77 // Measure all children so that they report the correct width 78 for (int i = 0; i < count; i++) { 79 measureChild(getChildAt(i), widthSpec, heightMeasureSpec); 80 } 81 } 82 83 @Override 84 public void onViewAdded(View child) { 85 super.onViewAdded(child); 86 ViewState vs = new ViewState(); 87 child.setTag(R.id.status_bar_view_state_tag, vs); 88 } 89 90 @Override 91 public void onViewRemoved(View child) { 92 super.onViewRemoved(child); 93 child.setTag(R.id.status_bar_view_state_tag, null); 94 } 95 96 /** 97 * Layout is happening from end -> start 98 */ 99 private void calculateIconTranslations() { 100 float width = getWidth(); 101 float translationX = width; 102 float contentStart = getPaddingStart(); 103 int childCount = getChildCount(); 104 // Underflow === don't show content until that index 105 int firstUnderflowIndex = -1; 106 if (DEBUG) android.util.Log.d(TAG, "calculateIconTransitions: start=" + translationX 107 + " width=" + width); 108 109 //TODO: Dots 110 for (int i = childCount - 1; i >= 0; i--) { 111 View child = getChildAt(i); 112 if (!(child instanceof StatusBarIconView)) { 113 continue; 114 } 115 116 ViewState childState = getViewStateFromChild(child); 117 if (childState == null ) { 118 continue; 119 } 120 121 // Rely on StatusBarIcon for truth about visibility 122 if (!((StatusBarIconView) child).getStatusBarIcon().visible) { 123 childState.hidden = true; 124 continue; 125 } 126 127 childState.xTranslation = translationX - child.getWidth(); 128 129 if (childState.xTranslation < contentStart) { 130 if (firstUnderflowIndex == -1) { 131 firstUnderflowIndex = i; 132 } 133 } 134 135 translationX -= child.getWidth(); 136 } 137 138 if (firstUnderflowIndex != -1) { 139 for (int i = 0; i <= firstUnderflowIndex; i++) { 140 View child = getChildAt(i); 141 ViewState vs = getViewStateFromChild(child); 142 if (vs != null) { 143 vs.hidden = true; 144 } 145 } 146 } 147 148 // Stole this from NotificationIconContainer. Not optimal but keeps the layout logic clean 149 if (isLayoutRtl()) { 150 for (int i = 0; i < childCount; i++) { 151 View child = getChildAt(i); 152 ViewState state = getViewStateFromChild(child); 153 state.xTranslation = width - state.xTranslation - child.getWidth(); 154 } 155 } 156 } 157 158 private void applyIconStates() { 159 for (int i = 0; i < getChildCount(); i++) { 160 View child = getChildAt(i); 161 ViewState vs = getViewStateFromChild(child); 162 if (vs != null) { 163 vs.applyToView(child); 164 } 165 } 166 } 167 168 private void resetViewStates() { 169 for (int i = 0; i < getChildCount(); i++) { 170 View child = getChildAt(i); 171 ViewState vs = getViewStateFromChild(child); 172 if (vs == null) { 173 continue; 174 } 175 176 vs.initFrom(child); 177 vs.alpha = 1.0f; 178 if (child instanceof StatusBarIconView) { 179 vs.hidden = !((StatusBarIconView)child).getStatusBarIcon().visible; 180 } else { 181 vs.hidden = false; 182 } 183 } 184 } 185 186 private static @Nullable ViewState getViewStateFromChild(View child) { 187 return (ViewState) child.getTag(R.id.status_bar_view_state_tag); 188 } 189} 190