Home | History | Annotate | Download | only in explorer
      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.explorer;
     18 
     19 import com.android.ddmlib.AdbCommandRejectedException;
     20 import com.android.ddmlib.DdmConstants;
     21 import com.android.ddmlib.FileListingService;
     22 import com.android.ddmlib.FileListingService.FileEntry;
     23 import com.android.ddmlib.IDevice;
     24 import com.android.ddmlib.IShellOutputReceiver;
     25 import com.android.ddmlib.ShellCommandUnresponsiveException;
     26 import com.android.ddmlib.SyncException;
     27 import com.android.ddmlib.SyncService;
     28 import com.android.ddmlib.SyncService.ISyncProgressMonitor;
     29 import com.android.ddmlib.TimeoutException;
     30 import com.android.ddmuilib.DdmUiPreferences;
     31 import com.android.ddmuilib.ImageLoader;
     32 import com.android.ddmuilib.Panel;
     33 import com.android.ddmuilib.SyncProgressHelper;
     34 import com.android.ddmuilib.SyncProgressHelper.SyncRunnable;
     35 import com.android.ddmuilib.TableHelper;
     36 import com.android.ddmuilib.actions.ICommonAction;
     37 import com.android.ddmuilib.console.DdmConsole;
     38 
     39 import org.eclipse.core.runtime.IStatus;
     40 import org.eclipse.core.runtime.Status;
     41 import org.eclipse.jface.dialogs.ErrorDialog;
     42 import org.eclipse.jface.dialogs.IInputValidator;
     43 import org.eclipse.jface.dialogs.InputDialog;
     44 import org.eclipse.jface.preference.IPreferenceStore;
     45 import org.eclipse.jface.viewers.DoubleClickEvent;
     46 import org.eclipse.jface.viewers.IDoubleClickListener;
     47 import org.eclipse.jface.viewers.ISelection;
     48 import org.eclipse.jface.viewers.ISelectionChangedListener;
     49 import org.eclipse.jface.viewers.IStructuredSelection;
     50 import org.eclipse.jface.viewers.SelectionChangedEvent;
     51 import org.eclipse.jface.viewers.TreeViewer;
     52 import org.eclipse.jface.viewers.ViewerDropAdapter;
     53 import org.eclipse.swt.SWT;
     54 import org.eclipse.swt.dnd.DND;
     55 import org.eclipse.swt.dnd.FileTransfer;
     56 import org.eclipse.swt.dnd.Transfer;
     57 import org.eclipse.swt.dnd.TransferData;
     58 import org.eclipse.swt.graphics.Image;
     59 import org.eclipse.swt.layout.FillLayout;
     60 import org.eclipse.swt.widgets.Composite;
     61 import org.eclipse.swt.widgets.Control;
     62 import org.eclipse.swt.widgets.DirectoryDialog;
     63 import org.eclipse.swt.widgets.Display;
     64 import org.eclipse.swt.widgets.FileDialog;
     65 import org.eclipse.swt.widgets.Tree;
     66 import org.eclipse.swt.widgets.TreeItem;
     67 
     68 import java.io.BufferedReader;
     69 import java.io.File;
     70 import java.io.IOException;
     71 import java.io.InputStreamReader;
     72 import java.util.ArrayList;
     73 import java.util.regex.Matcher;
     74 import java.util.regex.Pattern;
     75 
     76 /**
     77  * Device filesystem explorer class.
     78  */
     79 public class DeviceExplorer extends Panel {
     80 
     81     private final static String TRACE_KEY_EXT = ".key"; // $NON-NLS-1S
     82     private final static String TRACE_DATA_EXT = ".data"; // $NON-NLS-1S
     83 
     84     private static Pattern mKeyFilePattern = Pattern.compile(
     85             "(.+)\\" + TRACE_KEY_EXT); // $NON-NLS-1S
     86     private static Pattern mDataFilePattern = Pattern.compile(
     87             "(.+)\\" + TRACE_DATA_EXT); // $NON-NLS-1S
     88 
     89     public static String COLUMN_NAME = "android.explorer.name"; //$NON-NLS-1S
     90     public static String COLUMN_SIZE = "android.explorer.size"; //$NON-NLS-1S
     91     public static String COLUMN_DATE = "android.explorer.data"; //$NON-NLS-1S
     92     public static String COLUMN_TIME = "android.explorer.time"; //$NON-NLS-1S
     93     public static String COLUMN_PERMISSIONS = "android.explorer.permissions"; // $NON-NLS-1S
     94     public static String COLUMN_INFO = "android.explorer.info"; // $NON-NLS-1S
     95 
     96     private Composite mParent;
     97     private TreeViewer mTreeViewer;
     98     private Tree mTree;
     99     private DeviceContentProvider mContentProvider;
    100 
    101     private ICommonAction mPushAction;
    102     private ICommonAction mPullAction;
    103     private ICommonAction mDeleteAction;
    104     private ICommonAction mCreateNewFolderAction;
    105 
    106     private Image mFileImage;
    107     private Image mFolderImage;
    108     private Image mPackageImage;
    109     private Image mOtherImage;
    110 
    111     private IDevice mCurrentDevice;
    112 
    113     private String mDefaultSave;
    114 
    115     public DeviceExplorer() {
    116     }
    117 
    118     /**
    119      * Sets custom images for the device explorer. If none are set then defaults are used.
    120      * This can be useful to set platform-specific explorer icons.
    121      *
    122      * This should be called before {@link #createControl(Composite)}.
    123      *
    124      * @param fileImage the icon to represent a file.
    125      * @param folderImage the icon to represent a folder.
    126      * @param packageImage the icon to represent an apk.
    127      * @param otherImage the icon to represent other types of files.
    128      */
    129     public void setCustomImages(Image fileImage, Image folderImage, Image packageImage,
    130             Image otherImage) {
    131         mFileImage = fileImage;
    132         mFolderImage = folderImage;
    133         mPackageImage = packageImage;
    134         mOtherImage = otherImage;
    135     }
    136 
    137     /**
    138      * Sets the actions so that the device explorer can enable/disable them based on the current
    139      * selection
    140      * @param pushAction
    141      * @param pullAction
    142      * @param deleteAction
    143      * @param createNewFolderAction
    144      */
    145     public void setActions(ICommonAction pushAction, ICommonAction pullAction,
    146             ICommonAction deleteAction, ICommonAction createNewFolderAction) {
    147         mPushAction = pushAction;
    148         mPullAction = pullAction;
    149         mDeleteAction = deleteAction;
    150         mCreateNewFolderAction = createNewFolderAction;
    151     }
    152 
    153     /**
    154      * Creates a control capable of displaying some information.  This is
    155      * called once, when the application is initializing, from the UI thread.
    156      */
    157     @Override
    158     protected Control createControl(Composite parent) {
    159         mParent = parent;
    160         parent.setLayout(new FillLayout());
    161 
    162         ImageLoader loader = ImageLoader.getDdmUiLibLoader();
    163         if (mFileImage == null) {
    164             mFileImage = loader.loadImage("file.png", mParent.getDisplay());
    165         }
    166         if (mFolderImage == null) {
    167             mFolderImage = loader.loadImage("folder.png", mParent.getDisplay());
    168         }
    169         if (mPackageImage == null) {
    170             mPackageImage = loader.loadImage("android.png", mParent.getDisplay());
    171         }
    172         if (mOtherImage == null) {
    173             // TODO: find a default image for other.
    174         }
    175 
    176         mTree = new Tree(parent, SWT.MULTI | SWT.FULL_SELECTION | SWT.VIRTUAL);
    177         mTree.setHeaderVisible(true);
    178 
    179         IPreferenceStore store = DdmUiPreferences.getStore();
    180 
    181         // create columns
    182         TableHelper.createTreeColumn(mTree, "Name", SWT.LEFT,
    183                 "0000drwxrwxrwx", COLUMN_NAME, store); //$NON-NLS-1$
    184         TableHelper.createTreeColumn(mTree, "Size", SWT.RIGHT,
    185                 "000000", COLUMN_SIZE, store); //$NON-NLS-1$
    186         TableHelper.createTreeColumn(mTree, "Date", SWT.LEFT,
    187                 "2007-08-14", COLUMN_DATE, store); //$NON-NLS-1$
    188         TableHelper.createTreeColumn(mTree, "Time", SWT.LEFT,
    189                 "20:54", COLUMN_TIME, store); //$NON-NLS-1$
    190         TableHelper.createTreeColumn(mTree, "Permissions", SWT.LEFT,
    191                 "drwxrwxrwx", COLUMN_PERMISSIONS, store); //$NON-NLS-1$
    192         TableHelper.createTreeColumn(mTree, "Info", SWT.LEFT,
    193                 "drwxrwxrwx", COLUMN_INFO, store); //$NON-NLS-1$
    194 
    195         // create the jface wrapper
    196         mTreeViewer = new TreeViewer(mTree);
    197 
    198         // setup data provider
    199         mContentProvider = new DeviceContentProvider();
    200         mTreeViewer.setContentProvider(mContentProvider);
    201         mTreeViewer.setLabelProvider(new FileLabelProvider(mFileImage,
    202                 mFolderImage, mPackageImage, mOtherImage));
    203 
    204         // setup a listener for selection
    205         mTreeViewer.addSelectionChangedListener(new ISelectionChangedListener() {
    206             public void selectionChanged(SelectionChangedEvent event) {
    207                 ISelection sel = event.getSelection();
    208                 if (sel.isEmpty()) {
    209                     mPullAction.setEnabled(false);
    210                     mPushAction.setEnabled(false);
    211                     mDeleteAction.setEnabled(false);
    212                     mCreateNewFolderAction.setEnabled(false);
    213                     return;
    214                 }
    215                 if (sel instanceof IStructuredSelection) {
    216                     IStructuredSelection selection = (IStructuredSelection) sel;
    217                     Object element = selection.getFirstElement();
    218                     if (element == null)
    219                         return;
    220                     if (element instanceof FileEntry) {
    221                         mPullAction.setEnabled(true);
    222                         mPushAction.setEnabled(selection.size() == 1);
    223                         if (selection.size() == 1) {
    224                             FileEntry entry = (FileEntry) element;
    225                             setDeleteEnabledState(entry);
    226                             mCreateNewFolderAction.setEnabled(entry.isDirectory());
    227                         } else {
    228                             mDeleteAction.setEnabled(false);
    229                         }
    230                     }
    231                 }
    232             }
    233         });
    234 
    235         // add support for double click
    236         mTreeViewer.addDoubleClickListener(new IDoubleClickListener() {
    237             public void doubleClick(DoubleClickEvent event) {
    238                 ISelection sel = event.getSelection();
    239 
    240                 if (sel instanceof IStructuredSelection) {
    241                     IStructuredSelection selection = (IStructuredSelection) sel;
    242 
    243                     if (selection.size() == 1) {
    244                         FileEntry entry = (FileEntry)selection.getFirstElement();
    245                         String name = entry.getName();
    246 
    247                         FileEntry parentEntry = entry.getParent();
    248 
    249                         // can't really do anything with no parent
    250                         if (parentEntry == null) {
    251                             return;
    252                         }
    253 
    254                         // check this is a file like we want.
    255                         Matcher m = mKeyFilePattern.matcher(name);
    256                         if (m.matches()) {
    257                             // get the name w/o the extension
    258                             String baseName = m.group(1);
    259 
    260                             // add the data extension
    261                             String dataName = baseName + TRACE_DATA_EXT;
    262 
    263                             FileEntry dataEntry = parentEntry.findChild(dataName);
    264 
    265                             handleTraceDoubleClick(baseName, entry, dataEntry);
    266 
    267                         } else {
    268                             m = mDataFilePattern.matcher(name);
    269                             if (m.matches()) {
    270                                 // get the name w/o the extension
    271                                 String baseName = m.group(1);
    272 
    273                                 // add the key extension
    274                                 String keyName = baseName + TRACE_KEY_EXT;
    275 
    276                                 FileEntry keyEntry = parentEntry.findChild(keyName);
    277 
    278                                 handleTraceDoubleClick(baseName, keyEntry, entry);
    279                             }
    280                         }
    281                     }
    282                 }
    283             }
    284         });
    285 
    286         // setup drop listener
    287         mTreeViewer.addDropSupport(DND.DROP_COPY | DND.DROP_MOVE,
    288                 new Transfer[] { FileTransfer.getInstance() },
    289                 new ViewerDropAdapter(mTreeViewer) {
    290             @Override
    291             public boolean performDrop(Object data) {
    292                 // get the item on which we dropped the item(s)
    293                 FileEntry target = (FileEntry)getCurrentTarget();
    294 
    295                 // in case we drop at the same level as root
    296                 if (target == null) {
    297                     return false;
    298                 }
    299 
    300                 // if the target is not a directory, we get the parent directory
    301                 if (target.isDirectory() == false) {
    302                     target = target.getParent();
    303                 }
    304 
    305                 if (target == null) {
    306                     return false;
    307                 }
    308 
    309                 // get the list of files to drop
    310                 String[] files = (String[])data;
    311 
    312                 // do the drop
    313                 pushFiles(files, target);
    314 
    315                 // we need to finish with a refresh
    316                 refresh(target);
    317 
    318                 return true;
    319             }
    320 
    321             @Override
    322             public boolean validateDrop(Object target, int operation, TransferData transferType) {
    323                 if (target == null) {
    324                     return false;
    325                 }
    326 
    327                 // convert to the real item
    328                 FileEntry targetEntry = (FileEntry)target;
    329 
    330                 // if the target is not a directory, we get the parent directory
    331                 if (targetEntry.isDirectory() == false) {
    332                     target = targetEntry.getParent();
    333                 }
    334 
    335                 if (target == null) {
    336                     return false;
    337                 }
    338 
    339                 return true;
    340             }
    341         });
    342 
    343         // create and start the refresh thread
    344         new Thread("Device Ls refresher") {
    345             @Override
    346             public void run() {
    347                 while (true) {
    348                     try {
    349                         sleep(FileListingService.REFRESH_RATE);
    350                     } catch (InterruptedException e) {
    351                         return;
    352                     }
    353 
    354                     if (mTree != null && mTree.isDisposed() == false) {
    355                         Display display = mTree.getDisplay();
    356                         if (display.isDisposed() == false) {
    357                             display.asyncExec(new Runnable() {
    358                                 public void run() {
    359                                     if (mTree.isDisposed() == false) {
    360                                         mTreeViewer.refresh(true);
    361                                     }
    362                                 }
    363                             });
    364                         } else {
    365                             return;
    366                         }
    367                     } else {
    368                         return;
    369                     }
    370                 }
    371 
    372             }
    373         }.start();
    374 
    375         return mTree;
    376     }
    377 
    378     @Override
    379     protected void postCreation() {
    380 
    381     }
    382 
    383     /**
    384      * Sets the focus to the proper control inside the panel.
    385      */
    386     @Override
    387     public void setFocus() {
    388         mTree.setFocus();
    389     }
    390 
    391     /**
    392      * Processes a double click on a trace file
    393      * @param baseName the base name of the 2 files.
    394      * @param keyEntry The FileEntry for the .key file.
    395      * @param dataEntry The FileEntry for the .data file.
    396      */
    397     private void handleTraceDoubleClick(String baseName, FileEntry keyEntry,
    398             FileEntry dataEntry) {
    399         // first we need to download the files.
    400         File keyFile;
    401         File dataFile;
    402         String path;
    403         try {
    404             // create a temp file for keyFile
    405             File f = File.createTempFile(baseName, DdmConstants.DOT_TRACE);
    406             f.delete();
    407             f.mkdir();
    408 
    409             path = f.getAbsolutePath();
    410 
    411             keyFile = new File(path + File.separator + keyEntry.getName());
    412             dataFile = new File(path + File.separator + dataEntry.getName());
    413         } catch (IOException e) {
    414             return;
    415         }
    416 
    417         // download the files
    418         try {
    419             SyncService sync = mCurrentDevice.getSyncService();
    420             if (sync != null) {
    421                 ISyncProgressMonitor monitor = SyncService.getNullProgressMonitor();
    422                 sync.pullFile(keyEntry, keyFile.getAbsolutePath(), monitor);
    423                 sync.pullFile(dataEntry, dataFile.getAbsolutePath(), monitor);
    424 
    425                 // now that we have the file, we need to launch traceview
    426                 String[] command = new String[2];
    427                 command[0] = DdmUiPreferences.getTraceview();
    428                 command[1] = path + File.separator + baseName;
    429 
    430                 try {
    431                     final Process p = Runtime.getRuntime().exec(command);
    432 
    433                     // create a thread for the output
    434                     new Thread("Traceview output") {
    435                         @Override
    436                         public void run() {
    437                             // create a buffer to read the stderr output
    438                             InputStreamReader is = new InputStreamReader(p.getErrorStream());
    439                             BufferedReader resultReader = new BufferedReader(is);
    440 
    441                             // read the lines as they come. if null is returned, it's
    442                             // because the process finished
    443                             try {
    444                                 while (true) {
    445                                     String line = resultReader.readLine();
    446                                     if (line != null) {
    447                                         DdmConsole.printErrorToConsole("Traceview: " + line);
    448                                     } else {
    449                                         break;
    450                                     }
    451                                 }
    452                                 // get the return code from the process
    453                                 p.waitFor();
    454                             } catch (IOException e) {
    455                             } catch (InterruptedException e) {
    456 
    457                             }
    458                         }
    459                     }.start();
    460 
    461                 } catch (IOException e) {
    462                 }
    463             }
    464         } catch (IOException e) {
    465             DdmConsole.printErrorToConsole(String.format(
    466                     "Failed to pull %1$s: %2$s", keyEntry.getName(), e.getMessage()));
    467             return;
    468         } catch (SyncException e) {
    469             if (e.wasCanceled() == false) {
    470                 DdmConsole.printErrorToConsole(String.format(
    471                         "Failed to pull %1$s: %2$s", keyEntry.getName(), e.getMessage()));
    472                 return;
    473             }
    474         } catch (TimeoutException e) {
    475             DdmConsole.printErrorToConsole(String.format(
    476                     "Failed to pull %1$s: timeout", keyEntry.getName()));
    477         } catch (AdbCommandRejectedException e) {
    478             DdmConsole.printErrorToConsole(String.format(
    479                     "Failed to pull %1$s: %2$s", keyEntry.getName(), e.getMessage()));
    480         }
    481     }
    482 
    483     /**
    484      * Pull the current selection on the local drive. This method displays
    485      * a dialog box to let the user select where to store the file(s) and
    486      * folder(s).
    487      */
    488     public void pullSelection() {
    489         // get the selection
    490         TreeItem[] items = mTree.getSelection();
    491 
    492         // name of the single file pull, or null if we're pulling a directory
    493         // or more than one object.
    494         String filePullName = null;
    495         FileEntry singleEntry = null;
    496 
    497         // are we pulling a single file?
    498         if (items.length == 1) {
    499             singleEntry = (FileEntry)items[0].getData();
    500             if (singleEntry.getType() == FileListingService.TYPE_FILE) {
    501                 filePullName = singleEntry.getName();
    502             }
    503         }
    504 
    505         // where do we save by default?
    506         String defaultPath = mDefaultSave;
    507         if (defaultPath == null) {
    508             defaultPath = System.getProperty("user.home"); //$NON-NLS-1$
    509         }
    510 
    511         if (filePullName != null) {
    512             FileDialog fileDialog = new FileDialog(mParent.getShell(), SWT.SAVE);
    513 
    514             fileDialog.setText("Get Device File");
    515             fileDialog.setFileName(filePullName);
    516             fileDialog.setFilterPath(defaultPath);
    517 
    518             String fileName = fileDialog.open();
    519             if (fileName != null) {
    520                 mDefaultSave = fileDialog.getFilterPath();
    521 
    522                 pullFile(singleEntry, fileName);
    523             }
    524         } else {
    525             DirectoryDialog directoryDialog = new DirectoryDialog(mParent.getShell(), SWT.SAVE);
    526 
    527             directoryDialog.setText("Get Device Files/Folders");
    528             directoryDialog.setFilterPath(defaultPath);
    529 
    530             String directoryName = directoryDialog.open();
    531             if (directoryName != null) {
    532                 pullSelection(items, directoryName);
    533             }
    534         }
    535     }
    536 
    537     /**
    538      * Push new file(s) and folder(s) into the current selection. Current
    539      * selection must be single item. If the current selection is not a
    540      * directory, the parent directory is used.
    541      * This method displays a dialog to let the user choose file to push to
    542      * the device.
    543      */
    544     public void pushIntoSelection() {
    545         // get the name of the object we're going to pull
    546         TreeItem[] items = mTree.getSelection();
    547 
    548         if (items.length == 0) {
    549             return;
    550         }
    551 
    552         FileDialog dlg = new FileDialog(mParent.getShell(), SWT.OPEN);
    553         String fileName;
    554 
    555         dlg.setText("Put File on Device");
    556 
    557         // There should be only one.
    558         FileEntry entry = (FileEntry)items[0].getData();
    559         dlg.setFileName(entry.getName());
    560 
    561         String defaultPath = mDefaultSave;
    562         if (defaultPath == null) {
    563             defaultPath = System.getProperty("user.home"); //$NON-NLS-1$
    564         }
    565         dlg.setFilterPath(defaultPath);
    566 
    567         fileName = dlg.open();
    568         if (fileName != null) {
    569             mDefaultSave = dlg.getFilterPath();
    570 
    571             // we need to figure out the remote path based on the current selection type.
    572             String remotePath;
    573             FileEntry toRefresh = entry;
    574             if (entry.isDirectory()) {
    575                 remotePath = entry.getFullPath();
    576             } else {
    577                 toRefresh = entry.getParent();
    578                 remotePath = toRefresh.getFullPath();
    579             }
    580 
    581             pushFile(fileName, remotePath);
    582             mTreeViewer.refresh(toRefresh);
    583         }
    584     }
    585 
    586     public void deleteSelection() {
    587         // get the name of the object we're going to pull
    588         TreeItem[] items = mTree.getSelection();
    589 
    590         if (items.length != 1) {
    591             return;
    592         }
    593 
    594         FileEntry entry = (FileEntry)items[0].getData();
    595         final FileEntry parentEntry = entry.getParent();
    596 
    597         // create the delete command
    598         String command = "rm " + entry.getFullEscapedPath(); //$NON-NLS-1$
    599 
    600         try {
    601             mCurrentDevice.executeShellCommand(command, new IShellOutputReceiver() {
    602                 public void addOutput(byte[] data, int offset, int length) {
    603                     // pass
    604                     // TODO get output to display errors if any.
    605                 }
    606 
    607                 public void flush() {
    608                     mTreeViewer.refresh(parentEntry);
    609                 }
    610 
    611                 public boolean isCancelled() {
    612                     return false;
    613                 }
    614             });
    615         } catch (IOException e) {
    616             // adb failed somehow, we do nothing. We should be displaying the error from the output
    617             // of the shell command.
    618         } catch (TimeoutException e) {
    619             // adb failed somehow, we do nothing. We should be displaying the error from the output
    620             // of the shell command.
    621         } catch (AdbCommandRejectedException e) {
    622             // adb failed somehow, we do nothing. We should be displaying the error from the output
    623             // of the shell command.
    624         } catch (ShellCommandUnresponsiveException e) {
    625             // adb failed somehow, we do nothing. We should be displaying the error from the output
    626             // of the shell command.
    627         }
    628 
    629     }
    630 
    631     public void createNewFolderInSelection() {
    632         TreeItem[] items = mTree.getSelection();
    633 
    634         if (items.length != 1) {
    635             return;
    636         }
    637 
    638         final FileEntry entry = (FileEntry) items[0].getData();
    639 
    640         if (entry.isDirectory()) {
    641             InputDialog inputDialog = new InputDialog(mTree.getShell(), "New Folder",
    642                     "Please enter the new folder name", "New Folder", new IInputValidator() {
    643                         public String isValid(String newText) {
    644                             if ((newText != null) && (newText.length() > 0)
    645                                     && (newText.trim().length() > 0)
    646                                     && (newText.indexOf('/') == -1)
    647                                     && (newText.indexOf('\\') == -1)) {
    648                                 return null;
    649                             } else {
    650                                 return "Invalid name";
    651                             }
    652                         }
    653                     });
    654             inputDialog.open();
    655             String value = inputDialog.getValue();
    656 
    657             if (value != null) {
    658                 // create the mkdir command
    659                 String command = "mkdir " + entry.getFullEscapedPath() //$NON-NLS-1$
    660                         + FileListingService.FILE_SEPARATOR + FileEntry.escape(value);
    661 
    662                 try {
    663                     mCurrentDevice.executeShellCommand(command, new IShellOutputReceiver() {
    664 
    665                         public boolean isCancelled() {
    666                             return false;
    667                         }
    668 
    669                         public void flush() {
    670                             mTreeViewer.refresh(entry);
    671                         }
    672 
    673                         public void addOutput(byte[] data, int offset, int length) {
    674                             String errorMessage;
    675                             if (data != null) {
    676                                 errorMessage = new String(data);
    677                             } else {
    678                                 errorMessage = "";
    679                             }
    680                             Status status = new Status(IStatus.ERROR,
    681                                     "DeviceExplorer", 0, errorMessage, null); //$NON-NLS-1$
    682                             ErrorDialog.openError(mTree.getShell(), "New Folder Error",
    683                                     "New Folder Error", status);
    684                         }
    685                     });
    686                 } catch (TimeoutException e) {
    687                     // adb failed somehow, we do nothing. We should be
    688                     // displaying the error from the output of the shell
    689                     // command.
    690                 } catch (AdbCommandRejectedException e) {
    691                     // adb failed somehow, we do nothing. We should be
    692                     // displaying the error from the output of the shell
    693                     // command.
    694                 } catch (ShellCommandUnresponsiveException e) {
    695                     // adb failed somehow, we do nothing. We should be
    696                     // displaying the error from the output of the shell
    697                     // command.
    698                 } catch (IOException e) {
    699                     // adb failed somehow, we do nothing. We should be
    700                     // displaying the error from the output of the shell
    701                     // command.
    702                 }
    703             }
    704         }
    705     }
    706 
    707     /**
    708      * Force a full refresh of the explorer.
    709      */
    710     public void refresh() {
    711         mTreeViewer.refresh(true);
    712     }
    713 
    714     /**
    715      * Sets the new device to explorer
    716      */
    717     public void switchDevice(final IDevice device) {
    718         if (device != mCurrentDevice) {
    719             mCurrentDevice = device;
    720             // now we change the input. but we need to do that in the
    721             // ui thread.
    722             if (mTree.isDisposed() == false) {
    723                 Display d = mTree.getDisplay();
    724                 d.asyncExec(new Runnable() {
    725                     public void run() {
    726                         if (mTree.isDisposed() == false) {
    727                             // new service
    728                             if (mCurrentDevice != null) {
    729                                 FileListingService fls = mCurrentDevice.getFileListingService();
    730                                 mContentProvider.setListingService(fls);
    731                                 mTreeViewer.setInput(fls.getRoot());
    732                             }
    733                         }
    734                     }
    735                 });
    736             }
    737         }
    738     }
    739 
    740     /**
    741      * Refresh an entry from a non ui thread.
    742      * @param entry the entry to refresh.
    743      */
    744     private void refresh(final FileEntry entry) {
    745         Display d = mTreeViewer.getTree().getDisplay();
    746         d.asyncExec(new Runnable() {
    747             public void run() {
    748                 mTreeViewer.refresh(entry);
    749             }
    750         });
    751     }
    752 
    753     /**
    754      * Pulls the selection from a device.
    755      * @param items the tree selection the remote file on the device
    756      * @param localDirector the local directory in which to save the files.
    757      */
    758     private void pullSelection(TreeItem[] items, final String localDirectory) {
    759         try {
    760             final SyncService sync = mCurrentDevice.getSyncService();
    761             if (sync != null) {
    762                 // make a list of the FileEntry.
    763                 ArrayList<FileEntry> entries = new ArrayList<FileEntry>();
    764                 for (TreeItem item : items) {
    765                     Object data = item.getData();
    766                     if (data instanceof FileEntry) {
    767                         entries.add((FileEntry)data);
    768                     }
    769                 }
    770                 final FileEntry[] entryArray = entries.toArray(
    771                         new FileEntry[entries.size()]);
    772 
    773                 SyncProgressHelper.run(new SyncRunnable() {
    774                     public void run(ISyncProgressMonitor monitor)
    775                             throws SyncException, IOException, TimeoutException {
    776                         sync.pull(entryArray, localDirectory, monitor);
    777                     }
    778 
    779                     public void close() {
    780                         sync.close();
    781                     }
    782                 }, "Pulling file(s) from the device", mParent.getShell());
    783             }
    784         } catch (SyncException e) {
    785             if (e.wasCanceled() == false) {
    786                 DdmConsole.printErrorToConsole(String.format(
    787                         "Failed to pull selection: %1$s", e.getMessage()));
    788             }
    789         } catch (Exception e) {
    790             DdmConsole.printErrorToConsole( "Failed to pull selection");
    791             DdmConsole.printErrorToConsole(e.getMessage());
    792         }
    793     }
    794 
    795     /**
    796      * Pulls a file from a device.
    797      * @param remote the remote file on the device
    798      * @param local the destination filepath
    799      */
    800     private void pullFile(final FileEntry remote, final String local) {
    801         try {
    802             final SyncService sync = mCurrentDevice.getSyncService();
    803             if (sync != null) {
    804                 SyncProgressHelper.run(new SyncRunnable() {
    805                         public void run(ISyncProgressMonitor monitor)
    806                                 throws SyncException, IOException, TimeoutException {
    807                             sync.pullFile(remote, local, monitor);
    808                         }
    809 
    810                         public void close() {
    811                             sync.close();
    812                         }
    813                     }, String.format("Pulling %1$s from the device", remote.getName()),
    814                     mParent.getShell());
    815             }
    816         } catch (SyncException e) {
    817             if (e.wasCanceled() == false) {
    818                 DdmConsole.printErrorToConsole(String.format(
    819                         "Failed to pull selection: %1$s", e.getMessage()));
    820             }
    821         } catch (Exception e) {
    822             DdmConsole.printErrorToConsole( "Failed to pull selection");
    823             DdmConsole.printErrorToConsole(e.getMessage());
    824         }
    825     }
    826 
    827     /**
    828      * Pushes several files and directory into a remote directory.
    829      * @param localFiles
    830      * @param remoteDirectory
    831      */
    832     private void pushFiles(final String[] localFiles, final FileEntry remoteDirectory) {
    833         try {
    834             final SyncService sync = mCurrentDevice.getSyncService();
    835             if (sync != null) {
    836                 SyncProgressHelper.run(new SyncRunnable() {
    837                         public void run(ISyncProgressMonitor monitor)
    838                                 throws SyncException, IOException, TimeoutException {
    839                             sync.push(localFiles, remoteDirectory, monitor);
    840                         }
    841 
    842                         public void close() {
    843                             sync.close();
    844                         }
    845                     }, "Pushing file(s) to the device", mParent.getShell());
    846             }
    847         } catch (SyncException e) {
    848             if (e.wasCanceled() == false) {
    849                 DdmConsole.printErrorToConsole(String.format(
    850                         "Failed to push selection: %1$s", e.getMessage()));
    851             }
    852         } catch (Exception e) {
    853             DdmConsole.printErrorToConsole("Failed to push the items");
    854             DdmConsole.printErrorToConsole(e.getMessage());
    855         }
    856     }
    857 
    858     /**
    859      * Pushes a file on a device.
    860      * @param local the local filepath of the file to push
    861      * @param remoteDirectory the remote destination directory on the device
    862      */
    863     private void pushFile(final String local, final String remoteDirectory) {
    864         try {
    865             final SyncService sync = mCurrentDevice.getSyncService();
    866             if (sync != null) {
    867                 // get the file name
    868                 String[] segs = local.split(Pattern.quote(File.separator));
    869                 String name = segs[segs.length-1];
    870                 final String remoteFile = remoteDirectory + FileListingService.FILE_SEPARATOR
    871                         + name;
    872 
    873                 SyncProgressHelper.run(new SyncRunnable() {
    874                         public void run(ISyncProgressMonitor monitor)
    875                                 throws SyncException, IOException, TimeoutException {
    876                             sync.pushFile(local, remoteFile, monitor);
    877                         }
    878 
    879                         public void close() {
    880                             sync.close();
    881                         }
    882                     }, String.format("Pushing %1$s to the device.", name), mParent.getShell());
    883             }
    884         } catch (SyncException e) {
    885             if (e.wasCanceled() == false) {
    886                 DdmConsole.printErrorToConsole(String.format(
    887                         "Failed to push selection: %1$s", e.getMessage()));
    888             }
    889         } catch (Exception e) {
    890             DdmConsole.printErrorToConsole("Failed to push the item(s).");
    891             DdmConsole.printErrorToConsole(e.getMessage());
    892         }
    893     }
    894 
    895     /**
    896      * Sets the enabled state based on a FileEntry properties
    897      * @param element The selected FileEntry
    898      */
    899     protected void setDeleteEnabledState(FileEntry element) {
    900         mDeleteAction.setEnabled(element.getType() == FileListingService.TYPE_FILE);
    901     }
    902 }
    903