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