OSDN Git Service

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