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