• 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.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 
DeviceExplorer()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      */
setCustomImages(Image fileImage, Image folderImage, Image packageImage, Image otherImage)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      */
setActions(ICommonAction pushAction, ICommonAction pullAction, ICommonAction deleteAction, ICommonAction createNewFolderAction)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
createControl(Composite parent)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
postCreation()379     protected void postCreation() {
380 
381     }
382 
383     /**
384      * Sets the focus to the proper control inside the panel.
385      */
386     @Override
setFocus()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      */
handleTraceDoubleClick(String baseName, FileEntry keyEntry, FileEntry dataEntry)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      */
pullSelection()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      */
pushIntoSelection()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 
deleteSelection()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 
createNewFolderInSelection()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      */
refresh()710     public void refresh() {
711         mTreeViewer.refresh(true);
712     }
713 
714     /**
715      * Sets the new device to explorer
716      */
switchDevice(final IDevice device)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      */
refresh(final FileEntry entry)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      */
pullSelection(TreeItem[] items, final String localDirectory)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      */
pullFile(final FileEntry remote, final String local)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      */
pushFiles(final String[] localFiles, final FileEntry remoteDirectory)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      */
pushFile(final String local, final String remoteDirectory)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      */
setDeleteEnabledState(FileEntry element)899     protected void setDeleteEnabledState(FileEntry element) {
900         mDeleteAction.setEnabled(element.getType() == FileListingService.TYPE_FILE);
901     }
902 }
903