FileDocCategorySizeDatePackage
DeviceExplorer.javaAPI DocAndroid 1.5 API31592Wed May 06 22:41:08 BST 2009com.android.ddmuilib.explorer

DeviceExplorer.java

/*
 * 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.Device;
import com.android.ddmlib.FileListingService;
import com.android.ddmlib.IShellOutputReceiver;
import com.android.ddmlib.SyncService;
import com.android.ddmlib.FileListingService.FileEntry;
import com.android.ddmlib.SyncService.ISyncProgressMonitor;
import com.android.ddmlib.SyncService.SyncResult;
import com.android.ddmuilib.DdmUiPreferences;
import com.android.ddmuilib.Panel;
import com.android.ddmuilib.TableHelper;
import com.android.ddmuilib.actions.ICommonAction;
import com.android.ddmuilib.console.DdmConsole;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jface.dialogs.ProgressMonitorDialog;
import org.eclipse.jface.operation.IRunnableWithProgress;
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.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Device filesystem explorer class.
 */
public class DeviceExplorer extends Panel {

    private final static String TRACE_KEY_EXT = ".key"; // $NON-NLS-1S
    private final static String TRACE_DATA_EXT = ".data"; // $NON-NLS-1S

    private static Pattern mKeyFilePattern = Pattern.compile(
            "(.+)\\" + TRACE_KEY_EXT); // $NON-NLS-1S
    private static Pattern mDataFilePattern = Pattern.compile(
            "(.+)\\" + TRACE_DATA_EXT); // $NON-NLS-1S

    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 Image mFileImage;
    private Image mFolderImage;
    private Image mPackageImage;
    private Image mOtherImage;

    private Device mCurrentDevice;

    private String mDefaultSave;

    /**
     * Implementation of the SyncService.ISyncProgressMonitor. It wraps a jFace IProgressMonitor
     * and just forward the calls to the jFace object.
     */
    private static class SyncProgressMonitor implements ISyncProgressMonitor {

        private IProgressMonitor mMonitor;
        private String mName;

        SyncProgressMonitor(IProgressMonitor monitor, String name) {
            mMonitor = monitor;
            mName = name;
        }

        public void start(int totalWork) {
            mMonitor.beginTask(mName, totalWork);
        }

        public void stop() {
            mMonitor.done();
        }

        public void advance(int work) {
            mMonitor.worked(work);
        }

        public boolean isCanceled() {
            return mMonitor.isCanceled();
        }

        public void startSubTask(String name) {
            mMonitor.subTask(name);
        }
    }

    public DeviceExplorer() {

    }

    /**
     * Sets the images for the listview
     * @param fileImage
     * @param folderImage
     * @param otherImage
     */
    public void setImages(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
     */
    public void setActions(ICommonAction pushAction, ICommonAction pullAction,
            ICommonAction deleteAction) {
        mPushAction = pushAction;
        mPullAction = pullAction;
        mDeleteAction = deleteAction;
    }

    /**
     * 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());

        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() {
            public void selectionChanged(SelectionChangedEvent event) {
                ISelection sel = event.getSelection();
                if (sel.isEmpty()) {
                    mPullAction.setEnabled(false);
                    mPushAction.setEnabled(false);
                    mDeleteAction.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) {
                            setDeleteEnabledState((FileEntry)element);
                        } else {
                            mDeleteAction.setEnabled(false);
                        }
                    }
                }
            }
        });

        // add support for double click
        mTreeViewer.addDoubleClickListener(new IDoubleClickListener() {
            public void doubleClick(DoubleClickEvent event) {
                ISelection sel = event.getSelection();

                if (sel instanceof IStructuredSelection) {
                    IStructuredSelection selection = (IStructuredSelection) sel;

                    if (selection.size() == 1) {
                        FileEntry entry = (FileEntry)selection.getFirstElement();
                        String name = entry.getName();

                        FileEntry parentEntry = entry.getParent();

                        // can't really do anything with no parent
                        if (parentEntry == null) {
                            return;
                        }

                        // check this is a file like we want.
                        Matcher m = mKeyFilePattern.matcher(name);
                        if (m.matches()) {
                            // get the name w/o the extension
                            String baseName = m.group(1);

                            // add the data extension
                            String dataName = baseName + TRACE_DATA_EXT;

                            FileEntry dataEntry = parentEntry.findChild(dataName);

                            handleTraceDoubleClick(baseName, entry, dataEntry);

                        } else {
                            m = mDataFilePattern.matcher(name);
                            if (m.matches()) {
                                // get the name w/o the extension
                                String baseName = m.group(1);

                                // add the key extension
                                String keyName = baseName + TRACE_KEY_EXT;

                                FileEntry keyEntry = parentEntry.findChild(keyName);

                                handleTraceDoubleClick(baseName, keyEntry, entry);
                            }
                        }
                    }
                }
            }
        });

        // 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() {
                                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();
    }

    /**
     * Processes a double click on a trace file
     * @param baseName the base name of the 2 files.
     * @param keyEntry The FileEntry for the .key file.
     * @param dataEntry The FileEntry for the .data file.
     */
    private void handleTraceDoubleClick(String baseName, FileEntry keyEntry,
            FileEntry dataEntry) {
        // first we need to download the files.
        File keyFile;
        File dataFile;
        String path;
        try {
            // create a temp file for keyFile
            File f = File.createTempFile(baseName, ".trace");
            f.delete();
            f.mkdir();

            path = f.getAbsolutePath();

            keyFile = new File(path + File.separator + keyEntry.getName());
            dataFile = new File(path + File.separator + dataEntry.getName());
        } catch (IOException e) {
            return;
        }

        // download the files
        SyncService sync = mCurrentDevice.getSyncService();
        if (sync != null) {
            ISyncProgressMonitor monitor = SyncService.getNullProgressMonitor();
            SyncResult result = sync.pullFile(keyEntry, keyFile.getAbsolutePath(), monitor);
            if (result.getCode() != SyncService.RESULT_OK) {
                DdmConsole.printErrorToConsole(String.format(
                        "Failed to pull %1$s: %2$s", keyEntry.getName(), result.getMessage()));
                return;
            }

            result = sync.pullFile(dataEntry, dataFile.getAbsolutePath(), monitor);
            if (result.getCode() != SyncService.RESULT_OK) {
                DdmConsole.printErrorToConsole(String.format(
                        "Failed to pull %1$s: %2$s", dataEntry.getName(), result.getMessage()));
                return;
            }

            // now that we have the file, we need to launch traceview
            String[] command = new String[2];
            command[0] = DdmUiPreferences.getTraceview();
            command[1] = path + File.separator + baseName;

            try {
                final Process p = Runtime.getRuntime().exec(command);

                // create a thread for the output
                new Thread("Traceview output") {
                    @Override
                    public void run() {
                        // create a buffer to read the stderr output
                        InputStreamReader is = new InputStreamReader(p.getErrorStream());
                        BufferedReader resultReader = new BufferedReader(is);

                        // read the lines as they come. if null is returned, it's
                        // because the process finished
                        try {
                            while (true) {
                                String line = resultReader.readLine();
                                if (line != null) {
                                    DdmConsole.printErrorToConsole("Traceview: " + line);
                                } else {
                                    break;
                                }
                            }
                            // get the return code from the process
                            p.waitFor();
                        } catch (IOException e) {
                        } catch (InterruptedException e) {

                        }
                    }
                }.start();

            } catch (IOException e) {
            }
        }
    }

    /**
     * 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() {
                public void addOutput(byte[] data, int offset, int length) {
                    // pass
                    // TODO get output to display errors if any.
                }

                public void flush() {
                    mTreeViewer.refresh(parentEntry);
                }

                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.
        }

    }

    /**
     * Force a full refresh of the explorer.
     */
    public void refresh() {
        mTreeViewer.refresh(true);
    }

    /**
     * Sets the new device to explorer
     */
    public void switchDevice(final Device 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() {
                    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() {
            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) {
        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()]);

            // get a progressdialog
            try {
                new ProgressMonitorDialog(mParent.getShell()).run(true, true,
                        new IRunnableWithProgress() {
                    public void run(IProgressMonitor monitor)
                            throws InvocationTargetException,
                            InterruptedException {
                        // create a monitor wrapper around the jface monitor
                        SyncResult result = sync.pull(entryArray, localDirectory,
                                new SyncProgressMonitor(monitor,
                                        "Pulling file(s) from the device"));

                        if (result.getCode() != SyncService.RESULT_OK) {
                            DdmConsole.printErrorToConsole(String.format(
                                    "Failed to pull selection: %1$s", result.getMessage()));
                        }
                        sync.close();
                    }
                });
            } catch (InvocationTargetException e) {
                DdmConsole.printErrorToConsole( "Failed to pull selection");
                DdmConsole.printErrorToConsole(e.getMessage());
            } catch (InterruptedException 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) {
        final SyncService sync = mCurrentDevice.getSyncService();
        if (sync != null) {
            try {
                new ProgressMonitorDialog(mParent.getShell()).run(true, true,
                        new IRunnableWithProgress() {
                    public void run(IProgressMonitor monitor)
                            throws InvocationTargetException,
                            InterruptedException {
                        SyncResult result = sync.pullFile(remote, local, new SyncProgressMonitor(
                                monitor, String.format("Pulling %1$s from the device",
                                        remote.getName())));
                        if (result.getCode() != SyncService.RESULT_OK) {
                            DdmConsole.printErrorToConsole(String.format(
                                    "Failed to pull %1$s: %2$s", remote, result.getMessage()));
                        }

                        sync.close();
                    }
                });
            } catch (InvocationTargetException e) {
                DdmConsole.printErrorToConsole( "Failed to pull selection");
                DdmConsole.printErrorToConsole(e.getMessage());
            } catch (InterruptedException 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) {
        final SyncService sync = mCurrentDevice.getSyncService();
        if (sync != null) {
            try {
                new ProgressMonitorDialog(mParent.getShell()).run(true, true,
                        new IRunnableWithProgress() {
                    public void run(IProgressMonitor monitor)
                            throws InvocationTargetException,
                            InterruptedException {
                        SyncResult result = sync.push(localFiles, remoteDirectory,
                                    new SyncProgressMonitor(monitor,
                                            "Pushing file(s) to the device"));
                        if (result.getCode() != SyncService.RESULT_OK) {
                            DdmConsole.printErrorToConsole(String.format(
                                    "Failed to push the items: %1$s", result.getMessage()));
                        }

                        sync.close();
                    }
                });
            } catch (InvocationTargetException e) {
                DdmConsole.printErrorToConsole("Failed to push the items");
                DdmConsole.printErrorToConsole(e.getMessage());
            } catch (InterruptedException e) {
                DdmConsole.printErrorToConsole("Failed to push the items");
                DdmConsole.printErrorToConsole(e.getMessage());
            }
            return;
        }
    }

    /**
     * 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) {
        final SyncService sync = mCurrentDevice.getSyncService();
        if (sync != null) {
            try {
                new ProgressMonitorDialog(mParent.getShell()).run(true, true,
                        new IRunnableWithProgress() {
                    public void run(IProgressMonitor monitor)
                            throws InvocationTargetException,
                            InterruptedException {
                        // get the file name
                        String[] segs = local.split(Pattern.quote(File.separator));
                        String name = segs[segs.length-1];
                        String remoteFile = remoteDirectory + FileListingService.FILE_SEPARATOR
                                + name;

                        SyncResult result = sync.pushFile(local, remoteFile,
                                    new SyncProgressMonitor(monitor,
                                            String.format("Pushing %1$s to the device.", name)));
                        if (result.getCode() != SyncService.RESULT_OK) {
                            DdmConsole.printErrorToConsole(String.format(
                                    "Failed to push %1$s on %2$s: %3$s",
                                    name, mCurrentDevice.getSerialNumber(), result.getMessage()));
                        }

                        sync.close();
                    }
                });
            } catch (InvocationTargetException e) {
                DdmConsole.printErrorToConsole("Failed to push the item(s).");
                DdmConsole.printErrorToConsole(e.getMessage());
            } catch (InterruptedException e) {
                DdmConsole.printErrorToConsole("Failed to push the item(s).");
                DdmConsole.printErrorToConsole(e.getMessage());
            }
            return;
        }
    }

    /**
     * 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);
    }
}