1 /* 2 * Copyright (C) 2007 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.ddmuilib; 18 19 import com.android.ddmlib.AdbCommandRejectedException; 20 import com.android.ddmlib.IDevice; 21 import com.android.ddmlib.Log; 22 import com.android.ddmlib.RawImage; 23 import com.android.ddmlib.TimeoutException; 24 25 import org.eclipse.swt.SWT; 26 import org.eclipse.swt.SWTException; 27 import org.eclipse.swt.dnd.Clipboard; 28 import org.eclipse.swt.dnd.ImageTransfer; 29 import org.eclipse.swt.dnd.Transfer; 30 import org.eclipse.swt.events.SelectionAdapter; 31 import org.eclipse.swt.events.SelectionEvent; 32 import org.eclipse.swt.graphics.Image; 33 import org.eclipse.swt.graphics.ImageData; 34 import org.eclipse.swt.graphics.PaletteData; 35 import org.eclipse.swt.layout.GridData; 36 import org.eclipse.swt.layout.GridLayout; 37 import org.eclipse.swt.widgets.Button; 38 import org.eclipse.swt.widgets.Dialog; 39 import org.eclipse.swt.widgets.Display; 40 import org.eclipse.swt.widgets.FileDialog; 41 import org.eclipse.swt.widgets.Label; 42 import org.eclipse.swt.widgets.Shell; 43 44 import java.io.File; 45 import java.io.IOException; 46 import java.util.Calendar; 47 48 49 /** 50 * Gather a screen shot from the device and save it to a file. 51 */ 52 public class ScreenShotDialog extends Dialog { 53 54 private Label mBusyLabel; 55 private Label mImageLabel; 56 private Button mSave; 57 private IDevice mDevice; 58 private RawImage mRawImage; 59 private Clipboard mClipboard; 60 61 /** Number of 90 degree rotations applied to the current image */ 62 private int mRotateCount = 0; 63 64 /** 65 * Create with default style. 66 */ ScreenShotDialog(Shell parent)67 public ScreenShotDialog(Shell parent) { 68 this(parent, SWT.DIALOG_TRIM | SWT.APPLICATION_MODAL); 69 mClipboard = new Clipboard(parent.getDisplay()); 70 } 71 72 /** 73 * Create with app-defined style. 74 */ ScreenShotDialog(Shell parent, int style)75 public ScreenShotDialog(Shell parent, int style) { 76 super(parent, style); 77 } 78 79 /** 80 * Prepare and display the dialog. 81 * @param device The {@link IDevice} from which to get the screenshot. 82 */ open(IDevice device)83 public void open(IDevice device) { 84 mDevice = device; 85 86 Shell parent = getParent(); 87 Shell shell = new Shell(parent, getStyle()); 88 shell.setText("Device Screen Capture"); 89 90 createContents(shell); 91 shell.pack(); 92 shell.open(); 93 94 updateDeviceImage(shell); 95 96 Display display = parent.getDisplay(); 97 while (!shell.isDisposed()) { 98 if (!display.readAndDispatch()) 99 display.sleep(); 100 } 101 102 } 103 104 /* 105 * Create the screen capture dialog contents. 106 */ createContents(final Shell shell)107 private void createContents(final Shell shell) { 108 GridData data; 109 110 final int colCount = 5; 111 112 shell.setLayout(new GridLayout(colCount, true)); 113 114 // "refresh" button 115 Button refresh = new Button(shell, SWT.PUSH); 116 refresh.setText("Refresh"); 117 data = new GridData(GridData.HORIZONTAL_ALIGN_CENTER); 118 data.widthHint = 80; 119 refresh.setLayoutData(data); 120 refresh.addSelectionListener(new SelectionAdapter() { 121 @Override 122 public void widgetSelected(SelectionEvent e) { 123 updateDeviceImage(shell); 124 // RawImage only allows us to rotate the image 90 degrees at the time, 125 // so to preserve the current rotation we must call getRotated() 126 // the same number of times the user has done it manually. 127 // TODO: improve the RawImage class. 128 for (int i=0; i < mRotateCount; i++) { 129 mRawImage = mRawImage.getRotated(); 130 } 131 updateImageDisplay(shell); 132 } 133 }); 134 135 // "rotate" button 136 Button rotate = new Button(shell, SWT.PUSH); 137 rotate.setText("Rotate"); 138 data = new GridData(GridData.HORIZONTAL_ALIGN_CENTER); 139 data.widthHint = 80; 140 rotate.setLayoutData(data); 141 rotate.addSelectionListener(new SelectionAdapter() { 142 @Override 143 public void widgetSelected(SelectionEvent e) { 144 if (mRawImage != null) { 145 mRotateCount = (mRotateCount + 1) % 4; 146 mRawImage = mRawImage.getRotated(); 147 updateImageDisplay(shell); 148 } 149 } 150 }); 151 152 // "save" button 153 mSave = new Button(shell, SWT.PUSH); 154 mSave.setText("Save"); 155 data = new GridData(GridData.HORIZONTAL_ALIGN_CENTER); 156 data.widthHint = 80; 157 mSave.setLayoutData(data); 158 mSave.addSelectionListener(new SelectionAdapter() { 159 @Override 160 public void widgetSelected(SelectionEvent e) { 161 saveImage(shell); 162 } 163 }); 164 165 Button copy = new Button(shell, SWT.PUSH); 166 copy.setText("Copy"); 167 copy.setToolTipText("Copy the screenshot to the clipboard"); 168 data = new GridData(GridData.HORIZONTAL_ALIGN_CENTER); 169 data.widthHint = 80; 170 copy.setLayoutData(data); 171 copy.addSelectionListener(new SelectionAdapter() { 172 @Override 173 public void widgetSelected(SelectionEvent e) { 174 copy(); 175 } 176 }); 177 178 179 // "done" button 180 Button done = new Button(shell, SWT.PUSH); 181 done.setText("Done"); 182 data = new GridData(GridData.HORIZONTAL_ALIGN_CENTER); 183 data.widthHint = 80; 184 done.setLayoutData(data); 185 done.addSelectionListener(new SelectionAdapter() { 186 @Override 187 public void widgetSelected(SelectionEvent e) { 188 shell.close(); 189 } 190 }); 191 192 // title/"capturing" label 193 mBusyLabel = new Label(shell, SWT.NONE); 194 mBusyLabel.setText("Preparing..."); 195 data = new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING); 196 data.horizontalSpan = colCount; 197 mBusyLabel.setLayoutData(data); 198 199 // space for the image 200 mImageLabel = new Label(shell, SWT.BORDER); 201 data = new GridData(GridData.HORIZONTAL_ALIGN_CENTER); 202 data.horizontalSpan = colCount; 203 mImageLabel.setLayoutData(data); 204 Display display = shell.getDisplay(); 205 mImageLabel.setImage(ImageLoader.createPlaceHolderArt( 206 display, 50, 50, display.getSystemColor(SWT.COLOR_BLUE))); 207 208 209 shell.setDefaultButton(done); 210 } 211 212 /** 213 * Copies the content of {@link #mImageLabel} to the clipboard. 214 */ copy()215 private void copy() { 216 mClipboard.setContents( 217 new Object[] { 218 mImageLabel.getImage().getImageData() 219 }, new Transfer[] { 220 ImageTransfer.getInstance() 221 }); 222 } 223 224 /** 225 * Captures a new image from the device, and display it. 226 */ updateDeviceImage(Shell shell)227 private void updateDeviceImage(Shell shell) { 228 mBusyLabel.setText("Capturing..."); // no effect 229 230 shell.setCursor(shell.getDisplay().getSystemCursor(SWT.CURSOR_WAIT)); 231 232 mRawImage = getDeviceImage(); 233 234 updateImageDisplay(shell); 235 } 236 237 /** 238 * Updates the display with {@link #mRawImage}. 239 * @param shell 240 */ updateImageDisplay(Shell shell)241 private void updateImageDisplay(Shell shell) { 242 Image image; 243 if (mRawImage == null) { 244 Display display = shell.getDisplay(); 245 image = ImageLoader.createPlaceHolderArt( 246 display, 320, 240, display.getSystemColor(SWT.COLOR_BLUE)); 247 248 mSave.setEnabled(false); 249 mBusyLabel.setText("Screen not available"); 250 } else { 251 // convert raw data to an Image. 252 PaletteData palette = new PaletteData( 253 mRawImage.getRedMask(), 254 mRawImage.getGreenMask(), 255 mRawImage.getBlueMask()); 256 257 ImageData imageData = new ImageData(mRawImage.width, mRawImage.height, 258 mRawImage.bpp, palette, 1, mRawImage.data); 259 image = new Image(getParent().getDisplay(), imageData); 260 261 mSave.setEnabled(true); 262 mBusyLabel.setText("Captured image:"); 263 } 264 265 mImageLabel.setImage(image); 266 mImageLabel.pack(); 267 shell.pack(); 268 269 // there's no way to restore old cursor; assume it's ARROW 270 shell.setCursor(shell.getDisplay().getSystemCursor(SWT.CURSOR_ARROW)); 271 } 272 273 /** 274 * Grabs an image from an ADB-connected device and returns it as a {@link RawImage}. 275 */ getDeviceImage()276 private RawImage getDeviceImage() { 277 try { 278 return mDevice.getScreenshot(); 279 } 280 catch (IOException ioe) { 281 Log.w("ddms", "Unable to get frame buffer: " + ioe.getMessage()); 282 return null; 283 } catch (TimeoutException e) { 284 Log.w("ddms", "Unable to get frame buffer: timeout "); 285 return null; 286 } catch (AdbCommandRejectedException e) { 287 Log.w("ddms", "Unable to get frame buffer: " + e.getMessage()); 288 return null; 289 } 290 } 291 292 /* 293 * Prompt the user to save the image to disk. 294 */ saveImage(Shell shell)295 private void saveImage(Shell shell) { 296 FileDialog dlg = new FileDialog(shell, SWT.SAVE); 297 298 Calendar now = Calendar.getInstance(); 299 String fileName = String.format("device-%tF-%tH%tM%tS.png", 300 now, now, now, now); 301 302 dlg.setText("Save image..."); 303 dlg.setFileName(fileName); 304 305 String lastDir = DdmUiPreferences.getStore().getString("lastImageSaveDir"); 306 if (lastDir.length() == 0) { 307 lastDir = DdmUiPreferences.getStore().getString("imageSaveDir"); 308 } 309 dlg.setFilterPath(lastDir); 310 dlg.setFilterNames(new String[] { 311 "PNG Files (*.png)" 312 }); 313 dlg.setFilterExtensions(new String[] { 314 "*.png" //$NON-NLS-1$ 315 }); 316 317 fileName = dlg.open(); 318 if (fileName != null) { 319 // FileDialog.getFilterPath() does NOT always return the current 320 // directory of the FileDialog; on the Mac it sometimes just returns 321 // the value the dialog was initialized with. It does however return 322 // the full path as its return value, so just pick the path from 323 // there. 324 String saveDir = new File(fileName).getParent(); 325 if (saveDir != null) { 326 DdmUiPreferences.getStore().setValue("lastImageSaveDir", saveDir); 327 } 328 329 Log.d("ddms", "Saving image to " + fileName); 330 ImageData imageData = mImageLabel.getImage().getImageData(); 331 332 try { 333 org.eclipse.swt.graphics.ImageLoader loader = 334 new org.eclipse.swt.graphics.ImageLoader(); 335 336 loader.data = new ImageData[] { imageData }; 337 loader.save(fileName, SWT.IMAGE_PNG); 338 } 339 catch (SWTException e) { 340 Log.w("ddms", "Unable to save " + fileName + ": " + e.getMessage()); 341 } 342 } 343 } 344 345 } 346 347