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.ViewNode; 21 22 import org.eclipse.swt.graphics.Image; 23 import org.eclipse.swt.graphics.Point; 24 import org.eclipse.swt.widgets.Display; 25 26 import java.util.ArrayList; 27 28 public class PixelPerfectModel { 29 30 public static final int MIN_ZOOM = 2; 31 32 public static final int MAX_ZOOM = 24; 33 34 public static final int DEFAULT_ZOOM = 8; 35 36 public static final int DEFAULT_OVERLAY_TRANSPARENCY_PERCENTAGE = 50; 37 38 private IDevice mDevice; 39 40 private Image mImage; 41 42 private Point mCrosshairLocation; 43 44 private ViewNode mViewNode; 45 46 private ViewNode mSelectedNode; 47 48 private int mZoom; 49 50 private final ArrayList<IImageChangeListener> mImageChangeListeners = 51 new ArrayList<IImageChangeListener>(); 52 53 private Image mOverlayImage; 54 55 private double mOverlayTransparency = DEFAULT_OVERLAY_TRANSPARENCY_PERCENTAGE / 100.0; 56 57 private static PixelPerfectModel sModel; 58 59 public static PixelPerfectModel getModel() { 60 if (sModel == null) { 61 sModel = new PixelPerfectModel(); 62 } 63 return sModel; 64 } 65 66 public void setData(final IDevice device, final Image image, final ViewNode viewNode) { 67 final Image toDispose = this.mImage; 68 final Image toDispose2 = this.mOverlayImage; 69 Display.getDefault().syncExec(new Runnable() { 70 public void run() { 71 synchronized (PixelPerfectModel.this) { 72 PixelPerfectModel.this.mDevice = device; 73 PixelPerfectModel.this.mImage = image; 74 PixelPerfectModel.this.mViewNode = viewNode; 75 if (image != null) { 76 PixelPerfectModel.this.mCrosshairLocation = 77 new Point(image.getBounds().width / 2, image.getBounds().height / 2); 78 } else { 79 PixelPerfectModel.this.mCrosshairLocation = null; 80 } 81 mOverlayImage = null; 82 PixelPerfectModel.this.mSelectedNode = null; 83 mZoom = DEFAULT_ZOOM; 84 } 85 } 86 }); 87 notifyImageLoaded(); 88 if (toDispose != null) { 89 Display.getDefault().syncExec(new Runnable() { 90 public void run() { 91 toDispose.dispose(); 92 } 93 }); 94 } 95 if (toDispose2 != null) { 96 Display.getDefault().syncExec(new Runnable() { 97 public void run() { 98 toDispose2.dispose(); 99 } 100 }); 101 } 102 103 } 104 105 public void setCrosshairLocation(int x, int y) { 106 synchronized (this) { 107 mCrosshairLocation = new Point(x, y); 108 } 109 notifyCrosshairMoved(); 110 } 111 112 public void setSelected(ViewNode selected) { 113 synchronized (this) { 114 this.mSelectedNode = selected; 115 } 116 notifySelectionChanged(); 117 } 118 119 public void setTree(final ViewNode viewNode) { 120 Display.getDefault().syncExec(new Runnable() { 121 public void run() { 122 synchronized (PixelPerfectModel.this) { 123 PixelPerfectModel.this.mViewNode = viewNode; 124 PixelPerfectModel.this.mSelectedNode = null; 125 } 126 } 127 }); 128 notifyTreeChanged(); 129 } 130 131 public void setImage(final Image image) { 132 final Image toDispose = this.mImage; 133 Display.getDefault().syncExec(new Runnable() { 134 public void run() { 135 synchronized (PixelPerfectModel.this) { 136 PixelPerfectModel.this.mImage = image; 137 } 138 } 139 }); 140 notifyImageChanged(); 141 if (toDispose != null) { 142 Display.getDefault().syncExec(new Runnable() { 143 public void run() { 144 toDispose.dispose(); 145 } 146 }); 147 } 148 } 149 150 public void setZoom(int newZoom) { 151 synchronized (this) { 152 if (newZoom < MIN_ZOOM) { 153 newZoom = MIN_ZOOM; 154 } 155 if (newZoom > MAX_ZOOM) { 156 newZoom = MAX_ZOOM; 157 } 158 mZoom = newZoom; 159 } 160 notifyZoomChanged(); 161 } 162 163 public void setOverlayImage(final Image overlayImage) { 164 final Image toDispose = this.mOverlayImage; 165 Display.getDefault().syncExec(new Runnable() { 166 public void run() { 167 synchronized (PixelPerfectModel.this) { 168 PixelPerfectModel.this.mOverlayImage = overlayImage; 169 } 170 } 171 }); 172 notifyOverlayChanged(); 173 if (toDispose != null) { 174 Display.getDefault().syncExec(new Runnable() { 175 public void run() { 176 toDispose.dispose(); 177 } 178 }); 179 } 180 } 181 182 public void setOverlayTransparency(double value) { 183 synchronized (this) { 184 value = Math.max(value, 0); 185 value = Math.min(value, 1); 186 mOverlayTransparency = value; 187 } 188 notifyOverlayTransparencyChanged(); 189 } 190 191 public ViewNode getViewNode() { 192 synchronized (this) { 193 return mViewNode; 194 } 195 } 196 197 public Point getCrosshairLocation() { 198 synchronized (this) { 199 return mCrosshairLocation; 200 } 201 } 202 203 public Image getImage() { 204 synchronized (this) { 205 return mImage; 206 } 207 } 208 209 public ViewNode getSelected() { 210 synchronized (this) { 211 return mSelectedNode; 212 } 213 } 214 215 public IDevice getDevice() { 216 synchronized (this) { 217 return mDevice; 218 } 219 } 220 221 public int getZoom() { 222 synchronized (this) { 223 return mZoom; 224 } 225 } 226 227 public Image getOverlayImage() { 228 synchronized (this) { 229 return mOverlayImage; 230 } 231 } 232 233 public double getOverlayTransparency() { 234 synchronized (this) { 235 return mOverlayTransparency; 236 } 237 } 238 239 public static interface IImageChangeListener { 240 public void imageLoaded(); 241 242 public void imageChanged(); 243 244 public void crosshairMoved(); 245 246 public void selectionChanged(); 247 248 public void treeChanged(); 249 250 public void zoomChanged(); 251 252 public void overlayChanged(); 253 254 public void overlayTransparencyChanged(); 255 } 256 257 private IImageChangeListener[] getImageChangeListenerList() { 258 IImageChangeListener[] listeners = null; 259 synchronized (mImageChangeListeners) { 260 if (mImageChangeListeners.size() == 0) { 261 return null; 262 } 263 listeners = 264 mImageChangeListeners.toArray(new IImageChangeListener[mImageChangeListeners 265 .size()]); 266 } 267 return listeners; 268 } 269 270 public void notifyImageLoaded() { 271 IImageChangeListener[] listeners = getImageChangeListenerList(); 272 if (listeners != null) { 273 for (int i = 0; i < listeners.length; i++) { 274 listeners[i].imageLoaded(); 275 } 276 } 277 } 278 279 public void notifyImageChanged() { 280 IImageChangeListener[] listeners = getImageChangeListenerList(); 281 if (listeners != null) { 282 for (int i = 0; i < listeners.length; i++) { 283 listeners[i].imageChanged(); 284 } 285 } 286 } 287 288 public void notifyCrosshairMoved() { 289 IImageChangeListener[] listeners = getImageChangeListenerList(); 290 if (listeners != null) { 291 for (int i = 0; i < listeners.length; i++) { 292 listeners[i].crosshairMoved(); 293 } 294 } 295 } 296 297 public void notifySelectionChanged() { 298 IImageChangeListener[] listeners = getImageChangeListenerList(); 299 if (listeners != null) { 300 for (int i = 0; i < listeners.length; i++) { 301 listeners[i].selectionChanged(); 302 } 303 } 304 } 305 306 public void notifyTreeChanged() { 307 IImageChangeListener[] listeners = getImageChangeListenerList(); 308 if (listeners != null) { 309 for (int i = 0; i < listeners.length; i++) { 310 listeners[i].treeChanged(); 311 } 312 } 313 } 314 315 public void notifyZoomChanged() { 316 IImageChangeListener[] listeners = getImageChangeListenerList(); 317 if (listeners != null) { 318 for (int i = 0; i < listeners.length; i++) { 319 listeners[i].zoomChanged(); 320 } 321 } 322 } 323 324 public void notifyOverlayChanged() { 325 IImageChangeListener[] listeners = getImageChangeListenerList(); 326 if (listeners != null) { 327 for (int i = 0; i < listeners.length; i++) { 328 listeners[i].overlayChanged(); 329 } 330 } 331 } 332 333 public void notifyOverlayTransparencyChanged() { 334 IImageChangeListener[] listeners = getImageChangeListenerList(); 335 if (listeners != null) { 336 for (int i = 0; i < listeners.length; i++) { 337 listeners[i].overlayTransparencyChanged(); 338 } 339 } 340 } 341 342 public void addImageChangeListener(IImageChangeListener listener) { 343 synchronized (mImageChangeListeners) { 344 mImageChangeListeners.add(listener); 345 } 346 } 347 348 public void removeImageChangeListener(IImageChangeListener listener) { 349 synchronized (mImageChangeListeners) { 350 mImageChangeListeners.remove(listener); 351 } 352 } 353 } 354