blob: 0a1fab0b246b4f235eac04f493e52fa9261606ed [file] [log] [blame]
/*
* 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.android.uiautomator;
import com.android.uiautomator.tree.BasicTreeNode;
import com.android.uiautomator.tree.BasicTreeNode.IFindNodeListener;
import com.android.uiautomator.tree.UiHierarchyXmlLoader;
import com.android.uiautomator.tree.UiNode;
import org.eclipse.swt.SWTException;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.ImageData;
import org.eclipse.swt.graphics.ImageLoader;
import org.eclipse.swt.graphics.Rectangle;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
public class UiAutomatorModel {
private static UiAutomatorModel inst = null;
private File mScreenshotFile, mXmlDumpFile;
private UiAutomatorViewer mView;
private Image mScreenshot;
private BasicTreeNode mRootNode;
private BasicTreeNode mSelectedNode;
private Rectangle mCurrentDrawingRect;
private List<Rectangle> mNafNodes;
private List<File> mTmpDirs;
// determines whether we lookup the leaf UI node on mouse move of screenshot image
private boolean mExploreMode = true;
private boolean mShowNafNodes = false;
private UiAutomatorModel(UiAutomatorViewer view) {
mView = view;
mTmpDirs = new ArrayList<File>();
}
public static UiAutomatorModel createInstance(UiAutomatorViewer view) {
if (inst != null) {
throw new IllegalStateException("instance already created!");
}
inst = new UiAutomatorModel(view);
return inst;
}
public static UiAutomatorModel getModel() {
if (inst == null) {
throw new IllegalStateException("instance not created yet!");
}
return inst;
}
public File getScreenshotFile() {
return mScreenshotFile;
}
public File getXmlDumpFile() {
return mXmlDumpFile;
}
public boolean loadScreenshotAndXmlDump(File screenshotFile, File xmlDumpFile) {
if (screenshotFile != null && xmlDumpFile != null
&& screenshotFile.isFile() && xmlDumpFile.isFile()) {
ImageData[] data = null;
Image img = null;
try {
// use SWT's ImageLoader to read png from path
data = new ImageLoader().load(screenshotFile.getAbsolutePath());
} catch (SWTException e) {
e.printStackTrace();
return false;
}
// "data" is an array, probably used to handle images that has multiple frames
// i.e. gifs or icons, we just care if it has at least one here
if (data.length < 1) return false;
UiHierarchyXmlLoader loader = new UiHierarchyXmlLoader();
BasicTreeNode rootNode = loader.parseXml(xmlDumpFile
.getAbsolutePath());
if (rootNode == null) {
System.err.println("null rootnode after parsing.");
return false;
}
mNafNodes = loader.getNafNodes();
try {
// Image is tied to ImageData and a Display, so we only need to create once
// per new image
img = new Image(mView.getShell().getDisplay(), data[0]);
} catch (SWTException e) {
e.printStackTrace();
return false;
}
// only update screenhot and xml if both are loaded successfully
if (mScreenshot != null) {
mScreenshot.dispose();
}
mScreenshot = img;
if (mRootNode != null) {
mRootNode.clearAllChildren();
}
// TODO: we should verify here if the coordinates in the XML matches the png
// or not: think loading a phone screenshot with a tablet XML dump
mRootNode = rootNode;
mScreenshotFile = screenshotFile;
mXmlDumpFile = xmlDumpFile;
mExploreMode = true;
mView.loadScreenshotAndXml();
return true;
}
return false;
}
public BasicTreeNode getXmlRootNode() {
return mRootNode;
}
public Image getScreenshot() {
return mScreenshot;
}
public BasicTreeNode getSelectedNode() {
return mSelectedNode;
}
/**
* change node selection in the Model recalculate the rect to highlight,
* also notifies the View to refresh accordingly
*
* @param node
*/
public void setSelectedNode(BasicTreeNode node) {
mSelectedNode = node;
if (mSelectedNode != null && mSelectedNode instanceof UiNode) {
UiNode uiNode = (UiNode) mSelectedNode;
mCurrentDrawingRect = new Rectangle(uiNode.x, uiNode.y, uiNode.width, uiNode.height);
} else {
mCurrentDrawingRect = null;
}
mView.updateScreenshot();
if (mSelectedNode != null) {
mView.loadAttributeTable();
}
}
public Rectangle getCurrentDrawingRect() {
return mCurrentDrawingRect;
}
/**
* Do a search in tree to find a leaf node or deepest parent node containing the coordinate
*
* @param x
* @param y
*/
public void updateSelectionForCoordinates(int x, int y) {
if (mRootNode == null)
return;
MinAreaFindNodeListener listener = new MinAreaFindNodeListener();
boolean found = mRootNode.findLeafMostNodesAtPoint(x, y, listener);
if (found && listener.mNode != null && !listener.mNode.equals(mSelectedNode)) {
mView.updateTreeSelection(listener.mNode);
}
}
public boolean isExploreMode() {
return mExploreMode;
}
public void toggleExploreMode() {
mExploreMode = !mExploreMode;
mView.updateScreenshot();
}
public void setExploreMode(boolean exploreMode) {
mExploreMode = exploreMode;
}
private static class MinAreaFindNodeListener implements IFindNodeListener {
BasicTreeNode mNode = null;
@Override
public void onFoundNode(BasicTreeNode node) {
if (mNode == null) {
mNode = node;
} else {
if ((node.height * node.width) < (mNode.height * mNode.width)) {
mNode = node;
}
}
}
}
public List<Rectangle> getNafNodes() {
return mNafNodes;
}
public void toggleShowNaf() {
mShowNafNodes = !mShowNafNodes;
mView.updateScreenshot();
}
public boolean shouldShowNafNodes() {
return mShowNafNodes;
}
/**
* Registers a temporary directory for deletion when app exists
*
* @param tmpDir
*/
public void registerTempDirectory(File tmpDir) {
mTmpDirs.add(tmpDir);
}
/**
* Performs cleanup tasks when the app is exiting
*/
public void cleanUp() {
for (File dir : mTmpDirs) {
Utils.deleteRecursive(dir);
}
}
}