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 DeviceExplorer()107 public DeviceExplorer() { 108 109 } 110 111 /** 112 * Sets the images for the listview 113 * @param fileImage 114 * @param folderImage 115 * @param otherImage 116 */ setImages(Image fileImage, Image folderImage, Image packageImage, Image otherImage)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 */ setActions(ICommonAction pushAction, ICommonAction pullAction, ICommonAction deleteAction)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 createControl(Composite parent)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 postCreation()348 protected void postCreation() { 349 350 } 351 352 /** 353 * Sets the focus to the proper control inside the panel. 354 */ 355 @Override setFocus()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 */ handleTraceDoubleClick(String baseName, FileEntry keyEntry, FileEntry dataEntry)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 */ pullSelection()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 */ pushIntoSelection()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 deleteSelection()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 */ refresh()593 public void refresh() { 594 mTreeViewer.refresh(true); 595 } 596 597 /** 598 * Sets the new device to explorer 599 */ switchDevice(final IDevice device)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 */ refresh(final FileEntry entry)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 */ pullSelection(TreeItem[] items, final String localDirectory)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 */ pullFile(final FileEntry remote, final String local)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 */ pushFiles(final String[] localFiles, final FileEntry remoteDirectory)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 */ pushFile(final String local, final String remoteDirectory)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 */ setDeleteEnabledState(FileEntry element)788 protected void setDeleteEnabledState(FileEntry element) { 789 mDeleteAction.setEnabled(element.getType() == FileListingService.TYPE_FILE); 790 } 791 } 792