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

DeviceExplorer

public class DeviceExplorer extends com.android.ddmuilib.Panel
Device filesystem explorer class.

Fields Summary
private static final String
TRACE_KEY_EXT
private static final String
TRACE_DATA_EXT
private static Pattern
mKeyFilePattern
private static Pattern
mDataFilePattern
public static String
COLUMN_NAME
public static String
COLUMN_SIZE
public static String
COLUMN_DATE
public static String
COLUMN_TIME
public static String
COLUMN_PERMISSIONS
public static String
COLUMN_INFO
private org.eclipse.swt.widgets.Composite
mParent
private org.eclipse.jface.viewers.TreeViewer
mTreeViewer
private org.eclipse.swt.widgets.Tree
mTree
private DeviceContentProvider
mContentProvider
private com.android.ddmuilib.actions.ICommonAction
mPushAction
private com.android.ddmuilib.actions.ICommonAction
mPullAction
private com.android.ddmuilib.actions.ICommonAction
mDeleteAction
private org.eclipse.swt.graphics.Image
mFileImage
private org.eclipse.swt.graphics.Image
mFolderImage
private org.eclipse.swt.graphics.Image
mPackageImage
private org.eclipse.swt.graphics.Image
mOtherImage
private com.android.ddmlib.Device
mCurrentDevice
private String
mDefaultSave
Constructors Summary
public DeviceExplorer()


    
Methods Summary
protected org.eclipse.swt.widgets.ControlcreateControl(org.eclipse.swt.widgets.Composite parent)
Creates a control capable of displaying some information. This is called once, when the application is initializing, from the UI thread.

        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;
    
public voiddeleteSelection()

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

    
private voidhandleTraceDoubleClick(java.lang.String baseName, com.android.ddmlib.FileListingService.FileEntry keyEntry, com.android.ddmlib.FileListingService.FileEntry dataEntry)
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.

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

        
    
private voidpullFile(com.android.ddmlib.FileListingService.FileEntry remote, java.lang.String local)
Pulls a file from a device.

param
remote the remote file on the device
param
local the destination filepath

        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());
            }
        }
    
private voidpullSelection(org.eclipse.swt.widgets.TreeItem[] items, java.lang.String localDirectory)
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.

        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());
            }
        }
    
public voidpullSelection()
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).

        // 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);
            }
        }
    
private voidpushFile(java.lang.String local, java.lang.String remoteDirectory)
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

        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;
        }
    
private voidpushFiles(java.lang.String[] localFiles, com.android.ddmlib.FileListingService.FileEntry remoteDirectory)
Pushes several files and directory into a remote directory.

param
localFiles
param
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;
        }
    
public voidpushIntoSelection()
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.

        // 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 voidrefresh()
Force a full refresh of the explorer.

        mTreeViewer.refresh(true);
    
private voidrefresh(com.android.ddmlib.FileListingService.FileEntry entry)
Refresh an entry from a non ui thread.

param
entry the entry to refresh.

        Display d = mTreeViewer.getTree().getDisplay();
        d.asyncExec(new Runnable() {
            public void run() {
                mTreeViewer.refresh(entry);
            }
        });
    
public voidsetActions(com.android.ddmuilib.actions.ICommonAction pushAction, com.android.ddmuilib.actions.ICommonAction pullAction, com.android.ddmuilib.actions.ICommonAction deleteAction)
Sets the actions so that the device explorer can enable/disable them based on the current selection

param
pushAction
param
pullAction
param
deleteAction

        mPushAction = pushAction;
        mPullAction = pullAction;
        mDeleteAction = deleteAction;
    
protected voidsetDeleteEnabledState(com.android.ddmlib.FileListingService.FileEntry element)
Sets the enabled state based on a FileEntry properties

param
element The selected FileEntry

        mDeleteAction.setEnabled(element.getType() == FileListingService.TYPE_FILE);
    
public voidsetFocus()
Sets the focus to the proper control inside the panel.

        mTree.setFocus();
    
public voidsetImages(org.eclipse.swt.graphics.Image fileImage, org.eclipse.swt.graphics.Image folderImage, org.eclipse.swt.graphics.Image packageImage, org.eclipse.swt.graphics.Image otherImage)
Sets the images for the listview

param
fileImage
param
folderImage
param
otherImage

        mFileImage = fileImage;
        mFolderImage = folderImage;
        mPackageImage = packageImage;
        mOtherImage = otherImage;
    
public voidswitchDevice(com.android.ddmlib.Device device)
Sets the new device to explorer

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