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.content.Context;
20import android.graphics.drawable.Icon;
21import android.os.Bundle;
22import android.os.UserHandle;
23import android.text.TextUtils;
24import android.util.ArraySet;
25import android.view.Gravity;
26import android.view.View;
27import android.view.ViewGroup;
28import android.widget.ImageView;
29import android.widget.LinearLayout;
30
31import com.android.internal.statusbar.StatusBarIcon;
32import com.android.systemui.Dependency;
33import com.android.systemui.Dumpable;
34import com.android.systemui.R;
35import com.android.systemui.SysUiServiceProvider;
36import com.android.systemui.statusbar.CommandQueue;
37import com.android.systemui.statusbar.StatusBarIconView;
38import com.android.systemui.statusbar.policy.ConfigurationController;
39import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
40import com.android.systemui.statusbar.policy.DarkIconDispatcher;
41import com.android.systemui.tuner.TunerService;
42import com.android.systemui.tuner.TunerService.Tunable;
43
44import java.io.FileDescriptor;
45import java.io.PrintWriter;
46import java.util.ArrayList;
47
48/**
49 * Receives the callbacks from CommandQueue related to icons and tracks the state of
50 * all the icons. Dispatches this state to any IconManagers that are currently
51 * registered with it.
52 */
53public class StatusBarIconControllerImpl extends StatusBarIconList implements Tunable,
54        ConfigurationListener, Dumpable, CommandQueue.Callbacks, StatusBarIconController {
55
56    private final DarkIconDispatcher mDarkIconDispatcher;
57
58    private Context mContext;
59    private DemoStatusIcons mDemoStatusIcons;
60
61    private final ArrayList<IconManager> mIconGroups = new ArrayList<>();
62
63    private final ArraySet<String> mIconBlacklist = new ArraySet<>();
64
65    public StatusBarIconControllerImpl(Context context) {
66        super(context.getResources().getStringArray(
67                com.android.internal.R.array.config_statusBarIcons));
68        Dependency.get(ConfigurationController.class).addCallback(this);
69        mDarkIconDispatcher = Dependency.get(DarkIconDispatcher.class);
70        mContext = context;
71
72        loadDimens();
73
74        SysUiServiceProvider.getComponent(context, CommandQueue.class)
75                .addCallbacks(this);
76        Dependency.get(TunerService.class).addTunable(this, ICON_BLACKLIST);
77    }
78
79    @Override
80    public void addIconGroup(IconManager group) {
81        mIconGroups.add(group);
82        for (int i = 0; i < mIcons.size(); i++) {
83            StatusBarIcon icon = mIcons.get(i);
84            if (icon != null) {
85                String slot = mSlots.get(i);
86                boolean blocked = mIconBlacklist.contains(slot);
87                group.onIconAdded(getViewIndex(getSlotIndex(slot)), slot, blocked, icon);
88            }
89        }
90    }
91
92    @Override
93    public void removeIconGroup(IconManager group) {
94        group.destroy();
95        mIconGroups.remove(group);
96    }
97
98    @Override
99    public void onTuningChanged(String key, String newValue) {
100        if (!ICON_BLACKLIST.equals(key)) {
101            return;
102        }
103        mIconBlacklist.clear();
104        mIconBlacklist.addAll(StatusBarIconController.getIconBlacklist(newValue));
105        ArrayList<StatusBarIcon> current = new ArrayList<>(mIcons);
106        ArrayList<String> currentSlots = new ArrayList<>(mSlots);
107        // Remove all the icons.
108        for (int i = current.size() - 1; i >= 0; i--) {
109            removeIcon(currentSlots.get(i));
110        }
111        // Add them all back
112        for (int i = 0; i < current.size(); i++) {
113            setIcon(currentSlots.get(i), current.get(i));
114        }
115    }
116
117    private void loadDimens() {
118    }
119
120    private void addSystemIcon(int index, StatusBarIcon icon) {
121        String slot = getSlot(index);
122        int viewIndex = getViewIndex(index);
123        boolean blocked = mIconBlacklist.contains(slot);
124
125        mIconGroups.forEach(l -> l.onIconAdded(viewIndex, slot, blocked, icon));
126    }
127
128    @Override
129    public void setIcon(String slot, int resourceId, CharSequence contentDescription) {
130        int index = getSlotIndex(slot);
131        StatusBarIcon icon = getIcon(index);
132        if (icon == null) {
133            icon = new StatusBarIcon(UserHandle.SYSTEM, mContext.getPackageName(),
134                    Icon.createWithResource(mContext, resourceId), 0, 0, contentDescription);
135            setIcon(slot, icon);
136        } else {
137            icon.icon = Icon.createWithResource(mContext, resourceId);
138            icon.contentDescription = contentDescription;
139            handleSet(index, icon);
140        }
141    }
142
143    @Override
144    public void setExternalIcon(String slot) {
145        int viewIndex = getViewIndex(getSlotIndex(slot));
146        int height = mContext.getResources().getDimensionPixelSize(
147                R.dimen.status_bar_icon_drawing_size);
148        mIconGroups.forEach(l -> l.onIconExternal(viewIndex, height));
149    }
150
151    @Override
152    public void setIcon(String slot, StatusBarIcon icon) {
153        setIcon(getSlotIndex(slot), icon);
154    }
155
156    @Override
157    public void removeIcon(String slot) {
158        int index = getSlotIndex(slot);
159        removeIcon(index);
160    }
161
162    public void setIconVisibility(String slot, boolean visibility) {
163        int index = getSlotIndex(slot);
164        StatusBarIcon icon = getIcon(index);
165        if (icon == null || icon.visible == visibility) {
166            return;
167        }
168        icon.visible = visibility;
169        handleSet(index, icon);
170    }
171
172    @Override
173    public void removeIcon(int index) {
174        if (getIcon(index) == null) {
175            return;
176        }
177        super.removeIcon(index);
178        int viewIndex = getViewIndex(index);
179        mIconGroups.forEach(l -> l.onRemoveIcon(viewIndex));
180    }
181
182    @Override
183    public void setIcon(int index, StatusBarIcon icon) {
184        if (icon == null) {
185            removeIcon(index);
186            return;
187        }
188        boolean isNew = getIcon(index) == null;
189        super.setIcon(index, icon);
190        if (isNew) {
191            addSystemIcon(index, icon);
192        } else {
193            handleSet(index, icon);
194        }
195    }
196
197    private void handleSet(int index, StatusBarIcon icon) {
198        int viewIndex = getViewIndex(index);
199        mIconGroups.forEach(l -> l.onSetIcon(viewIndex, icon));
200    }
201
202    @Override
203    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
204        // TODO: Dump info about all icon groups?
205        ViewGroup statusIcons = mIconGroups.get(0).mGroup;
206        int N = statusIcons.getChildCount();
207        pw.println("  icon views: " + N);
208        for (int i = 0; i < N; i++) {
209            StatusBarIconView ic = (StatusBarIconView) statusIcons.getChildAt(i);
210            pw.println("    [" + i + "] icon=" + ic);
211        }
212        super.dump(pw);
213    }
214
215    public void dispatchDemoCommand(String command, Bundle args) {
216        if (mDemoStatusIcons == null) {
217            // TODO: Rework how we handle demo mode.
218            int iconSize = mContext.getResources().getDimensionPixelSize(
219                    com.android.internal.R.dimen.status_bar_icon_size);
220            mDemoStatusIcons = new DemoStatusIcons((LinearLayout) mIconGroups.get(0).mGroup,
221                    iconSize);
222        }
223        mDemoStatusIcons.dispatchDemoCommand(command, args);
224    }
225
226    @Override
227    public void onDensityOrFontScaleChanged() {
228        loadDimens();
229    }
230}
231