• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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