| /* |
| * Copyright (C) 2007 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.ddmuilib.explorer; |
| |
| import com.android.ddmlib.AdbCommandRejectedException; |
| import com.android.ddmlib.DdmConstants; |
| import com.android.ddmlib.FileListingService; |
| import com.android.ddmlib.FileListingService.FileEntry; |
| import com.android.ddmlib.IDevice; |
| import com.android.ddmlib.IShellOutputReceiver; |
| import com.android.ddmlib.ShellCommandUnresponsiveException; |
| import com.android.ddmlib.SyncException; |
| import com.android.ddmlib.SyncService; |
| import com.android.ddmlib.SyncService.ISyncProgressMonitor; |
| import com.android.ddmlib.TimeoutException; |
| import com.android.ddmuilib.DdmUiPreferences; |
| import com.android.ddmuilib.ImageLoader; |
| import com.android.ddmuilib.Panel; |
| import com.android.ddmuilib.SyncProgressHelper; |
| import com.android.ddmuilib.SyncProgressHelper.SyncRunnable; |
| import com.android.ddmuilib.TableHelper; |
| import com.android.ddmuilib.actions.ICommonAction; |
| import com.android.ddmuilib.console.DdmConsole; |
| |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.jface.dialogs.ErrorDialog; |
| import org.eclipse.jface.dialogs.IInputValidator; |
| import org.eclipse.jface.dialogs.InputDialog; |
| import org.eclipse.jface.preference.IPreferenceStore; |
| import org.eclipse.jface.viewers.DoubleClickEvent; |
| import org.eclipse.jface.viewers.IDoubleClickListener; |
| import org.eclipse.jface.viewers.ISelection; |
| import org.eclipse.jface.viewers.ISelectionChangedListener; |
| import org.eclipse.jface.viewers.IStructuredSelection; |
| import org.eclipse.jface.viewers.SelectionChangedEvent; |
| import org.eclipse.jface.viewers.TreeViewer; |
| import org.eclipse.jface.viewers.ViewerDropAdapter; |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.dnd.DND; |
| import org.eclipse.swt.dnd.FileTransfer; |
| import org.eclipse.swt.dnd.Transfer; |
| import org.eclipse.swt.dnd.TransferData; |
| import org.eclipse.swt.graphics.Image; |
| import org.eclipse.swt.layout.FillLayout; |
| import org.eclipse.swt.widgets.Composite; |
| import org.eclipse.swt.widgets.Control; |
| import org.eclipse.swt.widgets.DirectoryDialog; |
| import org.eclipse.swt.widgets.Display; |
| import org.eclipse.swt.widgets.FileDialog; |
| import org.eclipse.swt.widgets.Tree; |
| import org.eclipse.swt.widgets.TreeItem; |
| |
| import java.io.BufferedReader; |
| import java.io.File; |
| import java.io.IOException; |
| import java.io.InputStreamReader; |
| import java.util.ArrayList; |
| import java.util.regex.Matcher; |
| import java.util.regex.Pattern; |
| |
| /** |
| * Device filesystem explorer class. |
| */ |
| public class DeviceExplorer extends Panel { |
| |
| public static String COLUMN_NAME = "android.explorer.name"; //$NON-NLS-1S |
| public static String COLUMN_SIZE = "android.explorer.size"; //$NON-NLS-1S |
| public static String COLUMN_DATE = "android.explorer.data"; //$NON-NLS-1S |
| public static String COLUMN_TIME = "android.explorer.time"; //$NON-NLS-1S |
| public static String COLUMN_PERMISSIONS = "android.explorer.permissions"; // $NON-NLS-1S |
| public static String COLUMN_INFO = "android.explorer.info"; // $NON-NLS-1S |
| |
| private Composite mParent; |
| private TreeViewer mTreeViewer; |
| private Tree mTree; |
| private DeviceContentProvider mContentProvider; |
| |
| private ICommonAction mPushAction; |
| private ICommonAction mPullAction; |
| private ICommonAction mDeleteAction; |
| private ICommonAction mCreateNewFolderAction; |
| |
| private Image mFileImage; |
| private Image mFolderImage; |
| private Image mPackageImage; |
| private Image mOtherImage; |
| |
| private IDevice mCurrentDevice; |
| |
| private String mDefaultSave; |
| |
| public DeviceExplorer() { |
| } |
| |
| /** |
| * Sets custom images for the device explorer. If none are set then defaults are used. |
| * This can be useful to set platform-specific explorer icons. |
| * |
| * This should be called before {@link #createControl(Composite)}. |
| * |
| * @param fileImage the icon to represent a file. |
| * @param folderImage the icon to represent a folder. |
| * @param packageImage the icon to represent an apk. |
| * @param otherImage the icon to represent other types of files. |
| */ |
| public void setCustomImages(Image fileImage, Image folderImage, Image packageImage, |
| Image otherImage) { |
| mFileImage = fileImage; |
| mFolderImage = folderImage; |
| mPackageImage = packageImage; |
| mOtherImage = otherImage; |
| } |
| |
| /** |
| * Sets the actions so that the device explorer can enable/disable them based on the current |
| * selection |
| * @param pushAction |
| * @param pullAction |
| * @param deleteAction |
| * @param createNewFolderAction |
| */ |
| public void setActions(ICommonAction pushAction, ICommonAction pullAction, |
| ICommonAction deleteAction, ICommonAction createNewFolderAction) { |
| mPushAction = pushAction; |
| mPullAction = pullAction; |
| mDeleteAction = deleteAction; |
| mCreateNewFolderAction = createNewFolderAction; |
| } |
| |
| /** |
| * Creates a control capable of displaying some information. This is |
| * called once, when the application is initializing, from the UI thread. |
| */ |
| @Override |
| protected Control createControl(Composite parent) { |
| mParent = parent; |
| parent.setLayout(new FillLayout()); |
| |
| ImageLoader loader = ImageLoader.getDdmUiLibLoader(); |
| if (mFileImage == null) { |
| mFileImage = loader.loadImage("file.png", mParent.getDisplay()); |
| } |
| if (mFolderImage == null) { |
| mFolderImage = loader.loadImage("folder.png", mParent.getDisplay()); |
| } |
| if (mPackageImage == null) { |
| mPackageImage = loader.loadImage("android.png", mParent.getDisplay()); |
| } |
| if (mOtherImage == null) { |
| // TODO: find a default image for other. |
| } |
| |
| mTree = new Tree(parent, SWT.MULTI | SWT.FULL_SELECTION | SWT.VIRTUAL); |
| mTree.setHeaderVisible(true); |
| |
| IPreferenceStore store = DdmUiPreferences.getStore(); |
| |
| // create columns |
| TableHelper.createTreeColumn(mTree, "Name", SWT.LEFT, |
| "0000drwxrwxrwx", COLUMN_NAME, store); //$NON-NLS-1$ |
| TableHelper.createTreeColumn(mTree, "Size", SWT.RIGHT, |
| "000000", COLUMN_SIZE, store); //$NON-NLS-1$ |
| TableHelper.createTreeColumn(mTree, "Date", SWT.LEFT, |
| "2007-08-14", COLUMN_DATE, store); //$NON-NLS-1$ |
| TableHelper.createTreeColumn(mTree, "Time", SWT.LEFT, |
| "20:54", COLUMN_TIME, store); //$NON-NLS-1$ |
| TableHelper.createTreeColumn(mTree, "Permissions", SWT.LEFT, |
| "drwxrwxrwx", COLUMN_PERMISSIONS, store); //$NON-NLS-1$ |
| TableHelper.createTreeColumn(mTree, "Info", SWT.LEFT, |
| "drwxrwxrwx", COLUMN_INFO, store); //$NON-NLS-1$ |
| |
| // create the jface wrapper |
| mTreeViewer = new TreeViewer(mTree); |
| |
| // setup data provider |
| mContentProvider = new DeviceContentProvider(); |
| mTreeViewer.setContentProvider(mContentProvider); |
| mTreeViewer.setLabelProvider(new FileLabelProvider(mFileImage, |
| mFolderImage, mPackageImage, mOtherImage)); |
| |
| // setup a listener for selection |
| mTreeViewer.addSelectionChangedListener(new ISelectionChangedListener() { |
| @Override |
| public void selectionChanged(SelectionChangedEvent event) { |
| ISelection sel = event.getSelection(); |
| if (sel.isEmpty()) { |
| mPullAction.setEnabled(false); |
| mPushAction.setEnabled(false); |
| mDeleteAction.setEnabled(false); |
| mCreateNewFolderAction.setEnabled(false); |
| return; |
| } |
| if (sel instanceof IStructuredSelection) { |
| IStructuredSelection selection = (IStructuredSelection) sel; |
| Object element = selection.getFirstElement(); |
| if (element == null) |
| return; |
| if (element instanceof FileEntry) { |
| mPullAction.setEnabled(true); |
| mPushAction.setEnabled(selection.size() == 1); |
| if (selection.size() == 1) { |
| FileEntry entry = (FileEntry) element; |
| setDeleteEnabledState(entry); |
| mCreateNewFolderAction.setEnabled(entry.isDirectory()); |
| } else { |
| mDeleteAction.setEnabled(false); |
| } |
| } |
| } |
| } |
| }); |
| |
| // setup drop listener |
| mTreeViewer.addDropSupport(DND.DROP_COPY | DND.DROP_MOVE, |
| new Transfer[] { FileTransfer.getInstance() }, |
| new ViewerDropAdapter(mTreeViewer) { |
| @Override |
| public boolean performDrop(Object data) { |
| // get the item on which we dropped the item(s) |
| FileEntry target = (FileEntry)getCurrentTarget(); |
| |
| // in case we drop at the same level as root |
| if (target == null) { |
| return false; |
| } |
| |
| // if the target is not a directory, we get the parent directory |
| if (target.isDirectory() == false) { |
| target = target.getParent(); |
| } |
| |
| if (target == null) { |
| return false; |
| } |
| |
| // get the list of files to drop |
| String[] files = (String[])data; |
| |
| // do the drop |
| pushFiles(files, target); |
| |
| // we need to finish with a refresh |
| refresh(target); |
| |
| return true; |
| } |
| |
| @Override |
| public boolean validateDrop(Object target, int operation, TransferData transferType) { |
| if (target == null) { |
| return false; |
| } |
| |
| // convert to the real item |
| FileEntry targetEntry = (FileEntry)target; |
| |
| // if the target is not a directory, we get the parent directory |
| if (targetEntry.isDirectory() == false) { |
| target = targetEntry.getParent(); |
| } |
| |
| if (target == null) { |
| return false; |
| } |
| |
| return true; |
| } |
| }); |
| |
| // create and start the refresh thread |
| new Thread("Device Ls refresher") { |
| @Override |
| public void run() { |
| while (true) { |
| try { |
| sleep(FileListingService.REFRESH_RATE); |
| } catch (InterruptedException e) { |
| return; |
| } |
| |
| if (mTree != null && mTree.isDisposed() == false) { |
| Display display = mTree.getDisplay(); |
| if (display.isDisposed() == false) { |
| display.asyncExec(new Runnable() { |
| @Override |
| public void run() { |
| if (mTree.isDisposed() == false) { |
| mTreeViewer.refresh(true); |
| } |
| } |
| }); |
| } else { |
| return; |
| } |
| } else { |
| return; |
| } |
| } |
| |
| } |
| }.start(); |
| |
| return mTree; |
| } |
| |
| @Override |
| protected void postCreation() { |
| |
| } |
| |
| /** |
| * Sets the focus to the proper control inside the panel. |
| */ |
| @Override |
| public void setFocus() { |
| mTree.setFocus(); |
| } |
| |
| /** |
| * Pull the current selection on the local drive. This method displays |
| * a dialog box to let the user select where to store the file(s) and |
| * folder(s). |
| */ |
| public void pullSelection() { |
| // get the selection |
| TreeItem[] items = mTree.getSelection(); |
| |
| // name of the single file pull, or null if we're pulling a directory |
| // or more than one object. |
| String filePullName = null; |
| FileEntry singleEntry = null; |
| |
| // are we pulling a single file? |
| if (items.length == 1) { |
| singleEntry = (FileEntry)items[0].getData(); |
| if (singleEntry.getType() == FileListingService.TYPE_FILE) { |
| filePullName = singleEntry.getName(); |
| } |
| } |
| |
| // where do we save by default? |
| String defaultPath = mDefaultSave; |
| if (defaultPath == null) { |
| defaultPath = System.getProperty("user.home"); //$NON-NLS-1$ |
| } |
| |
| if (filePullName != null) { |
| FileDialog fileDialog = new FileDialog(mParent.getShell(), SWT.SAVE); |
| |
| fileDialog.setText("Get Device File"); |
| fileDialog.setFileName(filePullName); |
| fileDialog.setFilterPath(defaultPath); |
| |
| String fileName = fileDialog.open(); |
| if (fileName != null) { |
| mDefaultSave = fileDialog.getFilterPath(); |
| |
| pullFile(singleEntry, fileName); |
| } |
| } else { |
| DirectoryDialog directoryDialog = new DirectoryDialog(mParent.getShell(), SWT.SAVE); |
| |
| directoryDialog.setText("Get Device Files/Folders"); |
| directoryDialog.setFilterPath(defaultPath); |
| |
| String directoryName = directoryDialog.open(); |
| if (directoryName != null) { |
| pullSelection(items, directoryName); |
| } |
| } |
| } |
| |
| /** |
| * Push new file(s) and folder(s) into the current selection. Current |
| * selection must be single item. If the current selection is not a |
| * directory, the parent directory is used. |
| * This method displays a dialog to let the user choose file to push to |
| * the device. |
| */ |
| public void pushIntoSelection() { |
| // get the name of the object we're going to pull |
| TreeItem[] items = mTree.getSelection(); |
| |
| if (items.length == 0) { |
| return; |
| } |
| |
| FileDialog dlg = new FileDialog(mParent.getShell(), SWT.OPEN); |
| String fileName; |
| |
| dlg.setText("Put File on Device"); |
| |
| // There should be only one. |
| FileEntry entry = (FileEntry)items[0].getData(); |
| dlg.setFileName(entry.getName()); |
| |
| String defaultPath = mDefaultSave; |
| if (defaultPath == null) { |
| defaultPath = System.getProperty("user.home"); //$NON-NLS-1$ |
| } |
| dlg.setFilterPath(defaultPath); |
| |
| fileName = dlg.open(); |
| if (fileName != null) { |
| mDefaultSave = dlg.getFilterPath(); |
| |
| // we need to figure out the remote path based on the current selection type. |
| String remotePath; |
| FileEntry toRefresh = entry; |
| if (entry.isDirectory()) { |
| remotePath = entry.getFullPath(); |
| } else { |
| toRefresh = entry.getParent(); |
| remotePath = toRefresh.getFullPath(); |
| } |
| |
| pushFile(fileName, remotePath); |
| mTreeViewer.refresh(toRefresh); |
| } |
| } |
| |
| public void deleteSelection() { |
| // get the name of the object we're going to pull |
| TreeItem[] items = mTree.getSelection(); |
| |
| if (items.length != 1) { |
| return; |
| } |
| |
| FileEntry entry = (FileEntry)items[0].getData(); |
| final FileEntry parentEntry = entry.getParent(); |
| |
| // create the delete command |
| String command = "rm " + entry.getFullEscapedPath(); //$NON-NLS-1$ |
| |
| try { |
| mCurrentDevice.executeShellCommand(command, new IShellOutputReceiver() { |
| @Override |
| public void addOutput(byte[] data, int offset, int length) { |
| // pass |
| // TODO get output to display errors if any. |
| } |
| |
| @Override |
| public void flush() { |
| mTreeViewer.refresh(parentEntry); |
| } |
| |
| @Override |
| public boolean isCancelled() { |
| return false; |
| } |
| }); |
| } catch (IOException e) { |
| // adb failed somehow, we do nothing. We should be displaying the error from the output |
| // of the shell command. |
| } catch (TimeoutException e) { |
| // adb failed somehow, we do nothing. We should be displaying the error from the output |
| // of the shell command. |
| } catch (AdbCommandRejectedException e) { |
| // adb failed somehow, we do nothing. We should be displaying the error from the output |
| // of the shell command. |
| } catch (ShellCommandUnresponsiveException e) { |
| // adb failed somehow, we do nothing. We should be displaying the error from the output |
| // of the shell command. |
| } |
| |
| } |
| |
| public void createNewFolderInSelection() { |
| TreeItem[] items = mTree.getSelection(); |
| |
| if (items.length != 1) { |
| return; |
| } |
| |
| final FileEntry entry = (FileEntry) items[0].getData(); |
| |
| if (entry.isDirectory()) { |
| InputDialog inputDialog = new InputDialog(mTree.getShell(), "New Folder", |
| "Please enter the new folder name", "New Folder", new IInputValidator() { |
| @Override |
| public String isValid(String newText) { |
| if ((newText != null) && (newText.length() > 0) |
| && (newText.trim().length() > 0) |
| && (newText.indexOf('/') == -1) |
| && (newText.indexOf('\\') == -1)) { |
| return null; |
| } else { |
| return "Invalid name"; |
| } |
| } |
| }); |
| inputDialog.open(); |
| String value = inputDialog.getValue(); |
| |
| if (value != null) { |
| // create the mkdir command |
| String command = "mkdir " + entry.getFullEscapedPath() //$NON-NLS-1$ |
| + FileListingService.FILE_SEPARATOR + FileEntry.escape(value); |
| |
| try { |
| mCurrentDevice.executeShellCommand(command, new IShellOutputReceiver() { |
| |
| @Override |
| public boolean isCancelled() { |
| return false; |
| } |
| |
| @Override |
| public void flush() { |
| mTreeViewer.refresh(entry); |
| } |
| |
| @Override |
| public void addOutput(byte[] data, int offset, int length) { |
| String errorMessage; |
| if (data != null) { |
| errorMessage = new String(data); |
| } else { |
| errorMessage = ""; |
| } |
| Status status = new Status(IStatus.ERROR, |
| "DeviceExplorer", 0, errorMessage, null); //$NON-NLS-1$ |
| ErrorDialog.openError(mTree.getShell(), "New Folder Error", |
| "New Folder Error", status); |
| } |
| }); |
| } catch (TimeoutException e) { |
| // adb failed somehow, we do nothing. We should be |
| // displaying the error from the output of the shell |
| // command. |
| } catch (AdbCommandRejectedException e) { |
| // adb failed somehow, we do nothing. We should be |
| // displaying the error from the output of the shell |
| // command. |
| } catch (ShellCommandUnresponsiveException e) { |
| // adb failed somehow, we do nothing. We should be |
| // displaying the error from the output of the shell |
| // command. |
| } catch (IOException e) { |
| // adb failed somehow, we do nothing. We should be |
| // displaying the error from the output of the shell |
| // command. |
| } |
| } |
| } |
| } |
| |
| /** |
| * Force a full refresh of the explorer. |
| */ |
| public void refresh() { |
| mTreeViewer.refresh(true); |
| } |
| |
| /** |
| * Sets the new device to explorer |
| */ |
| public void switchDevice(final IDevice device) { |
| if (device != mCurrentDevice) { |
| mCurrentDevice = device; |
| // now we change the input. but we need to do that in the |
| // ui thread. |
| if (mTree.isDisposed() == false) { |
| Display d = mTree.getDisplay(); |
| d.asyncExec(new Runnable() { |
| @Override |
| public void run() { |
| if (mTree.isDisposed() == false) { |
| // new service |
| if (mCurrentDevice != null) { |
| FileListingService fls = mCurrentDevice.getFileListingService(); |
| mContentProvider.setListingService(fls); |
| mTreeViewer.setInput(fls.getRoot()); |
| } |
| } |
| } |
| }); |
| } |
| } |
| } |
| |
| /** |
| * Refresh an entry from a non ui thread. |
| * @param entry the entry to refresh. |
| */ |
| private void refresh(final FileEntry entry) { |
| Display d = mTreeViewer.getTree().getDisplay(); |
| d.asyncExec(new Runnable() { |
| @Override |
| public void run() { |
| mTreeViewer.refresh(entry); |
| } |
| }); |
| } |
| |
| /** |
| * Pulls the selection from a device. |
| * @param items the tree selection the remote file on the device |
| * @param localDirector the local directory in which to save the files. |
| */ |
| private void pullSelection(TreeItem[] items, final String localDirectory) { |
| try { |
| final SyncService sync = mCurrentDevice.getSyncService(); |
| if (sync != null) { |
| // make a list of the FileEntry. |
| ArrayList<FileEntry> entries = new ArrayList<FileEntry>(); |
| for (TreeItem item : items) { |
| Object data = item.getData(); |
| if (data instanceof FileEntry) { |
| entries.add((FileEntry)data); |
| } |
| } |
| final FileEntry[] entryArray = entries.toArray( |
| new FileEntry[entries.size()]); |
| |
| SyncProgressHelper.run(new SyncRunnable() { |
| @Override |
| public void run(ISyncProgressMonitor monitor) |
| throws SyncException, IOException, TimeoutException { |
| sync.pull(entryArray, localDirectory, monitor); |
| } |
| |
| @Override |
| public void close() { |
| sync.close(); |
| } |
| }, "Pulling file(s) from the device", mParent.getShell()); |
| } |
| } catch (SyncException e) { |
| if (e.wasCanceled() == false) { |
| DdmConsole.printErrorToConsole(String.format( |
| "Failed to pull selection: %1$s", e.getMessage())); |
| } |
| } catch (Exception e) { |
| DdmConsole.printErrorToConsole( "Failed to pull selection"); |
| DdmConsole.printErrorToConsole(e.getMessage()); |
| } |
| } |
| |
| /** |
| * Pulls a file from a device. |
| * @param remote the remote file on the device |
| * @param local the destination filepath |
| */ |
| private void pullFile(final FileEntry remote, final String local) { |
| try { |
| final SyncService sync = mCurrentDevice.getSyncService(); |
| if (sync != null) { |
| SyncProgressHelper.run(new SyncRunnable() { |
| @Override |
| public void run(ISyncProgressMonitor monitor) |
| throws SyncException, IOException, TimeoutException { |
| sync.pullFile(remote, local, monitor); |
| } |
| |
| @Override |
| public void close() { |
| sync.close(); |
| } |
| }, String.format("Pulling %1$s from the device", remote.getName()), |
| mParent.getShell()); |
| } |
| } catch (SyncException e) { |
| if (e.wasCanceled() == false) { |
| DdmConsole.printErrorToConsole(String.format( |
| "Failed to pull selection: %1$s", e.getMessage())); |
| } |
| } catch (Exception e) { |
| DdmConsole.printErrorToConsole( "Failed to pull selection"); |
| DdmConsole.printErrorToConsole(e.getMessage()); |
| } |
| } |
| |
| /** |
| * Pushes several files and directory into a remote directory. |
| * @param localFiles |
| * @param remoteDirectory |
| */ |
| private void pushFiles(final String[] localFiles, final FileEntry remoteDirectory) { |
| try { |
| final SyncService sync = mCurrentDevice.getSyncService(); |
| if (sync != null) { |
| SyncProgressHelper.run(new SyncRunnable() { |
| @Override |
| public void run(ISyncProgressMonitor monitor) |
| throws SyncException, IOException, TimeoutException { |
| sync.push(localFiles, remoteDirectory, monitor); |
| } |
| |
| @Override |
| public void close() { |
| sync.close(); |
| } |
| }, "Pushing file(s) to the device", mParent.getShell()); |
| } |
| } catch (SyncException e) { |
| if (e.wasCanceled() == false) { |
| DdmConsole.printErrorToConsole(String.format( |
| "Failed to push selection: %1$s", e.getMessage())); |
| } |
| } catch (Exception e) { |
| DdmConsole.printErrorToConsole("Failed to push the items"); |
| DdmConsole.printErrorToConsole(e.getMessage()); |
| } |
| } |
| |
| /** |
| * Pushes a file on a device. |
| * @param local the local filepath of the file to push |
| * @param remoteDirectory the remote destination directory on the device |
| */ |
| private void pushFile(final String local, final String remoteDirectory) { |
| try { |
| final SyncService sync = mCurrentDevice.getSyncService(); |
| if (sync != null) { |
| // get the file name |
| String[] segs = local.split(Pattern.quote(File.separator)); |
| String name = segs[segs.length-1]; |
| final String remoteFile = remoteDirectory + FileListingService.FILE_SEPARATOR |
| + name; |
| |
| SyncProgressHelper.run(new SyncRunnable() { |
| @Override |
| public void run(ISyncProgressMonitor monitor) |
| throws SyncException, IOException, TimeoutException { |
| sync.pushFile(local, remoteFile, monitor); |
| } |
| |
| @Override |
| public void close() { |
| sync.close(); |
| } |
| }, String.format("Pushing %1$s to the device.", name), mParent.getShell()); |
| } |
| } catch (SyncException e) { |
| if (e.wasCanceled() == false) { |
| DdmConsole.printErrorToConsole(String.format( |
| "Failed to push selection: %1$s", e.getMessage())); |
| } |
| } catch (Exception e) { |
| DdmConsole.printErrorToConsole("Failed to push the item(s)."); |
| DdmConsole.printErrorToConsole(e.getMessage()); |
| } |
| } |
| |
| /** |
| * Sets the enabled state based on a FileEntry properties |
| * @param element The selected FileEntry |
| */ |
| protected void setDeleteEnabledState(FileEntry element) { |
| mDeleteAction.setEnabled(element.getType() == FileListingService.TYPE_FILE); |
| } |
| } |