StatusIconContainer.java revision 6303613b216dd56a58033b8e00ef718dce1d7e02
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
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() {
91        float width = getWidth();
92        float translationX = width;
93        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        }
137
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        }
146    }
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}
180