1 /* 2 * Copyright (C) 2010 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.hierarchyviewerlib.models; 18 19 import com.android.ddmlib.IDevice; 20 import com.android.hierarchyviewerlib.device.DeviceBridge.ViewServerInfo; 21 import com.android.hierarchyviewerlib.device.Window; 22 23 import java.util.ArrayList; 24 import java.util.HashMap; 25 26 /** 27 * This class stores the list of windows for each connected device. It notifies 28 * listeners of any changes as well as knows which window is currently selected 29 * in the device selector. 30 */ 31 public class DeviceSelectionModel { 32 33 private final HashMap<IDevice, DeviceInfo> mDeviceMap = new HashMap<IDevice, DeviceInfo>(); 34 35 private final HashMap<IDevice, Integer> mFocusedWindowHashes = new HashMap<IDevice, Integer>(); 36 37 private final ArrayList<IDevice> mDeviceList = new ArrayList<IDevice>(); 38 39 private final ArrayList<IWindowChangeListener> mWindowChangeListeners = 40 new ArrayList<IWindowChangeListener>(); 41 42 private IDevice mSelectedDevice; 43 44 private Window mSelectedWindow; 45 46 private static DeviceSelectionModel sModel; 47 48 private static class DeviceInfo { 49 Window[] windows; 50 ViewServerInfo viewServerInfo; 51 52 private DeviceInfo(Window[] windows, ViewServerInfo viewServerInfo) { 53 this.windows = windows; 54 this.viewServerInfo = viewServerInfo; 55 } 56 } 57 public static DeviceSelectionModel getModel() { 58 if (sModel == null) { 59 sModel = new DeviceSelectionModel(); 60 } 61 return sModel; 62 } 63 64 public boolean containsDevice(IDevice device) { 65 synchronized (mDeviceMap) { 66 return mDeviceMap.containsKey(device); 67 } 68 } 69 70 public void addDevice(IDevice device, Window[] windows, ViewServerInfo info) { 71 synchronized (mDeviceMap) { 72 mDeviceMap.put(device, new DeviceInfo(windows, info)); 73 mDeviceList.add(device); 74 } 75 notifyDeviceConnected(device); 76 } 77 78 public void removeDevice(IDevice device) { 79 boolean selectionChanged = false; 80 synchronized (mDeviceMap) { 81 mDeviceList.remove(device); 82 if (!mDeviceList.contains(device)) { 83 mDeviceMap.remove(device); 84 mFocusedWindowHashes.remove(device); 85 if (mSelectedDevice == device) { 86 mSelectedDevice = null; 87 mSelectedWindow = null; 88 selectionChanged = true; 89 } 90 } 91 } 92 notifyDeviceDisconnected(device); 93 if (selectionChanged) { 94 notifySelectionChanged(mSelectedDevice, mSelectedWindow); 95 } 96 } 97 98 public void updateDevice(IDevice device, Window[] windows) { 99 boolean selectionChanged = false; 100 synchronized (mDeviceMap) { 101 DeviceInfo oldDeviceInfo = mDeviceMap.get(device); 102 ViewServerInfo oldViewServerInfo = null; 103 if (oldDeviceInfo != null) { 104 oldViewServerInfo = oldDeviceInfo.viewServerInfo; 105 } 106 mDeviceMap.put(device, new DeviceInfo(windows, oldViewServerInfo)); 107 // If the selected window no longer exists, we clear the selection. 108 if (mSelectedDevice == device && mSelectedWindow != null) { 109 boolean windowStillExists = false; 110 for (int i = 0; i < windows.length && !windowStillExists; i++) { 111 if (windows[i].equals(mSelectedWindow)) { 112 windowStillExists = true; 113 } 114 } 115 if (!windowStillExists) { 116 mSelectedDevice = null; 117 mSelectedWindow = null; 118 selectionChanged = true; 119 } 120 } 121 } 122 notifyDeviceChanged(device); 123 if (selectionChanged) { 124 notifySelectionChanged(mSelectedDevice, mSelectedWindow); 125 } 126 } 127 128 /* 129 * Change which window has focus and notify the listeners. 130 */ 131 public void updateFocusedWindow(IDevice device, int focusedWindow) { 132 Integer oldValue = null; 133 synchronized (mDeviceMap) { 134 oldValue = mFocusedWindowHashes.put(device, new Integer(focusedWindow)); 135 } 136 // Only notify if the values are different. It would be cool if Java 137 // containers accepted basic types like int. 138 if (oldValue == null || (oldValue != null && oldValue.intValue() != focusedWindow)) { 139 notifyFocusChanged(device); 140 } 141 } 142 143 public static interface IWindowChangeListener { 144 public void deviceConnected(IDevice device); 145 146 public void deviceChanged(IDevice device); 147 148 public void deviceDisconnected(IDevice device); 149 150 public void focusChanged(IDevice device); 151 152 public void selectionChanged(IDevice device, Window window); 153 } 154 155 private IWindowChangeListener[] getWindowChangeListenerList() { 156 IWindowChangeListener[] listeners = null; 157 synchronized (mWindowChangeListeners) { 158 if (mWindowChangeListeners.size() == 0) { 159 return null; 160 } 161 listeners = 162 mWindowChangeListeners.toArray(new IWindowChangeListener[mWindowChangeListeners 163 .size()]); 164 } 165 return listeners; 166 } 167 168 private void notifyDeviceConnected(IDevice device) { 169 IWindowChangeListener[] listeners = getWindowChangeListenerList(); 170 if (listeners != null) { 171 for (int i = 0; i < listeners.length; i++) { 172 listeners[i].deviceConnected(device); 173 } 174 } 175 } 176 177 private void notifyDeviceChanged(IDevice device) { 178 IWindowChangeListener[] listeners = getWindowChangeListenerList(); 179 if (listeners != null) { 180 for (int i = 0; i < listeners.length; i++) { 181 listeners[i].deviceChanged(device); 182 } 183 } 184 } 185 186 private void notifyDeviceDisconnected(IDevice device) { 187 IWindowChangeListener[] listeners = getWindowChangeListenerList(); 188 if (listeners != null) { 189 for (int i = 0; i < listeners.length; i++) { 190 listeners[i].deviceDisconnected(device); 191 } 192 } 193 } 194 195 private void notifyFocusChanged(IDevice device) { 196 IWindowChangeListener[] listeners = getWindowChangeListenerList(); 197 if (listeners != null) { 198 for (int i = 0; i < listeners.length; i++) { 199 listeners[i].focusChanged(device); 200 } 201 } 202 } 203 204 private void notifySelectionChanged(IDevice device, Window window) { 205 IWindowChangeListener[] listeners = getWindowChangeListenerList(); 206 if (listeners != null) { 207 for (int i = 0; i < listeners.length; i++) { 208 listeners[i].selectionChanged(device, window); 209 } 210 } 211 } 212 213 public void addWindowChangeListener(IWindowChangeListener listener) { 214 synchronized (mWindowChangeListeners) { 215 mWindowChangeListeners.add(listener); 216 } 217 } 218 219 public void removeWindowChangeListener(IWindowChangeListener listener) { 220 synchronized (mWindowChangeListeners) { 221 mWindowChangeListeners.remove(listener); 222 } 223 } 224 225 public IDevice[] getDevices() { 226 synchronized (mDeviceMap) { 227 return mDeviceList.toArray(new IDevice[mDeviceList.size()]); 228 } 229 } 230 231 public Window[] getWindows(IDevice device) { 232 Window[] windows = null; 233 synchronized (mDeviceMap) { 234 DeviceInfo info = mDeviceMap.get(device); 235 if (info != null) { 236 windows = mDeviceMap.get(device).windows; 237 } 238 } 239 return windows; 240 } 241 242 // Returns the window that currently has focus or -1. Note that this means 243 // that a window with hashcode -1 gets highlighted. If you remember, this is 244 // the infamous <Focused Window> 245 public int getFocusedWindow(IDevice device) { 246 synchronized (mDeviceMap) { 247 Integer focusedWindow = mFocusedWindowHashes.get(device); 248 if (focusedWindow == null) { 249 return -1; 250 } 251 return focusedWindow.intValue(); 252 } 253 } 254 255 public void setSelection(IDevice device, Window window) { 256 synchronized (mDeviceMap) { 257 mSelectedDevice = device; 258 mSelectedWindow = window; 259 } 260 notifySelectionChanged(device, window); 261 } 262 263 public IDevice getSelectedDevice() { 264 synchronized (mDeviceMap) { 265 return mSelectedDevice; 266 } 267 } 268 269 public Window getSelectedWindow() { 270 synchronized (mDeviceMap) { 271 return mSelectedWindow; 272 } 273 } 274 275 public ViewServerInfo getSelectedDeviceInfo() { 276 synchronized (mDeviceMap) { 277 ViewServerInfo viewServerInfo = null; 278 if (mSelectedDevice != null) { 279 return mDeviceMap.get(mSelectedDevice).viewServerInfo; 280 } 281 return null; 282 } 283 } 284 285 } 286