Home | History | Annotate | Download | only in ddmuilib
      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      */
     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      */
     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      */
     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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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             if (!fileName.endsWith(".png")) {
    325                 fileName = fileName + ".png";
    326             }
    327 
    328             String saveDir = new File(fileName).getParent();
    329             if (saveDir != null) {
    330                 DdmUiPreferences.getStore().setValue("lastImageSaveDir", saveDir);
    331             }
    332 
    333             Log.d("ddms", "Saving image to " + fileName);
    334             ImageData imageData = mImageLabel.getImage().getImageData();
    335 
    336             try {
    337                 org.eclipse.swt.graphics.ImageLoader loader =
    338                         new org.eclipse.swt.graphics.ImageLoader();
    339 
    340                 loader.data = new ImageData[] { imageData };
    341                 loader.save(fileName, SWT.IMAGE_PNG);
    342             }
    343             catch (SWTException e) {
    344                 Log.w("ddms", "Unable to save " + fileName + ": " + e.getMessage());
    345             }
    346         }
    347     }
    348 
    349 }
    350 
    351