1 /* 2 * Copyright (C) 2015 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 package com.android.systemui.statusbar.phone; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import com.android.internal.annotations.VisibleForTesting; 22 import com.android.internal.statusbar.StatusBarIcon; 23 import java.io.PrintWriter; 24 import java.util.ArrayList; 25 import java.util.List; 26 27 import static com.android.systemui.statusbar.phone.StatusBarIconController.TAG_PRIMARY; 28 29 public class StatusBarIconList { 30 private ArrayList<Slot> mSlots = new ArrayList<>(); 31 32 public StatusBarIconList(String[] slots) { 33 final int N = slots.length; 34 for (int i=0; i < N; i++) { 35 mSlots.add(new Slot(slots[i], null)); 36 } 37 } 38 39 public int getSlotIndex(String slot) { 40 final int N = mSlots.size(); 41 for (int i=0; i<N; i++) { 42 Slot item = mSlots.get(i); 43 if (item.getName().equals(slot)) { 44 return i; 45 } 46 } 47 // Auto insert new items at the beginning. 48 mSlots.add(0, new Slot(slot, null)); 49 return 0; 50 } 51 52 protected ArrayList<Slot> getSlots() { 53 return new ArrayList<>(mSlots); 54 } 55 56 protected Slot getSlot(String name) { 57 return mSlots.get(getSlotIndex(name)); 58 } 59 60 public int size() { 61 return mSlots.size(); 62 } 63 64 public void setIcon(int index, @NonNull StatusBarIconHolder holder) { 65 mSlots.get(index).addHolder(holder); 66 } 67 68 public void removeIcon(int index, int tag) { 69 mSlots.get(index).removeForTag(tag); 70 } 71 72 public String getSlotName(int index) { 73 return mSlots.get(index).getName(); 74 } 75 76 public StatusBarIconHolder getIcon(int index, int tag) { 77 return mSlots.get(index).getHolderForTag(tag); 78 } 79 80 public int getViewIndex(int slotIndex, int tag) { 81 int count = 0; 82 for (int i = 0; i < slotIndex; i++) { 83 Slot item = mSlots.get(i); 84 if (item.hasIconsInSlot()) { 85 count += item.numberOfIcons(); 86 } 87 } 88 89 Slot viewItem = mSlots.get(slotIndex); 90 return count + viewItem.viewIndexOffsetForTag(tag); 91 } 92 93 public void dump(PrintWriter pw) { 94 pw.println("StatusBarIconList state:"); 95 final int N = mSlots.size(); 96 pw.println(" icon slots: " + N); 97 for (int i=0; i<N; i++) { 98 pw.printf(" %2d:%s\n", i, mSlots.get(i).toString()); 99 } 100 } 101 102 public static class Slot { 103 private final String mName; 104 private StatusBarIconHolder mHolder; 105 /** 106 * Only used if multiple icons are added to the same slot. 107 * 108 * If there are mSubSlots, then these are structured like: 109 * [ First item | (the rest) ] 110 * 111 * The tricky thing to keep in mind here is that the list [mHolder, mSubSlots] is ordered 112 * ascending, but for view logic we should go backwards through the list. I.e., the first 113 * element (mHolder) should be the highest index, because higher priority items go to the 114 * right of lower priority items 115 */ 116 private ArrayList<StatusBarIconHolder> mSubSlots; 117 118 public Slot(String name, StatusBarIconHolder iconHolder) { 119 mName = name; 120 mHolder = iconHolder; 121 } 122 123 public String getName() { 124 return mName; 125 } 126 127 @Nullable 128 public StatusBarIconHolder getHolderForTag(int tag) { 129 if (tag == TAG_PRIMARY) { 130 return mHolder; 131 } 132 133 if (mSubSlots != null) { 134 for (StatusBarIconHolder holder : mSubSlots) { 135 if (holder.getTag() == tag) { 136 return holder; 137 } 138 } 139 } 140 141 return null; 142 } 143 144 public void addHolder(StatusBarIconHolder holder) { 145 int tag = holder.getTag(); 146 if (tag == TAG_PRIMARY) { 147 mHolder = holder; 148 } else { 149 setSubSlot(holder, tag); 150 } 151 } 152 153 public void removeForTag(int tag) { 154 if (tag == TAG_PRIMARY) { 155 mHolder = null; 156 } else { 157 int index = getIndexForTag(tag); 158 if (index != -1) { 159 mSubSlots.remove(index); 160 } 161 } 162 } 163 164 @VisibleForTesting 165 public void clear() { 166 mHolder = null; 167 if (mSubSlots != null) { 168 mSubSlots = null; 169 } 170 } 171 172 private void setSubSlot(StatusBarIconHolder holder, int tag) { 173 if (mSubSlots == null) { 174 mSubSlots = new ArrayList<>(); 175 mSubSlots.add(holder); 176 return; 177 } 178 179 if (getIndexForTag(tag) != -1) { 180 // Holder exists for tag; no-op 181 return; 182 } 183 184 // These holders get added to the end. Confused yet? 185 mSubSlots.add(holder); 186 } 187 188 private int getIndexForTag(int tag) { 189 for (int i = 0; i < mSubSlots.size(); i++) { 190 StatusBarIconHolder h = mSubSlots.get(i); 191 if (h.getTag() == tag) { 192 return i; 193 } 194 } 195 196 return -1; 197 } 198 199 public boolean hasIconsInSlot() { 200 if (mHolder != null) return true; 201 if (mSubSlots == null) return false; 202 203 return mSubSlots.size() > 0; 204 } 205 206 public int numberOfIcons() { 207 int num = mHolder == null ? 0 : 1; 208 if (mSubSlots == null) return num; 209 210 return num + mSubSlots.size(); 211 } 212 213 /** 214 * View index is inverted from regular index, because they are laid out back-to-front 215 * @param tag the tag of the holder being viewed 216 * @return (1 + mSubSlots.size() - indexOfTag) 217 */ 218 public int viewIndexOffsetForTag(int tag) { 219 if (mSubSlots == null) { 220 return 0; 221 } 222 223 int subSlots = mSubSlots.size(); 224 if (tag == TAG_PRIMARY) { 225 return subSlots; 226 } 227 228 return subSlots - getIndexForTag(tag) - 1; 229 } 230 231 /** 232 * Build a list of the {@link StatusBarIconHolder}s in the same order they appear in their 233 * view group. This provides a safe list that can be iterated and inserted into its group. 234 * 235 * @return all holders contained here, in view order 236 */ 237 public List<StatusBarIconHolder> getHolderListInViewOrder() { 238 ArrayList<StatusBarIconHolder> holders = new ArrayList<>(); 239 if (mSubSlots != null) { 240 for (int i = mSubSlots.size() - 1; i >= 0; i--) { 241 holders.add(mSubSlots.get(i)); 242 } 243 } 244 245 if (mHolder != null) { 246 holders.add(mHolder); 247 } 248 249 return holders; 250 } 251 252 @Override 253 public String toString() { 254 return String.format("(%s) %s", mName, subSlotsString()); 255 } 256 257 private String subSlotsString() { 258 if (mSubSlots == null) { 259 return ""; 260 } 261 262 return "" + mSubSlots.size() + " subSlots"; 263 } 264 } 265 } 266