blob: 1da50ad65d623d81a048dd9d27c7e17ae751b475 [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";
39 private static final int MAX_ICONS = 5;
40 private static final int MAX_DOTS = 3;
41
42 public StatusIconContainer(Context context, AttributeSet attrs) {
43 super(context, attrs);
44 }
45
46 @Override
47 protected void onLayout(boolean changed, int l, int t, int r, int b) {
48 float midY = getHeight() / 2.0f;
49
50 // Layout all child views so that we can move them around later
51 for (int i = 0; i < getChildCount(); i++) {
52 View child = getChildAt(i);
53 int width = child.getMeasuredWidth();
54 int height = child.getMeasuredHeight();
55 int top = (int) (midY - height / 2.0f);
56 child.layout(0, top, width, top + height);
57 }
58
59 resetViewStates();
60 calculateIconTranslations();
61 applyIconStates();
62 }
63
64 @Override
65 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
66 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
67 final int count = getChildCount();
68 // Measure all children so that they report the correct width
69 for (int i = 0; i < count; i++) {
70 measureChild(getChildAt(i), widthMeasureSpec, heightMeasureSpec);
71 }
72 }
73
74 @Override
75 public void onViewAdded(View child) {
76 super.onViewAdded(child);
77 ViewState vs = new ViewState();
78 child.setTag(R.id.status_bar_view_state_tag, vs);
79 }
80
81 @Override
82 public void onViewRemoved(View child) {
83 super.onViewRemoved(child);
84 child.setTag(R.id.status_bar_view_state_tag, null);
85 }
86
87 /**
88 * Layout is happening from end -> start
89 */
90 private void calculateIconTranslations() {
Evan Laird63036132018-02-02 16:30:41 -050091 float width = getWidth();
92 float translationX = width;
Evan Laird058c8ae2018-01-12 14:26:10 -050093 float contentStart = getPaddingStart();
94 int childCount = getChildCount();
95 // Underflow === don't show content until that index
96 int firstUnderflowIndex = -1;
97 android.util.Log.d(TAG, "calculateIconTransitions: start=" + translationX);
98
99 //TODO: Dots
100 for (int i = childCount - 1; i >= 0; i--) {
101 View child = getChildAt(i);
102 if (!(child instanceof StatusBarIconView)) {
103 continue;
104 }
105
106 ViewState childState = getViewStateFromChild(child);
107 if (childState == null ) {
108 continue;
109 }
110
111 // Rely on StatusBarIcon for truth about visibility
112 if (!((StatusBarIconView) child).getStatusBarIcon().visible) {
113 childState.hidden = true;
114 continue;
115 }
116
117 childState.xTranslation = translationX - child.getWidth();
118
119 if (childState.xTranslation < contentStart) {
120 if (firstUnderflowIndex == -1) {
121 firstUnderflowIndex = i;
122 }
123 }
124
125 translationX -= child.getWidth();
126 }
127
128 if (firstUnderflowIndex != -1) {
129 for (int i = 0; i <= firstUnderflowIndex; i++) {
130 View child = getChildAt(i);
131 ViewState vs = getViewStateFromChild(child);
132 if (vs != null) {
133 vs.hidden = true;
134 }
135 }
136 }
Evan Laird63036132018-02-02 16:30:41 -0500137
138 // Stole this from NotificationIconContainer. Not optimal but keeps the layout logic clean
139 if (isLayoutRtl()) {
140 for (int i = 0; i < childCount; i++) {
141 View child = getChildAt(i);
142 ViewState state = getViewStateFromChild(child);
143 state.xTranslation = width - state.xTranslation - child.getWidth();
144 }
145 }
Evan Laird058c8ae2018-01-12 14:26:10 -0500146 }
147
148 private void applyIconStates() {
149 for (int i = 0; i < getChildCount(); i++) {
150 View child = getChildAt(i);
151 ViewState vs = getViewStateFromChild(child);
152 if (vs != null) {
153 vs.applyToView(child);
154 }
155 }
156 }
157
158 private void resetViewStates() {
159 for (int i = 0; i < getChildCount(); i++) {
160 View child = getChildAt(i);
161 ViewState vs = getViewStateFromChild(child);
162 if (vs == null) {
163 continue;
164 }
165
166 vs.initFrom(child);
167 vs.alpha = 1.0f;
168 if (child instanceof StatusBarIconView) {
169 vs.hidden = !((StatusBarIconView)child).getStatusBarIcon().visible;
170 } else {
171 vs.hidden = false;
172 }
173 }
174 }
175
176 private static @Nullable ViewState getViewStateFromChild(View child) {
177 return (ViewState) child.getTag(R.id.status_bar_view_state_tag);
178 }
179}