com.smartmonkey.recrep.SMonkeyViewer.java Source code

Java tutorial

Introduction

Here is the source code for com.smartmonkey.recrep.SMonkeyViewer.java

Source

/*
 * Copyright (C) 2012 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.smartmonkey.recrep;

import java.util.ArrayList;
import java.util.List;

import org.eclipse.swt.graphics.Color;

import com.android.chimpchat.core.PhysicalButton;
import com.android.chimpchat.core.TouchPressType;
import com.smartmonkey.recrep.actions.ClearRecordingAction;
import com.smartmonkey.recrep.actions.ExpandAllAction;
import com.smartmonkey.recrep.actions.FBLoginAction;
import com.smartmonkey.recrep.actions.ImageHelper;
import com.smartmonkey.recrep.actions.OpenFilesAction;
import com.smartmonkey.recrep.actions.OpenRecordFileAction;
import com.smartmonkey.recrep.actions.ReplayAction;
import com.smartmonkey.recrep.actions.SaveFilesAction;
import com.smartmonkey.recrep.actions.SaveRecordFileAction;
import com.smartmonkey.recrep.actions.ScreenshotAction;
import com.smartmonkey.recrep.actions.ToggleAutoRefreshAction;
import com.smartmonkey.recrep.actions.ToggleNafAction;
import com.smartmonkey.recrep.chimpevent.ChimpEvent;
import com.smartmonkey.recrep.tree.AttributePair;
import com.smartmonkey.recrep.tree.BasicTreeNode;
import com.smartmonkey.recrep.tree.BasicTreeNodeContentProvider;

import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.IMenuCreator;
import org.eclipse.jface.action.ToolBarManager;
import org.eclipse.jface.layout.TableColumnLayout;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.CellEditor;
import org.eclipse.jface.viewers.ColumnLabelProvider;
import org.eclipse.jface.viewers.ColumnWeightData;
import org.eclipse.jface.viewers.EditingSupport;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.StyledCellLabelProvider;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.TableViewerColumn;
import org.eclipse.jface.viewers.TextCellEditor;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.ViewerCell;
import org.eclipse.jface.window.ApplicationWindow;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.SashForm;
import org.eclipse.swt.custom.StyleRange;
import org.eclipse.swt.events.HelpListener;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseListener;
import org.eclipse.swt.events.MouseMoveListener;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.graphics.Transform;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.Text;
import org.eclipse.swt.widgets.Tree;

public class SMonkeyViewer extends ApplicationWindow {

    private static final int IMG_BORDER = 2;

    private Canvas mScreenshotCanvas;

    private Text mText;
    private Button mSendTextButton;

    private TreeViewer mTreeViewer;

    private TableViewer chimpEventTableViewer;

    private Action mOpenFilesAction;
    private Action mSaveFilesAction;
    private Action mExpandAllAction;
    private Action mScreenshotAction;
    private Action mToggleNafAction;
    private Action mToggleAutoRefreshAction;
    private Action mReplayAction;
    private Action mFBLoginAction;
    private Action mClearRecordingAction;
    private Action mOpenRecordFileAction;
    private Action mSaveRecordFileAction;

    private TableViewer mTableViewer;

    private float mScale = 1.0f;
    private int mDx, mDy;

    /**
     * Create the application window.
     */
    public SMonkeyViewer() {
        super(null);
        SMonkeyModel.createInstance(this);
        createActions();

    }

    /**
     * Create contents of the application window.
     * 
     * @param parent
     */
    @Override
    protected Control createContents(Composite parent) {
        parent.setLayout(new FillLayout());
        SashForm baseSash = new SashForm(parent, SWT.HORIZONTAL | SWT.NONE);
        // draw the canvas with border, so the divider area for sash form can be
        // highlighted
        SashForm leftSash = new SashForm(baseSash, SWT.VERTICAL);
        mScreenshotCanvas = new Canvas(leftSash, SWT.BORDER | SWT.NO_REDRAW_RESIZE);
        mScreenshotCanvas.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseUp(MouseEvent e) {
                if (e.button == 3) // Right Click
                {
                    SMonkeyModel.getModel().toggleExploreMode();
                } else if (e.button == 1) // Left Click
                {
                    SMonkeyModel.getModel().touch();
                }
            }
        });

        mScreenshotCanvas.setBackground(getShell().getDisplay().getSystemColor(SWT.COLOR_WIDGET_BACKGROUND));
        mScreenshotCanvas.addPaintListener(new PaintListener() {
            @Override
            public void paintControl(PaintEvent e) {
                Image image = SMonkeyModel.getModel().getScreenshot();
                if (image != null) {
                    updateScreenshotTransformation();
                    // shifting the image here, so that there's a border around
                    // screen shot
                    // this makes highlighting red rectangles on the screen shot
                    // edges more visible
                    Transform t = new Transform(e.gc.getDevice());
                    t.translate(mDx, mDy);
                    t.scale(mScale, mScale);
                    e.gc.setTransform(t);
                    e.gc.drawImage(image, 0, 0);
                    // this resets the transformation to identity transform,
                    // i.e. no change
                    // we don't use transformation here because it will cause
                    // the line pattern
                    // and line width of highlight rect to be scaled, causing to
                    // appear to be blurry
                    e.gc.setTransform(null);
                    if (SMonkeyModel.getModel().shouldShowNafNodes()) {
                        // highlight the "Not Accessibility Friendly" nodes
                        e.gc.setForeground(e.gc.getDevice().getSystemColor(SWT.COLOR_YELLOW));
                        e.gc.setBackground(e.gc.getDevice().getSystemColor(SWT.COLOR_YELLOW));
                        for (Rectangle r : SMonkeyModel.getModel().getNafNodes()) {
                            e.gc.setAlpha(50);
                            e.gc.fillRectangle(mDx + getScaledSize(r.x), mDy + getScaledSize(r.y),
                                    getScaledSize(r.width), getScaledSize(r.height));
                            e.gc.setAlpha(255);
                            e.gc.setLineStyle(SWT.LINE_SOLID);
                            e.gc.setLineWidth(2);
                            e.gc.drawRectangle(mDx + getScaledSize(r.x), mDy + getScaledSize(r.y),
                                    getScaledSize(r.width), getScaledSize(r.height));
                        }
                    }
                    // draw the mouseover rects
                    Rectangle rect = SMonkeyModel.getModel().getCurrentDrawingRect();
                    if (rect != null) {
                        e.gc.setForeground(e.gc.getDevice().getSystemColor(SWT.COLOR_RED));
                        if (SMonkeyModel.getModel().isExploreMode()) {
                            // when we highlight nodes dynamically on mouse
                            // move,
                            // use dashed borders
                            e.gc.setLineStyle(SWT.LINE_DASH);
                            e.gc.setLineWidth(1);
                        } else {
                            // when highlighting nodes on tree node selection,
                            // use solid borders
                            e.gc.setLineStyle(SWT.LINE_SOLID);
                            e.gc.setLineWidth(2);
                        }
                        e.gc.drawRectangle(mDx + getScaledSize(rect.x), mDy + getScaledSize(rect.y),
                                getScaledSize(rect.width), getScaledSize(rect.height));
                    }
                }
            }
        });
        mScreenshotCanvas.addMouseMoveListener(new MouseMoveListener() {
            @Override
            public void mouseMove(MouseEvent e) {
                if (SMonkeyModel.getModel().isExploreMode()) {
                    SMonkeyModel.getModel().updateSelectionForCoordinates(getInverseScaledSize(e.x - mDx),
                            getInverseScaledSize(e.y - mDy));
                }
            }
        });

        // Lower Left Base contains the physical buttons on the phone
        SashForm lowerLeftBase = new SashForm(leftSash, SWT.HORIZONTAL);

        Composite buttonComposite = new Composite(lowerLeftBase, SWT.BORDER);
        ToolBarManager physicalButtonToolbarManager = new ToolBarManager();
        physicalButtonToolbarManager.add(new Action() {
            @Override
            public ImageDescriptor getImageDescriptor() {
                return ImageHelper.loadImageDescriptorFromResource("images/back_gray.png");
            }

            public void run() {
                SMonkeyModel.getModel().pressButton(PhysicalButton.BACK);
            }
        });
        physicalButtonToolbarManager.add(new Action() {
            @Override
            public ImageDescriptor getImageDescriptor() {
                return ImageHelper.loadImageDescriptorFromResource("images/menu_gray.png");
            }

            public void run() {
                SMonkeyModel.getModel().pressButton(PhysicalButton.MENU);
            }
        });
        physicalButtonToolbarManager.add(new Action() {
            @Override
            public ImageDescriptor getImageDescriptor() {
                return ImageHelper.loadImageDescriptorFromResource("images/home_gray.png");
            }

            public void run() {
                SMonkeyModel.getModel().pressButton(PhysicalButton.HOME);
            }
        });
        physicalButtonToolbarManager.add(new Action() {
            @Override
            public ImageDescriptor getImageDescriptor() {
                return ImageHelper.loadImageDescriptorFromResource("images/noop.png");
            }

            public void run() {
                SMonkeyModel.getModel().noop();
            }
        });
        physicalButtonToolbarManager.add(mFBLoginAction);

        physicalButtonToolbarManager.createControl(buttonComposite);

        Composite textComposite = new Composite(lowerLeftBase, SWT.BORDER);
        mText = new Text(textComposite, SWT.SINGLE);
        mSendTextButton = new Button(textComposite, SWT.PUSH);
        mSendTextButton.setText("Send\nText");
        mSendTextButton.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseUp(MouseEvent e) {
                SMonkeyModel.getModel().sendText(mText.getText());
            }
        });
        textComposite.setLayout(new FillLayout());

        leftSash.setWeights(new int[] { 6, 1 });

        // middle sash contains the list of events, which are highlighted as
        // they happen.
        // TODO: Add a fast forward button to perform the next event, skipping
        // the wait
        SashForm middleSash = new SashForm(baseSash, SWT.VERTICAL);

        ToolBarManager replayToolbarManager = new ToolBarManager(SWT.FLAT);
        replayToolbarManager.add(mClearRecordingAction);
        replayToolbarManager.add(mOpenRecordFileAction);
        replayToolbarManager.add(mSaveRecordFileAction);
        replayToolbarManager.add(mToggleAutoRefreshAction);
        replayToolbarManager.add(mReplayAction);

        replayToolbarManager.createControl(middleSash);

        chimpEventTableViewer = new TableViewer(middleSash,
                SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL | SWT.FULL_SELECTION | SWT.BORDER);

        chimpEventTableViewer.getTable().setMenu(new Menu(chimpEventTableViewer.getTable()));

        TableViewerColumn waitColumn = new TableViewerColumn(chimpEventTableViewer, SWT.NONE);
        waitColumn.getColumn().setText("Wait time");
        waitColumn.getColumn().setWidth(100);
        waitColumn.setLabelProvider(new ColumnLabelProvider() {
            @Override
            public String getText(Object element) {
                if (element instanceof ChimpEvent) {
                    ChimpEvent chimp = (ChimpEvent) element;
                    return chimp.getWait();
                }
                return super.getText(element);
            }
        });

        TableViewerColumn typeColumn = new TableViewerColumn(chimpEventTableViewer, SWT.NONE);
        typeColumn.getColumn().setText("Type");
        typeColumn.getColumn().setWidth(100);
        typeColumn.setLabelProvider(new ColumnLabelProvider() {
            @Override
            public String getText(Object element) {
                if (element instanceof ChimpEvent) {
                    ChimpEvent chimp = (ChimpEvent) element;
                    return chimp.getType();
                }
                return super.getText(element);
            }
        });

        TableViewerColumn miscColumn = new TableViewerColumn(getChimpEventTableViewer(), SWT.NONE);
        miscColumn.getColumn().setText("Misc");
        miscColumn.getColumn().setWidth(100);
        miscColumn.setLabelProvider(new ColumnLabelProvider() {
            @Override
            public String getText(Object element) {
                if (element instanceof ChimpEvent) {
                    ChimpEvent chimp = (ChimpEvent) element;
                    return chimp.getMisc();
                }
                return super.getText(element);
            }
        });
        chimpEventTableViewer.getTable().setLinesVisible(true);
        chimpEventTableViewer.getTable().setHeaderVisible(true);
        chimpEventTableViewer.setContentProvider(ArrayContentProvider.getInstance());
        chimpEventTableViewer.setInput(SMonkeyModel.getModel().getLog());

        middleSash.setWeights(new int[] { 1, 20 });

        // right sash is split into 2 parts: upper-right and lower-right
        // both are composites with borders, so that the horizontal divider can
        // be highlighted by
        // the borders
        SashForm rightSash = new SashForm(baseSash, SWT.VERTICAL);

        // upper-right base contains the toolbar and the tree
        Composite upperRightBase = new Composite(rightSash, SWT.BORDER);
        upperRightBase.setLayout(new GridLayout(1, false));
        ToolBarManager toolBarManager = new ToolBarManager(SWT.FLAT);
        toolBarManager.add(mOpenFilesAction);
        toolBarManager.add(mSaveFilesAction);
        toolBarManager.add(mExpandAllAction);
        toolBarManager.add(mScreenshotAction);
        toolBarManager.add(mToggleNafAction);

        // toolBarManager.add(mClearRecordingAction);
        // toolBarManager.add(mSaveRecordingAction);
        toolBarManager.createControl(upperRightBase);

        // Button b = new Button(upperRightBase.getShell(),SWT.CHECK);
        // b.setText("Auto");

        mTreeViewer = new TreeViewer(upperRightBase, SWT.NONE);
        mTreeViewer.setContentProvider(new BasicTreeNodeContentProvider());
        // default LabelProvider uses toString() to generate text to display
        mTreeViewer.setLabelProvider(new LabelProvider());
        mTreeViewer.addSelectionChangedListener(new ISelectionChangedListener() {
            @Override
            public void selectionChanged(SelectionChangedEvent event) {
                if (event.getSelection().isEmpty()) {
                    SMonkeyModel.getModel().setSelectedNode(null);
                } else if (event.getSelection() instanceof IStructuredSelection) {
                    IStructuredSelection selection = (IStructuredSelection) event.getSelection();
                    Object o = selection.toArray()[0];
                    if (o instanceof BasicTreeNode) {
                        SMonkeyModel.getModel().setSelectedNode((BasicTreeNode) o);
                    }
                }
            }
        });
        Tree tree = mTreeViewer.getTree();
        tree.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));
        // move focus so that it's not on tool bar (looks weird)
        tree.setFocus();

        // lower-right base contains the detail group
        Composite lowerRightBase = new Composite(rightSash, SWT.BORDER);
        lowerRightBase.setLayout(new FillLayout());
        Group grpNodeDetail = new Group(lowerRightBase, SWT.NONE);
        grpNodeDetail.setLayout(new FillLayout(SWT.HORIZONTAL));
        grpNodeDetail.setText("Node Detail");

        Composite tableContainer = new Composite(grpNodeDetail, SWT.NONE);

        TableColumnLayout columnLayout = new TableColumnLayout();
        tableContainer.setLayout(columnLayout);

        mTableViewer = new TableViewer(tableContainer, SWT.NONE | SWT.FULL_SELECTION);
        Table table = mTableViewer.getTable();
        table.setLinesVisible(true);
        // use ArrayContentProvider here, it assumes the input to the
        // TableViewer
        // is an array, where each element represents a row in the table
        mTableViewer.setContentProvider(ArrayContentProvider.getInstance());

        TableViewerColumn tableViewerColumnKey = new TableViewerColumn(mTableViewer, SWT.NONE);
        TableColumn tblclmnKey = tableViewerColumnKey.getColumn();
        tableViewerColumnKey.setLabelProvider(new ColumnLabelProvider() {
            @Override
            public String getText(Object element) {
                if (element instanceof AttributePair) {
                    // first column, shows the attribute name
                    return ((AttributePair) element).key;
                }
                return super.getText(element);
            }
        });
        columnLayout.setColumnData(tblclmnKey, new ColumnWeightData(1, ColumnWeightData.MINIMUM_WIDTH, true));

        TableViewerColumn tableViewerColumnValue = new TableViewerColumn(mTableViewer, SWT.NONE);
        tableViewerColumnValue.setEditingSupport(new AttributeTableEditingSupport(mTableViewer));
        TableColumn tblclmnValue = tableViewerColumnValue.getColumn();
        columnLayout.setColumnData(tblclmnValue, new ColumnWeightData(2, ColumnWeightData.MINIMUM_WIDTH, true));
        tableViewerColumnValue.setLabelProvider(new ColumnLabelProvider() {
            @Override
            public String getText(Object element) {
                if (element instanceof AttributePair) {
                    // second column, shows the attribute value
                    return ((AttributePair) element).value;
                }
                return super.getText(element);
            }
        });
        // sets the ratio of the vertical split: left 5 vs middle 3 vs right 3
        baseSash.setWeights(new int[] { 5, 5, 3 });
        return baseSash;
    }

    /**
     * Create the actions.
     */
    private void createActions() {
        mOpenFilesAction = new OpenFilesAction(this);
        mSaveFilesAction = new SaveFilesAction(this);
        mExpandAllAction = new ExpandAllAction(this);
        mScreenshotAction = new ScreenshotAction(this);
        mToggleNafAction = new ToggleNafAction();
        mToggleAutoRefreshAction = new ToggleAutoRefreshAction();
        mReplayAction = new ReplayAction();
        mFBLoginAction = new FBLoginAction();
        mClearRecordingAction = new ClearRecordingAction();
        mOpenRecordFileAction = new OpenRecordFileAction(this);
        mSaveRecordFileAction = new SaveRecordFileAction(this);

    }

    /**
     * Launch the application.
     * 
     * @param args
     */
    public static void main(String args[]) {
        try {
            SMonkeyViewer window = new SMonkeyViewer();
            window.setBlockOnOpen(true);
            window.open();
            window.getShell().setMaximized(true);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * Configure the shell.
     * 
     * @param newShell
     */
    @Override
    protected void configureShell(Shell newShell) {
        super.configureShell(newShell);
        newShell.setText("UI Automator Viewer");
        newShell.setMaximized(true);
    }

    /**
     * Asks the Model for screenshot and xml tree data, then populates the
     * screenshot area and tree view accordingly
     */
    public void loadScreenshotAndXml() {
        mScreenshotCanvas.redraw();
        // load xml into tree
        BasicTreeNode wrapper = new BasicTreeNode();
        // putting another root node on top of existing root node
        // because Tree seems to like to hide the root node
        wrapper.addChild(SMonkeyModel.getModel().getXmlRootNode());
        mTreeViewer.setInput(wrapper);
        mTreeViewer.getTree().setFocus();
    }

    /*
     * Causes a redraw of the canvas.
     * 
     * The drawing code of canvas will handle highlighted nodes and etc based on
     * data retrieved from Model
     */
    public void updateScreenshot() {
        mScreenshotCanvas.redraw();
    }

    public void expandAll() {
        mTreeViewer.expandAll();
    }

    public void updateTreeSelection(BasicTreeNode node) {
        mTreeViewer.setSelection(new StructuredSelection(node), true);
    }

    public void loadAttributeTable() {
        // udpate the lower right corner table to show the attributes of the
        // node
        mTableViewer.setInput(SMonkeyModel.getModel().getSelectedNode().getAttributesArray());
    }

    @Override
    protected Point getInitialSize() {
        return new Point(800, 600);
    }

    private void updateScreenshotTransformation() {
        Rectangle canvas = mScreenshotCanvas.getBounds();
        Rectangle image = SMonkeyModel.getModel().getScreenshot().getBounds();
        float scaleX = (canvas.width - 2 * IMG_BORDER - 1) / (float) image.width;
        float scaleY = (canvas.height - 2 * IMG_BORDER - 1) / (float) image.height;
        // use the smaller scale here so that we can fit the entire screenshot
        mScale = Math.min(scaleX, scaleY);
        // calculate translation values to center the image on the canvas
        mDx = (canvas.width - getScaledSize(image.width) - IMG_BORDER * 2) / 2 + IMG_BORDER;
        mDy = (canvas.height - getScaledSize(image.height) - IMG_BORDER * 2) / 2 + IMG_BORDER;
    }

    private int getScaledSize(int size) {
        if (mScale == 1.0f) {
            return size;
        } else {
            return new Double(Math.floor((size * mScale))).intValue();
        }
    }

    private int getInverseScaledSize(int size) {
        if (mScale == 1.0f) {
            return size;
        } else {
            return new Double(Math.floor((size / mScale))).intValue();
        }
    }

    public TableViewer getChimpEventTableViewer() {
        return chimpEventTableViewer;
    }

    public void setChimpEventTableViewer(TableViewer chimpEventTableViewer) {
        this.chimpEventTableViewer = chimpEventTableViewer;
    }

    private class AttributeTableEditingSupport extends EditingSupport {

        private TableViewer mViewer;

        public AttributeTableEditingSupport(TableViewer viewer) {
            super(viewer);
            mViewer = viewer;
        }

        @Override
        protected boolean canEdit(Object arg0) {
            return true;
        }

        @Override
        protected CellEditor getCellEditor(Object arg0) {
            return new TextCellEditor(mViewer.getTable());
        }

        @Override
        protected Object getValue(Object o) {
            return ((AttributePair) o).value;
        }

        @Override
        protected void setValue(Object arg0, Object arg1) {
        }

    }
}