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