blob: 255e10e2680d357f12218df4dddbe05fc0955728 [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
Evan Laird058c8ae2018-01-12 14:26:10 -050017package com.android.systemui.statusbar.phone;
18
19import android.annotation.Nullable;
20import android.content.Context;
Evan Laird058c8ae2018-01-12 14:26:10 -050021import android.util.AttributeSet;
Evan Lairde1d13c92018-03-20 16:58:01 -040022import android.util.Log;
Evan Laird058c8ae2018-01-12 14:26:10 -050023
24import android.view.View;
25import com.android.keyguard.AlphaOptimizedLinearLayout;
26import com.android.systemui.R;
Evan Lairde1d13c92018-03-20 16:58:01 -040027import com.android.systemui.statusbar.StatusIconDisplayable;
Evan Laird058c8ae2018-01-12 14:26:10 -050028import com.android.systemui.statusbar.stack.ViewState;
29
Evan Laird937d9fa2018-02-08 10:12:16 -080030/**
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 */
Evan Laird058c8ae2018-01-12 14:26:10 -050036public 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
Evan Laird937d9fa2018-02-08 10:12:16 -080043 public StatusIconContainer(Context context) {
44 this(context, null);
45 }
46
Evan Laird058c8ae2018-01-12 14:26:10 -050047 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);
Evan Laird264275e2018-03-01 19:39:39 -050072
73 int width = MeasureSpec.getSize(widthMeasureSpec);
74 int widthSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.UNSPECIFIED);
75
Evan Laird058c8ae2018-01-12 14:26:10 -050076 final int count = getChildCount();
77 // Measure all children so that they report the correct width
78 for (int i = 0; i < count; i++) {
Evan Laird264275e2018-03-01 19:39:39 -050079 measureChild(getChildAt(i), widthSpec, heightMeasureSpec);
Evan Laird058c8ae2018-01-12 14:26:10 -050080 }
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() {
Evan Lairde1d13c92018-03-20 16:58:01 -0400100 float width = getWidth() - getPaddingEnd();
Evan Laird63036132018-02-02 16:30:41 -0500101 float translationX = width;
Evan Laird058c8ae2018-01-12 14:26:10 -0500102 float contentStart = getPaddingStart();
103 int childCount = getChildCount();
104 // Underflow === don't show content until that index
105 int firstUnderflowIndex = -1;
Evan Laird264275e2018-03-01 19:39:39 -0500106 if (DEBUG) android.util.Log.d(TAG, "calculateIconTransitions: start=" + translationX
107 + " width=" + width);
Evan Laird058c8ae2018-01-12 14:26:10 -0500108
109 //TODO: Dots
110 for (int i = childCount - 1; i >= 0; i--) {
111 View child = getChildAt(i);
Evan Lairde1d13c92018-03-20 16:58:01 -0400112 if (!(child instanceof StatusIconDisplayable)) {
113 if (DEBUG) Log.d(TAG, "skipping child (wrong type)");
Evan Laird058c8ae2018-01-12 14:26:10 -0500114 continue;
115 }
116
Evan Lairde1d13c92018-03-20 16:58:01 -0400117 StatusIconDisplayable iconView = (StatusIconDisplayable) child;
118
Evan Laird058c8ae2018-01-12 14:26:10 -0500119 ViewState childState = getViewStateFromChild(child);
120 if (childState == null ) {
Evan Lairde1d13c92018-03-20 16:58:01 -0400121 if (DEBUG) Log.d(TAG, "skipping child (" + iconView.getSlot() + ") no ViewState");
Evan Laird058c8ae2018-01-12 14:26:10 -0500122 continue;
123 }
124
Evan Lairde1d13c92018-03-20 16:58:01 -0400125 if (!iconView.isIconVisible() || iconView.isIconBlocked()) {
Evan Laird058c8ae2018-01-12 14:26:10 -0500126 childState.hidden = true;
Evan Lairde1d13c92018-03-20 16:58:01 -0400127 if (DEBUG) Log.d(TAG, "skipping child (" + iconView.getSlot() + ") not visible");
Evan Laird058c8ae2018-01-12 14:26:10 -0500128 continue;
129 }
130
131 childState.xTranslation = translationX - child.getWidth();
132
133 if (childState.xTranslation < contentStart) {
134 if (firstUnderflowIndex == -1) {
135 firstUnderflowIndex = i;
136 }
137 }
138
139 translationX -= child.getWidth();
140 }
141
142 if (firstUnderflowIndex != -1) {
143 for (int i = 0; i <= firstUnderflowIndex; i++) {
144 View child = getChildAt(i);
145 ViewState vs = getViewStateFromChild(child);
146 if (vs != null) {
147 vs.hidden = true;
148 }
149 }
150 }
Evan Laird63036132018-02-02 16:30:41 -0500151
152 // Stole this from NotificationIconContainer. Not optimal but keeps the layout logic clean
153 if (isLayoutRtl()) {
154 for (int i = 0; i < childCount; i++) {
155 View child = getChildAt(i);
156 ViewState state = getViewStateFromChild(child);
157 state.xTranslation = width - state.xTranslation - child.getWidth();
158 }
159 }
Evan Laird058c8ae2018-01-12 14:26:10 -0500160 }
161
162 private void applyIconStates() {
163 for (int i = 0; i < getChildCount(); i++) {
164 View child = getChildAt(i);
165 ViewState vs = getViewStateFromChild(child);
166 if (vs != null) {
167 vs.applyToView(child);
168 }
169 }
170 }
171
172 private void resetViewStates() {
173 for (int i = 0; i < getChildCount(); i++) {
174 View child = getChildAt(i);
175 ViewState vs = getViewStateFromChild(child);
176 if (vs == null) {
177 continue;
178 }
179
180 vs.initFrom(child);
181 vs.alpha = 1.0f;
Evan Lairde1d13c92018-03-20 16:58:01 -0400182 if (child instanceof StatusIconDisplayable) {
183 vs.hidden = !((StatusIconDisplayable)child).isIconVisible();
Evan Laird058c8ae2018-01-12 14:26:10 -0500184 } else {
185 vs.hidden = false;
186 }
187 }
188 }
189
190 private static @Nullable ViewState getViewStateFromChild(View child) {
191 return (ViewState) child.getTag(R.id.status_bar_view_state_tag);
192 }
193}