();
@@ -85,29 +85,29 @@ public final class Device implements IDevice {
* Socket for the connection monitoring client connection/disconnection.
*/
private SocketChannel mSocketChannel;
-
- /*
+
+ /*
* (non-Javadoc)
* @see com.android.ddmlib.IDevice#getSerialNumber()
*/
public String getSerialNumber() {
return serialNumber;
}
-
- public String getVmName() {
- return mVmName;
+
+ public String getAvdName() {
+ return mAvdName;
}
-
- /*
+
+ /*
* (non-Javadoc)
* @see com.android.ddmlib.IDevice#getState()
*/
public DeviceState getState() {
return state;
}
-
- /*
+
+ /*
* (non-Javadoc)
* @see com.android.ddmlib.IDevice#getProperties()
*/
@@ -115,7 +115,7 @@ public final class Device implements IDevice {
return Collections.unmodifiableMap(mProperties);
}
- /*
+ /*
* (non-Javadoc)
* @see com.android.ddmlib.IDevice#getPropertyCount()
*/
@@ -123,21 +123,21 @@ public final class Device implements IDevice {
return mProperties.size();
}
- /*
+ /*
* (non-Javadoc)
* @see com.android.ddmlib.IDevice#getProperty(java.lang.String)
*/
public String getProperty(String name) {
return mProperties.get(name);
}
-
+
@Override
public String toString() {
return serialNumber;
}
- /*
+ /*
* (non-Javadoc)
* @see com.android.ddmlib.IDevice#isOnline()
*/
@@ -145,7 +145,7 @@ public final class Device implements IDevice {
return state == DeviceState.ONLINE;
}
- /*
+ /*
* (non-Javadoc)
* @see com.android.ddmlib.IDevice#isEmulator()
*/
@@ -153,7 +153,7 @@ public final class Device implements IDevice {
return serialNumber.matches(RE_EMULATOR_SN);
}
- /*
+ /*
* (non-Javadoc)
* @see com.android.ddmlib.IDevice#isOffline()
*/
@@ -161,7 +161,7 @@ public final class Device implements IDevice {
return state == DeviceState.OFFLINE;
}
- /*
+ /*
* (non-Javadoc)
* @see com.android.ddmlib.IDevice#isBootLoader()
*/
@@ -169,7 +169,7 @@ public final class Device implements IDevice {
return state == DeviceState.BOOTLOADER;
}
- /*
+ /*
* (non-Javadoc)
* @see com.android.ddmlib.IDevice#hasClients()
*/
@@ -177,7 +177,7 @@ public final class Device implements IDevice {
return mClients.size() > 0;
}
- /*
+ /*
* (non-Javadoc)
* @see com.android.ddmlib.IDevice#getClients()
*/
@@ -186,8 +186,8 @@ public final class Device implements IDevice {
return mClients.toArray(new Client[mClients.size()]);
}
}
-
- /*
+
+ /*
* (non-Javadoc)
* @see com.android.ddmlib.IDevice#getClient(java.lang.String)
*/
@@ -204,7 +204,7 @@ public final class Device implements IDevice {
return null;
}
- /*
+ /*
* (non-Javadoc)
* @see com.android.ddmlib.IDevice#getSyncService()
*/
@@ -217,7 +217,7 @@ public final class Device implements IDevice {
return null;
}
- /*
+ /*
* (non-Javadoc)
* @see com.android.ddmlib.IDevice#getFileListingService()
*/
@@ -225,7 +225,7 @@ public final class Device implements IDevice {
return new FileListingService(this);
}
- /*
+ /*
* (non-Javadoc)
* @see com.android.ddmlib.IDevice#getScreenshot()
*/
@@ -233,7 +233,7 @@ public final class Device implements IDevice {
return AdbHelper.getFrameBuffer(AndroidDebugBridge.sSocketAddr, this);
}
- /*
+ /*
* (non-Javadoc)
* @see com.android.ddmlib.IDevice#executeShellCommand(java.lang.String, com.android.ddmlib.IShellOutputReceiver)
*/
@@ -242,16 +242,25 @@ public final class Device implements IDevice {
AdbHelper.executeRemoteCommand(AndroidDebugBridge.sSocketAddr, command, this,
receiver);
}
-
- /*
+
+ /*
* (non-Javadoc)
* @see com.android.ddmlib.IDevice#runEventLogService(com.android.ddmlib.log.LogReceiver)
*/
public void runEventLogService(LogReceiver receiver) throws IOException {
AdbHelper.runEventLogService(AndroidDebugBridge.sSocketAddr, this, receiver);
}
-
- /*
+
+ /*
+ * (non-Javadoc)
+ * @see com.android.ddmlib.IDevice#runLogService(com.android.ddmlib.log.LogReceiver)
+ */
+ public void runLogService(String logname,
+ LogReceiver receiver) throws IOException {
+ AdbHelper.runLogService(AndroidDebugBridge.sSocketAddr, this, logname, receiver);
+ }
+
+ /*
* (non-Javadoc)
* @see com.android.ddmlib.IDevice#createForward(int, int)
*/
@@ -265,7 +274,7 @@ public final class Device implements IDevice {
}
}
- /*
+ /*
* (non-Javadoc)
* @see com.android.ddmlib.IDevice#removeForward(int, int)
*/
@@ -279,7 +288,7 @@ public final class Device implements IDevice {
}
}
- /*
+ /*
* (non-Javadoc)
* @see com.android.ddmlib.IDevice#getClientName(int)
*/
@@ -325,7 +334,7 @@ public final class Device implements IDevice {
return false;
}
-
+
void clearClientList() {
synchronized (mClients) {
mClients.clear();
diff --git a/tools/ddms/libs/ddmlib/src/com/android/ddmlib/DeviceMonitor.java b/tools/ddms/libs/ddmlib/src/com/android/ddmlib/DeviceMonitor.java
index 8547ac1f4..f9d0fa070 100644
--- a/tools/ddms/libs/ddmlib/src/com/android/ddmlib/DeviceMonitor.java
+++ b/tools/ddms/libs/ddmlib/src/com/android/ddmlib/DeviceMonitor.java
@@ -420,11 +420,11 @@ final class DeviceMonitor {
device.executeShellCommand(GetPropReceiver.GETPROP_COMMAND,
new GetPropReceiver(device));
- // now get the emulator VM name (if applicable).
+ // now get the emulator Virtual Device name (if applicable).
if (device.isEmulator()) {
EmulatorConsole console = EmulatorConsole.getConsole(device);
if (console != null) {
- device.mVmName = console.getVmName();
+ device.mAvdName = console.getAvdName();
}
}
} catch (IOException e) {
@@ -470,7 +470,7 @@ final class DeviceMonitor {
} catch (IOException e1) {
// we can ignore that one. It may already have been closed.
}
- Log.e("DeviceMonitor",
+ Log.d("DeviceMonitor",
"Connection Failure when starting to monitor device '"
+ device + "' : " + e.getMessage());
}
@@ -558,7 +558,7 @@ final class DeviceMonitor {
processIncomingJdwpData(device, socket, length);
} catch (IOException ioe) {
- Log.e("DeviceMonitor",
+ Log.d("DeviceMonitor",
"Error reading jdwp list: " + ioe.getMessage());
socket.close();
diff --git a/tools/ddms/libs/ddmlib/src/com/android/ddmlib/EmulatorConsole.java b/tools/ddms/libs/ddmlib/src/com/android/ddmlib/EmulatorConsole.java
index e00073c15..f3986ed4e 100644
--- a/tools/ddms/libs/ddmlib/src/com/android/ddmlib/EmulatorConsole.java
+++ b/tools/ddms/libs/ddmlib/src/com/android/ddmlib/EmulatorConsole.java
@@ -54,7 +54,7 @@ public final class EmulatorConsole {
private final static String HOST = "127.0.0.1"; //$NON-NLS-1$
private final static String COMMAND_PING = "help\r\n"; //$NON-NLS-1$
- private final static String COMMAND_VM_NAME = "vm name\r\n"; //$NON-NLS-1$
+ private final static String COMMAND_AVD_NAME = "avd name\r\n"; //$NON-NLS-1$
private final static String COMMAND_KILL = "kill\r\n"; //$NON-NLS-1$
private final static String COMMAND_GSM_STATUS = "gsm status\r\n"; //$NON-NLS-1$
private final static String COMMAND_GSM_CALL = "gsm call %1$s\r\n"; //$NON-NLS-1$
@@ -309,8 +309,8 @@ public final class EmulatorConsole {
}
}
- public synchronized String getVmName() {
- if (sendCommand(COMMAND_VM_NAME)) {
+ public synchronized String getAvdName() {
+ if (sendCommand(COMMAND_AVD_NAME)) {
String[] result = readLines();
if (result != null && result.length == 2) { // this should be the name on first line,
// and ok on 2nd line
diff --git a/tools/ddms/libs/ddmlib/src/com/android/ddmlib/IDevice.java b/tools/ddms/libs/ddmlib/src/com/android/ddmlib/IDevice.java
index 61d1ca462..5dbce9292 100755
--- a/tools/ddms/libs/ddmlib/src/com/android/ddmlib/IDevice.java
+++ b/tools/ddms/libs/ddmlib/src/com/android/ddmlib/IDevice.java
@@ -44,15 +44,15 @@ public interface IDevice {
* Returns the serial number of the device.
*/
public String getSerialNumber();
-
+
/**
- * Returns the name of the VM the emulator is running.
+ * Returns the name of the AVD the emulator is running.
* This is only valid if {@link #isEmulator()} returns true.
- * If the emulator is not running any VM (for instance it's running from an Android source
+ * If the emulator is not running any AVD (for instance it's running from an Android source
* tree build), this method will return "<build>".
- * @return the name of the VM or null if there isn't any.
+ * @return the name of the AVD or null if there isn't any.
*/
- public String getVmName();
+ public String getAvdName();
/**
* Returns the state of the device.
@@ -151,6 +151,14 @@ public interface IDevice {
*/
public void runEventLogService(LogReceiver receiver) throws IOException;
+ /**
+ * Runs the log service for the given log and outputs the log to the {@link LogReceiver}.
+ * @param logname the logname of the log to read from.
+ * @param receiver the receiver to receive the event log entries.
+ * @throws IOException
+ */
+ public void runLogService(String logname, LogReceiver receiver) throws IOException;
+
/**
* Creates a port forwarding between a local and a remote port.
* @param localPort the local port to forward
diff --git a/tools/ddms/libs/ddmlib/src/com/android/ddmlib/testrunner/ITestRunListener.java b/tools/ddms/libs/ddmlib/src/com/android/ddmlib/testrunner/ITestRunListener.java
index 4c0d9debd..b61a69861 100644
--- a/tools/ddms/libs/ddmlib/src/com/android/ddmlib/testrunner/ITestRunListener.java
+++ b/tools/ddms/libs/ddmlib/src/com/android/ddmlib/testrunner/ITestRunListener.java
@@ -17,56 +17,69 @@
package com.android.ddmlib.testrunner;
/**
- * Listener for instrumentation test runs
- *
- * Modeled after junit.runner.TestRunListener
+ * Receives event notifications during instrumentation test runs.
+ * Patterned after {@link junit.runner.TestRunListener}.
*/
public interface ITestRunListener {
- public static final int STATUS_ERROR = 1;
- public static final int STATUS_FAILURE = 2;
+
+ /**
+ * Types of test failures.
+ */
+ enum TestFailure {
+ /** Test failed due to unanticipated uncaught exception. */
+ ERROR,
+ /** Test failed due to a false assertion. */
+ FAILURE
+ }
/**
- * Reports the start of a test run
- * @param testCount - total number of tests in test run
- * */
+ * Reports the start of a test run.
+ *
+ * @param testCount total number of tests in test run
+ */
public void testRunStarted(int testCount);
/**
- * Reports end of test run
- * @param elapsedTime - device reported elapsed time, in milliseconds
+ * Reports end of test run.
+ *
+ * @param elapsedTime device reported elapsed time, in milliseconds
*/
public void testRunEnded(long elapsedTime);
/**
- * Reports test run stopped before completion
- * @param elapsedTime - device reported elapsed time, in milliseconds
+ * Reports test run stopped before completion.
+ *
+ * @param elapsedTime device reported elapsed time, in milliseconds
*/
public void testRunStopped(long elapsedTime);
/**
- * Reports the start of an individual test case
- */
- public void testStarted(String className, String testName);
-
- /**
- * Reports the execution end of an individual test case
- * If no testFailed has been reported, this is a passed test
- */
- public void testEnded(String className, String testName);
-
- /**
- * Reports the failure of a individual test case
- * Will be called between testStarted and testEnded
+ * Reports the start of an individual test case.
*
- * @param status - one of STATUS_ERROR, STATUS_FAILURE
- * @param className - name of test class
- * @param testName - name of test method
- * @param trace - stack trace of failure
+ * @param test identifies the test
*/
- public void testFailed(int status, String className, String testName, String trace);
+ public void testStarted(TestIdentifier test);
+
+ /**
+ * Reports the execution end of an individual test case.
+ * If {@link #testFailed} was not invoked, this test passed.
+ *
+ * @param test identifies the test
+ */
+ public void testEnded(TestIdentifier test);
+
+ /**
+ * Reports the failure of a individual test case.
+ * Will be called between testStarted and testEnded.
+ *
+ * @param status failure type
+ * @param test identifies the test
+ * @param trace stack trace of failure
+ */
+ public void testFailed(TestFailure status, TestIdentifier test, String trace);
/**
- * Reports test run failed to execute due to a fatal error
+ * Reports test run failed to execute due to a fatal error.
*/
public void testRunFailed(String errorMessage);
}
diff --git a/tools/ddms/libs/ddmlib/src/com/android/ddmlib/testrunner/InstrumentationResultParser.java b/tools/ddms/libs/ddmlib/src/com/android/ddmlib/testrunner/InstrumentationResultParser.java
index d47bd5652..bc1834f61 100755
--- a/tools/ddms/libs/ddmlib/src/com/android/ddmlib/testrunner/InstrumentationResultParser.java
+++ b/tools/ddms/libs/ddmlib/src/com/android/ddmlib/testrunner/InstrumentationResultParser.java
@@ -20,24 +20,21 @@ import com.android.ddmlib.IShellOutputReceiver;
import com.android.ddmlib.Log;
import com.android.ddmlib.MultiLineReceiver;
-import java.util.Hashtable;
-import java.util.Map;
-
/**
- * Parses the 'raw output mode' results of an instrument test run from shell, and informs a
- * ITestRunListener of the results
+ * Parses the 'raw output mode' results of an instrumentation test run from shell and informs a
+ * ITestRunListener of the results.
*
- * Expects the following output:
+ * Expects the following output:
*
- * If fatal error occurred when attempted to run the tests:
- * INSTRUMENTATION_FAILED:
+ *
If fatal error occurred when attempted to run the tests:
+ *
INSTRUMENTATION_FAILED:
*
- * Otherwise, expect a series of test results, each one containing a set of status key/value
+ * Otherwise, expect a series of test results, each one containing a set of status key/value
* pairs, delimited by a start(1)/pass(0)/fail(-2)/error(-1) status code result. At end of test
* run, expects that the elapsed test time in seconds will be displayed
*
- * i.e.
- *
+ *
For example:
+ *
* INSTRUMENTATION_STATUS_CODE: 1
* INSTRUMENTATION_STATUS: class=com.foo.FooTest
* INSTRUMENTATION_STATUS: test=testFoo
@@ -48,64 +45,85 @@ import java.util.Map;
* ...
*
* Time: X
- *
- *
- * Note that the "value" portion of the key-value pair may wrap over several text lines
+ *
+ * Note that the "value" portion of the key-value pair may wrap over several text lines
*/
public class InstrumentationResultParser extends MultiLineReceiver {
- // relevant test status keys
- private static final String CODE_KEY = "code";
- private static final String TEST_KEY = "test";
- private static final String CLASS_KEY = "class";
- private static final String STACK_KEY = "stack";
- private static final String NUMTESTS_KEY = "numtests";
+ /** Relevant test status keys. */
+ private static class StatusKeys {
+ private static final String TEST = "test";
+ private static final String CLASS = "class";
+ private static final String STACK = "stack";
+ private static final String NUMTESTS = "numtests";
+ }
- // test result status codes
- private static final int FAILURE_STATUS_CODE = -2;
- private static final int START_STATUS_CODE = 1;
- private static final int ERROR_STATUS_CODE = -1;
- private static final int OK_STATUS_CODE = 0;
+ /** Test result status codes. */
+ private static class StatusCodes {
+ private static final int FAILURE = -2;
+ private static final int START = 1;
+ private static final int ERROR = -1;
+ private static final int OK = 0;
+ }
- // recognized output patterns
- private static final String STATUS_PREFIX = "INSTRUMENTATION_STATUS: ";
- private static final String STATUS_PREFIX_CODE = "INSTRUMENTATION_STATUS_CODE: ";
- private static final String STATUS_FAILED = "INSTRUMENTATION_FAILED: ";
- private static final String TIME_REPORT = "Time: ";
+ /** Prefixes used to identify output. */
+ private static class Prefixes {
+ private static final String STATUS = "INSTRUMENTATION_STATUS: ";
+ private static final String STATUS_CODE = "INSTRUMENTATION_STATUS_CODE: ";
+ private static final String STATUS_FAILED = "INSTRUMENTATION_FAILED: ";
+ private static final String TIME_REPORT = "Time: ";
+ }
private final ITestRunListener mTestListener;
- /** key-value map for current test */
- private Map mStatusValues;
- /** stores the current "key" portion of the status key-value being parsed */
- private String mCurrentKey;
- /** stores the current "value" portion of the status key-value being parsed */
- private StringBuilder mCurrentValue;
- /** true if start of test has already been reported to listener */
- private boolean mTestStartReported;
- /** the elapsed time of the test run, in ms */
- private long mTestTime;
- /** true if current test run has been canceled by user */
- private boolean mIsCancelled;
+
+ /**
+ * Test result data
+ */
+ private static class TestResult {
+ private Integer mCode = null;
+ private String mTestName = null;
+ private String mTestClass = null;
+ private String mStackTrace = null;
+ private Integer mNumTests = null;
+
+ /** Returns true if all expected values have been parsed */
+ boolean isComplete() {
+ return mCode != null && mTestName != null && mTestClass != null;
+ }
+ }
+
+ /** Stores the status values for the test result currently being parsed */
+ private TestResult mCurrentTestResult = null;
+
+ /** Stores the current "key" portion of the status key-value being parsed. */
+ private String mCurrentKey = null;
+
+ /** Stores the current "value" portion of the status key-value being parsed. */
+ private StringBuilder mCurrentValue = null;
+
+ /** True if start of test has already been reported to listener. */
+ private boolean mTestStartReported = false;
+
+ /** The elapsed time of the test run, in milliseconds. */
+ private long mTestTime = 0;
+
+ /** True if current test run has been canceled by user. */
+ private boolean mIsCancelled = false;
private static final String LOG_TAG = "InstrumentationResultParser";
/**
- * Creates the InstrumentationResultParser
- * @param listener - listener to report results to. will be informed of test results as the
- * tests are executing
+ * Creates the InstrumentationResultParser.
+ *
+ * @param listener informed of test results as the tests are executing
*/
public InstrumentationResultParser(ITestRunListener listener) {
- mStatusValues = new Hashtable();
- mCurrentKey = null;
- setTrimLine(false);
mTestListener = listener;
- mTestStartReported = false;
- mTestTime = 0;
- mIsCancelled = false;
}
/**
- * Processes the instrumentation test output from shell
+ * Processes the instrumentation test output from shell.
+ *
* @see MultiLineReceiver#processNewLines
*/
@Override
@@ -116,31 +134,37 @@ public class InstrumentationResultParser extends MultiLineReceiver {
}
/**
- * Parse an individual output line. Expects a line that either is:
- * a) the start of a new status line (ie. starts with STATUS_PREFIX or STATUS_PREFIX_CODE),
- * and thus there is a new key=value pair to parse, and the previous key-value pair is
- * finished
- * b) a continuation of the previous status (ie the "value" portion of the key has wrapped
- * to the next line.
- * c) a line reporting a fatal error in the test run (STATUS_FAILED)
- * d) a line reporting the total elapsed time of the test run.
+ * Parse an individual output line. Expects a line that is one of:
+ *
+ * -
+ * The start of a new status line (starts with Prefixes.STATUS or Prefixes.STATUS_CODE),
+ * and thus there is a new key=value pair to parse, and the previous key-value pair is
+ * finished.
+ *
+ * -
+ * A continuation of the previous status (the "value" portion of the key has wrapped
+ * to the next line).
+ *
+ * - A line reporting a fatal error in the test run (Prefixes.STATUS_FAILED)
+ * - A line reporting the total elapsed time of the test run. (Prefixes.TIME_REPORT)
+ *
*
- * @param line - text output line
+ * @param line Text output line
*/
private void parse(String line) {
- if (line.startsWith(STATUS_PREFIX_CODE)) {
+ if (line.startsWith(Prefixes.STATUS_CODE)) {
// Previous status key-value has been collected. Store it.
submitCurrentKeyValue();
parseStatusCode(line);
- } else if (line.startsWith(STATUS_PREFIX)) {
+ } else if (line.startsWith(Prefixes.STATUS)) {
// Previous status key-value has been collected. Store it.
submitCurrentKeyValue();
- parseKey(line, STATUS_PREFIX.length());
- } else if (line.startsWith(STATUS_FAILED)) {
+ parseKey(line, Prefixes.STATUS.length());
+ } else if (line.startsWith(Prefixes.STATUS_FAILED)) {
Log.e(LOG_TAG, "test run failed " + line);
mTestListener.testRunFailed(line);
- } else if (line.startsWith(TIME_REPORT)) {
- parseTime(line, TIME_REPORT.length());
+ } else if (line.startsWith(Prefixes.TIME_REPORT)) {
+ parseTime(line, Prefixes.TIME_REPORT.length());
} else {
if (mCurrentValue != null) {
// this is a value that has wrapped to next line.
@@ -153,21 +177,53 @@ public class InstrumentationResultParser extends MultiLineReceiver {
}
/**
- * Stores the currently parsed key-value pair in the status map
+ * Stores the currently parsed key-value pair into mCurrentTestInfo.
*/
private void submitCurrentKeyValue() {
if (mCurrentKey != null && mCurrentValue != null) {
- mStatusValues.put(mCurrentKey, mCurrentValue.toString());
+ TestResult testInfo = getCurrentTestInfo();
+ String statusValue = mCurrentValue.toString();
+
+ if (mCurrentKey.equals(StatusKeys.CLASS)) {
+ testInfo.mTestClass = statusValue.trim();
+ }
+ else if (mCurrentKey.equals(StatusKeys.TEST)) {
+ testInfo.mTestName = statusValue.trim();
+ }
+ else if (mCurrentKey.equals(StatusKeys.NUMTESTS)) {
+ try {
+ testInfo.mNumTests = Integer.parseInt(statusValue);
+ }
+ catch (NumberFormatException e) {
+ Log.e(LOG_TAG, "Unexpected integer number of tests, received " + statusValue);
+ }
+ }
+ else if (mCurrentKey.equals(StatusKeys.STACK)) {
+ testInfo.mStackTrace = statusValue;
+ }
+
mCurrentKey = null;
mCurrentValue = null;
}
}
+ private TestResult getCurrentTestInfo() {
+ if (mCurrentTestResult == null) {
+ mCurrentTestResult = new TestResult();
+ }
+ return mCurrentTestResult;
+ }
+
+ private void clearCurrentTestInfo() {
+ mCurrentTestResult = null;
+ }
+
/**
- * Parses the key from the current line
- * Expects format of "key=value",
- * @param line - full line of text to parse
- * @param keyStartPos - the starting position of the key in the given line
+ * Parses the key from the current line.
+ * Expects format of "key=value".
+ *
+ * @param line full line of text to parse
+ * @param keyStartPos the starting position of the key in the given line
*/
private void parseKey(String line, int keyStartPos) {
int endKeyPos = line.indexOf('=', keyStartPos);
@@ -178,7 +234,8 @@ public class InstrumentationResultParser extends MultiLineReceiver {
}
/**
- * Parses the start of a key=value pair.
+ * Parses the start of a key=value pair.
+ *
* @param line - full line of text to parse
* @param valueStartPos - the starting position of the value in the given line
*/
@@ -188,20 +245,25 @@ public class InstrumentationResultParser extends MultiLineReceiver {
}
/**
- * Parses out a status code result. For consistency, stores the result as a CODE entry in
- * key-value status map
+ * Parses out a status code result.
*/
private void parseStatusCode(String line) {
- String value = line.substring(STATUS_PREFIX_CODE.length()).trim();
- mStatusValues.put(CODE_KEY, value);
+ String value = line.substring(Prefixes.STATUS_CODE.length()).trim();
+ TestResult testInfo = getCurrentTestInfo();
+ try {
+ testInfo.mCode = Integer.parseInt(value);
+ }
+ catch (NumberFormatException e) {
+ Log.e(LOG_TAG, "Expected integer status code, received: " + value);
+ }
// this means we're done with current test result bundle
- reportResult(mStatusValues);
- mStatusValues.clear();
+ reportResult(testInfo);
+ clearCurrentTestInfo();
}
/**
- * Returns true if test run canceled
+ * Returns true if test run canceled.
*
* @see IShellOutputReceiver#isCancelled()
*/
@@ -210,7 +272,7 @@ public class InstrumentationResultParser extends MultiLineReceiver {
}
/**
- * Requests cancellation of test result parsing
+ * Requests cancellation of test run.
*/
public void cancel() {
mIsCancelled = true;
@@ -219,82 +281,62 @@ public class InstrumentationResultParser extends MultiLineReceiver {
/**
* Reports a test result to the test run listener. Must be called when a individual test
* result has been fully parsed.
- * @param statusMap - key-value status pairs of test result
+ *
+ * @param statusMap key-value status pairs of test result
*/
- private void reportResult(Map statusMap) {
- String className = statusMap.get(CLASS_KEY);
- String testName = statusMap.get(TEST_KEY);
- String statusCodeString = statusMap.get(CODE_KEY);
-
- if (className == null || testName == null || statusCodeString == null) {
- Log.e(LOG_TAG, "invalid instrumentation status bundle " + statusMap.toString());
+ private void reportResult(TestResult testInfo) {
+ if (!testInfo.isComplete()) {
+ Log.e(LOG_TAG, "invalid instrumentation status bundle " + testInfo.toString());
return;
}
- className = className.trim();
- testName = testName.trim();
+ reportTestRunStarted(testInfo);
+ TestIdentifier testId = new TestIdentifier(testInfo.mTestClass, testInfo.mTestName);
- reportTestStarted(statusMap);
-
- try {
- int statusCode = Integer.parseInt(statusCodeString);
-
- switch (statusCode) {
- case START_STATUS_CODE:
- mTestListener.testStarted(className, testName);
- break;
- case FAILURE_STATUS_CODE:
- mTestListener.testFailed(ITestRunListener.STATUS_FAILURE, className, testName,
- getTrace(statusMap));
- mTestListener.testEnded(className, testName);
- break;
- case ERROR_STATUS_CODE:
- mTestListener.testFailed(ITestRunListener.STATUS_ERROR, className, testName,
- getTrace(statusMap));
- mTestListener.testEnded(className, testName);
- break;
- case OK_STATUS_CODE:
- mTestListener.testEnded(className, testName);
- break;
- default:
- Log.e(LOG_TAG, "Expected status code, received: " + statusCodeString);
- mTestListener.testEnded(className, testName);
- break;
- }
- }
- catch (NumberFormatException e) {
- Log.e(LOG_TAG, "Expected integer status code, received: " + statusCodeString);
+ switch (testInfo.mCode) {
+ case StatusCodes.START:
+ mTestListener.testStarted(testId);
+ break;
+ case StatusCodes.FAILURE:
+ mTestListener.testFailed(ITestRunListener.TestFailure.FAILURE, testId,
+ getTrace(testInfo));
+ mTestListener.testEnded(testId);
+ break;
+ case StatusCodes.ERROR:
+ mTestListener.testFailed(ITestRunListener.TestFailure.ERROR, testId,
+ getTrace(testInfo));
+ mTestListener.testEnded(testId);
+ break;
+ case StatusCodes.OK:
+ mTestListener.testEnded(testId);
+ break;
+ default:
+ Log.e(LOG_TAG, "Unknown status code received: " + testInfo.mCode);
+ mTestListener.testEnded(testId);
+ break;
}
+
}
/**
* Reports the start of a test run, and the total test count, if it has not been previously
- * reported
- * @param statusMap - key-value status pairs
+ * reported.
+ *
+ * @param testInfo current test status values
*/
- private void reportTestStarted(Map statusMap) {
+ private void reportTestRunStarted(TestResult testInfo) {
// if start test run not reported yet
- if (!mTestStartReported) {
- String numTestsString = statusMap.get(NUMTESTS_KEY);
- if (numTestsString != null) {
- try {
- int numTests = Integer.parseInt(numTestsString);
- mTestListener.testRunStarted(numTests);
- mTestStartReported = true;
- }
- catch (NumberFormatException e) {
- Log.e(LOG_TAG, "Unexpected numTests format " + numTestsString);
- }
- }
+ if (!mTestStartReported && testInfo.mNumTests != null) {
+ mTestListener.testRunStarted(testInfo.mNumTests);
+ mTestStartReported = true;
}
}
/**
- * Returns the stack trace of the current failed test, from the provided key-value status map
+ * Returns the stack trace of the current failed test, from the provided testInfo.
*/
- private String getTrace(Map statusMap) {
- String stackTrace = statusMap.get(STACK_KEY);
- if (stackTrace != null) {
- return stackTrace;
+ private String getTrace(TestResult testInfo) {
+ if (testInfo.mStackTrace != null) {
+ return testInfo.mStackTrace;
}
else {
Log.e(LOG_TAG, "Could not find stack trace for failed test ");
@@ -303,7 +345,7 @@ public class InstrumentationResultParser extends MultiLineReceiver {
}
/**
- * Parses out and store the elapsed time
+ * Parses out and store the elapsed time.
*/
private void parseTime(String line, int startPos) {
String timeString = line.substring(startPos);
diff --git a/tools/ddms/libs/ddmlib/src/com/android/ddmlib/testrunner/RemoteAndroidTestRunner.java b/tools/ddms/libs/ddmlib/src/com/android/ddmlib/testrunner/RemoteAndroidTestRunner.java
index 5de632e73..4edbbbbd3 100644
--- a/tools/ddms/libs/ddmlib/src/com/android/ddmlib/testrunner/RemoteAndroidTestRunner.java
+++ b/tools/ddms/libs/ddmlib/src/com/android/ddmlib/testrunner/RemoteAndroidTestRunner.java
@@ -23,7 +23,7 @@ import com.android.ddmlib.Log;
import java.io.IOException;
/**
- * Runs a Android test command remotely and reports results
+ * Runs a Android test command remotely and reports results.
*/
public class RemoteAndroidTestRunner {
@@ -43,11 +43,12 @@ public class RemoteAndroidTestRunner {
"android.test.InstrumentationTestRunner";
/**
- * Creates a remote android test runner.
- * @param packageName - the Android application package that contains the tests to run
- * @param runnerName - the instrumentation test runner to execute. If null, will use default
+ * Creates a remote Android test runner.
+ *
+ * @param packageName the Android application package that contains the tests to run
+ * @param runnerName the instrumentation test runner to execute. If null, will use default
* runner
- * @param remoteDevice - the Android device to execute tests on
+ * @param remoteDevice the Android device to execute tests on
*/
public RemoteAndroidTestRunner(String packageName,
String runnerName,
@@ -62,9 +63,10 @@ public class RemoteAndroidTestRunner {
}
/**
- * Alternate constructor. Uses default instrumentation runner
- * @param packageName - the Android application package that contains the tests to run
- * @param remoteDevice - the Android device to execute tests on
+ * Alternate constructor. Uses default instrumentation runner.
+ *
+ * @param packageName the Android application package that contains the tests to run
+ * @param remoteDevice the Android device to execute tests on
*/
public RemoteAndroidTestRunner(String packageName,
IDevice remoteDevice) {
@@ -72,14 +74,14 @@ public class RemoteAndroidTestRunner {
}
/**
- * Returns the application package name
+ * Returns the application package name.
*/
public String getPackageName() {
return mPackageName;
}
/**
- * Returns the runnerName
+ * Returns the runnerName.
*/
public String getRunnerName() {
if (mRunnerName == null) {
@@ -89,7 +91,7 @@ public class RemoteAndroidTestRunner {
}
/**
- * Returns the complete instrumentation component path
+ * Returns the complete instrumentation component path.
*/
private String getRunnerPath() {
return getPackageName() + RUNNER_SEPARATOR + getRunnerName();
@@ -97,8 +99,9 @@ public class RemoteAndroidTestRunner {
/**
* Sets to run only tests in this class
- * Must be called before 'run'
- * @param className - fully qualified class name (eg x.y.z)
+ * Must be called before 'run'.
+ *
+ * @param className fully qualified class name (eg x.y.z)
*/
public void setClassName(String className) {
mClassArg = className;
@@ -106,10 +109,12 @@ public class RemoteAndroidTestRunner {
/**
* Sets to run only tests in the provided classes
- * Must be called before 'run'
+ * Must be called before 'run'.
+ *
* If providing more than one class, requires a InstrumentationTestRunner that supports
- * the multiple class argument syntax
- * @param classNames - array of fully qualified class name (eg x.y.z)
+ * the multiple class argument syntax.
+ *
+ * @param classNames array of fully qualified class names (eg x.y.z)
*/
public void setClassNames(String[] classNames) {
StringBuilder classArgBuilder = new StringBuilder();
@@ -125,9 +130,10 @@ public class RemoteAndroidTestRunner {
/**
* Sets to run only specified test method
- * Must be called before 'run'
- * @param className - fully qualified class name (eg x.y.z)
- * @param testName - method name
+ * Must be called before 'run'.
+ *
+ * @param className fully qualified class name (eg x.y.z)
+ * @param testName method name
*/
public void setMethodName(String className, String testName) {
mClassArg = className + METHOD_SEPARATOR + testName;
@@ -135,8 +141,9 @@ public class RemoteAndroidTestRunner {
/**
* Sets extra arguments to include in instrumentation command.
- * Must be called before 'run'
- * @param instrumentationArgs - must not be null
+ * Must be called before 'run'.
+ *
+ * @param instrumentationArgs must not be null
*/
public void setExtraArgs(String instrumentationArgs) {
if (instrumentationArgs == null) {
@@ -146,23 +153,23 @@ public class RemoteAndroidTestRunner {
}
/**
- * Returns the extra instrumentation arguments
+ * Returns the extra instrumentation arguments.
*/
public String getExtraArgs() {
return mExtraArgs;
}
/**
- * Sets this test run to log only mode - skips test execution
+ * Sets this test run to log only mode - skips test execution.
*/
public void setLogOnly(boolean logOnly) {
mLogOnlyMode = logOnly;
}
/**
- * Execute this test run
+ * Execute this test run.
*
- * @param listener - listener to report results to
+ * @param listener listens for test results
*/
public void run(ITestRunListener listener) {
final String runCaseCommandStr = "am instrument -w -r "
@@ -179,7 +186,7 @@ public class RemoteAndroidTestRunner {
}
/**
- * Requests cancellation of this test run
+ * Requests cancellation of this test run.
*/
public void cancel() {
if (mParser != null) {
@@ -188,7 +195,7 @@ public class RemoteAndroidTestRunner {
}
/**
- * Returns the test class argument
+ * Returns the test class argument.
*/
private String getClassArg() {
return mClassArg;
@@ -196,7 +203,7 @@ public class RemoteAndroidTestRunner {
/**
* Returns the full instrumentation command which specifies the test classes to execute.
- * Returns an empty string if no classes were specified
+ * Returns an empty string if no classes were specified.
*/
private String getClassCmd() {
String classArg = getClassArg();
@@ -208,7 +215,7 @@ public class RemoteAndroidTestRunner {
/**
* Returns the full command to enable log only mode - if specified. Otherwise returns an
- * empty string
+ * empty string.
*/
private String getLogCmd() {
if (mLogOnlyMode) {
diff --git a/tools/ddms/libs/ddmlib/src/com/android/ddmlib/testrunner/TestIdentifier.java b/tools/ddms/libs/ddmlib/src/com/android/ddmlib/testrunner/TestIdentifier.java
new file mode 100644
index 000000000..4d3b1080b
--- /dev/null
+++ b/tools/ddms/libs/ddmlib/src/com/android/ddmlib/testrunner/TestIdentifier.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2008 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.ddmlib.testrunner;
+
+/**
+ * Identifies a parsed instrumentation test
+ */
+public class TestIdentifier {
+
+ private final String mClassName;
+ private final String mTestName;
+
+ /**
+ * Creates a test identifier
+ *
+ * @param className fully qualified class name of the test. Cannot be null.
+ * @param testName name of the test. Cannot be null.
+ */
+ public TestIdentifier(String className, String testName) {
+ if (className == null || testName == null) {
+ throw new IllegalArgumentException("className and testName must " +
+ "be non-null");
+ }
+ mClassName = className;
+ mTestName = testName;
+ }
+
+ /**
+ * Returns the fully qualified class name of the test
+ */
+ public String getClassName() {
+ return mClassName;
+ }
+
+ /**
+ * Returns the name of the test
+ */
+ public String getTestName() {
+ return mTestName;
+ }
+
+ /**
+ * Tests equality by comparing class and method name
+ */
+ @Override
+ public boolean equals(Object other) {
+ if (!(other instanceof TestIdentifier)) {
+ return false;
+ }
+ TestIdentifier otherTest = (TestIdentifier)other;
+ return getClassName().equals(otherTest.getClassName()) &&
+ getTestName().equals(otherTest.getTestName());
+ }
+
+ /**
+ * Generates hashCode based on class and method name.
+ */
+ @Override
+ public int hashCode() {
+ return getClassName().hashCode() * 31 + getTestName().hashCode();
+ }
+}
diff --git a/tools/ddms/libs/ddmlib/tests/src/com/android/ddmlib/testrunner/InstrumentationResultParserTest.java b/tools/ddms/libs/ddmlib/tests/src/com/android/ddmlib/testrunner/InstrumentationResultParserTest.java
index 67f61987d..77d10c1d1 100644
--- a/tools/ddms/libs/ddmlib/tests/src/com/android/ddmlib/testrunner/InstrumentationResultParserTest.java
+++ b/tools/ddms/libs/ddmlib/tests/src/com/android/ddmlib/testrunner/InstrumentationResultParserTest.java
@@ -20,7 +20,7 @@ import junit.framework.TestCase;
/**
- * Tests InstrumentationResultParser
+ * Tests InstrumentationResultParser.
*/
public class InstrumentationResultParserTest extends TestCase {
@@ -51,7 +51,7 @@ public class InstrumentationResultParserTest extends TestCase {
/**
* Tests that the test run started and test start events is sent on first
- * bundle received
+ * bundle received.
*/
public void testTestStarted() {
StringBuilder output = buildCommonResult();
@@ -63,7 +63,7 @@ public class InstrumentationResultParserTest extends TestCase {
}
/**
- * Tests that a single successful test execution
+ * Tests that a single successful test execution.
*/
public void testTestSuccess() {
StringBuilder output = buildCommonResult();
@@ -74,11 +74,11 @@ public class InstrumentationResultParserTest extends TestCase {
injectTestString(output.toString());
assertCommonAttributes();
assertEquals(1, mTestResult.mNumTestsRun);
- assertEquals(0, mTestResult.mTestStatus);
+ assertEquals(null, mTestResult.mTestStatus);
}
/**
- * Test basic parsing of failed test case
+ * Test basic parsing of failed test case.
*/
public void testTestFailed() {
StringBuilder output = buildCommonResult();
@@ -91,12 +91,12 @@ public class InstrumentationResultParserTest extends TestCase {
assertCommonAttributes();
assertEquals(1, mTestResult.mNumTestsRun);
- assertEquals(ITestRunListener.STATUS_FAILURE, mTestResult.mTestStatus);
+ assertEquals(ITestRunListener.TestFailure.FAILURE, mTestResult.mTestStatus);
assertEquals(STACK_TRACE, mTestResult.mTrace);
}
/**
- * Test basic parsing and conversion of time from output
+ * Test basic parsing and conversion of time from output.
*/
public void testTimeParsing() {
final String timeString = "Time: 4.9";
@@ -105,7 +105,7 @@ public class InstrumentationResultParserTest extends TestCase {
}
/**
- * builds a common test result using TEST_NAME and TEST_CLASS
+ * builds a common test result using TEST_NAME and TEST_CLASS.
*/
private StringBuilder buildCommonResult() {
StringBuilder output = new StringBuilder();
@@ -118,7 +118,7 @@ public class InstrumentationResultParserTest extends TestCase {
}
/**
- * Adds common status results to the provided output
+ * Adds common status results to the provided output.
*/
private void addCommonStatus(StringBuilder output) {
addStatusKey(output, "stream", "\r\n" + CLASS_NAME);
@@ -130,7 +130,7 @@ public class InstrumentationResultParserTest extends TestCase {
}
/**
- * Adds a stack trace status bundle to output
+ * Adds a stack trace status bundle to output.
*/
private void addStackTrace(StringBuilder output) {
addStatusKey(output, "stack", STACK_TRACE);
@@ -138,7 +138,7 @@ public class InstrumentationResultParserTest extends TestCase {
}
/**
- * Helper method to add a status key-value bundle
+ * Helper method to add a status key-value bundle.
*/
private void addStatusKey(StringBuilder outputBuilder, String key,
String value) {
@@ -168,7 +168,7 @@ public class InstrumentationResultParserTest extends TestCase {
}
/**
- * inject a test string into the result parser
+ * inject a test string into the result parser.
*
* @param result
*/
@@ -185,7 +185,7 @@ public class InstrumentationResultParserTest extends TestCase {
}
/**
- * A specialized test listener that stores a single test events
+ * A specialized test listener that stores a single test events.
*/
private class VerifyingTestResult implements ITestRunListener {
@@ -194,29 +194,28 @@ public class InstrumentationResultParserTest extends TestCase {
int mNumTestsRun;
String mTestName;
long mTestTime;
- int mTestStatus;
+ TestFailure mTestStatus;
String mTrace;
boolean mStopped;
VerifyingTestResult() {
mNumTestsRun = 0;
- mTestStatus = 0;
+ mTestStatus = null;
mStopped = false;
}
- public void testEnded(String className, String testName) {
+ public void testEnded(TestIdentifier test) {
mNumTestsRun++;
- assertEquals("Unexpected class name", mSuiteName, className);
- assertEquals("Unexpected test ended", mTestName, testName);
+ assertEquals("Unexpected class name", mSuiteName, test.getClassName());
+ assertEquals("Unexpected test ended", mTestName, test.getTestName());
}
- public void testFailed(int status, String className, String testName,
- String trace) {
+ public void testFailed(TestFailure status, TestIdentifier test, String trace) {
mTestStatus = status;
mTrace = trace;
- assertEquals("Unexpected class name", mSuiteName, className);
- assertEquals("Unexpected test ended", mTestName, testName);
+ assertEquals("Unexpected class name", mSuiteName, test.getClassName());
+ assertEquals("Unexpected test ended", mTestName, test.getTestName());
}
public void testRunEnded(long elapsedTime) {
@@ -233,9 +232,9 @@ public class InstrumentationResultParserTest extends TestCase {
mStopped = true;
}
- public void testStarted(String className, String testName) {
- mSuiteName = className;
- mTestName = testName;
+ public void testStarted(TestIdentifier test) {
+ mSuiteName = test.getClassName();
+ mTestName = test.getTestName();
}
public void testRunFailed(String errorMessage) {
diff --git a/tools/ddms/libs/ddmlib/tests/src/com/android/ddmlib/testrunner/RemoteAndroidTestRunnerTest.java b/tools/ddms/libs/ddmlib/tests/src/com/android/ddmlib/testrunner/RemoteAndroidTestRunnerTest.java
index 556fc9b8e..9acaaf954 100644
--- a/tools/ddms/libs/ddmlib/tests/src/com/android/ddmlib/testrunner/RemoteAndroidTestRunnerTest.java
+++ b/tools/ddms/libs/ddmlib/tests/src/com/android/ddmlib/testrunner/RemoteAndroidTestRunnerTest.java
@@ -31,16 +31,16 @@ import java.io.IOException;
import java.util.Map;
/**
- * Test RemoteAndroidTestRunner.
+ * Tests RemoteAndroidTestRunner.
*/
public class RemoteAndroidTestRunnerTest extends TestCase {
private RemoteAndroidTestRunner mRunner;
private MockDevice mMockDevice;
-
+
private static final String TEST_PACKAGE = "com.test";
private static final String TEST_RUNNER = "com.test.InstrumentationTestRunner";
-
+
/**
* @see junit.framework.TestCase#setUp()
*/
@@ -49,40 +49,40 @@ public class RemoteAndroidTestRunnerTest extends TestCase {
mMockDevice = new MockDevice();
mRunner = new RemoteAndroidTestRunner(TEST_PACKAGE, TEST_RUNNER, mMockDevice);
}
-
+
/**
- * Test the basic case building of the instrumentation runner command with no arguments
+ * Test the basic case building of the instrumentation runner command with no arguments.
*/
public void testRun() {
mRunner.run(new EmptyListener());
- assertStringsEquals(String.format("am instrument -w -r %s/%s", TEST_PACKAGE, TEST_RUNNER),
+ assertStringsEquals(String.format("am instrument -w -r %s/%s", TEST_PACKAGE, TEST_RUNNER),
mMockDevice.getLastShellCommand());
}
/**
- * Test the building of the instrumentation runner command with log set
+ * Test the building of the instrumentation runner command with log set.
*/
public void testRunWithLog() {
mRunner.setLogOnly(true);
mRunner.run(new EmptyListener());
- assertStringsEquals(String.format("am instrument -w -r -e log true %s/%s", TEST_PACKAGE,
+ assertStringsEquals(String.format("am instrument -w -r -e log true %s/%s", TEST_PACKAGE,
TEST_RUNNER), mMockDevice.getLastShellCommand());
}
/**
- * Test the building of the instrumentation runner command with method set
+ * Test the building of the instrumentation runner command with method set.
*/
public void testRunWithMethod() {
final String className = "FooTest";
final String testName = "fooTest";
mRunner.setMethodName(className, testName);
mRunner.run(new EmptyListener());
- assertStringsEquals(String.format("am instrument -w -r -e class %s#%s %s/%s", className,
+ assertStringsEquals(String.format("am instrument -w -r -e class %s#%s %s/%s", className,
testName, TEST_PACKAGE, TEST_RUNNER), mMockDevice.getLastShellCommand());
}
-
+
/**
- * Test the building of the instrumentation runner command with extra args set
+ * Test the building of the instrumentation runner command with extra args set.
*/
public void testRunWithExtraArgs() {
final String extraArgs = "blah";
@@ -94,37 +94,37 @@ public class RemoteAndroidTestRunnerTest extends TestCase {
/**
- * Assert two strings are equal ignoring whitespace
+ * Assert two strings are equal ignoring whitespace.
*/
private void assertStringsEquals(String str1, String str2) {
String strippedStr1 = str1.replaceAll(" ", "");
String strippedStr2 = str2.replaceAll(" ", "");
assertEquals(strippedStr1, strippedStr2);
}
-
+
/**
- * A dummy device that does nothing except store the provided executed shell command for
- * later retrieval
+ * A dummy device that does nothing except store the provided executed shell command for
+ * later retrieval.
*/
private static class MockDevice implements IDevice {
private String mLastShellCommand;
-
+
/**
- * Stores the provided command for later retrieval from getLastShellCommand
+ * Stores the provided command for later retrieval from getLastShellCommand.
*/
public void executeShellCommand(String command,
IShellOutputReceiver receiver) throws IOException {
mLastShellCommand = command;
}
-
+
/**
- * Get the last command provided to executeShellCommand
+ * Get the last command provided to executeShellCommand.
*/
public String getLastShellCommand() {
return mLastShellCommand;
}
-
+
public boolean createForward(int localPort, int remotePort) {
throw new UnsupportedOperationException();
}
@@ -201,22 +201,26 @@ public class RemoteAndroidTestRunnerTest extends TestCase {
throw new UnsupportedOperationException();
}
- public String getVmName() {
+ public void runLogService(String logname, LogReceiver receiver) throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ public String getAvdName() {
return "";
}
}
-
- /** An empty implementation of TestRunListener
+
+ /**
+ * An empty implementation of ITestRunListener.
*/
private static class EmptyListener implements ITestRunListener {
- public void testEnded(String className, String testName) {
+ public void testEnded(TestIdentifier test) {
// ignore
}
- public void testFailed(int status, String className, String testName,
- String trace) {
+ public void testFailed(TestFailure status, TestIdentifier test, String trace) {
// ignore
}
@@ -236,9 +240,9 @@ public class RemoteAndroidTestRunnerTest extends TestCase {
// ignore
}
- public void testStarted(String className, String testName) {
+ public void testStarted(TestIdentifier test) {
// ignore
}
-
+
}
}
diff --git a/tools/ddms/libs/ddmuilib/src/com/android/ddmuilib/DevicePanel.java b/tools/ddms/libs/ddmuilib/src/com/android/ddmuilib/DevicePanel.java
index 1331a097c..81b757e75 100644
--- a/tools/ddms/libs/ddmuilib/src/com/android/ddmuilib/DevicePanel.java
+++ b/tools/ddms/libs/ddmuilib/src/com/android/ddmuilib/DevicePanel.java
@@ -200,22 +200,30 @@ public final class DevicePanel extends Panel implements IDebugBridgeChangeListen
case DEVICE_COL_STATE:
return getStateString(device);
case DEVICE_COL_BUILD: {
- String vmName = device.getVmName();
- String debuggable = device.getProperty(Device.PROP_DEBUGGABLE);
String version = device.getProperty(Device.PROP_BUILD_VERSION);
- if (device.isEmulator()) {
- if (debuggable != null && debuggable.equals("1")) { //$NON-NLS-1$
- return String.format("%1$s [%2$s, debug]", vmName, //$NON-NLS-1$
- version);
+ if (version != null) {
+ String debuggable = device.getProperty(Device.PROP_DEBUGGABLE);
+ if (device.isEmulator()) {
+ String avdName = device.getAvdName();
+ if (avdName == null) {
+ avdName = "?"; // the device is probably not online yet, so
+ // we don't know its AVD name just yet.
+ }
+ if (debuggable != null && debuggable.equals("1")) { //$NON-NLS-1$
+ return String.format("%1$s [%2$s, debug]", avdName,
+ version);
+ } else {
+ return String.format("%1$s [%2$s]", avdName, version); //$NON-NLS-1$
+ }
} else {
- return String.format("%1$s [%2$s]", vmName, version); //$NON-NLS-1$
+ if (debuggable != null && debuggable.equals("1")) { //$NON-NLS-1$
+ return String.format("%1$s, debug", version);
+ } else {
+ return String.format("%1$s", version); //$NON-NLS-1$
+ }
}
} else {
- if (debuggable != null && debuggable.equals("1")) { //$NON-NLS-1$
- return String.format("%1$s, debug", version); //$NON-NLS-1$
- } else {
- return String.format("%1$s", version); //$NON-NLS-1$
- }
+ return "unknown";
}
}
}
diff --git a/tools/ddms/libs/ddmuilib/src/com/android/ddmuilib/NativeHeapPanel.java b/tools/ddms/libs/ddmuilib/src/com/android/ddmuilib/NativeHeapPanel.java
index 149d689f0..46461bf32 100644
--- a/tools/ddms/libs/ddmuilib/src/com/android/ddmuilib/NativeHeapPanel.java
+++ b/tools/ddms/libs/ddmuilib/src/com/android/ddmuilib/NativeHeapPanel.java
@@ -237,7 +237,7 @@ public final class NativeHeapPanel extends BaseHeapPanel {
*/
private HashMap mSourceCache =
new HashMap();
- private int mTotalSize;
+ private long mTotalSize;
private Button mSaveButton;
private Button mSymbolsButton;
diff --git a/tools/ddms/libs/ddmuilib/src/com/android/ddmuilib/log/event/DisplayFilteredLog.java b/tools/ddms/libs/ddmuilib/src/com/android/ddmuilib/log/event/DisplayFilteredLog.java
new file mode 100644
index 000000000..473387aa2
--- /dev/null
+++ b/tools/ddms/libs/ddmuilib/src/com/android/ddmuilib/log/event/DisplayFilteredLog.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2008 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.log.event;
+
+import com.android.ddmlib.log.EventContainer;
+import com.android.ddmlib.log.EventLogParser;
+
+import java.util.ArrayList;
+
+public class DisplayFilteredLog extends DisplayLog {
+
+ public DisplayFilteredLog(String name) {
+ super(name);
+ }
+
+ /**
+ * Adds event to the display.
+ */
+ @Override
+ void newEvent(EventContainer event, EventLogParser logParser) {
+ ArrayList valueDescriptors =
+ new ArrayList();
+
+ ArrayList occurrenceDescriptors =
+ new ArrayList();
+
+ if (filterEvent(event, valueDescriptors, occurrenceDescriptors)) {
+ addToLog(event, logParser, valueDescriptors, occurrenceDescriptors);
+ }
+ }
+
+ /**
+ * Gets display type
+ *
+ * @return display type as an integer
+ */
+ @Override
+ int getDisplayType() {
+ return DISPLAY_TYPE_FILTERED_LOG;
+ }
+}
diff --git a/tools/ddms/libs/ddmuilib/src/com/android/ddmuilib/log/event/DisplayGraph.java b/tools/ddms/libs/ddmuilib/src/com/android/ddmuilib/log/event/DisplayGraph.java
new file mode 100644
index 000000000..0cffd7e07
--- /dev/null
+++ b/tools/ddms/libs/ddmuilib/src/com/android/ddmuilib/log/event/DisplayGraph.java
@@ -0,0 +1,422 @@
+/*
+ * Copyright (C) 2008 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.log.event;
+
+import com.android.ddmlib.log.EventContainer;
+import com.android.ddmlib.log.EventLogParser;
+import com.android.ddmlib.log.EventValueDescription;
+import com.android.ddmlib.log.InvalidTypeException;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.jfree.chart.axis.AxisLocation;
+import org.jfree.chart.axis.NumberAxis;
+import org.jfree.chart.plot.XYPlot;
+import org.jfree.chart.renderer.xy.AbstractXYItemRenderer;
+import org.jfree.chart.renderer.xy.XYAreaRenderer;
+import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
+import org.jfree.data.time.Millisecond;
+import org.jfree.data.time.TimeSeries;
+import org.jfree.data.time.TimeSeriesCollection;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+public class DisplayGraph extends EventDisplay {
+
+ public DisplayGraph(String name) {
+ super(name);
+ }
+
+ /**
+ * Resets the display.
+ */
+ @Override
+ void resetUI() {
+ Collection datasets = mValueTypeDataSetMap.values();
+ for (TimeSeriesCollection dataset : datasets) {
+ dataset.removeAllSeries();
+ }
+ if (mOccurrenceDataSet != null) {
+ mOccurrenceDataSet.removeAllSeries();
+ }
+ mValueDescriptorSeriesMap.clear();
+ mOcurrenceDescriptorSeriesMap.clear();
+ }
+
+ /**
+ * Creates the UI for the event display.
+ * @param parent the parent composite.
+ * @param logParser the current log parser.
+ * @return the created control (which may have children).
+ */
+ @Override
+ public Control createComposite(final Composite parent, EventLogParser logParser,
+ final ILogColumnListener listener) {
+ String title = getChartTitle(logParser);
+ return createCompositeChart(parent, logParser, title);
+ }
+
+ /**
+ * Adds event to the display.
+ */
+ @Override
+ void newEvent(EventContainer event, EventLogParser logParser) {
+ ArrayList valueDescriptors =
+ new ArrayList();
+
+ ArrayList occurrenceDescriptors =
+ new ArrayList();
+
+ if (filterEvent(event, valueDescriptors, occurrenceDescriptors)) {
+ updateChart(event, logParser, valueDescriptors, occurrenceDescriptors);
+ }
+ }
+
+ /**
+ * Updates the chart with the {@link EventContainer} by adding the values/occurrences defined
+ * by the {@link ValueDisplayDescriptor} and {@link OccurrenceDisplayDescriptor} objects from
+ * the two lists.
+ * This method is only called when at least one of the descriptor list is non empty.
+ * @param event
+ * @param logParser
+ * @param valueDescriptors
+ * @param occurrenceDescriptors
+ */
+ private void updateChart(EventContainer event, EventLogParser logParser,
+ ArrayList valueDescriptors,
+ ArrayList occurrenceDescriptors) {
+ Map tagMap = logParser.getTagMap();
+
+ Millisecond millisecondTime = null;
+ long msec = -1;
+
+ // If the event container is a cpu container (tag == 2721), and there is no descriptor
+ // for the total CPU load, then we do accumulate all the values.
+ boolean accumulateValues = false;
+ double accumulatedValue = 0;
+
+ if (event.mTag == 2721) {
+ accumulateValues = true;
+ for (ValueDisplayDescriptor descriptor : valueDescriptors) {
+ accumulateValues &= (descriptor.valueIndex != 0);
+ }
+ }
+
+ for (ValueDisplayDescriptor descriptor : valueDescriptors) {
+ try {
+ // get the hashmap for this descriptor
+ HashMap map = mValueDescriptorSeriesMap.get(descriptor);
+
+ // if it's not there yet, we create it.
+ if (map == null) {
+ map = new HashMap();
+ mValueDescriptorSeriesMap.put(descriptor, map);
+ }
+
+ // get the TimeSeries for this pid
+ TimeSeries timeSeries = map.get(event.pid);
+
+ // if it doesn't exist yet, we create it
+ if (timeSeries == null) {
+ // get the series name
+ String seriesFullName = null;
+ String seriesLabel = getSeriesLabel(event, descriptor);
+
+ switch (mValueDescriptorCheck) {
+ case EVENT_CHECK_SAME_TAG:
+ seriesFullName = String.format("%1$s / %2$s", seriesLabel,
+ descriptor.valueName);
+ break;
+ case EVENT_CHECK_SAME_VALUE:
+ seriesFullName = String.format("%1$s", seriesLabel);
+ break;
+ default:
+ seriesFullName = String.format("%1$s / %2$s: %3$s", seriesLabel,
+ tagMap.get(descriptor.eventTag),
+ descriptor.valueName);
+ break;
+ }
+
+ // get the data set for this ValueType
+ TimeSeriesCollection dataset = getValueDataset(
+ logParser.getEventInfoMap().get(event.mTag)[descriptor.valueIndex]
+ .getValueType(),
+ accumulateValues);
+
+ // create the series
+ timeSeries = new TimeSeries(seriesFullName, Millisecond.class);
+ if (mMaximumChartItemAge != -1) {
+ timeSeries.setMaximumItemAge(mMaximumChartItemAge * 1000);
+ }
+
+ dataset.addSeries(timeSeries);
+
+ // add it to the map.
+ map.put(event.pid, timeSeries);
+ }
+
+ // update the timeSeries.
+
+ // get the value from the event
+ double value = event.getValueAsDouble(descriptor.valueIndex);
+
+ // accumulate the values if needed.
+ if (accumulateValues) {
+ accumulatedValue += value;
+ value = accumulatedValue;
+ }
+
+ // get the time
+ if (millisecondTime == null) {
+ msec = (long)event.sec * 1000L + (event.nsec / 1000000L);
+ millisecondTime = new Millisecond(new Date(msec));
+ }
+
+ // add the value to the time series
+ timeSeries.addOrUpdate(millisecondTime, value);
+ } catch (InvalidTypeException e) {
+ // just ignore this descriptor if there's a type mismatch
+ }
+ }
+
+ for (OccurrenceDisplayDescriptor descriptor : occurrenceDescriptors) {
+ try {
+ // get the hashmap for this descriptor
+ HashMap map = mOcurrenceDescriptorSeriesMap.get(descriptor);
+
+ // if it's not there yet, we create it.
+ if (map == null) {
+ map = new HashMap();
+ mOcurrenceDescriptorSeriesMap.put(descriptor, map);
+ }
+
+ // get the TimeSeries for this pid
+ TimeSeries timeSeries = map.get(event.pid);
+
+ // if it doesn't exist yet, we create it.
+ if (timeSeries == null) {
+ String seriesLabel = getSeriesLabel(event, descriptor);
+
+ String seriesFullName = String.format("[%1$s:%2$s]",
+ tagMap.get(descriptor.eventTag), seriesLabel);
+
+ timeSeries = new TimeSeries(seriesFullName, Millisecond.class);
+ if (mMaximumChartItemAge != -1) {
+ timeSeries.setMaximumItemAge(mMaximumChartItemAge);
+ }
+
+ getOccurrenceDataSet().addSeries(timeSeries);
+
+ map.put(event.pid, timeSeries);
+ }
+
+ // update the series
+
+ // get the time
+ if (millisecondTime == null) {
+ msec = (long)event.sec * 1000L + (event.nsec / 1000000L);
+ millisecondTime = new Millisecond(new Date(msec));
+ }
+
+ // add the value to the time series
+ timeSeries.addOrUpdate(millisecondTime, 0); // the value is unused
+ } catch (InvalidTypeException e) {
+ // just ignore this descriptor if there's a type mismatch
+ }
+ }
+
+ // go through all the series and remove old values.
+ if (msec != -1 && mMaximumChartItemAge != -1) {
+ Collection> pidMapValues =
+ mValueDescriptorSeriesMap.values();
+
+ for (HashMap pidMapValue : pidMapValues) {
+ Collection seriesCollection = pidMapValue.values();
+
+ for (TimeSeries timeSeries : seriesCollection) {
+ timeSeries.removeAgedItems(msec, true);
+ }
+ }
+
+ pidMapValues = mOcurrenceDescriptorSeriesMap.values();
+ for (HashMap pidMapValue : pidMapValues) {
+ Collection seriesCollection = pidMapValue.values();
+
+ for (TimeSeries timeSeries : seriesCollection) {
+ timeSeries.removeAgedItems(msec, true);
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns a {@link TimeSeriesCollection} for a specific {@link com.android.ddmlib.log.EventValueDescription.ValueType}.
+ * If the data set is not yet created, it is first allocated and set up into the
+ * {@link org.jfree.chart.JFreeChart} object.
+ * @param type the {@link com.android.ddmlib.log.EventValueDescription.ValueType} of the data set.
+ * @param accumulateValues
+ */
+ private TimeSeriesCollection getValueDataset(EventValueDescription.ValueType type, boolean accumulateValues) {
+ TimeSeriesCollection dataset = mValueTypeDataSetMap.get(type);
+ if (dataset == null) {
+ // create the data set and store it in the map
+ dataset = new TimeSeriesCollection();
+ mValueTypeDataSetMap.put(type, dataset);
+
+ // create the renderer and configure it depending on the ValueType
+ AbstractXYItemRenderer renderer;
+ if (type == EventValueDescription.ValueType.PERCENT && accumulateValues) {
+ renderer = new XYAreaRenderer();
+ } else {
+ XYLineAndShapeRenderer r = new XYLineAndShapeRenderer();
+ r.setBaseShapesVisible(type != EventValueDescription.ValueType.PERCENT);
+
+ renderer = r;
+ }
+
+ // set both the dataset and the renderer in the plot object.
+ XYPlot xyPlot = mChart.getXYPlot();
+ xyPlot.setDataset(mDataSetCount, dataset);
+ xyPlot.setRenderer(mDataSetCount, renderer);
+
+ // put a new axis label, and configure it.
+ NumberAxis axis = new NumberAxis(type.toString());
+
+ if (type == EventValueDescription.ValueType.PERCENT) {
+ // force percent range to be (0,100) fixed.
+ axis.setAutoRange(false);
+ axis.setRange(0., 100.);
+ }
+
+ // for the index, we ignore the occurrence dataset
+ int count = mDataSetCount;
+ if (mOccurrenceDataSet != null) {
+ count--;
+ }
+
+ xyPlot.setRangeAxis(count, axis);
+ if ((count % 2) == 0) {
+ xyPlot.setRangeAxisLocation(count, AxisLocation.BOTTOM_OR_LEFT);
+ } else {
+ xyPlot.setRangeAxisLocation(count, AxisLocation.TOP_OR_RIGHT);
+ }
+
+ // now we link the dataset and the axis
+ xyPlot.mapDatasetToRangeAxis(mDataSetCount, count);
+
+ mDataSetCount++;
+ }
+
+ return dataset;
+ }
+
+ /**
+ * Return the series label for this event. This only contains the pid information.
+ * @param event the {@link EventContainer}
+ * @param descriptor the {@link OccurrenceDisplayDescriptor}
+ * @return the series label.
+ * @throws InvalidTypeException
+ */
+ private String getSeriesLabel(EventContainer event, OccurrenceDisplayDescriptor descriptor)
+ throws InvalidTypeException {
+ if (descriptor.seriesValueIndex != -1) {
+ if (descriptor.includePid == false) {
+ return event.getValueAsString(descriptor.seriesValueIndex);
+ } else {
+ return String.format("%1$s (%2$d)",
+ event.getValueAsString(descriptor.seriesValueIndex), event.pid);
+ }
+ }
+
+ return Integer.toString(event.pid);
+ }
+
+ /**
+ * Returns the {@link TimeSeriesCollection} for the occurrence display. If the data set is not
+ * yet created, it is first allocated and set up into the {@link org.jfree.chart.JFreeChart} object.
+ */
+ private TimeSeriesCollection getOccurrenceDataSet() {
+ if (mOccurrenceDataSet == null) {
+ mOccurrenceDataSet = new TimeSeriesCollection();
+
+ XYPlot xyPlot = mChart.getXYPlot();
+ xyPlot.setDataset(mDataSetCount, mOccurrenceDataSet);
+
+ OccurrenceRenderer renderer = new OccurrenceRenderer();
+ renderer.setBaseShapesVisible(false);
+ xyPlot.setRenderer(mDataSetCount, renderer);
+
+ mDataSetCount++;
+ }
+
+ return mOccurrenceDataSet;
+ }
+
+ /**
+ * Gets display type
+ *
+ * @return display type as an integer
+ */
+ @Override
+ int getDisplayType() {
+ return DISPLAY_TYPE_GRAPH;
+ }
+
+ /**
+ * Sets the current {@link EventLogParser} object.
+ */
+ @Override
+ protected void setNewLogParser(EventLogParser logParser) {
+ if (mChart != null) {
+ mChart.setTitle(getChartTitle(logParser));
+ }
+ }
+ /**
+ * Returns a meaningful chart title based on the value of {@link #mValueDescriptorCheck}.
+ *
+ * @param logParser the logParser.
+ * @return the chart title.
+ */
+ private String getChartTitle(EventLogParser logParser) {
+ if (mValueDescriptors.size() > 0) {
+ String chartDesc = null;
+ switch (mValueDescriptorCheck) {
+ case EVENT_CHECK_SAME_TAG:
+ if (logParser != null) {
+ chartDesc = logParser.getTagMap().get(mValueDescriptors.get(0).eventTag);
+ }
+ break;
+ case EVENT_CHECK_SAME_VALUE:
+ if (logParser != null) {
+ chartDesc = String.format("%1$s / %2$s",
+ logParser.getTagMap().get(mValueDescriptors.get(0).eventTag),
+ mValueDescriptors.get(0).valueName);
+ }
+ break;
+ }
+
+ if (chartDesc != null) {
+ return String.format("%1$s - %2$s", mName, chartDesc);
+ }
+ }
+
+ return mName;
+ }
+}
\ No newline at end of file
diff --git a/tools/ddms/libs/ddmuilib/src/com/android/ddmuilib/log/event/DisplayLog.java b/tools/ddms/libs/ddmuilib/src/com/android/ddmuilib/log/event/DisplayLog.java
new file mode 100644
index 000000000..26296f31c
--- /dev/null
+++ b/tools/ddms/libs/ddmuilib/src/com/android/ddmuilib/log/event/DisplayLog.java
@@ -0,0 +1,379 @@
+/*
+ * Copyright (C) 2008 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.log.event;
+
+import com.android.ddmlib.log.EventContainer;
+import com.android.ddmlib.log.EventLogParser;
+import com.android.ddmlib.log.EventValueDescription;
+import com.android.ddmlib.log.InvalidTypeException;
+import com.android.ddmuilib.DdmUiPreferences;
+import com.android.ddmuilib.TableHelper;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ControlAdapter;
+import org.eclipse.swt.events.ControlEvent;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.ScrollBar;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableColumn;
+import org.eclipse.swt.widgets.TableItem;
+
+import java.util.ArrayList;
+import java.util.Calendar;
+
+public class DisplayLog extends EventDisplay {
+ public DisplayLog(String name) {
+ super(name);
+ }
+
+ private final static String PREFS_COL_DATE = "EventLogPanel.log.Col1"; //$NON-NLS-1$
+ private final static String PREFS_COL_PID = "EventLogPanel.log.Col2"; //$NON-NLS-1$
+ private final static String PREFS_COL_EVENTTAG = "EventLogPanel.log.Col3"; //$NON-NLS-1$
+ private final static String PREFS_COL_VALUENAME = "EventLogPanel.log.Col4"; //$NON-NLS-1$
+ private final static String PREFS_COL_VALUE = "EventLogPanel.log.Col5"; //$NON-NLS-1$
+ private final static String PREFS_COL_TYPE = "EventLogPanel.log.Col6"; //$NON-NLS-1$
+
+ /**
+ * Resets the display.
+ */
+ @Override
+ void resetUI() {
+ mLogTable.removeAll();
+ }
+
+ /**
+ * Adds event to the display.
+ */
+ @Override
+ void newEvent(EventContainer event, EventLogParser logParser) {
+ addToLog(event, logParser);
+ }
+
+ /**
+ * Creates the UI for the event display.
+ *
+ * @param parent the parent composite.
+ * @param logParser the current log parser.
+ * @return the created control (which may have children).
+ */
+ @Override
+ Control createComposite(Composite parent, EventLogParser logParser, ILogColumnListener listener) {
+ return createLogUI(parent, listener);
+ }
+
+ /**
+ * Adds an {@link EventContainer} to the log.
+ *
+ * @param event the event.
+ * @param logParser the log parser.
+ */
+ private void addToLog(EventContainer event, EventLogParser logParser) {
+ ScrollBar bar = mLogTable.getVerticalBar();
+ boolean scroll = bar.getMaximum() == bar.getSelection() + bar.getThumb();
+
+ // get the date.
+ Calendar c = Calendar.getInstance();
+ long msec = (long) event.sec * 1000L;
+ c.setTimeInMillis(msec);
+
+ // convert the time into a string
+ String date = String.format("%1$tF %1$tT", c);
+
+ String eventName = logParser.getTagMap().get(event.mTag);
+ String pidName = Integer.toString(event.pid);
+
+ // get the value description
+ EventValueDescription[] valueDescription = logParser.getEventInfoMap().get(event.mTag);
+ if (valueDescription != null) {
+ for (int i = 0; i < valueDescription.length; i++) {
+ EventValueDescription description = valueDescription[i];
+ try {
+ String value = event.getValueAsString(i);
+
+ logValue(date, pidName, eventName, description.getName(), value,
+ description.getEventValueType(), description.getValueType());
+ } catch (InvalidTypeException e) {
+ logValue(date, pidName, eventName, description.getName(), e.getMessage(),
+ description.getEventValueType(), description.getValueType());
+ }
+ }
+
+ // scroll if needed, by showing the last item
+ if (scroll) {
+ int itemCount = mLogTable.getItemCount();
+ if (itemCount > 0) {
+ mLogTable.showItem(mLogTable.getItem(itemCount - 1));
+ }
+ }
+ }
+ }
+
+ /**
+ * Adds an {@link EventContainer} to the log. Only add the values/occurrences defined by
+ * the list of descriptors. If an event is configured to be displayed by value and occurrence,
+ * only the values are displayed (as they mark an event occurrence anyway).
+ * This method is only called when at least one of the descriptor list is non empty.
+ *
+ * @param event
+ * @param logParser
+ * @param valueDescriptors
+ * @param occurrenceDescriptors
+ */
+ protected void addToLog(EventContainer event, EventLogParser logParser,
+ ArrayList valueDescriptors,
+ ArrayList occurrenceDescriptors) {
+ ScrollBar bar = mLogTable.getVerticalBar();
+ boolean scroll = bar.getMaximum() == bar.getSelection() + bar.getThumb();
+
+ // get the date.
+ Calendar c = Calendar.getInstance();
+ long msec = (long) event.sec * 1000L;
+ c.setTimeInMillis(msec);
+
+ // convert the time into a string
+ String date = String.format("%1$tF %1$tT", c);
+
+ String eventName = logParser.getTagMap().get(event.mTag);
+ String pidName = Integer.toString(event.pid);
+
+ if (valueDescriptors.size() > 0) {
+ for (ValueDisplayDescriptor descriptor : valueDescriptors) {
+ logDescriptor(event, descriptor, date, pidName, eventName, logParser);
+ }
+ } else {
+ // we display the event. Since the StringBuilder contains the header (date, event name,
+ // pid) at this point, there isn't anything else to display.
+ }
+
+ // scroll if needed, by showing the last item
+ if (scroll) {
+ int itemCount = mLogTable.getItemCount();
+ if (itemCount > 0) {
+ mLogTable.showItem(mLogTable.getItem(itemCount - 1));
+ }
+ }
+ }
+
+
+ /**
+ * Logs a value in the ui.
+ *
+ * @param date
+ * @param pid
+ * @param event
+ * @param valueName
+ * @param value
+ * @param eventValueType
+ * @param valueType
+ */
+ private void logValue(String date, String pid, String event, String valueName,
+ String value, EventContainer.EventValueType eventValueType, EventValueDescription.ValueType valueType) {
+
+ TableItem item = new TableItem(mLogTable, SWT.NONE);
+ item.setText(0, date);
+ item.setText(1, pid);
+ item.setText(2, event);
+ item.setText(3, valueName);
+ item.setText(4, value);
+
+ String type;
+ if (valueType != EventValueDescription.ValueType.NOT_APPLICABLE) {
+ type = String.format("%1$s, %2$s", eventValueType.toString(), valueType.toString());
+ } else {
+ type = eventValueType.toString();
+ }
+
+ item.setText(5, type);
+ }
+
+ /**
+ * Logs a value from an {@link EventContainer} as defined by the {@link ValueDisplayDescriptor}.
+ *
+ * @param event the EventContainer
+ * @param descriptor the ValueDisplayDescriptor defining which value to display.
+ * @param date the date of the event in a string.
+ * @param pidName
+ * @param eventName
+ * @param logParser
+ */
+ private void logDescriptor(EventContainer event, ValueDisplayDescriptor descriptor,
+ String date, String pidName, String eventName, EventLogParser logParser) {
+
+ String value;
+ try {
+ value = event.getValueAsString(descriptor.valueIndex);
+ } catch (InvalidTypeException e) {
+ value = e.getMessage();
+ }
+
+ EventValueDescription[] values = logParser.getEventInfoMap().get(event.mTag);
+
+ EventValueDescription valueDescription = values[descriptor.valueIndex];
+
+ logValue(date, pidName, eventName, descriptor.valueName, value,
+ valueDescription.getEventValueType(), valueDescription.getValueType());
+ }
+
+ /**
+ * Creates the UI for a log display.
+ *
+ * @param parent the parent {@link Composite}
+ * @param listener the {@link ILogColumnListener} to notify on column resize events.
+ * @return the top Composite of the UI.
+ */
+ private Control createLogUI(Composite parent, final ILogColumnListener listener) {
+ Composite mainComp = new Composite(parent, SWT.NONE);
+ GridLayout gl;
+ mainComp.setLayout(gl = new GridLayout(1, false));
+ gl.marginHeight = gl.marginWidth = 0;
+ mainComp.addDisposeListener(new DisposeListener() {
+ public void widgetDisposed(DisposeEvent e) {
+ mLogTable = null;
+ }
+ });
+
+ Label l = new Label(mainComp, SWT.CENTER);
+ l.setText(mName);
+ l.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+
+ mLogTable = new Table(mainComp, SWT.MULTI | SWT.FULL_SELECTION | SWT.V_SCROLL |
+ SWT.BORDER);
+ mLogTable.setLayoutData(new GridData(GridData.FILL_BOTH));
+
+ IPreferenceStore store = DdmUiPreferences.getStore();
+
+ TableColumn col = TableHelper.createTableColumn(
+ mLogTable, "Time",
+ SWT.LEFT, "0000-00-00 00:00:00", PREFS_COL_DATE, store); //$NON-NLS-1$
+ col.addControlListener(new ControlAdapter() {
+ @Override
+ public void controlResized(ControlEvent e) {
+ Object source = e.getSource();
+ if (source instanceof TableColumn) {
+ listener.columnResized(0, (TableColumn) source);
+ }
+ }
+ });
+
+ col = TableHelper.createTableColumn(
+ mLogTable, "pid",
+ SWT.LEFT, "0000", PREFS_COL_PID, store); //$NON-NLS-1$
+ col.addControlListener(new ControlAdapter() {
+ @Override
+ public void controlResized(ControlEvent e) {
+ Object source = e.getSource();
+ if (source instanceof TableColumn) {
+ listener.columnResized(1, (TableColumn) source);
+ }
+ }
+ });
+
+ col = TableHelper.createTableColumn(
+ mLogTable, "Event",
+ SWT.LEFT, "abcdejghijklmno", PREFS_COL_EVENTTAG, store); //$NON-NLS-1$
+ col.addControlListener(new ControlAdapter() {
+ @Override
+ public void controlResized(ControlEvent e) {
+ Object source = e.getSource();
+ if (source instanceof TableColumn) {
+ listener.columnResized(2, (TableColumn) source);
+ }
+ }
+ });
+
+ col = TableHelper.createTableColumn(
+ mLogTable, "Name",
+ SWT.LEFT, "Process Name", PREFS_COL_VALUENAME, store); //$NON-NLS-1$
+ col.addControlListener(new ControlAdapter() {
+ @Override
+ public void controlResized(ControlEvent e) {
+ Object source = e.getSource();
+ if (source instanceof TableColumn) {
+ listener.columnResized(3, (TableColumn) source);
+ }
+ }
+ });
+
+ col = TableHelper.createTableColumn(
+ mLogTable, "Value",
+ SWT.LEFT, "0000000", PREFS_COL_VALUE, store); //$NON-NLS-1$
+ col.addControlListener(new ControlAdapter() {
+ @Override
+ public void controlResized(ControlEvent e) {
+ Object source = e.getSource();
+ if (source instanceof TableColumn) {
+ listener.columnResized(4, (TableColumn) source);
+ }
+ }
+ });
+
+ col = TableHelper.createTableColumn(
+ mLogTable, "Type",
+ SWT.LEFT, "long, seconds", PREFS_COL_TYPE, store); //$NON-NLS-1$
+ col.addControlListener(new ControlAdapter() {
+ @Override
+ public void controlResized(ControlEvent e) {
+ Object source = e.getSource();
+ if (source instanceof TableColumn) {
+ listener.columnResized(5, (TableColumn) source);
+ }
+ }
+ });
+
+ mLogTable.setHeaderVisible(true);
+ mLogTable.setLinesVisible(true);
+
+ return mainComp;
+ }
+
+ /**
+ * Resizes the index-th column of the log {@link Table} (if applicable).
+ *
+ * This does nothing if the Table object is null (because the display
+ * type does not use a column) or if the index-th column is in fact the originating
+ * column passed as argument.
+ *
+ * @param index the index of the column to resize
+ * @param sourceColumn the original column that was resize, and on which we need to sync the
+ * index-th column width.
+ */
+ @Override
+ void resizeColumn(int index, TableColumn sourceColumn) {
+ if (mLogTable != null) {
+ TableColumn col = mLogTable.getColumn(index);
+ if (col != sourceColumn) {
+ col.setWidth(sourceColumn.getWidth());
+ }
+ }
+ }
+
+ /**
+ * Gets display type
+ *
+ * @return display type as an integer
+ */
+ @Override
+ int getDisplayType() {
+ return DISPLAY_TYPE_LOG_ALL;
+ }
+}
diff --git a/tools/ddms/libs/ddmuilib/src/com/android/ddmuilib/log/event/DisplaySync.java b/tools/ddms/libs/ddmuilib/src/com/android/ddmuilib/log/event/DisplaySync.java
new file mode 100644
index 000000000..82cc7a440
--- /dev/null
+++ b/tools/ddms/libs/ddmuilib/src/com/android/ddmuilib/log/event/DisplaySync.java
@@ -0,0 +1,293 @@
+/*
+ * Copyright (C) 2008 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.log.event;
+
+import com.android.ddmlib.log.EventContainer;
+import com.android.ddmlib.log.EventLogParser;
+import com.android.ddmlib.log.InvalidTypeException;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.jfree.chart.labels.CustomXYToolTipGenerator;
+import org.jfree.chart.plot.XYPlot;
+import org.jfree.chart.renderer.xy.XYBarRenderer;
+import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
+import org.jfree.data.time.FixedMillisecond;
+import org.jfree.data.time.SimpleTimePeriod;
+import org.jfree.data.time.TimePeriodValues;
+import org.jfree.data.time.TimePeriodValuesCollection;
+import org.jfree.data.time.TimeSeries;
+import org.jfree.data.time.TimeSeriesCollection;
+import org.jfree.util.ShapeUtilities;
+
+import java.awt.Color;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Scanner;
+import java.util.regex.Pattern;
+
+public class DisplaySync extends SyncCommon {
+
+ // Information to graph for each authority
+ private TimePeriodValues mDatasetsSync[];
+ private List mTooltipsSync[];
+ private CustomXYToolTipGenerator mTooltipGenerators[];
+ private TimeSeries mDatasetsSyncTickle[];
+
+ // Dataset of error events to graph
+ private TimeSeries mDatasetError;
+
+ public DisplaySync(String name) {
+ super(name);
+ }
+
+ /**
+ * Creates the UI for the event display.
+ * @param parent the parent composite.
+ * @param logParser the current log parser.
+ * @return the created control (which may have children).
+ */
+ @Override
+ public Control createComposite(final Composite parent, EventLogParser logParser,
+ final ILogColumnListener listener) {
+ Control composite = createCompositeChart(parent, logParser, "Sync Status");
+ resetUI();
+ return composite;
+ }
+
+ /**
+ * Resets the display.
+ */
+ @Override
+ void resetUI() {
+ super.resetUI();
+ XYPlot xyPlot = mChart.getXYPlot();
+
+ XYBarRenderer br = new XYBarRenderer();
+ mDatasetsSync = new TimePeriodValues[NUM_AUTHS];
+ mTooltipsSync = new List[NUM_AUTHS];
+ mTooltipGenerators = new CustomXYToolTipGenerator[NUM_AUTHS];
+
+ TimePeriodValuesCollection tpvc = new TimePeriodValuesCollection();
+ xyPlot.setDataset(tpvc);
+ xyPlot.setRenderer(0, br);
+
+ XYLineAndShapeRenderer ls = new XYLineAndShapeRenderer();
+ ls.setBaseLinesVisible(false);
+ mDatasetsSyncTickle = new TimeSeries[NUM_AUTHS];
+ TimeSeriesCollection tsc = new TimeSeriesCollection();
+ xyPlot.setDataset(1, tsc);
+ xyPlot.setRenderer(1, ls);
+
+ mDatasetError = new TimeSeries("Errors", FixedMillisecond.class);
+ xyPlot.setDataset(2, new TimeSeriesCollection(mDatasetError));
+ XYLineAndShapeRenderer errls = new XYLineAndShapeRenderer();
+ errls.setBaseLinesVisible(false);
+ errls.setSeriesPaint(0, Color.RED);
+ xyPlot.setRenderer(2, errls);
+
+ for (int i = 0; i < NUM_AUTHS; i++) {
+ br.setSeriesPaint(i, AUTH_COLORS[i]);
+ ls.setSeriesPaint(i, AUTH_COLORS[i]);
+ mDatasetsSync[i] = new TimePeriodValues(AUTH_NAMES[i]);
+ tpvc.addSeries(mDatasetsSync[i]);
+ mTooltipsSync[i] = new ArrayList();
+ mTooltipGenerators[i] = new CustomXYToolTipGenerator();
+ br.setSeriesToolTipGenerator(i, mTooltipGenerators[i]);
+ mTooltipGenerators[i].addToolTipSeries(mTooltipsSync[i]);
+
+ mDatasetsSyncTickle[i] = new TimeSeries(AUTH_NAMES[i] + " tickle",
+ FixedMillisecond.class);
+ tsc.addSeries(mDatasetsSyncTickle[i]);
+ ls.setSeriesShape(i, ShapeUtilities.createUpTriangle(2.5f));
+ }
+ }
+
+ /**
+ * Updates the display with a new event.
+ *
+ * @param event The event
+ * @param logParser The parser providing the event.
+ */
+ @Override
+ void newEvent(EventContainer event, EventLogParser logParser) {
+ super.newEvent(event, logParser); // Handle sync operation
+ try {
+ if (event.mTag == EVENT_TICKLE) {
+ int auth = getAuth(event.getValueAsString(0));
+ if (auth >= 0) {
+ long msec = (long)event.sec * 1000L + (event.nsec / 1000000L);
+ mDatasetsSyncTickle[auth].addOrUpdate(new FixedMillisecond(msec), -1);
+ }
+ }
+ } catch (InvalidTypeException e) {
+ }
+ }
+
+ /**
+ * Generate the height for an event.
+ * Height is somewhat arbitrarily the count of "things" that happened
+ * during the sync.
+ * When network traffic measurements are available, code should be modified
+ * to use that instead.
+ * @param details The details string associated with the event
+ * @return The height in arbirary units (0-100)
+ */
+ private int getHeightFromDetails(String details) {
+ if (details == null) {
+ return 1; // Arbitrary
+ }
+ int total = 0;
+ String parts[] = details.split("[a-zA-Z]");
+ for (String part : parts) {
+ if ("".equals(part)) continue;
+ total += Integer.parseInt(part);
+ }
+ if (total == 0) {
+ total = 1;
+ }
+ return total;
+ }
+
+ /**
+ * Generates the tooltips text for an event.
+ * This method decodes the cryptic details string.
+ * @param auth The authority associated with the event
+ * @param details The details string
+ * @param eventSource server, poll, etc.
+ * @return The text to display in the tooltips
+ */
+ private String getTextFromDetails(int auth, String details, int eventSource) {
+
+ StringBuffer sb = new StringBuffer();
+ sb.append(AUTH_NAMES[auth]).append(": \n");
+
+ Scanner scanner = new Scanner(details);
+ Pattern charPat = Pattern.compile("[a-zA-Z]");
+ Pattern numPat = Pattern.compile("[0-9]+");
+ while (scanner.hasNext()) {
+ String key = scanner.findInLine(charPat);
+ int val = Integer.parseInt(scanner.findInLine(numPat));
+ if (auth == GMAIL && "M".equals(key)) {
+ sb.append("messages from server: ").append(val).append("\n");
+ } else if (auth == GMAIL && "L".equals(key)) {
+ sb.append("labels from server: ").append(val).append("\n");
+ } else if (auth == GMAIL && "C".equals(key)) {
+ sb.append("check conversation requests from server: ").append(val).append("\n");
+ } else if (auth == GMAIL && "A".equals(key)) {
+ sb.append("attachments from server: ").append(val).append("\n");
+ } else if (auth == GMAIL && "U".equals(key)) {
+ sb.append("op updates from server: ").append(val).append("\n");
+ } else if (auth == GMAIL && "u".equals(key)) {
+ sb.append("op updates to server: ").append(val).append("\n");
+ } else if (auth == GMAIL && "S".equals(key)) {
+ sb.append("send/receive cycles: ").append(val).append("\n");
+ } else if ("Q".equals(key)) {
+ sb.append("queries to server: ").append(val).append("\n");
+ } else if ("E".equals(key)) {
+ sb.append("entries from server: ").append(val).append("\n");
+ } else if ("u".equals(key)) {
+ sb.append("updates from client: ").append(val).append("\n");
+ } else if ("i".equals(key)) {
+ sb.append("inserts from client: ").append(val).append("\n");
+ } else if ("d".equals(key)) {
+ sb.append("deletes from client: ").append(val).append("\n");
+ } else if ("f".equals(key)) {
+ sb.append("full sync requested\n");
+ } else if ("r".equals(key)) {
+ sb.append("partial sync unavailable\n");
+ } else if ("X".equals(key)) {
+ sb.append("hard error\n");
+ } else if ("e".equals(key)) {
+ sb.append("number of parse exceptions: ").append(val).append("\n");
+ } else if ("c".equals(key)) {
+ sb.append("number of conflicts: ").append(val).append("\n");
+ } else if ("a".equals(key)) {
+ sb.append("number of auth exceptions: ").append(val).append("\n");
+ } else if ("D".equals(key)) {
+ sb.append("too many deletions\n");
+ } else if ("R".equals(key)) {
+ sb.append("too many retries: ").append(val).append("\n");
+ } else if ("b".equals(key)) {
+ sb.append("database error\n");
+ } else if ("x".equals(key)) {
+ sb.append("soft error\n");
+ } else if ("l".equals(key)) {
+ sb.append("sync already in progress\n");
+ } else if ("I".equals(key)) {
+ sb.append("io exception\n");
+ } else if (auth == CONTACTS && "p".equals(key)) {
+ sb.append("photos uploaded from client: ").append(val).append("\n");
+ } else if (auth == CONTACTS && "P".equals(key)) {
+ sb.append("photos downloaded from server: ").append(val).append("\n");
+ } else if (auth == CALENDAR && "F".equals(key)) {
+ sb.append("server refresh\n");
+ } else if (auth == CALENDAR && "s".equals(key)) {
+ sb.append("server diffs fetched\n");
+ } else {
+ sb.append(key).append("=").append(val);
+ }
+ }
+ if (eventSource == 0) {
+ sb.append("(server)");
+ } else if (eventSource == 1) {
+ sb.append("(local)");
+ } else if (eventSource == 2) {
+ sb.append("(poll)");
+ } else if (eventSource == 3) {
+ sb.append("(user)");
+ }
+ return sb.toString();
+ }
+
+
+ /**
+ * Callback to process a sync event.
+ */
+ @Override
+ void processSyncEvent(EventContainer event, int auth, long startTime, long stopTime,
+ String details, boolean newEvent, int syncSource) {
+ if (!newEvent) {
+ // Details arrived for a previous sync event
+ // Remove event before reinserting.
+ int lastItem = mDatasetsSync[auth].getItemCount();
+ mDatasetsSync[auth].delete(lastItem-1, lastItem-1);
+ mTooltipsSync[auth].remove(lastItem-1);
+ }
+ double height = getHeightFromDetails(details);
+ height = height / (stopTime - startTime + 1) * 10000;
+ if (height > 30) {
+ height = 30;
+ }
+ mDatasetsSync[auth].add(new SimpleTimePeriod(startTime, stopTime), height);
+ mTooltipsSync[auth].add(getTextFromDetails(auth, details, syncSource));
+ mTooltipGenerators[auth].addToolTipSeries(mTooltipsSync[auth]);
+ if (details.indexOf('x') >= 0 || details.indexOf('X') >= 0) {
+ long msec = (long)event.sec * 1000L + (event.nsec / 1000000L);
+ mDatasetError.addOrUpdate(new FixedMillisecond(msec), -1);
+ }
+ }
+
+ /**
+ * Gets display type
+ *
+ * @return display type as an integer
+ */
+ @Override
+ int getDisplayType() {
+ return DISPLAY_TYPE_SYNC;
+ }
+}
\ No newline at end of file
diff --git a/tools/ddms/libs/ddmuilib/src/com/android/ddmuilib/log/event/DisplaySyncHistogram.java b/tools/ddms/libs/ddmuilib/src/com/android/ddmuilib/log/event/DisplaySyncHistogram.java
new file mode 100644
index 000000000..36d90ce61
--- /dev/null
+++ b/tools/ddms/libs/ddmuilib/src/com/android/ddmuilib/log/event/DisplaySyncHistogram.java
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2008 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.log.event;
+
+import com.android.ddmlib.log.EventContainer;
+import com.android.ddmlib.log.EventLogParser;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.jfree.chart.plot.XYPlot;
+import org.jfree.chart.renderer.xy.AbstractXYItemRenderer;
+import org.jfree.chart.renderer.xy.XYBarRenderer;
+import org.jfree.data.time.RegularTimePeriod;
+import org.jfree.data.time.SimpleTimePeriod;
+import org.jfree.data.time.TimePeriodValues;
+import org.jfree.data.time.TimePeriodValuesCollection;
+
+import java.util.Calendar;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.TimeZone;
+
+public class DisplaySyncHistogram extends SyncCommon {
+
+ Map mTimePeriodMap[];
+
+ // Information to graph for each authority
+ private TimePeriodValues mDatasetsSyncHist[];
+
+ public DisplaySyncHistogram(String name) {
+ super(name);
+ }
+
+ /**
+ * Creates the UI for the event display.
+ * @param parent the parent composite.
+ * @param logParser the current log parser.
+ * @return the created control (which may have children).
+ */
+ @Override
+ public Control createComposite(final Composite parent, EventLogParser logParser,
+ final ILogColumnListener listener) {
+ Control composite = createCompositeChart(parent, logParser, "Sync Histogram");
+ resetUI();
+ return composite;
+ }
+
+ /**
+ * Resets the display.
+ */
+ @Override
+ void resetUI() {
+ super.resetUI();
+ XYPlot xyPlot = mChart.getXYPlot();
+
+ AbstractXYItemRenderer br = new XYBarRenderer();
+ mDatasetsSyncHist = new TimePeriodValues[NUM_AUTHS+1];
+ mTimePeriodMap = new HashMap[NUM_AUTHS + 1];
+
+ TimePeriodValuesCollection tpvc = new TimePeriodValuesCollection();
+ xyPlot.setDataset(tpvc);
+ xyPlot.setRenderer(br);
+
+ for (int i = 0; i < NUM_AUTHS + 1; i++) {
+ br.setSeriesPaint(i, AUTH_COLORS[i]);
+ mDatasetsSyncHist[i] = new TimePeriodValues(AUTH_NAMES[i]);
+ tpvc.addSeries(mDatasetsSyncHist[i]);
+ mTimePeriodMap[i] = new HashMap();
+
+ }
+ }
+
+ /**
+ * Callback to process a sync event.
+ *
+ * @param event The sync event
+ * @param startTime Start time (ms) of events
+ * @param stopTime Stop time (ms) of events
+ * @param details Details associated with the event.
+ * @param newEvent True if this event is a new sync event. False if this event
+ * @param syncSource
+ */
+ @Override
+ void processSyncEvent(EventContainer event, int auth, long startTime, long stopTime,
+ String details, boolean newEvent, int syncSource) {
+ if (newEvent) {
+ if (details.indexOf('x') >= 0 || details.indexOf('X') >= 0) {
+ auth = ERRORS;
+ }
+ double delta = (stopTime - startTime) * 100. / 1000 / 3600; // Percent of hour
+ addHistEvent(0, auth, delta);
+ } else {
+ // sync_details arrived for an event that has already been graphed.
+ if (details.indexOf('x') >= 0 || details.indexOf('X') >= 0) {
+ // Item turns out to be in error, so transfer time from old auth to error.
+ double delta = (stopTime - startTime) * 100. / 1000 / 3600; // Percent of hour
+ addHistEvent(0, auth, -delta);
+ addHistEvent(0, ERRORS, delta);
+ }
+ }
+ }
+
+ /**
+ * Helper to add an event to the data series.
+ * Also updates error series if appropriate (x or X in details).
+ * @param stopTime Time event ends
+ * @param auth Sync authority
+ * @param value Value to graph for event
+ */
+ private void addHistEvent(long stopTime, int auth, double value) {
+ SimpleTimePeriod hour = getTimePeriod(stopTime, mHistWidth);
+
+ // Loop over all datasets to do the stacking.
+ for (int i = auth; i <= ERRORS; i++) {
+ addToPeriod(mDatasetsSyncHist, i, hour, value);
+ }
+ }
+
+ private void addToPeriod(TimePeriodValues tpv[], int auth, SimpleTimePeriod period,
+ double value) {
+ int index;
+ if (mTimePeriodMap[auth].containsKey(period)) {
+ index = mTimePeriodMap[auth].get(period);
+ double oldValue = tpv[auth].getValue(index).doubleValue();
+ tpv[auth].update(index, oldValue + value);
+ } else {
+ index = tpv[auth].getItemCount();
+ mTimePeriodMap[auth].put(period, index);
+ tpv[auth].add(period, value);
+ }
+ }
+
+ /**
+ * Creates a multiple-hour time period for the histogram.
+ * @param time Time in milliseconds.
+ * @param numHoursWide: should divide into a day.
+ * @return SimpleTimePeriod covering the number of hours and containing time.
+ */
+ private SimpleTimePeriod getTimePeriod(long time, long numHoursWide) {
+ Date date = new Date(time);
+ TimeZone zone = RegularTimePeriod.DEFAULT_TIME_ZONE;
+ Calendar calendar = Calendar.getInstance(zone);
+ calendar.setTime(date);
+ long hoursOfYear = calendar.get(Calendar.HOUR_OF_DAY) +
+ calendar.get(Calendar.DAY_OF_YEAR) * 24;
+ int year = calendar.get(Calendar.YEAR);
+ hoursOfYear = (hoursOfYear / numHoursWide) * numHoursWide;
+ calendar.clear();
+ calendar.set(year, 0, 1, 0, 0); // Jan 1
+ long start = calendar.getTimeInMillis() + hoursOfYear * 3600 * 1000;
+ return new SimpleTimePeriod(start, start + numHoursWide * 3600 * 1000);
+ }
+
+ /**
+ * Gets display type
+ *
+ * @return display type as an integer
+ */
+ @Override
+ int getDisplayType() {
+ return DISPLAY_TYPE_SYNC_HIST;
+ }
+}
diff --git a/tools/ddms/libs/ddmuilib/src/com/android/ddmuilib/log/event/DisplaySyncPerf.java b/tools/ddms/libs/ddmuilib/src/com/android/ddmuilib/log/event/DisplaySyncPerf.java
new file mode 100644
index 000000000..9ce704550
--- /dev/null
+++ b/tools/ddms/libs/ddmuilib/src/com/android/ddmuilib/log/event/DisplaySyncPerf.java
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) 2009 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.log.event;
+
+import com.android.ddmlib.log.EventContainer;
+import com.android.ddmlib.log.EventLogParser;
+import com.android.ddmlib.log.InvalidTypeException;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.jfree.chart.labels.CustomXYToolTipGenerator;
+import org.jfree.chart.plot.XYPlot;
+import org.jfree.chart.renderer.xy.XYBarRenderer;
+import org.jfree.data.time.SimpleTimePeriod;
+import org.jfree.data.time.TimePeriodValues;
+import org.jfree.data.time.TimePeriodValuesCollection;
+
+import java.awt.Color;
+import java.util.ArrayList;
+import java.util.List;
+
+public class DisplaySyncPerf extends SyncCommon {
+
+ CustomXYToolTipGenerator mTooltipGenerator;
+ List mTooltips[];
+
+ // The series number for each graphed item.
+ // sync authorities are 0-3
+ private static final int DB_QUERY = 4;
+ private static final int DB_WRITE = 5;
+ private static final int HTTP_NETWORK = 6;
+ private static final int HTTP_PROCESSING = 7;
+ private static final int NUM_SERIES = (HTTP_PROCESSING + 1);
+ private static final String SERIES_NAMES[] = {"Calendar", "Gmail", "Feeds", "Contacts",
+ "DB Query", "DB Write", "HTTP Response", "HTTP Processing",};
+ private static final Color SERIES_COLORS[] = {Color.MAGENTA, Color.GREEN, Color.BLUE,
+ Color.ORANGE, Color.RED, Color.CYAN, Color.PINK, Color.DARK_GRAY};
+ private static final double SERIES_YCOORD[] = {0, 0, 0, 0, 1, 1, 2, 2};
+
+ // Values from data/etc/event-log-tags
+ private static final int EVENT_DB_OPERATION = 52000;
+ private static final int EVENT_HTTP_STATS = 52001;
+ // op types for EVENT_DB_OPERATION
+ final int EVENT_DB_QUERY = 0;
+ final int EVENT_DB_WRITE = 1;
+
+ // Information to graph for each authority
+ private TimePeriodValues mDatasets[];
+
+ /**
+ * TimePeriodValuesCollection that supports Y intervals. This allows the
+ * creation of "floating" bars, rather than bars rooted to the axis.
+ */
+ class YIntervalTimePeriodValuesCollection extends TimePeriodValuesCollection {
+ /** default serial UID */
+ private static final long serialVersionUID = 1L;
+
+ private double yheight;
+
+ /**
+ * Constructs a collection of bars with a fixed Y height.
+ *
+ * @param yheight The height of the bars.
+ */
+ YIntervalTimePeriodValuesCollection(double yheight) {
+ this.yheight = yheight;
+ }
+
+ /**
+ * Returns ending Y value that is a fixed amount greater than the starting value.
+ *
+ * @param series the series (zero-based index).
+ * @param item the item (zero-based index).
+ * @return The ending Y value for the specified series and item.
+ */
+ @Override
+ public Number getEndY(int series, int item) {
+ return getY(series, item).doubleValue() + yheight;
+ }
+ }
+
+ /**
+ * Constructs a graph of network and database stats.
+ *
+ * @param name The name of this graph in the graph list.
+ */
+ public DisplaySyncPerf(String name) {
+ super(name);
+ }
+
+ /**
+ * Creates the UI for the event display.
+ *
+ * @param parent the parent composite.
+ * @param logParser the current log parser.
+ * @return the created control (which may have children).
+ */
+ @Override
+ public Control createComposite(final Composite parent, EventLogParser logParser,
+ final ILogColumnListener listener) {
+ Control composite = createCompositeChart(parent, logParser, "Sync Performance");
+ resetUI();
+ return composite;
+ }
+
+ /**
+ * Resets the display.
+ */
+ @Override
+ void resetUI() {
+ super.resetUI();
+ XYPlot xyPlot = mChart.getXYPlot();
+ xyPlot.getRangeAxis().setVisible(false);
+ mTooltipGenerator = new CustomXYToolTipGenerator();
+ mTooltips = new List[NUM_SERIES];
+
+ XYBarRenderer br = new XYBarRenderer();
+ br.setUseYInterval(true);
+ mDatasets = new TimePeriodValues[NUM_SERIES];
+
+ TimePeriodValuesCollection tpvc = new YIntervalTimePeriodValuesCollection(1);
+ xyPlot.setDataset(tpvc);
+ xyPlot.setRenderer(br);
+
+ for (int i = 0; i < NUM_SERIES; i++) {
+ br.setSeriesPaint(i, SERIES_COLORS[i]);
+ mDatasets[i] = new TimePeriodValues(SERIES_NAMES[i]);
+ tpvc.addSeries(mDatasets[i]);
+ mTooltips[i] = new ArrayList();
+ mTooltipGenerator.addToolTipSeries(mTooltips[i]);
+ br.setSeriesToolTipGenerator(i, mTooltipGenerator);
+ }
+ }
+
+ /**
+ * Updates the display with a new event.
+ *
+ * @param event The event
+ * @param logParser The parser providing the event.
+ */
+ @Override
+ void newEvent(EventContainer event, EventLogParser logParser) {
+ super.newEvent(event, logParser); // Handle sync operation
+ try {
+ if (event.mTag == EVENT_DB_OPERATION) {
+ // 52000 db_operation (name|3),(op_type|1|5),(time|2|3)
+ String tip = event.getValueAsString(0);
+ long endTime = (long) event.sec * 1000L + (event.nsec / 1000000L);
+ int opType = Integer.parseInt(event.getValueAsString(1));
+ long duration = Long.parseLong(event.getValueAsString(2));
+
+ if (opType == EVENT_DB_QUERY) {
+ mDatasets[DB_QUERY].add(new SimpleTimePeriod(endTime - duration, endTime),
+ SERIES_YCOORD[DB_QUERY]);
+ mTooltips[DB_QUERY].add(tip);
+ } else if (opType == EVENT_DB_WRITE) {
+ mDatasets[DB_WRITE].add(new SimpleTimePeriod(endTime - duration, endTime),
+ SERIES_YCOORD[DB_WRITE]);
+ mTooltips[DB_WRITE].add(tip);
+ }
+ } else if (event.mTag == EVENT_HTTP_STATS) {
+ // 52001 http_stats (useragent|3),(response|2|3),(processing|2|3),(tx|1|2),(rx|1|2)
+ String tip = event.getValueAsString(0) + ", tx:" + event.getValueAsString(3) +
+ ", rx: " + event.getValueAsString(4);
+ long endTime = (long) event.sec * 1000L + (event.nsec / 1000000L);
+ long netEndTime = endTime - Long.parseLong(event.getValueAsString(2));
+ long netStartTime = netEndTime - Long.parseLong(event.getValueAsString(1));
+ mDatasets[HTTP_NETWORK].add(new SimpleTimePeriod(netStartTime, netEndTime),
+ SERIES_YCOORD[HTTP_NETWORK]);
+ mDatasets[HTTP_PROCESSING].add(new SimpleTimePeriod(netEndTime, endTime),
+ SERIES_YCOORD[HTTP_PROCESSING]);
+ mTooltips[HTTP_NETWORK].add(tip);
+ mTooltips[HTTP_PROCESSING].add(tip);
+ }
+ } catch (InvalidTypeException e) {
+ }
+ }
+
+ /**
+ * Callback from super.newEvent to process a sync event.
+ *
+ * @param event The sync event
+ * @param startTime Start time (ms) of events
+ * @param stopTime Stop time (ms) of events
+ * @param details Details associated with the event.
+ * @param newEvent True if this event is a new sync event. False if this event
+ * @param syncSource
+ */
+ @Override
+ void processSyncEvent(EventContainer event, int auth, long startTime, long stopTime,
+ String details, boolean newEvent, int syncSource) {
+ if (newEvent) {
+ mDatasets[auth].add(new SimpleTimePeriod(startTime, stopTime), SERIES_YCOORD[auth]);
+ }
+ }
+
+ /**
+ * Gets display type
+ *
+ * @return display type as an integer
+ */
+ @Override
+ int getDisplayType() {
+ return DISPLAY_TYPE_SYNC_PERF;
+ }
+}
diff --git a/tools/ddms/libs/ddmuilib/src/com/android/ddmuilib/log/event/EventDisplay.java b/tools/ddms/libs/ddmuilib/src/com/android/ddmuilib/log/event/EventDisplay.java
index e36192c08..2223a4d70 100644
--- a/tools/ddms/libs/ddmuilib/src/com/android/ddmuilib/log/event/EventDisplay.java
+++ b/tools/ddms/libs/ddmuilib/src/com/android/ddmuilib/log/event/EventDisplay.java
@@ -21,86 +21,47 @@ import com.android.ddmlib.log.EventContainer;
import com.android.ddmlib.log.EventContainer.CompareMethod;
import com.android.ddmlib.log.EventContainer.EventValueType;
import com.android.ddmlib.log.EventLogParser;
-import com.android.ddmlib.log.EventValueDescription;
import com.android.ddmlib.log.EventValueDescription.ValueType;
import com.android.ddmlib.log.InvalidTypeException;
-import com.android.ddmuilib.DdmUiPreferences;
-import com.android.ddmuilib.TableHelper;
-
-import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.swt.SWT;
-import org.eclipse.swt.events.ControlAdapter;
-import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.FontData;
-import org.eclipse.swt.layout.GridData;
-import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
-import org.eclipse.swt.widgets.Label;
-import org.eclipse.swt.widgets.ScrollBar;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
-import org.eclipse.swt.widgets.TableItem;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.JFreeChart;
-import org.jfree.chart.axis.AxisLocation;
-import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.event.ChartChangeEvent;
import org.jfree.chart.event.ChartChangeEventType;
import org.jfree.chart.event.ChartChangeListener;
-import org.jfree.chart.labels.CustomXYToolTipGenerator;
import org.jfree.chart.plot.XYPlot;
-import org.jfree.chart.renderer.xy.AbstractXYItemRenderer;
-import org.jfree.chart.renderer.xy.XYAreaRenderer;
-import org.jfree.chart.renderer.xy.XYBarRenderer;
-import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.chart.title.TextTitle;
-import org.jfree.data.time.FixedMillisecond;
import org.jfree.data.time.Millisecond;
-import org.jfree.data.time.RegularTimePeriod;
-import org.jfree.data.time.SimpleTimePeriod;
-import org.jfree.data.time.TimePeriodValues;
-import org.jfree.data.time.TimePeriodValuesCollection;
import org.jfree.data.time.TimeSeries;
import org.jfree.data.time.TimeSeriesCollection;
import org.jfree.experimental.chart.swt.ChartComposite;
import org.jfree.experimental.swt.SWTUtils;
-import org.jfree.util.ShapeUtilities;
-import java.awt.Color;
import java.security.InvalidParameterException;
import java.util.ArrayList;
-import java.util.Calendar;
-import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Scanner;
import java.util.Set;
-import java.util.TimeZone;
import java.util.regex.Pattern;
/**
* Represents a custom display of one or more events.
*/
-final class EventDisplay {
-
+abstract class EventDisplay {
+
private final static String DISPLAY_DATA_STORAGE_SEPARATOR = ":"; //$NON-NLS-1$
private final static String PID_STORAGE_SEPARATOR = ","; //$NON-NLS-1$
private final static String DESCRIPTOR_STORAGE_SEPARATOR = "$"; //$NON-NLS-1$
private final static String DESCRIPTOR_DATA_STORAGE_SEPARATOR = "!"; //$NON-NLS-1$
-
- private final static String PREFS_COL_DATE = "EventLogPanel.log.Col1"; //$NON-NLS-1$
- private final static String PREFS_COL_PID = "EventLogPanel.log.Col2"; //$NON-NLS-1$
- private final static String PREFS_COL_EVENTTAG = "EventLogPanel.log.Col3"; //$NON-NLS-1$
- private final static String PREFS_COL_VALUENAME = "EventLogPanel.log.Col4"; //$NON-NLS-1$
- private final static String PREFS_COL_VALUE = "EventLogPanel.log.Col5"; //$NON-NLS-1$
- private final static String PREFS_COL_TYPE = "EventLogPanel.log.Col6"; //$NON-NLS-1$
private final static String FILTER_VALUE_NULL = ""; //$NON-NLS-1$
@@ -109,20 +70,76 @@ final class EventDisplay {
public final static int DISPLAY_TYPE_GRAPH = 2;
public final static int DISPLAY_TYPE_SYNC = 3;
public final static int DISPLAY_TYPE_SYNC_HIST = 4;
+ public final static int DISPLAY_TYPE_SYNC_PERF = 5;
private final static int EVENT_CHECK_FAILED = 0;
- private final static int EVENT_CHECK_SAME_TAG = 1;
- private final static int EVENT_CHECK_SAME_VALUE = 2;
-
+ protected final static int EVENT_CHECK_SAME_TAG = 1;
+ protected final static int EVENT_CHECK_SAME_VALUE = 2;
+
+ /**
+ * Creates the appropriate EventDisplay subclass.
+ *
+ * @param type the type of display (DISPLAY_TYPE_LOG_ALL, etc)
+ * @param name the name of the display
+ * @return the created object
+ */
+ public static EventDisplay eventDisplayFactory(int type, String name) {
+ switch (type) {
+ case DISPLAY_TYPE_LOG_ALL:
+ return new DisplayLog(name);
+ case DISPLAY_TYPE_FILTERED_LOG:
+ return new DisplayFilteredLog(name);
+ case DISPLAY_TYPE_SYNC:
+ return new DisplaySync(name);
+ case DISPLAY_TYPE_SYNC_HIST:
+ return new DisplaySyncHistogram(name);
+ case DISPLAY_TYPE_GRAPH:
+ return new DisplayGraph(name);
+ case DISPLAY_TYPE_SYNC_PERF:
+ return new DisplaySyncPerf(name);
+ default:
+ throw new InvalidParameterException("Unknown Display Type " + type); //$NON-NLS-1$
+ }
+ }
+
+ /**
+ * Adds event to the display.
+ * @param event The event
+ * @param logParser The log parser.
+ */
+ abstract void newEvent(EventContainer event, EventLogParser logParser);
+
+ /**
+ * Resets the display.
+ */
+ abstract void resetUI();
+
+ /**
+ * Gets display type
+ *
+ * @return display type as an integer
+ */
+ abstract int getDisplayType();
+
+ /**
+ * Creates the UI for the event display.
+ *
+ * @param parent the parent composite.
+ * @param logParser the current log parser.
+ * @return the created control (which may have children).
+ */
+ abstract Control createComposite(final Composite parent, EventLogParser logParser,
+ final ILogColumnListener listener);
+
interface ILogColumnListener {
void columnResized(int index, TableColumn sourceColumn);
}
/**
- * Describes an event to be displayed.
+ * Describes an event to be displayed.
*/
static class OccurrenceDisplayDescriptor {
-
+
int eventTag = -1;
int seriesValueIndex = -1;
boolean includePid = false;
@@ -158,17 +175,19 @@ final class EventDisplay {
/**
* Loads the descriptor parameter from a storage string. The storage string must have
* been generated with {@link #getStorageString()}.
+ *
* @param storageString the storage string
*/
final void loadFrom(String storageString) {
String[] values = storageString.split(Pattern.quote(DESCRIPTOR_DATA_STORAGE_SEPARATOR));
loadFrom(values, 0);
}
-
+
/**
* Loads the parameters from an array of strings.
+ *
* @param storageStrings the strings representing each parameter.
- * @param index the starting index in the array of strings.
+ * @param index the starting index in the array of strings.
* @return the new index in the array.
*/
protected int loadFrom(String[] storageStrings, int index) {
@@ -221,7 +240,7 @@ final class EventDisplay {
}
/**
- * Describes an event value to be displayed.
+ * Describes an event value to be displayed.
*/
static final class ValueDisplayDescriptor extends OccurrenceDisplayDescriptor {
String valueName;
@@ -253,7 +272,7 @@ final class EventDisplay {
void replaceWith(OccurrenceDisplayDescriptor descriptor) {
super.replaceWith(descriptor);
if (descriptor instanceof ValueDisplayDescriptor) {
- ValueDisplayDescriptor valueDescriptor = (ValueDisplayDescriptor)descriptor;
+ ValueDisplayDescriptor valueDescriptor = (ValueDisplayDescriptor) descriptor;
valueName = valueDescriptor.valueName;
valueIndex = valueDescriptor.valueIndex;
}
@@ -261,8 +280,9 @@ final class EventDisplay {
/**
* Loads the parameters from an array of strings.
+ *
* @param storageStrings the strings representing each parameter.
- * @param index the starting index in the array of strings.
+ * @param index the starting index in the array of strings.
* @return the new index in the array.
*/
@Override
@@ -294,100 +314,78 @@ final class EventDisplay {
/* ==================
* Event Display parameters.
* ================== */
- private String mName;
-
- private int mDisplayType = DISPLAY_TYPE_GRAPH;
+ protected String mName;
+
private boolean mPidFiltering = false;
private ArrayList mPidFilterList = null;
-
- private final ArrayList mValueDescriptors =
- new ArrayList();
+
+ protected final ArrayList mValueDescriptors =
+ new ArrayList();
private final ArrayList mOccurrenceDescriptors =
- new ArrayList();
+ new ArrayList();
/* ==================
* Event Display members for display purpose.
* ================== */
// chart objects
- /** This is a map of (descriptor, map2) where map2 is a map of (pid, chart-series) */
- private final HashMap> mValueDescriptorSeriesMap =
- new HashMap>();
- /** This is a map of (descriptor, map2) where map2 is a map of (pid, chart-series) */
- private final HashMap> mOcurrenceDescriptorSeriesMap =
- new HashMap>();
+ /**
+ * This is a map of (descriptor, map2) where map2 is a map of (pid, chart-series)
+ */
+ protected final HashMap> mValueDescriptorSeriesMap =
+ new HashMap>();
+ /**
+ * This is a map of (descriptor, map2) where map2 is a map of (pid, chart-series)
+ */
+ protected final HashMap> mOcurrenceDescriptorSeriesMap =
+ new HashMap>();
- /** This is a map of (ValueType, dataset) */
- private final HashMap mValueTypeDataSetMap =
- new HashMap();
+ /**
+ * This is a map of (ValueType, dataset)
+ */
+ protected final HashMap mValueTypeDataSetMap =
+ new HashMap();
- private JFreeChart mChart;
- private TimeSeriesCollection mOccurrenceDataSet;
- private int mDataSetCount;
+ protected JFreeChart mChart;
+ protected TimeSeriesCollection mOccurrenceDataSet;
+ protected int mDataSetCount;
private ChartComposite mChartComposite;
- private long mMaximumChartItemAge = -1;
- private long mHistWidth = 1;
+ protected long mMaximumChartItemAge = -1;
+ protected long mHistWidth = 1;
// log objects.
- private Table mLogTable;
+ protected Table mLogTable;
/* ==================
* Misc data.
* ================== */
- private int mValueDescriptorCheck = EVENT_CHECK_FAILED;
-
- /**
- * Loads a new {@link EventDisplay} from a storage string. The string must have been created
- * with {@link #getStorageString()}.
- * @param storageString the storage string
- * @return a new {@link EventDisplay} or null if the load failed.
- */
- static EventDisplay load(String storageString) {
- EventDisplay ed = new EventDisplay();
- if (ed.loadFrom(storageString)) {
- return ed;
- }
-
- return null;
- }
+ protected int mValueDescriptorCheck = EVENT_CHECK_FAILED;
EventDisplay(String name) {
mName = name;
}
- /**
- * Builds an {@link EventDisplay}.
- * @param name the name of the display
- * @param displayType the display type: {@link #DISPLAY_TYPE_GRAPH} or
- * {@value #DISPLAY_TYPE_FILTERED_LOG}.
- * @param filterByPid the flag indicating whether to filter by pid.
- */
- EventDisplay(String name, int displayType, boolean filterByPid) {
- mName = name;
- mDisplayType = displayType;
- mPidFiltering = filterByPid;
- }
-
- EventDisplay(EventDisplay from) {
- mName = from.mName;
- mDisplayType = from.mDisplayType;
- mPidFiltering = from.mPidFiltering;
- mMaximumChartItemAge = from.mMaximumChartItemAge;
- mHistWidth = from.mHistWidth;
+ static EventDisplay clone(EventDisplay from) {
+ EventDisplay ed = eventDisplayFactory(from.getDisplayType(), from.getName());
+ ed.mName = from.mName;
+ ed.mPidFiltering = from.mPidFiltering;
+ ed.mMaximumChartItemAge = from.mMaximumChartItemAge;
+ ed.mHistWidth = from.mHistWidth;
if (from.mPidFilterList != null) {
- mPidFilterList = new ArrayList();
- mPidFilterList.addAll(from.mPidFilterList);
+ ed.mPidFilterList = new ArrayList();
+ ed.mPidFilterList.addAll(from.mPidFilterList);
}
for (ValueDisplayDescriptor desc : from.mValueDescriptors) {
- mValueDescriptors.add(new ValueDisplayDescriptor(desc));
+ ed.mValueDescriptors.add(new ValueDisplayDescriptor(desc));
}
- mValueDescriptorCheck = from.mValueDescriptorCheck;
+ ed.mValueDescriptorCheck = from.mValueDescriptorCheck;
for (OccurrenceDisplayDescriptor desc : from.mOccurrenceDescriptors) {
- mOccurrenceDescriptors.add(new OccurrenceDisplayDescriptor(desc));
+ ed.mOccurrenceDescriptors.add(new OccurrenceDisplayDescriptor(desc));
}
+ return ed;
}
/**
@@ -398,7 +396,7 @@ final class EventDisplay {
sb.append(mName);
sb.append(DISPLAY_DATA_STORAGE_SEPARATOR);
- sb.append(mDisplayType);
+ sb.append(getDisplayType());
sb.append(DISPLAY_DATA_STORAGE_SEPARATOR);
sb.append(Boolean.toString(mPidFiltering));
sb.append(DISPLAY_DATA_STORAGE_SEPARATOR);
@@ -419,35 +417,27 @@ final class EventDisplay {
void setName(String name) {
mName = name;
}
-
+
String getName() {
return mName;
}
-
- void setDisplayType(int value) {
- mDisplayType = value;
- }
-
- int getDisplayType() {
- return mDisplayType;
- }
-
+
void setPidFiltering(boolean filterByPid) {
mPidFiltering = filterByPid;
}
-
+
boolean getPidFiltering() {
return mPidFiltering;
}
-
+
void setPidFilterList(ArrayList pids) {
if (mPidFiltering == false) {
new InvalidParameterException();
}
-
+
mPidFilterList = pids;
}
-
+
ArrayList getPidFilterList() {
return mPidFilterList;
}
@@ -456,11 +446,11 @@ final class EventDisplay {
if (mPidFiltering == false) {
new InvalidParameterException();
}
-
+
if (mPidFilterList == null) {
mPidFilterList = new ArrayList();
}
-
+
mPidFilterList.add(pid);
}
@@ -485,27 +475,29 @@ final class EventDisplay {
Iterator getOccurrenceDescriptors() {
return mOccurrenceDescriptors.iterator();
}
-
+
/**
- * Adds a descriptor. This can be a {@link OccurrenceDisplayDescriptor} or a
+ * Adds a descriptor. This can be a {@link OccurrenceDisplayDescriptor} or a
* {@link ValueDisplayDescriptor}.
+ *
* @param descriptor the descriptor to be added.
*/
void addDescriptor(OccurrenceDisplayDescriptor descriptor) {
if (descriptor instanceof ValueDisplayDescriptor) {
- mValueDescriptors.add((ValueDisplayDescriptor)descriptor);
+ mValueDescriptors.add((ValueDisplayDescriptor) descriptor);
mValueDescriptorCheck = checkDescriptors();
} else {
mOccurrenceDescriptors.add(descriptor);
}
}
-
+
/**
* Returns a descriptor by index and class (extending {@link OccurrenceDisplayDescriptor}).
+ *
* @param descriptorClass the class of the descriptor to return.
- * @param index the index of the descriptor to return.
+ * @param index the index of the descriptor to return.
* @return either a {@link OccurrenceDisplayDescriptor} or a {@link ValueDisplayDescriptor}
- * or null if descriptorClass is another class.
+ * or null if descriptorClass is another class.
*/
OccurrenceDisplayDescriptor getDescriptor(
Class extends OccurrenceDisplayDescriptor> descriptorClass, int index) {
@@ -515,14 +507,15 @@ final class EventDisplay {
} else if (descriptorClass == ValueDisplayDescriptor.class) {
return mValueDescriptors.get(index);
}
-
+
return null;
}
-
+
/**
* Removes a descriptor based on its class and index.
+ *
* @param descriptorClass the class of the descriptor.
- * @param index the index of the descriptor to be removed.
+ * @param index the index of the descriptor to be removed.
*/
void removeDescriptor(Class extends OccurrenceDisplayDescriptor> descriptorClass, int index) {
if (descriptorClass == OccurrenceDisplayDescriptor.class) {
@@ -533,137 +526,107 @@ final class EventDisplay {
}
}
- /**
- * Creates the UI for the event display.
- * @param parent the parent composite.
- * @param logParser the current log parser.
- * @return the created control (which may have children).
- */
- Control createComposite(final Composite parent, EventLogParser logParser,
- final ILogColumnListener listener) {
- switch (mDisplayType) {
- case DISPLAY_TYPE_LOG_ALL:
- // intended fall-through
- case DISPLAY_TYPE_FILTERED_LOG:
- return createLogUI(parent, listener);
- case DISPLAY_TYPE_GRAPH:
- // intended fall-through
- case DISPLAY_TYPE_SYNC:
- // intended fall-through
- case DISPLAY_TYPE_SYNC_HIST:
- String title = getChartTitle(logParser);
- mChart = ChartFactory.createTimeSeriesChart(
- null,
- null /* timeAxisLabel */,
- null /* valueAxisLabel */,
- null, /* dataset. set below */
- true /* legend */,
- false /* tooltips */,
- false /* urls */);
-
- // get the font to make a proper title. We need to convert the swt font,
- // into an awt font.
- Font f = parent.getFont();
- FontData[] fData = f.getFontData();
-
- // event though on Mac OS there could be more than one fontData, we'll only use
- // the first one.
- FontData firstFontData = fData[0];
+ Control createCompositeChart(final Composite parent, EventLogParser logParser,
+ String title) {
+ mChart = ChartFactory.createTimeSeriesChart(
+ null,
+ null /* timeAxisLabel */,
+ null /* valueAxisLabel */,
+ null, /* dataset. set below */
+ true /* legend */,
+ false /* tooltips */,
+ false /* urls */);
- java.awt.Font awtFont = SWTUtils.toAwtFont(parent.getDisplay(),
- firstFontData, true /* ensureSameSize */);
+ // get the font to make a proper title. We need to convert the swt font,
+ // into an awt font.
+ Font f = parent.getFont();
+ FontData[] fData = f.getFontData();
- if (mDisplayType == DISPLAY_TYPE_SYNC) {
- title = "Sync Status";
- } else if (mDisplayType == DISPLAY_TYPE_SYNC_HIST) {
- title = "Sync Histogram";
- }
+ // event though on Mac OS there could be more than one fontData, we'll only use
+ // the first one.
+ FontData firstFontData = fData[0];
- mChart.setTitle(new TextTitle(title, awtFont));
-
- final XYPlot xyPlot = mChart.getXYPlot();
- xyPlot.setRangeCrosshairVisible(true);
- xyPlot.setRangeCrosshairLockedOnData(true);
- xyPlot.setDomainCrosshairVisible(true);
- xyPlot.setDomainCrosshairLockedOnData(true);
+ java.awt.Font awtFont = SWTUtils.toAwtFont(parent.getDisplay(),
+ firstFontData, true /* ensureSameSize */);
- mChart.addChangeListener(new ChartChangeListener() {
- public void chartChanged(ChartChangeEvent event) {
- ChartChangeEventType type = event.getType();
- if (type == ChartChangeEventType.GENERAL) {
- // because the value we need (rangeCrosshair and domainCrosshair) are
- // updated on the draw, but the notification happens before the draw,
- // we process the click in a future runnable!
- parent.getDisplay().asyncExec(new Runnable() {
- public void run() {
- processClick(xyPlot);
- }
- });
+
+ mChart.setTitle(new TextTitle(title, awtFont));
+
+ final XYPlot xyPlot = mChart.getXYPlot();
+ xyPlot.setRangeCrosshairVisible(true);
+ xyPlot.setRangeCrosshairLockedOnData(true);
+ xyPlot.setDomainCrosshairVisible(true);
+ xyPlot.setDomainCrosshairLockedOnData(true);
+
+ mChart.addChangeListener(new ChartChangeListener() {
+ public void chartChanged(ChartChangeEvent event) {
+ ChartChangeEventType type = event.getType();
+ if (type == ChartChangeEventType.GENERAL) {
+ // because the value we need (rangeCrosshair and domainCrosshair) are
+ // updated on the draw, but the notification happens before the draw,
+ // we process the click in a future runnable!
+ parent.getDisplay().asyncExec(new Runnable() {
+ public void run() {
+ processClick(xyPlot);
}
- }
- });
-
- mChartComposite = new ChartComposite(parent, SWT.BORDER, mChart,
- ChartComposite.DEFAULT_WIDTH,
- ChartComposite.DEFAULT_HEIGHT,
- ChartComposite.DEFAULT_MINIMUM_DRAW_WIDTH,
- ChartComposite.DEFAULT_MINIMUM_DRAW_HEIGHT,
- 3000, // max draw width. We don't want it to zoom, so we put a big number
- 3000, // max draw height. We don't want it to zoom, so we put a big number
- true, // off-screen buffer
- true, // properties
- true, // save
- true, // print
- true, // zoom
- true); // tooltips
-
- mChartComposite.addDisposeListener(new DisposeListener() {
- public void widgetDisposed(DisposeEvent e) {
- mValueTypeDataSetMap.clear();
- mDataSetCount = 0;
- mOccurrenceDataSet = null;
- mChart = null;
- mChartComposite = null;
- mValueDescriptorSeriesMap.clear();
- mOcurrenceDescriptorSeriesMap.clear();
- }
- });
-
- if (mDisplayType == DISPLAY_TYPE_SYNC) {
- initSyncDisplay();
- } else if (mDisplayType == DISPLAY_TYPE_SYNC_HIST) {
- initSyncHistDisplay();
+ });
}
+ }
+ });
+
+ mChartComposite = new ChartComposite(parent, SWT.BORDER, mChart,
+ ChartComposite.DEFAULT_WIDTH,
+ ChartComposite.DEFAULT_HEIGHT,
+ ChartComposite.DEFAULT_MINIMUM_DRAW_WIDTH,
+ ChartComposite.DEFAULT_MINIMUM_DRAW_HEIGHT,
+ 3000, // max draw width. We don't want it to zoom, so we put a big number
+ 3000, // max draw height. We don't want it to zoom, so we put a big number
+ true, // off-screen buffer
+ true, // properties
+ true, // save
+ true, // print
+ true, // zoom
+ true); // tooltips
+
+ mChartComposite.addDisposeListener(new DisposeListener() {
+ public void widgetDisposed(DisposeEvent e) {
+ mValueTypeDataSetMap.clear();
+ mDataSetCount = 0;
+ mOccurrenceDataSet = null;
+ mChart = null;
+ mChartComposite = null;
+ mValueDescriptorSeriesMap.clear();
+ mOcurrenceDescriptorSeriesMap.clear();
+ }
+ });
+
+ return mChartComposite;
- return mChartComposite;
- default:
- throw new InvalidParameterException("Unknown Display Type"); //$NON-NLS-1$
- }
}
private void processClick(XYPlot xyPlot) {
double rangeValue = xyPlot.getRangeCrosshairValue();
- if (rangeValue != 0) {
+ if (rangeValue != 0) {
double domainValue = xyPlot.getDomainCrosshairValue();
-
- Millisecond msec = new Millisecond(new Date((long)domainValue));
-
+
+ Millisecond msec = new Millisecond(new Date((long) domainValue));
+
// look for values in the dataset that contains data at this TimePeriod
Set descKeys = mValueDescriptorSeriesMap.keySet();
-
+
for (ValueDisplayDescriptor descKey : descKeys) {
HashMap map = mValueDescriptorSeriesMap.get(descKey);
-
+
Set pidKeys = map.keySet();
-
+
for (Integer pidKey : pidKeys) {
TimeSeries series = map.get(pidKey);
-
+
Number value = series.getValue(msec);
if (value != null) {
// found a match. lets check against the actual value.
if (value.doubleValue() == rangeValue) {
-
+
return;
}
}
@@ -672,176 +635,29 @@ final class EventDisplay {
}
}
- /**
- * Creates the UI for a log display.
- * @param parent the parent {@link Composite}
- * @param listener the {@link ILogColumnListener} to notify on column resize events.
- * @return the top Composite of the UI.
- */
- private Control createLogUI(Composite parent, final ILogColumnListener listener) {
- Composite mainComp = new Composite(parent, SWT.NONE);
- GridLayout gl;
- mainComp.setLayout(gl = new GridLayout(1, false));
- gl.marginHeight = gl.marginWidth = 0;
- mainComp.addDisposeListener(new DisposeListener() {
- public void widgetDisposed(DisposeEvent e) {
- mLogTable = null;
- }
- });
- Label l = new Label(mainComp, SWT.CENTER);
- l.setText(mName);
- l.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
-
- mLogTable = new Table(mainComp, SWT.MULTI | SWT.FULL_SELECTION | SWT.V_SCROLL |
- SWT.BORDER);
- mLogTable.setLayoutData(new GridData(GridData.FILL_BOTH));
-
- IPreferenceStore store = DdmUiPreferences.getStore();
-
- TableColumn col = TableHelper.createTableColumn(
- mLogTable, "Time",
- SWT.LEFT, "0000-00-00 00:00:00", PREFS_COL_DATE, store); //$NON-NLS-1$
- col.addControlListener(new ControlAdapter() {
- @Override
- public void controlResized(ControlEvent e) {
- Object source = e.getSource();
- if (source instanceof TableColumn) {
- listener.columnResized(0, (TableColumn)source);
- }
- }
- });
-
- col = TableHelper.createTableColumn(
- mLogTable, "pid",
- SWT.LEFT, "0000", PREFS_COL_PID, store); //$NON-NLS-1$
- col.addControlListener(new ControlAdapter() {
- @Override
- public void controlResized(ControlEvent e) {
- Object source = e.getSource();
- if (source instanceof TableColumn) {
- listener.columnResized(1, (TableColumn)source);
- }
- }
- });
-
- col = TableHelper.createTableColumn(
- mLogTable, "Event",
- SWT.LEFT, "abcdejghijklmno", PREFS_COL_EVENTTAG, store); //$NON-NLS-1$
- col.addControlListener(new ControlAdapter() {
- @Override
- public void controlResized(ControlEvent e) {
- Object source = e.getSource();
- if (source instanceof TableColumn) {
- listener.columnResized(2, (TableColumn)source);
- }
- }
- });
-
- col = TableHelper.createTableColumn(
- mLogTable, "Name",
- SWT.LEFT, "Process Name", PREFS_COL_VALUENAME, store); //$NON-NLS-1$
- col.addControlListener(new ControlAdapter() {
- @Override
- public void controlResized(ControlEvent e) {
- Object source = e.getSource();
- if (source instanceof TableColumn) {
- listener.columnResized(3, (TableColumn)source);
- }
- }
- });
-
- col = TableHelper.createTableColumn(
- mLogTable, "Value",
- SWT.LEFT, "0000000", PREFS_COL_VALUE, store); //$NON-NLS-1$
- col.addControlListener(new ControlAdapter() {
- @Override
- public void controlResized(ControlEvent e) {
- Object source = e.getSource();
- if (source instanceof TableColumn) {
- listener.columnResized(4, (TableColumn)source);
- }
- }
- });
-
- col = TableHelper.createTableColumn(
- mLogTable, "Type",
- SWT.LEFT, "long, seconds", PREFS_COL_TYPE, store); //$NON-NLS-1$
- col.addControlListener(new ControlAdapter() {
- @Override
- public void controlResized(ControlEvent e) {
- Object source = e.getSource();
- if (source instanceof TableColumn) {
- listener.columnResized(5, (TableColumn)source);
- }
- }
- });
-
- mLogTable.setHeaderVisible(true);
- mLogTable.setLinesVisible(true);
-
- return mainComp;
- }
-
/**
* Resizes the index-th column of the log {@link Table} (if applicable).
+ * Subclasses can override if necessary.
*
* This does nothing if the Table object is null (because the display
* type does not use a column) or if the index-th column is in fact the originating
* column passed as argument.
- * @param index the index of the column to resize
+ *
+ * @param index the index of the column to resize
* @param sourceColumn the original column that was resize, and on which we need to sync the
- * index-th column width.
+ * index-th column width.
*/
void resizeColumn(int index, TableColumn sourceColumn) {
- if (mLogTable != null) {
- TableColumn col = mLogTable.getColumn(index);
- if (col != sourceColumn) {
- col.setWidth(sourceColumn.getWidth());
- }
- }
}
-
+
/**
* Sets the current {@link EventLogParser} object.
+ * Subclasses can override if necessary.
*/
- void setNewLogParser(EventLogParser logParser) {
- if (mDisplayType == DISPLAY_TYPE_GRAPH) {
- if (mChart != null) {
- mChart.setTitle(getChartTitle(logParser));
- }
- }
+ protected void setNewLogParser(EventLogParser logParser) {
}
-
- void resetUI() {
- switch (mDisplayType) {
- case DISPLAY_TYPE_LOG_ALL:
- // intended fall-through
- case DISPLAY_TYPE_FILTERED_LOG:
- mLogTable.removeAll();
- break;
- case DISPLAY_TYPE_SYNC:
- initSyncDisplay();
- break;
- case DISPLAY_TYPE_SYNC_HIST:
- initSyncHistDisplay();
- break;
- case DISPLAY_TYPE_GRAPH:
- Collection datasets = mValueTypeDataSetMap.values();
- for (TimeSeriesCollection dataset : datasets) {
- dataset.removeAllSeries();
- }
- if (mOccurrenceDataSet != null) {
- mOccurrenceDataSet.removeAllSeries();
- }
- mValueDescriptorSeriesMap.clear();
- mOcurrenceDescriptorSeriesMap.clear();
- break;
- default:
- throw new InvalidParameterException("Unknown Display Type"); //$NON-NLS-1$
- }
- }
-
+
/**
* Prepares the {@link EventDisplay} for a multi event display.
*/
@@ -860,103 +676,71 @@ final class EventDisplay {
}
}
- /**
- * Processes a new event. This must be called from the ui thread.
- * @param event the event to process.
- */
- void newEvent(EventContainer event, EventLogParser logParser) {
- ArrayList valueDescriptors =
- new ArrayList();
-
- ArrayList occurrenceDescriptors =
- new ArrayList();
-
- if (filterEvent(event, valueDescriptors, occurrenceDescriptors)) {
- switch (mDisplayType) {
- case DISPLAY_TYPE_LOG_ALL:
- addToLog(event, logParser);
- break;
- case DISPLAY_TYPE_FILTERED_LOG:
- addToLog(event, logParser, valueDescriptors, occurrenceDescriptors);
- break;
- case DISPLAY_TYPE_SYNC:
- updateSyncDisplay(event);
- break;
- case DISPLAY_TYPE_SYNC_HIST:
- updateSyncHistDisplay(event);
- break;
- case DISPLAY_TYPE_GRAPH:
- updateChart(event, logParser, valueDescriptors, occurrenceDescriptors);
- break;
- default:
- throw new InvalidParameterException("Unknown Display Type"); //$NON-NLS-1$
- }
- }
- }
-
/**
* Returns the {@link Table} object used to display events, if any.
+ *
* @return a Table object or null.
*/
Table getTable() {
return mLogTable;
}
- /** Private constructor used for loading from storage */
- private EventDisplay() {
- // nothing to be done here.
- }
-
/**
- * Loads the {@link EventDisplay} parameters from the storage string.
- * @param storageString the string containing the parameters.
+ * Loads a new {@link EventDisplay} from a storage string. The string must have been created
+ * with {@link #getStorageString()}.
+ *
+ * @param storageString the storage string
+ * @return a new {@link EventDisplay} or null if the load failed.
*/
- private boolean loadFrom(String storageString) {
+ static EventDisplay load(String storageString) {
if (storageString.length() > 0) {
// the storage string is separated by ':'
String[] values = storageString.split(Pattern.quote(DISPLAY_DATA_STORAGE_SEPARATOR));
-
+
try {
int index = 0;
-
- mName = values[index++];
- mDisplayType = Integer.parseInt(values[index++]);
- mPidFiltering = Boolean.parseBoolean(values[index++]);
+
+ String name = values[index++];
+ int displayType = Integer.parseInt(values[index++]);
+ boolean pidFiltering = Boolean.parseBoolean(values[index++]);
+
+ EventDisplay ed = eventDisplayFactory(displayType, name);
+ ed.setPidFiltering(pidFiltering);
// because empty sections are removed by String.split(), we have to check
// the index for those.
if (index < values.length) {
- loadPidFilters(values[index++]);
- }
-
- if (index < values.length) {
- loadValueDescriptors(values[index++]);
- }
-
- if (index < values.length) {
- loadOccurrenceDescriptors(values[index++]);
- }
-
- updateValueDescriptorCheck();
-
- if (index < values.length) {
- mMaximumChartItemAge = Long.parseLong(values[index++]);
+ ed.loadPidFilters(values[index++]);
}
if (index < values.length) {
- mHistWidth = Long.parseLong(values[index++]);
+ ed.loadValueDescriptors(values[index++]);
}
-
- return true;
+
+ if (index < values.length) {
+ ed.loadOccurrenceDescriptors(values[index++]);
+ }
+
+ ed.updateValueDescriptorCheck();
+
+ if (index < values.length) {
+ ed.mMaximumChartItemAge = Long.parseLong(values[index++]);
+ }
+
+ if (index < values.length) {
+ ed.mHistWidth = Long.parseLong(values[index++]);
+ }
+
+ return ed;
} catch (RuntimeException re) {
- // we'll return false below.
+ // we'll return null below.
Log.e("ddms", re);
}
}
-
- return false;
+
+ return null;
}
-
+
private String getPidStorageString() {
if (mPidFilterList != null) {
StringBuilder sb = new StringBuilder();
@@ -969,17 +753,17 @@ final class EventDisplay {
}
sb.append(i);
}
-
+
return sb.toString();
}
return ""; //$NON-NLS-1$
}
-
+
private void loadPidFilters(String storageString) {
if (storageString.length() > 0) {
String[] values = storageString.split(Pattern.quote(PID_STORAGE_SEPARATOR));
-
+
for (String value : values) {
if (mPidFilterList == null) {
mPidFilterList = new ArrayList();
@@ -988,12 +772,12 @@ final class EventDisplay {
}
}
}
-
+
private String getDescriptorStorageString(
ArrayList extends OccurrenceDisplayDescriptor> descriptorList) {
StringBuilder sb = new StringBuilder();
boolean first = true;
-
+
for (OccurrenceDisplayDescriptor descriptor : descriptorList) {
if (first == false) {
sb.append(DESCRIPTOR_STORAGE_SEPARATOR);
@@ -1002,7 +786,7 @@ final class EventDisplay {
}
sb.append(descriptor.getStorageString());
}
-
+
return sb.toString();
}
@@ -1012,21 +796,21 @@ final class EventDisplay {
}
String[] values = storageString.split(Pattern.quote(DESCRIPTOR_STORAGE_SEPARATOR));
-
+
for (String value : values) {
OccurrenceDisplayDescriptor desc = new OccurrenceDisplayDescriptor();
desc.loadFrom(value);
mOccurrenceDescriptors.add(desc);
}
}
-
+
private void loadValueDescriptors(String storageString) {
if (storageString.length() == 0) {
return;
}
String[] values = storageString.split(Pattern.quote(DESCRIPTOR_STORAGE_SEPARATOR));
-
+
for (String value : values) {
ValueDisplayDescriptor desc = new ValueDisplayDescriptor();
desc.loadFrom(value);
@@ -1034,293 +818,13 @@ final class EventDisplay {
}
}
- /**
- * Returns the {@link TimeSeriesCollection} for the occurrence display. If the data set is not
- * yet created, it is first allocated and set up into the {@link JFreeChart} object.
- */
- private TimeSeriesCollection getOccurrenceDataSet() {
- if (mOccurrenceDataSet == null) {
- mOccurrenceDataSet = new TimeSeriesCollection();
-
- XYPlot xyPlot = mChart.getXYPlot();
- xyPlot.setDataset(mDataSetCount, mOccurrenceDataSet);
-
- OccurrenceRenderer renderer = new OccurrenceRenderer();
- renderer.setBaseShapesVisible(false);
- xyPlot.setRenderer(mDataSetCount, renderer);
-
- mDataSetCount++;
- }
-
- return mOccurrenceDataSet;
- }
-
- /**
- * Returns a {@link TimeSeriesCollection} for a specific {@link ValueType}.
- * If the data set is not yet created, it is first allocated and set up into the
- * {@link JFreeChart} object.
- * @param type the {@link ValueType} of the data set.
- * @param accumulateValues
- */
- private TimeSeriesCollection getValueDataset(ValueType type, boolean accumulateValues) {
- TimeSeriesCollection dataset = mValueTypeDataSetMap.get(type);
- if (dataset == null) {
- // create the data set and store it in the map
- dataset = new TimeSeriesCollection();
- mValueTypeDataSetMap.put(type, dataset);
-
- // create the renderer and configure it depending on the ValueType
- AbstractXYItemRenderer renderer;
- if (type == ValueType.PERCENT && accumulateValues) {
- renderer = new XYAreaRenderer();
- } else {
- XYLineAndShapeRenderer r = new XYLineAndShapeRenderer();
- r.setBaseShapesVisible(type != ValueType.PERCENT);
-
- renderer = r;
- }
-
- // set both the dataset and the renderer in the plot object.
- XYPlot xyPlot = mChart.getXYPlot();
- xyPlot.setDataset(mDataSetCount, dataset);
- xyPlot.setRenderer(mDataSetCount, renderer);
-
- // put a new axis label, and configure it.
- NumberAxis axis = new NumberAxis(type.toString());
-
- if (type == ValueType.PERCENT) {
- // force percent range to be (0,100) fixed.
- axis.setAutoRange(false);
- axis.setRange(0., 100.);
- }
-
- // for the index, we ignore the occurrence dataset
- int count = mDataSetCount;
- if (mOccurrenceDataSet != null) {
- count--;
- }
-
- xyPlot.setRangeAxis(count, axis);
- if ((count % 2) == 0) {
- xyPlot.setRangeAxisLocation(count, AxisLocation.BOTTOM_OR_LEFT);
- } else {
- xyPlot.setRangeAxisLocation(count, AxisLocation.TOP_OR_RIGHT);
- }
-
- // now we link the dataset and the axis
- xyPlot.mapDatasetToRangeAxis(mDataSetCount, count);
-
- mDataSetCount++;
- }
-
- return dataset;
- }
-
-
- /**
- * Updates the chart with the {@link EventContainer} by adding the values/occurrences defined
- * by the {@link ValueDisplayDescriptor} and {@link OccurrenceDisplayDescriptor} objects from
- * the two lists.
- * This method is only called when at least one of the descriptor list is non empty.
- * @param event
- * @param logParser
- * @param valueDescriptors
- * @param occurrenceDescriptors
- */
- private void updateChart(EventContainer event, EventLogParser logParser,
- ArrayList valueDescriptors,
- ArrayList occurrenceDescriptors) {
- Map tagMap = logParser.getTagMap();
-
- Millisecond millisecondTime = null;
- long msec = -1;
-
- // If the event container is a cpu container (tag == 2721), and there is no descriptor
- // for the total CPU load, then we do accumulate all the values.
- boolean accumulateValues = false;
- double accumulatedValue = 0;
-
- if (event.mTag == 2721) {
- accumulateValues = true;
- for (ValueDisplayDescriptor descriptor : valueDescriptors) {
- accumulateValues &= (descriptor.valueIndex != 0);
- }
- }
-
- for (ValueDisplayDescriptor descriptor : valueDescriptors) {
- try {
- // get the hashmap for this descriptor
- HashMap map = mValueDescriptorSeriesMap.get(descriptor);
-
- // if it's not there yet, we create it.
- if (map == null) {
- map = new HashMap();
- mValueDescriptorSeriesMap.put(descriptor, map);
- }
-
- // get the TimeSeries for this pid
- TimeSeries timeSeries = map.get(event.pid);
-
- // if it doesn't exist yet, we create it
- if (timeSeries == null) {
- // get the series name
- String seriesFullName = null;
- String seriesLabel = getSeriesLabel(event, descriptor);
-
- switch (mValueDescriptorCheck) {
- case EVENT_CHECK_SAME_TAG:
- seriesFullName = String.format("%1$s / %2$s", seriesLabel,
- descriptor.valueName);
- break;
- case EVENT_CHECK_SAME_VALUE:
- seriesFullName = String.format("%1$s", seriesLabel);
- break;
- default:
- seriesFullName = String.format("%1$s / %2$s: %3$s", seriesLabel,
- tagMap.get(descriptor.eventTag),
- descriptor.valueName);
- break;
- }
-
- // get the data set for this ValueType
- TimeSeriesCollection dataset = getValueDataset(
- logParser.getEventInfoMap().get(event.mTag)[descriptor.valueIndex]
- .getValueType(),
- accumulateValues);
-
- // create the series
- timeSeries = new TimeSeries(seriesFullName, Millisecond.class);
- if (mMaximumChartItemAge != -1) {
- timeSeries.setMaximumItemAge(mMaximumChartItemAge * 1000);
- }
-
- dataset.addSeries(timeSeries);
-
- // add it to the map.
- map.put(event.pid, timeSeries);
- }
-
- // update the timeSeries.
-
- // get the value from the event
- double value = event.getValueAsDouble(descriptor.valueIndex);
-
- // accumulate the values if needed.
- if (accumulateValues) {
- accumulatedValue += value;
- value = accumulatedValue;
- }
-
- // get the time
- if (millisecondTime == null) {
- msec = (long)event.sec * 1000L + (event.nsec / 1000000L);
- millisecondTime = new Millisecond(new Date(msec));
- }
-
- // add the value to the time series
- timeSeries.addOrUpdate(millisecondTime, value);
- } catch (InvalidTypeException e) {
- // just ignore this descriptor if there's a type mismatch
- }
- }
-
- for (OccurrenceDisplayDescriptor descriptor : occurrenceDescriptors) {
- try {
- // get the hashmap for this descriptor
- HashMap map = mOcurrenceDescriptorSeriesMap.get(descriptor);
-
- // if it's not there yet, we create it.
- if (map == null) {
- map = new HashMap();
- mOcurrenceDescriptorSeriesMap.put(descriptor, map);
- }
-
- // get the TimeSeries for this pid
- TimeSeries timeSeries = map.get(event.pid);
-
- // if it doesn't exist yet, we create it.
- if (timeSeries == null) {
- String seriesLabel = getSeriesLabel(event, descriptor);
-
- String seriesFullName = String.format("[%1$s:%2$s]",
- tagMap.get(descriptor.eventTag), seriesLabel);
-
- timeSeries = new TimeSeries(seriesFullName, Millisecond.class);
- if (mMaximumChartItemAge != -1) {
- timeSeries.setMaximumItemAge(mMaximumChartItemAge);
- }
-
- getOccurrenceDataSet().addSeries(timeSeries);
-
- map.put(event.pid, timeSeries);
- }
-
- // update the series
-
- // get the time
- if (millisecondTime == null) {
- msec = (long)event.sec * 1000L + (event.nsec / 1000000L);
- millisecondTime = new Millisecond(new Date(msec));
- }
-
- // add the value to the time series
- timeSeries.addOrUpdate(millisecondTime, 0); // the value is unused
- } catch (InvalidTypeException e) {
- // just ignore this descriptor if there's a type mismatch
- }
- }
-
- // go through all the series and remove old values.
- if (msec != -1 && mMaximumChartItemAge != -1) {
- Collection> pidMapValues =
- mValueDescriptorSeriesMap.values();
-
- for (HashMap pidMapValue : pidMapValues) {
- Collection seriesCollection = pidMapValue.values();
-
- for (TimeSeries timeSeries : seriesCollection) {
- timeSeries.removeAgedItems(msec, true);
- }
- }
-
- pidMapValues = mOcurrenceDescriptorSeriesMap.values();
- for (HashMap pidMapValue : pidMapValues) {
- Collection seriesCollection = pidMapValue.values();
-
- for (TimeSeries timeSeries : seriesCollection) {
- timeSeries.removeAgedItems(msec, true);
- }
- }
- }
- }
-
- /**
- * Return the series label for this event. This only contains the pid information.
- * @param event the {@link EventContainer}
- * @param descriptor the {@link OccurrenceDisplayDescriptor}
- * @return the series label.
- * @throws InvalidTypeException
- */
- private String getSeriesLabel(EventContainer event, OccurrenceDisplayDescriptor descriptor)
- throws InvalidTypeException {
- if (descriptor.seriesValueIndex != -1) {
- if (descriptor.includePid == false) {
- return event.getValueAsString(descriptor.seriesValueIndex);
- } else {
- return String.format("%1$s (%2$d)",
- event.getValueAsString(descriptor.seriesValueIndex), event.pid);
- }
- }
-
- return Integer.toString(event.pid);
- }
-
/**
* Fills a list with {@link OccurrenceDisplayDescriptor} (or a subclass of it) from another
* list if they are configured to display the {@link EventContainer}
- * @param event the event container
+ *
+ * @param event the event container
* @param fullList the list with all the descriptors.
- * @param outList the list to fill.
+ * @param outList the list to fill.
*/
@SuppressWarnings("unchecked")
private void getDescriptors(EventContainer event,
@@ -1348,200 +852,21 @@ final class EventDisplay {
}
}
}
-
- /**
- * Adds an {@link EventContainer} to the log.
- * @param event the event.
- * @param logParser the log parser.
- */
- private void addToLog(EventContainer event, EventLogParser logParser) {
- ScrollBar bar = mLogTable.getVerticalBar();
- boolean scroll = bar.getMaximum() == bar.getSelection() + bar.getThumb();
-
- // get the date.
- Calendar c = Calendar.getInstance();
- long msec = (long)event.sec * 1000L;
- c.setTimeInMillis(msec);
-
- // convert the time into a string
- String date = String.format("%1$tF %1$tT", c);
-
- String eventName = logParser.getTagMap().get(event.mTag);
- String pidName = Integer.toString(event.pid);
-
- // get the value description
- EventValueDescription[] valueDescription = logParser.getEventInfoMap().get(event.mTag);
- if (valueDescription != null) {
- for (int i = 0 ; i < valueDescription.length ; i++) {
- EventValueDescription description = valueDescription[i];
- try {
- String value = event.getValueAsString(i);
-
- logValue(date, pidName, eventName, description.getName(), value,
- description.getEventValueType(), description.getValueType());
- } catch (InvalidTypeException e) {
- logValue(date, pidName, eventName, description.getName(), e.getMessage(),
- description.getEventValueType(), description.getValueType());
- }
- }
-
- // scroll if needed, by showing the last item
- if (scroll) {
- int itemCount = mLogTable.getItemCount();
- if (itemCount > 0) {
- mLogTable.showItem(mLogTable.getItem(itemCount-1));
- }
- }
- }
- }
/**
- * Adds an {@link EventContainer} to the log. Only add the values/occurrences defined by
- * the list of descriptors. If an event is configured to be displayed by value and occurrence,
- * only the values are displayed (as they mark an event occurrence anyway).
- * This method is only called when at least one of the descriptor list is non empty.
- * @param event
- * @param logParser
- * @param valueDescriptors
- * @param occurrenceDescriptors
- */
- private void addToLog(EventContainer event, EventLogParser logParser,
- ArrayList valueDescriptors,
- ArrayList occurrenceDescriptors) {
- ScrollBar bar = mLogTable.getVerticalBar();
- boolean scroll = bar.getMaximum() == bar.getSelection() + bar.getThumb();
-
- // get the date.
- Calendar c = Calendar.getInstance();
- long msec = (long)event.sec * 1000L;
- c.setTimeInMillis(msec);
-
- // convert the time into a string
- String date = String.format("%1$tF %1$tT", c);
-
- String eventName = logParser.getTagMap().get(event.mTag);
- String pidName = Integer.toString(event.pid);
-
- if (valueDescriptors.size() > 0) {
- for (ValueDisplayDescriptor descriptor : valueDescriptors) {
- logDescriptor(event, descriptor, date, pidName, eventName, logParser);
- }
- } else {
- // we display the event. Since the StringBuilder contains the header (date, event name,
- // pid) at this point, there isn't anything else to display.
- }
-
- // scroll if needed, by showing the last item
- if (scroll) {
- int itemCount = mLogTable.getItemCount();
- if (itemCount > 0) {
- mLogTable.showItem(mLogTable.getItem(itemCount-1));
- }
- }
- }
-
- /**
- * Logs a value from an {@link EventContainer} as defined by the {@link ValueDisplayDescriptor}.
- * @param event the EventContainer
- * @param descriptor the ValueDisplayDescriptor defining which value to display.
- * @param date the date of the event in a string.
- * @param pidName
- * @param eventName
- * @param logParser
- */
- private void logDescriptor(EventContainer event, ValueDisplayDescriptor descriptor,
- String date, String pidName, String eventName, EventLogParser logParser) {
-
- String value;
- try {
- value = event.getValueAsString(descriptor.valueIndex);
- } catch (InvalidTypeException e) {
- value = e.getMessage();
- }
-
- EventValueDescription[] values = logParser.getEventInfoMap().get(event.mTag);
-
- EventValueDescription valueDescription = values[descriptor.valueIndex];
-
- logValue(date, pidName, eventName, descriptor.valueName, value,
- valueDescription.getEventValueType(), valueDescription.getValueType());
- }
-
- /**
- * Logs a value in the ui.
- * @param date
- * @param pid
- * @param event
- * @param valueName
- * @param value
- * @param eventValueType
- * @param valueType
- */
- private void logValue(String date, String pid, String event, String valueName,
- String value, EventValueType eventValueType, ValueType valueType) {
-
- TableItem item = new TableItem(mLogTable, SWT.NONE);
- item.setText(0, date);
- item.setText(1, pid);
- item.setText(2, event);
- item.setText(3, valueName);
- item.setText(4, value);
-
- String type;
- if (valueType != ValueType.NOT_APPLICABLE) {
- type = String.format("%1$s, %2$s", eventValueType.toString(), valueType.toString());
- } else {
- type = eventValueType.toString();
- }
-
- item.setText(5, type);
- }
-
- /**
- * Show the current value(s) of an {@link EventContainer}. The values to show are defined by
- * the {@link ValueDisplayDescriptor}s and {@link OccurrenceDisplayDescriptor}s passed in the
- * two lists.
- * @param event
- * @param logParser
- * @param valueDescriptors
- * @param occurrenceDescriptors
- */
- private void showCurrent(EventContainer event, EventLogParser logParser,
- ArrayList valueDescriptors,
- ArrayList occurrenceDescriptors) {
- // TODO Auto-generated method stub
- }
-
- // Values from data/etc/event-log-tags
- final int EVENT_SYNC = 2720;
- final int EVENT_TICKLE = 2742;
- final int EVENT_SYNC_DETAILS = 2743;
-
- /**
- * Filters the {@link EventContainer}, and fills two list of {@link ValueDisplayDescriptor}
- * and {@link OccurrenceDisplayDescriptor} configured to display the event.
+ * Filters the {@link com.android.ddmlib.log.EventContainer}, and fills two list of {@link com.android.ddmuilib.log.event.EventDisplay.ValueDisplayDescriptor}
+ * and {@link com.android.ddmuilib.log.event.EventDisplay.OccurrenceDisplayDescriptor} configured to display the event.
+ *
* @param event
* @param valueDescriptors
* @param occurrenceDescriptors
* @return true if the event should be displayed.
*/
- private boolean filterEvent(EventContainer event,
+
+ protected boolean filterEvent(EventContainer event,
ArrayList valueDescriptors,
ArrayList occurrenceDescriptors) {
- if (mDisplayType == DISPLAY_TYPE_LOG_ALL) {
- return true;
- }
-
- if (mDisplayType == DISPLAY_TYPE_SYNC || mDisplayType == DISPLAY_TYPE_SYNC_HIST) {
- if (event.mTag == EVENT_SYNC || event.mTag == EVENT_TICKLE ||
- event.mTag == EVENT_SYNC_DETAILS) {
- return true;
- } else {
- return false;
- }
- }
-
// test the pid first (if needed)
if (mPidFiltering && mPidFilterList != null) {
boolean found = false;
@@ -1551,7 +876,7 @@ final class EventDisplay {
break;
}
}
-
+
if (found == false) {
return false;
}
@@ -1569,7 +894,8 @@ final class EventDisplay {
* Checks all the {@link ValueDisplayDescriptor} for similarity.
* If all the event values are from the same tag, the method will return EVENT_CHECK_SAME_TAG.
* If all the event/value are the same, the method will return EVENT_CHECK_SAME_VALUE
- * @return
+ *
+ * @return flag as described above
*/
private int checkDescriptors() {
if (mValueDescriptors.size() < 2) {
@@ -1598,39 +924,8 @@ final class EventDisplay {
if (index == -1) {
return EVENT_CHECK_SAME_TAG;
}
-
- return EVENT_CHECK_SAME_VALUE;
- }
-
- /**
- * Returns a meaningful chart title based on the value of {@link #mValueDescriptorCheck}.
- * @param logParser the logParser.
- * @return the chart title.
- */
- private String getChartTitle(EventLogParser logParser) {
- if (mValueDescriptors.size() > 0) {
- String chartDesc = null;
- switch (mValueDescriptorCheck) {
- case EVENT_CHECK_SAME_TAG:
- if (logParser != null) {
- chartDesc = logParser.getTagMap().get(mValueDescriptors.get(0).eventTag);
- }
- break;
- case EVENT_CHECK_SAME_VALUE:
- if (logParser != null) {
- chartDesc = String.format("%1$s / %2$s",
- logParser.getTagMap().get(mValueDescriptors.get(0).eventTag),
- mValueDescriptors.get(0).valueName);
- }
- break;
- }
-
- if (chartDesc != null) {
- return String.format("%1$s - %2$s", mName, chartDesc);
- }
- }
- return mName;
+ return EVENT_CHECK_SAME_VALUE;
}
/**
@@ -1642,17 +937,19 @@ final class EventDisplay {
/**
* Sets the time limit on the charts.
+ *
* @param timeLimit the time limit in seconds.
*/
void setChartTimeLimit(long timeLimit) {
mMaximumChartItemAge = timeLimit;
}
-
+
long getChartTimeLimit() {
return mMaximumChartItemAge;
}
/**
+ * m
* Resets the histogram width
*/
void resetHistWidth() {
@@ -1661,443 +958,14 @@ final class EventDisplay {
/**
* Sets the histogram width
+ *
* @param histWidth the width in hours
*/
void setHistWidth(long histWidth) {
mHistWidth = histWidth;
}
-
+
long getHistWidth() {
return mHistWidth;
}
-
- // Implementation of the Sync display
- // TODO: DISPLAY_TYPE_LOG, DISPLAY_TYPE_GRAPH, and DISPLAY_TYPE_SYNC should be subclasses
- // of EventDisplay.java
-
- private static final int CALENDAR = 0;
- private static final int GMAIL = 1;
- private static final int FEEDS = 2;
- private static final int CONTACTS = 3;
- private static final int ERRORS = 4;
- private static final int NUM_AUTHS = (CONTACTS+1);
- private static final String AUTH_NAMES[] = {"Calendar", "Gmail", "Feeds", "Contacts", "Errors"};
- private static final Color AUTH_COLORS[] = {Color.MAGENTA, Color.GREEN, Color.BLUE, Color.ORANGE, Color.RED};
-
- // Information to graph for each authority
- private TimePeriodValues mDatasetsSync[];
- private List mTooltipsSync[];
- private CustomXYToolTipGenerator mTooltipGenerators[];
- private TimeSeries mDatasetsSyncTickle[];
-
- // Dataset of error events to graph
- private TimeSeries mDatasetError;
-
- /**
- * Initialize the Plot and series data for the sync display.
- */
- void initSyncDisplay() {
- XYPlot xyPlot = mChart.getXYPlot();
-
- XYBarRenderer br = new XYBarRenderer();
- mDatasetsSync = new TimePeriodValues[NUM_AUTHS];
- mTooltipsSync = new List[NUM_AUTHS];
- mTooltipGenerators = new CustomXYToolTipGenerator[NUM_AUTHS];
- mLastDetails = "";
-
- TimePeriodValuesCollection tpvc = new TimePeriodValuesCollection();
- xyPlot.setDataset(tpvc);
- xyPlot.setRenderer(0, br);
-
- XYLineAndShapeRenderer ls = new XYLineAndShapeRenderer();
- ls.setBaseLinesVisible(false);
- mDatasetsSyncTickle = new TimeSeries[NUM_AUTHS];
- TimeSeriesCollection tsc = new TimeSeriesCollection();
- xyPlot.setDataset(1, tsc);
- xyPlot.setRenderer(1, ls);
-
- mDatasetError = new TimeSeries("Errors", FixedMillisecond.class);
- xyPlot.setDataset(2, new TimeSeriesCollection(mDatasetError));
- XYLineAndShapeRenderer errls = new XYLineAndShapeRenderer();
- errls.setBaseLinesVisible(false);
- errls.setSeriesPaint(0, Color.RED);
- xyPlot.setRenderer(2, errls);
-
- for (int i = 0; i < NUM_AUTHS; i++) {
- br.setSeriesPaint(i, AUTH_COLORS[i]);
- ls.setSeriesPaint(i, AUTH_COLORS[i]);
- mDatasetsSync[i] = new TimePeriodValues(AUTH_NAMES[i]);
- tpvc.addSeries(mDatasetsSync[i]);
- mTooltipsSync[i] = new ArrayList();
- mTooltipGenerators[i] = new CustomXYToolTipGenerator();
- br.setSeriesToolTipGenerator(i, mTooltipGenerators[i]);
- mTooltipGenerators[i].addToolTipSeries(mTooltipsSync[i]);
-
- mDatasetsSyncTickle[i] = new TimeSeries(AUTH_NAMES[i] + " tickle", FixedMillisecond.class);
- tsc.addSeries(mDatasetsSyncTickle[i]);
- ls.setSeriesShape(i, ShapeUtilities.createUpTriangle(2.5f));
- }
- }
-
- // State information while processing the event stream
- private int mLastState; // 0 if event started, 1 if event stopped
- private long mLastStartTime; // ms
- private long mLastStopTime; //ms
- private String mLastDetails;
- private int mLastEvent; // server, poll, etc
-
- /**
- * Updates the display with a new event. This is the main entry point for
- * each event. This method has the logic to tie together the start event,
- * stop event, and details event into one graph item. Note that the details
- * can happen before or after the stop event.
- * @param event The event
- */
- private void updateSyncDisplay(EventContainer event) {
- try {
- if (event.mTag == EVENT_SYNC) {
- int state = Integer.parseInt(event.getValueAsString(1));
- if (state == 0) { // start
- mLastStartTime = (long)event.sec * 1000L + (event.nsec / 1000000L);
- mLastState = 0;
- mLastEvent = Integer.parseInt(event.getValueAsString(2));
- mLastDetails = "";
- } else if (state == 1) { // stop
- if (mLastState == 0) {
- mLastStopTime = (long)event.sec * 1000L + (event.nsec / 1000000L);
- if (mLastStartTime == 0) {
- // Log starts with a stop event
- mLastStartTime = mLastStopTime;
- }
- addEvent(event);
- mLastState = 1;
- }
- }
- } else if (event.mTag == EVENT_TICKLE) {
- int auth = getAuth(event.getValueAsString(0));
- if (auth >= 0) {
- long msec = (long)event.sec * 1000L + (event.nsec / 1000000L);
- mDatasetsSyncTickle[auth].addOrUpdate(new FixedMillisecond(msec), -1);
- }
- } else if (event.mTag == EVENT_SYNC_DETAILS) {
- int auth = getAuth(event.getValueAsString(0));
- mLastDetails = event.getValueAsString(3);
- if (mLastState != 0) { // Not inside event
- long updateTime = (long)event.sec * 1000L + (event.nsec / 1000000L);
- if (updateTime - mLastStopTime <= 250) {
- // Got details within 250ms after event, so delete and re-insert
- // Details later than 250ms (arbitrary) are discarded as probably
- // unrelated.
- int lastItem = mDatasetsSync[auth].getItemCount();
- mDatasetsSync[auth].delete(lastItem-1, lastItem-1);
- mTooltipsSync[auth].remove(lastItem-1);
- addEvent(event);
- }
- }
- }
- } catch (InvalidTypeException e) {
- }
- }
-
- /**
- * Convert authority name to auth number.
- * @param authname "calendar", etc.
- * @return number series number associated with the authority
- */
- private int getAuth(String authname) throws InvalidTypeException {
- if ("calendar".equals(authname) || "cl".equals(authname)) {
- return CALENDAR;
- } else if ("contacts".equals(authname) || "cp".equals(authname)) {
- return CONTACTS;
- } else if ("subscribedfeeds".equals(authname)) {
- return FEEDS;
- } else if ("gmail-ls".equals(authname) || "mail".equals(authname)) {
- return GMAIL;
- } else if ("gmail-live".equals(authname)) {
- return GMAIL;
- } else if ("unknown".equals(authname)) {
- return -1; // Unknown tickles; discard
- } else {
- throw new InvalidTypeException("Unknown authname " + authname);
- }
- }
-
- /**
- * Generate the height for an event.
- * Height is somewhat arbitrarily the count of "things" that happened
- * during the sync.
- * When network traffic measurements are available, code should be modified
- * to use that instead.
- * @param details The details string associated with the event
- * @return The height in arbirary units (0-100)
- */
- private int getHeightFromDetails(String details) {
- if (details == null) {
- return 1; // Arbitrary
- }
- int total = 0;
- String parts[] = details.split("[a-zA-Z]");
- for (String part : parts) {
- if ("".equals(part)) continue;
- total += Integer.parseInt(part);
- }
- if (total == 0) {
- total = 1;
- }
- return total;
- }
-
- /**
- * Generates the tooltips text for an event.
- * This method decodes the cryptic details string.
- * @param auth The authority associated with the event
- * @param details The details string
- * @param eventSource server, poll, etc.
- * @return The text to display in the tooltips
- */
- private String getTextFromDetails(int auth, String details, int eventSource) {
-
- StringBuffer sb = new StringBuffer();
- sb.append(AUTH_NAMES[auth]).append(": \n");
-
- Scanner scanner = new Scanner(details);
- Pattern charPat = Pattern.compile("[a-zA-Z]");
- Pattern numPat = Pattern.compile("[0-9]+");
- while (scanner.hasNext()) {
- String key = scanner.findInLine(charPat);
- int val = Integer.parseInt(scanner.findInLine(numPat));
- if (auth == GMAIL && "M".equals(key)) {
- sb.append("messages from server: ").append(val).append("\n");
- } else if (auth == GMAIL && "L".equals(key)) {
- sb.append("labels from server: ").append(val).append("\n");
- } else if (auth == GMAIL && "C".equals(key)) {
- sb.append("check conversation requests from server: ").append(val).append("\n");
- } else if (auth == GMAIL && "A".equals(key)) {
- sb.append("attachments from server: ").append(val).append("\n");
- } else if (auth == GMAIL && "U".equals(key)) {
- sb.append("op updates from server: ").append(val).append("\n");
- } else if (auth == GMAIL && "u".equals(key)) {
- sb.append("op updates to server: ").append(val).append("\n");
- } else if (auth == GMAIL && "S".equals(key)) {
- sb.append("send/receive cycles: ").append(val).append("\n");
- } else if ("Q".equals(key)) {
- sb.append("queries to server: ").append(val).append("\n");
- } else if ("E".equals(key)) {
- sb.append("entries from server: ").append(val).append("\n");
- } else if ("u".equals(key)) {
- sb.append("updates from client: ").append(val).append("\n");
- } else if ("i".equals(key)) {
- sb.append("inserts from client: ").append(val).append("\n");
- } else if ("d".equals(key)) {
- sb.append("deletes from client: ").append(val).append("\n");
- } else if ("f".equals(key)) {
- sb.append("full sync requested\n");
- } else if ("r".equals(key)) {
- sb.append("partial sync unavailable\n");
- } else if ("X".equals(key)) {
- sb.append("hard error\n");
- } else if ("e".equals(key)) {
- sb.append("number of parse exceptions: ").append(val).append("\n");
- } else if ("c".equals(key)) {
- sb.append("number of conflicts: ").append(val).append("\n");
- } else if ("a".equals(key)) {
- sb.append("number of auth exceptions: ").append(val).append("\n");
- } else if ("D".equals(key)) {
- sb.append("too many deletions\n");
- } else if ("R".equals(key)) {
- sb.append("too many retries: ").append(val).append("\n");
- } else if ("b".equals(key)) {
- sb.append("database error\n");
- } else if ("x".equals(key)) {
- sb.append("soft error\n");
- } else if ("l".equals(key)) {
- sb.append("sync already in progress\n");
- } else if ("I".equals(key)) {
- sb.append("io exception\n");
- } else if (auth == CONTACTS && "p".equals(key)) {
- sb.append("photos uploaded from client: ").append(val).append("\n");
- } else if (auth == CONTACTS && "P".equals(key)) {
- sb.append("photos downloaded from server: ").append(val).append("\n");
- } else if (auth == CALENDAR && "F".equals(key)) {
- sb.append("server refresh\n");
- } else if (auth == CALENDAR && "s".equals(key)) {
- sb.append("server diffs fetched\n");
- } else {
- sb.append(key).append("=").append(val);
- }
- }
- if (eventSource == 0) {
- sb.append("(server)");
- } else if (eventSource == 1) {
- sb.append("(local)");
- } else if (eventSource == 2) {
- sb.append("(poll)");
- } else if (eventSource == 3) {
- sb.append("(user)");
- }
- return sb.toString();
- }
-
- /**
- * Helper to add an event to the data series.
- * Also updates error series if appropriate (x or X in details).
- * @param event The event
- */
- private void addEvent(EventContainer event) {
- try {
- int auth = getAuth(event.getValueAsString(0));
- double height = getHeightFromDetails(mLastDetails);
- height = height / (mLastStopTime - mLastStartTime + 1) * 10000;
- if (height > 30) {
- height = 30;
- }
- mDatasetsSync[auth].add(new SimpleTimePeriod(mLastStartTime, mLastStopTime), height);
- mTooltipsSync[auth].add(getTextFromDetails(auth, mLastDetails,
- mLastEvent));
- mTooltipGenerators[auth].addToolTipSeries(mTooltipsSync[auth]);
- if (mLastDetails.indexOf('x') >= 0 || mLastDetails.indexOf('X') >= 0) {
- long msec = (long)event.sec * 1000L + (event.nsec / 1000000L);
- mDatasetError.addOrUpdate(new FixedMillisecond(msec), -1);
- }
- } catch (InvalidTypeException e) {
- e.printStackTrace();
- }
- }
-
- // Implementation of the Sync Histogram display
-
- // Information to graph for each authority
- private TimePeriodValues mDatasetsSyncHist[];
-
- /**
- * Initialize the Plot and series data for the sync display.
- */
- void initSyncHistDisplay() {
- XYPlot xyPlot = mChart.getXYPlot();
-
- AbstractXYItemRenderer br = new XYBarRenderer();
- mDatasetsSyncHist = new TimePeriodValues[NUM_AUTHS+1];
- mLastDetails = "";
- mTimePeriodMap = new HashMap[NUM_AUTHS + 1];
-
- TimePeriodValuesCollection tpvc = new TimePeriodValuesCollection();
- xyPlot.setDataset(tpvc);
- xyPlot.setRenderer(br);
-
- for (int i = 0; i < NUM_AUTHS + 1; i++) {
- br.setSeriesPaint(i, AUTH_COLORS[i]);
- mDatasetsSyncHist[i] = new TimePeriodValues(AUTH_NAMES[i]);
- tpvc.addSeries(mDatasetsSyncHist[i]);
- mTimePeriodMap[i] = new HashMap();
-
- }
- }
-
- /**
- * Updates the display with a new event. This is the main entry point for
- * each event. This method has the logic to tie together the start event,
- * stop event, and details event into one graph item. Note that the details
- * can happen before or after the stop event.
- * @param event The event
- */
- private void updateSyncHistDisplay(EventContainer event) {
- try {
- if (event.mTag == EVENT_SYNC) {
- int state = Integer.parseInt(event.getValueAsString(1));
- if (state == 0) { // start
- mLastStartTime = (long)event.sec * 1000L + (event.nsec / 1000000L);
- mLastState = 0;
- mLastEvent = Integer.parseInt(event.getValueAsString(2));
- mLastDetails = "";
- } else if (state == 1) { // stop
- if (mLastState == 0) {
- mLastStopTime = (long)event.sec * 1000L + (event.nsec / 1000000L);
- if (mLastStartTime == 0) {
- // Log starts with a stop event
- mLastStartTime = mLastStopTime;
- }
- int auth = getAuth(event.getValueAsString(0));
- if (mLastDetails.indexOf('x') >= 0 || mLastDetails.indexOf('X') >= 0) {
- auth = ERRORS;
- }
- double delta = (mLastStopTime - mLastStartTime) * 100. / 1000 / 3600; // Percent of hour
- addHistEvent(event, auth, delta);
- mLastState = 1;
- }
- }
- } else if (event.mTag == EVENT_SYNC_DETAILS) {
- int auth = getAuth(event.getValueAsString(0));
- mLastDetails = event.getValueAsString(3);
- if (mLastState != 0) { // Not inside event
- long updateTime = (long)event.sec * 1000L + (event.nsec / 1000000L);
- if (updateTime - mLastStopTime <= 250) {
- // Got details within 250ms after event, so delete and re-insert
- // Details later than 250ms (arbitrary) are discarded as probably
- // unrelated.
- //int lastItem = mDatasetsSync[auth].getItemCount();
- //addHistEvent(event);
- if (mLastDetails.indexOf('x') >= 0 || mLastDetails.indexOf('X') >= 0) {
- // Item turns out to be in error, so transfer time from old auth to error.
-
- double delta = (mLastStopTime - mLastStartTime) * 100. / 1000 / 3600; // Percent of hour
- addHistEvent(event, auth, -delta);
- addHistEvent(event, ERRORS, delta);
- }
- }
- }
- }
- } catch (InvalidTypeException e) {
- }
- }
-
- /**
- * Helper to add an event to the data series.
- * Also updates error series if appropriate (x or X in details).
- * @param event The event
- * @param auth
- * @param value
- */
- private void addHistEvent(EventContainer event, int auth, double value) {
- SimpleTimePeriod hour = getTimePeriod(mLastStopTime, mHistWidth);
-
- // Loop over all datasets to do the stacking.
- for (int i = auth; i <= ERRORS; i++) {
- addToPeriod(mDatasetsSyncHist, i, hour, value);
- }
- }
-
- Map mTimePeriodMap[];
-
- private void addToPeriod(TimePeriodValues tpv[], int auth, SimpleTimePeriod period, double value) {
- int index;
- if (mTimePeriodMap[auth].containsKey(period)) {
- index = mTimePeriodMap[auth].get(period);
- double oldValue = tpv[auth].getValue(index).doubleValue();
- tpv[auth].update(index, oldValue + value);
- } else {
- index = tpv[auth].getItemCount();
- mTimePeriodMap[auth].put(period, index);
- tpv[auth].add(period, value);
- }
- }
-
- /**
- * Creates a multiple-hour time period for the histogram.
- * @param time Time in milliseconds.
- * @param numHoursWide: should divide into a day.
- * @return SimpleTimePeriod covering the number of hours and containing time.
- */
- private SimpleTimePeriod getTimePeriod(long time, long numHoursWide) {
- Date date = new Date(time);
- TimeZone zone = RegularTimePeriod.DEFAULT_TIME_ZONE;
- Calendar calendar = Calendar.getInstance(zone);
- calendar.setTime(date);
- long hoursOfYear = calendar.get(Calendar.HOUR_OF_DAY) + calendar.get(Calendar.DAY_OF_YEAR) * 24;
- int year = calendar.get(Calendar.YEAR);
- hoursOfYear = (hoursOfYear / numHoursWide) * numHoursWide;
- calendar.clear();
- calendar.set(year, 0, 1, 0, 0); // Jan 1
- long start = calendar.getTimeInMillis() + hoursOfYear * 3600 * 1000;
- return new SimpleTimePeriod(start, start + numHoursWide * 3600 * 1000);
- }
}
diff --git a/tools/ddms/libs/ddmuilib/src/com/android/ddmuilib/log/event/EventDisplayOptions.java b/tools/ddms/libs/ddmuilib/src/com/android/ddmuilib/log/event/EventDisplayOptions.java
index 94f04d7b2..88c3cb299 100644
--- a/tools/ddms/libs/ddmuilib/src/com/android/ddmuilib/log/event/EventDisplayOptions.java
+++ b/tools/ddms/libs/ddmuilib/src/com/android/ddmuilib/log/event/EventDisplayOptions.java
@@ -23,7 +23,6 @@ import com.android.ddmuilib.DdmUiPreferences;
import com.android.ddmuilib.IImageLoader;
import com.android.ddmuilib.log.event.EventDisplay.OccurrenceDisplayDescriptor;
import com.android.ddmuilib.log.event.EventDisplay.ValueDisplayDescriptor;
-
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ModifyEvent;
@@ -439,15 +438,19 @@ class EventDisplayOptions extends Dialog {
mDisplayTypeCombo.add("Filtered Log");
mDisplayTypeCombo.add("Graph");
mDisplayTypeCombo.add("Sync");
- mDisplayTypeCombo.add("Sync histogram");
+ mDisplayTypeCombo.add("Sync Histogram");
+ mDisplayTypeCombo.add("Sync Performance");
mDisplayTypeCombo.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
EventDisplay eventDisplay = getCurrentEventDisplay();
- if (eventDisplay != null) {
+ if (eventDisplay != null && eventDisplay.getDisplayType() != mDisplayTypeCombo.getSelectionIndex()) {
+ /* Replace the EventDisplay object with a different subclass */
setModified();
- eventDisplay.setDisplayType(mDisplayTypeCombo.getSelectionIndex());
- fillUiWith(eventDisplay);
+ String name = eventDisplay.getName();
+ EventDisplay newEventDisplay = EventDisplay.eventDisplayFactory(mDisplayTypeCombo.getSelectionIndex(), name);
+ setCurrentEventDisplay(newEventDisplay);
+ fillUiWith(newEventDisplay);
}
}
});
@@ -693,7 +696,7 @@ class EventDisplayOptions extends Dialog {
private void duplicateEventDisplay(ArrayList displayList) {
for (EventDisplay eventDisplay : displayList) {
- mDisplayList.add(new EventDisplay(eventDisplay));
+ mDisplayList.add(EventDisplay.clone(eventDisplay));
}
}
@@ -744,7 +747,7 @@ class EventDisplayOptions extends Dialog {
String name = String.format("display %1$d", count + 1);
- EventDisplay eventDisplay = new EventDisplay(name);
+ EventDisplay eventDisplay = EventDisplay.eventDisplayFactory(0 /* type*/, name);
mDisplayList.add(eventDisplay);
mEventDisplayList.add(name);
@@ -779,6 +782,13 @@ class EventDisplayOptions extends Dialog {
return null;
}
+
+ private void setCurrentEventDisplay(EventDisplay eventDisplay) {
+ int selection = mEventDisplayList.getSelectionIndex();
+ if (selection != -1) {
+ mDisplayList.set(selection, eventDisplay);
+ }
+ }
private void handleEventDisplaySelection() {
EventDisplay eventDisplay = getCurrentEventDisplay();
diff --git a/tools/ddms/libs/ddmuilib/src/com/android/ddmuilib/log/event/EventLogImporter.java b/tools/ddms/libs/ddmuilib/src/com/android/ddmuilib/log/event/EventLogImporter.java
index 2ace78afe..a1303f675 100644
--- a/tools/ddms/libs/ddmuilib/src/com/android/ddmuilib/log/event/EventLogImporter.java
+++ b/tools/ddms/libs/ddmuilib/src/com/android/ddmuilib/log/event/EventLogImporter.java
@@ -38,7 +38,7 @@ public class EventLogImporter {
if (top == null) {
throw new FileNotFoundException();
}
- final String tagFile = top + "/data/etc/event-log-tags";
+ final String tagFile = top + "/system/core/logcat/event-log-tags";
BufferedReader tagReader = new BufferedReader(
new InputStreamReader(new FileInputStream(tagFile)));
BufferedReader eventReader = new BufferedReader(
diff --git a/tools/ddms/libs/ddmuilib/src/com/android/ddmuilib/log/event/SyncCommon.java b/tools/ddms/libs/ddmuilib/src/com/android/ddmuilib/log/event/SyncCommon.java
new file mode 100644
index 000000000..108c097a4
--- /dev/null
+++ b/tools/ddms/libs/ddmuilib/src/com/android/ddmuilib/log/event/SyncCommon.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2009 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.log.event;
+
+import com.android.ddmlib.log.EventContainer;
+import com.android.ddmlib.log.EventLogParser;
+import com.android.ddmlib.log.InvalidTypeException;
+
+import java.awt.Color;
+
+abstract public class SyncCommon extends EventDisplay {
+
+ // State information while processing the event stream
+ private int mLastState; // 0 if event started, 1 if event stopped
+ private long mLastStartTime; // ms
+ private long mLastStopTime; //ms
+ private String mLastDetails;
+ private int mLastSyncSource; // poll, server, user, etc.
+
+ // Some common variables for sync display. These define the sync backends
+ //and how they should be displayed.
+ protected static final int CALENDAR = 0;
+ protected static final int GMAIL = 1;
+ protected static final int FEEDS = 2;
+ protected static final int CONTACTS = 3;
+ protected static final int ERRORS = 4;
+ protected static final int NUM_AUTHS = (CONTACTS + 1);
+ protected static final String AUTH_NAMES[] = {"Calendar", "Gmail", "Feeds", "Contacts",
+ "Errors"};
+ protected static final Color AUTH_COLORS[] = {Color.MAGENTA, Color.GREEN, Color.BLUE,
+ Color.ORANGE, Color.RED};
+
+ // Values from data/etc/event-log-tags
+ final int EVENT_SYNC = 2720;
+ final int EVENT_TICKLE = 2742;
+ final int EVENT_SYNC_DETAILS = 2743;
+
+ protected SyncCommon(String name) {
+ super(name);
+ }
+
+ /**
+ * Resets the display.
+ */
+ @Override
+ void resetUI() {
+ mLastStartTime = 0;
+ mLastStopTime = 0;
+ mLastState = -1;
+ mLastSyncSource = -1;
+ mLastDetails = "";
+ }
+
+ /**
+ * Updates the display with a new event. This is the main entry point for
+ * each event. This method has the logic to tie together the start event,
+ * stop event, and details event into one graph item. The combined sync event
+ * is handed to the subclass via processSycnEvent. Note that the details
+ * can happen before or after the stop event.
+ *
+ * @param event The event
+ * @param logParser The parser providing the event.
+ */
+ @Override
+ void newEvent(EventContainer event, EventLogParser logParser) {
+ try {
+ if (event.mTag == EVENT_SYNC) {
+ int state = Integer.parseInt(event.getValueAsString(1));
+ if (state == 0) { // start
+ mLastStartTime = (long) event.sec * 1000L + (event.nsec / 1000000L);
+ mLastState = 0;
+ mLastSyncSource = Integer.parseInt(event.getValueAsString(2));
+ mLastDetails = "";
+ } else if (state == 1) { // stop
+ if (mLastState == 0) {
+ mLastStopTime = (long) event.sec * 1000L + (event.nsec / 1000000L);
+ if (mLastStartTime == 0) {
+ // Log starts with a stop event
+ mLastStartTime = mLastStopTime;
+ }
+ int auth = getAuth(event.getValueAsString(0));
+ processSyncEvent(event, auth, mLastStartTime, mLastStopTime, mLastDetails,
+ true, mLastSyncSource);
+ mLastState = 1;
+ }
+ }
+ } else if (event.mTag == EVENT_SYNC_DETAILS) {
+ mLastDetails = event.getValueAsString(3);
+ if (mLastState != 0) { // Not inside event
+ long updateTime = (long) event.sec * 1000L + (event.nsec / 1000000L);
+ if (updateTime - mLastStopTime <= 250) {
+ // Got details within 250ms after event, so delete and re-insert
+ // Details later than 250ms (arbitrary) are discarded as probably
+ // unrelated.
+ int auth = getAuth(event.getValueAsString(0));
+ processSyncEvent(event, auth, mLastStartTime, mLastStopTime, mLastDetails,
+ false, mLastSyncSource);
+ }
+ }
+ }
+ } catch (InvalidTypeException e) {
+ }
+ }
+
+ /**
+ * Callback hook for subclass to process a sync event. newEvent has the logic
+ * to combine start and stop events and passes a processed event to the
+ * subclass.
+ *
+ * @param event The sync event
+ * @param auth The sync authority
+ * @param startTime Start time (ms) of events
+ * @param stopTime Stop time (ms) of events
+ * @param details Details associated with the event.
+ * @param newEvent True if this event is a new sync event. False if this event
+ * @param syncSource Poll, user, server, etc.
+ */
+ abstract void processSyncEvent(EventContainer event, int auth, long startTime, long stopTime,
+ String details, boolean newEvent, int syncSource);
+
+ /**
+ * Converts authority name to auth number.
+ *
+ * @param authname "calendar", etc.
+ * @return number series number associated with the authority
+ */
+ protected int getAuth(String authname) throws InvalidTypeException {
+ if ("calendar".equals(authname) || "cl".equals(authname)) {
+ return CALENDAR;
+ } else if ("contacts".equals(authname) || "cp".equals(authname)) {
+ return CONTACTS;
+ } else if ("subscribedfeeds".equals(authname)) {
+ return FEEDS;
+ } else if ("gmail-ls".equals(authname) || "mail".equals(authname)) {
+ return GMAIL;
+ } else if ("gmail-live".equals(authname)) {
+ return GMAIL;
+ } else if ("unknown".equals(authname)) {
+ return -1; // Unknown tickles; discard
+ } else {
+ throw new InvalidTypeException("Unknown authname " + authname);
+ }
+ }
+}
diff --git a/tools/draw9patch/etc/draw9patch.bat b/tools/draw9patch/etc/draw9patch.bat
index 1d56d8595..e267b0665 100755
--- a/tools/draw9patch/etc/draw9patch.bat
+++ b/tools/draw9patch/etc/draw9patch.bat
@@ -20,9 +20,9 @@ rem Set up prog to be the path of this script, including following symlinks,
rem and set up progdir to be the fully-qualified pathname of its directory.
set prog=%~f0
-rem Change current directory to where ddms is, to avoid issues with directories
-rem containing whitespaces.
-cd %~dp0
+rem Change current directory and drive to where the script is, to avoid
+rem issues with directories containing whitespaces.
+cd /d %~dp0
set jarfile=draw9patch.jar
set frameworkdir=
diff --git a/tools/draw9patch/src/com/android/draw9patch/Application.java b/tools/draw9patch/src/com/android/draw9patch/Application.java
index c7c6aaf95..68c792ab7 100644
--- a/tools/draw9patch/src/com/android/draw9patch/Application.java
+++ b/tools/draw9patch/src/com/android/draw9patch/Application.java
@@ -40,11 +40,12 @@ public class Application {
}
}
- public static void main(String... args) {
+ public static void main(final String... args) {
initUserInterface();
SwingUtilities.invokeLater(new Runnable() {
public void run() {
- MainFrame frame = new MainFrame();
+ String arg = args.length > 0 ? args[0] : null;
+ MainFrame frame = new MainFrame(arg);
frame.setDefaultCloseOperation(MainFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
diff --git a/tools/draw9patch/src/com/android/draw9patch/ui/ImageEditorPanel.java b/tools/draw9patch/src/com/android/draw9patch/ui/ImageEditorPanel.java
index 86c801f12..84b96a538 100644
--- a/tools/draw9patch/src/com/android/draw9patch/ui/ImageEditorPanel.java
+++ b/tools/draw9patch/src/com/android/draw9patch/ui/ImageEditorPanel.java
@@ -478,6 +478,14 @@ class ImageEditorPanel extends JPanel {
start = rect.x;
}
}
+ } else {
+ int start = -1;
+ for (Rectangle rect : patches) {
+ if (rect.x > start) {
+ horizontalPatchesSum += rect.width;
+ start = rect.x;
+ }
+ }
}
verticalPatchesSum = 0;
@@ -489,6 +497,14 @@ class ImageEditorPanel extends JPanel {
start = rect.y;
}
}
+ } else {
+ int start = -1;
+ for (Rectangle rect : patches) {
+ if (rect.y > start) {
+ verticalPatchesSum += rect.height;
+ start = rect.y;
+ }
+ }
}
setSize(size);
@@ -528,8 +544,7 @@ class ImageEditorPanel extends JPanel {
x = 0;
y = 0;
- if (patches.size() == 0 || horizontalPatches.size() == 0 ||
- verticalPatches.size() == 0) {
+ if (patches.size() == 0) {
g.drawImage(image, 0, 0, scaledWidth, scaledHeight, null);
g2.dispose();
return;
@@ -651,6 +666,7 @@ class ImageEditorPanel extends JPanel {
private int lastPositionX;
private int lastPositionY;
+ private int currentButton;
private boolean showCursor;
private JLabel helpLabel;
@@ -687,16 +703,20 @@ class ImageEditorPanel extends JPanel {
addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent event) {
- paint(event.getX(), event.getY(), event.isShiftDown() ? MouseEvent.BUTTON3 :
- event.getButton());
+ // Store the button here instead of retrieving it again in MouseDragged
+ // below, because on linux, calling MouseEvent.getButton() for the drag
+ // event returns 0, which appears to be technically correct (no button
+ // changed state).
+ currentButton = event.isShiftDown() ? MouseEvent.BUTTON3 : event.getButton();
+ paint(event.getX(), event.getY(), currentButton);
}
});
addMouseMotionListener(new MouseMotionAdapter() {
@Override
public void mouseDragged(MouseEvent event) {
if (!checkLockedRegion(event.getX(), event.getY())) {
- paint(event.getX(), event.getY(), event.isShiftDown() ? MouseEvent.BUTTON3 :
- event.getButton());
+ // use the stored button, see note above
+ paint(event.getX(), event.getY(), currentButton);
}
}
@@ -1023,7 +1043,15 @@ class ImageEditorPanel extends JPanel {
horizontalPatches = getRectangles(left.first, top.second);
verticalPatches = getRectangles(left.second, top.first);
} else {
- horizontalPatches = verticalPatches = new ArrayList(0);
+ if (top.first.size() > 0) {
+ horizontalPatches = new ArrayList(0);
+ verticalPatches = getVerticalRectangles(top.first);
+ } else if (left.first.size() > 0) {
+ horizontalPatches = getHorizontalRectangles(left.first);
+ verticalPatches = new ArrayList(0);
+ } else {
+ horizontalPatches = verticalPatches = new ArrayList(0);
+ }
}
row = GraphicsUtilities.getPixels(image, 0, height - 1, width, 1, row);
@@ -1036,6 +1064,28 @@ class ImageEditorPanel extends JPanel {
verticalPadding = getPadding(left.first);
}
+ private List getVerticalRectangles(List> topPairs) {
+ List rectangles = new ArrayList();
+ for (Pair top : topPairs) {
+ int x = top.first;
+ int width = top.second - top.first;
+
+ rectangles.add(new Rectangle(x, 1, width, image.getHeight() - 2));
+ }
+ return rectangles;
+ }
+
+ private List getHorizontalRectangles(List> leftPairs) {
+ List rectangles = new ArrayList();
+ for (Pair left : leftPairs) {
+ int y = left.first;
+ int height = left.second - left.first;
+
+ rectangles.add(new Rectangle(1, y, image.getWidth() - 2, height));
+ }
+ return rectangles;
+ }
+
private Pair getPadding(List> pairs) {
if (pairs.size() == 0) {
return new Pair(0, 0);
@@ -1058,7 +1108,7 @@ class ImageEditorPanel extends JPanel {
for (Pair left : leftPairs) {
int y = left.first;
int height = left.second - left.first;
- for (Pair top: topPairs) {
+ for (Pair top : topPairs) {
int x = top.first;
int width = top.second - top.first;
@@ -1103,6 +1153,7 @@ class ImageEditorPanel extends JPanel {
startWithPatch[0] = true;
fixed.clear();
}
+
return new Pair>>(fixed, patches);
}
diff --git a/tools/draw9patch/src/com/android/draw9patch/ui/ImageTransferHandler.java b/tools/draw9patch/src/com/android/draw9patch/ui/ImageTransferHandler.java
index a62884fe8..f14cd7764 100644
--- a/tools/draw9patch/src/com/android/draw9patch/ui/ImageTransferHandler.java
+++ b/tools/draw9patch/src/com/android/draw9patch/ui/ImageTransferHandler.java
@@ -36,25 +36,48 @@ class ImageTransferHandler extends TransferHandler {
@Override
public boolean importData(JComponent component, Transferable transferable) {
try {
- Object data = transferable.getTransferData(DataFlavor.javaFileListFlavor);
- //noinspection unchecked
- final File file = ((List) data).get(0);
- mainFrame.open(file).execute();
+ for (DataFlavor flavor : transferable.getTransferDataFlavors()) {
+ if (flavor.isFlavorJavaFileListType()) {
+ Object data = transferable.getTransferData(DataFlavor.javaFileListFlavor);
+ //noinspection unchecked
+ final File file = ((List) data).get(0);
+ mainFrame.open(file).execute();
+ return true;
+ } else if (flavor.isFlavorTextType()) {
+ if (flavor.getRepresentationClass() == String.class) {
+ String mime = flavor.getMimeType();
+ DataFlavor flave = new DataFlavor(mime);
+ Object data = transferable.getTransferData(flave);
+ final String path = convertPath(data.toString());
+ mainFrame.open(new File(path)).execute();
+ return true;
+ }
+ }
+ }
} catch (UnsupportedFlavorException e) {
- return false;
+ // Ignore
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
+ } catch (Exception e) {
+ e.printStackTrace();
}
- return true;
+ return false;
+ }
+
+ private static String convertPath(String path) {
+ if (path.startsWith("file://")) path = path.substring("file://".length());
+ if (path.indexOf('\n') != -1) path = path.substring(0, path.indexOf('\n'));
+ if (path.indexOf('\r') != -1) path = path.substring(0, path.indexOf('\r'));
+ return path;
}
@Override
public boolean canImport(JComponent component, DataFlavor[] dataFlavors) {
for (DataFlavor flavor : dataFlavors) {
- if (flavor.isFlavorJavaFileListType()) {
+ if (flavor.isFlavorJavaFileListType() || flavor.isFlavorTextType()) {
return true;
}
}
diff --git a/tools/draw9patch/src/com/android/draw9patch/ui/MainFrame.java b/tools/draw9patch/src/com/android/draw9patch/ui/MainFrame.java
index 9ffd93e6a..d5b640918 100644
--- a/tools/draw9patch/src/com/android/draw9patch/ui/MainFrame.java
+++ b/tools/draw9patch/src/com/android/draw9patch/ui/MainFrame.java
@@ -40,14 +40,24 @@ public class MainFrame extends JFrame {
private JMenuItem saveMenuItem;
private ImageEditorPanel imageEditor;
- public MainFrame() throws HeadlessException {
+ public MainFrame(String path) throws HeadlessException {
super("Draw 9-patch");
buildActions();
buildMenuBar();
buildContent();
- showOpenFilePanel();
+ if (path == null) {
+ showOpenFilePanel();
+ } else {
+ try {
+ File file = new File(path);
+ BufferedImage img = GraphicsUtilities.loadCompatibleImage(file.toURI().toURL());
+ showImageEditor(img, file.getAbsolutePath());
+ } catch (Exception ex) {
+ showOpenFilePanel();
+ }
+ }
// pack();
setSize(1024, 600);
diff --git a/tools/eclipse/buildConfig/allElements.xml b/tools/eclipse/buildConfig/allElements.xml
index 99ab3aad8..2c8229c28 100644
--- a/tools/eclipse/buildConfig/allElements.xml
+++ b/tools/eclipse/buildConfig/allElements.xml
@@ -14,6 +14,11 @@
+
+
+
+
+
@@ -44,6 +49,10 @@
+
+
+
+
diff --git a/tools/eclipse/changes.txt b/tools/eclipse/changes.txt
index 5cb82452a..8cd843e20 100644
--- a/tools/eclipse/changes.txt
+++ b/tools/eclipse/changes.txt
@@ -1,14 +1,19 @@
0.9.0 (work in progress)
-- Support for SDK with multiple versions of the Android platform and vendor supplied add-ons.
+- Projects now store generated Java files (R.java/Manifest.java and output from aidl) in a 'gen' source folder.
+- Support for the new Android SDK with support for multiple versions of the Android platform and for vendor supplied add-ons.
+ * New Project Wizard lets you choose which platform/add-on to target.
+ * Project properties (right click project in Package Explorer, then "Properties"), lets you edit project target.
+ * New Launch configuration option to choose debug deployment target.
+- Ability to export multiple apk from one project, using resource filters. See the 'android' property for Android projects.
0.8.1:
-- Alternate Layout wizard. In the layout editor, the "create" button is now enabled, and allows to easily create alternate versions.
+- Alternate Layout wizard. In the layout editor, the "create" button is now enabled to easily create alternate versions of the current layout.
- Fixed issue with custom themes/styles in the layout editor.
-- Export Wizard: To export an application for release, sign with a non debug key. Accessible from the export menu, from the Android Tools contextual menu, or from the overview page of the manifest editor.
+- Export Wizard: To export an application for release, and sign it with a non debug key. Accessible from the export menu, from the Android Tools contextual menu, or from the overview page of the manifest editor.
- New XML File Wizard: To easily create new XML resources file in the /res directory.
- New checks on launch when attempting to debug on a device.
-- Basic support for drag'n'drop in Graphical layout editor. You can add new items by drag'n'drop from the palette. There's is no support for moving/resizing yet.
+- Basic support for drag'n'drop in Graphical layout editor. You can add new items by drag'n'drop from the palette. There is no support for moving/resizing yet.
- Undo/redo support in all XML form editors and Graphical layout editor.
0.8.0:
diff --git a/tools/eclipse/features/com.android.ide.eclipse.adt/feature.xml b/tools/eclipse/features/com.android.ide.eclipse.adt/feature.xml
index 676a89e49..97bc8b14a 100644
--- a/tools/eclipse/features/com.android.ide.eclipse.adt/feature.xml
+++ b/tools/eclipse/features/com.android.ide.eclipse.adt/feature.xml
@@ -7,15 +7,101 @@
plugin="com.android.ide.eclipse.adt">
- This feature provides support for Android Projects in Eclipse.
+ Android Developer Tools.
Copyright (C) 2007 The Android Open Source Project
-
- License TBD.
+
+ Eclipse Public License - v 1.0
+
+THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.
+
+1. DEFINITIONS
+
+"Contribution" means:
+
+a) in the case of the initial Contributor, the initial code and documentation distributed under this Agreement, and
+
+b) in the case of each subsequent Contributor:
+
+i) changes to the Program, and
+
+ii) additions to the Program;
+
+where such changes and/or additions to the Program originate from and are distributed by that particular Contributor. A Contribution 'originates' from a Contributor if it was added to the Program by such Contributor itself or anyone acting on such Contributor's behalf. Contributions do not include additions to the Program which: (i) are separate modules of software distributed in conjunction with the Program under their own license agreement, and (ii) are not derivative works of the Program.
+
+"Contributor" means any person or entity that distributes the Program.
+
+"Licensed Patents" mean patent claims licensable by a Contributor which are necessarily infringed by the use or sale of its Contribution alone or when combined with the Program.
+
+"Program" means the Contributions distributed in accordance with this Agreement.
+
+"Recipient" means anyone who receives the Program under this Agreement, including all Contributors.
+
+2. GRANT OF RIGHTS
+
+a) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, distribute and sublicense the Contribution of such Contributor, if any, and such derivative works, in source code and object code form.
+
+b) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free patent license under Licensed Patents to make, use, sell, offer to sell, import and otherwise transfer the Contribution of such Contributor, if any, in source code and object code form. This patent license shall apply to the combination of the Contribution and the Program if, at the time the Contribution is added by the Contributor, such addition of the Contribution causes such combination to be covered by the Licensed Patents. The patent license shall not apply to any other combinations which include the Contribution. No hardware per se is licensed hereunder.
+
+c) Recipient understands that although each Contributor grants the licenses to its Contributions set forth herein, no assurances are provided by any Contributor that the Program does not infringe the patent or other intellectual property rights of any other entity. Each Contributor disclaims any liability to Recipient for claims brought by any other entity based on infringement of intellectual property rights or otherwise. As a condition to exercising the rights and licenses granted hereunder, each Recipient hereby assumes sole responsibility to secure any other intellectual property rights needed, if any. For example, if a third party patent license is required to allow Recipient to distribute the Program, it is Recipient's responsibility to acquire that license before distributing the Program.
+
+d) Each Contributor represents that to its knowledge it has sufficient copyright rights in its Contribution, if any, to grant the copyright license set forth in this Agreement.
+
+3. REQUIREMENTS
+
+A Contributor may choose to distribute the Program in object code form under its own license agreement, provided that:
+
+a) it complies with the terms and conditions of this Agreement; and
+
+b) its license agreement:
+
+i) effectively disclaims on behalf of all Contributors all warranties and conditions, express and implied, including warranties or conditions of title and non-infringement, and implied warranties or conditions of merchantability and fitness for a particular purpose;
+
+ii) effectively excludes on behalf of all Contributors all liability for damages, including direct, indirect, special, incidental and consequential damages, such as lost profits;
+
+iii) states that any provisions which differ from this Agreement are offered by that Contributor alone and not by any other party; and
+
+iv) states that source code for the Program is available from such Contributor, and informs licensees how to obtain it in a reasonable manner on or through a medium customarily used for software exchange.
+
+When the Program is made available in source code form:
+
+a) it must be made available under this Agreement; and
+
+b) a copy of this Agreement must be included with each copy of the Program.
+
+Contributors may not remove or alter any copyright notices contained within the Program.
+
+Each Contributor must identify itself as the originator of its Contribution, if any, in a manner that reasonably allows subsequent Recipients to identify the originator of the Contribution.
+
+4. COMMERCIAL DISTRIBUTION
+
+Commercial distributors of software may accept certain responsibilities with respect to end users, business partners and the like. While this license is intended to facilitate the commercial use of the Program, the Contributor who includes the Program in a commercial product offering should do so in a manner which does not create potential liability for other Contributors. Therefore, if a Contributor includes the Program in a commercial product offering, such Contributor ("Commercial Contributor") hereby agrees to defend and indemnify every other Contributor ("Indemnified Contributor") against any losses, damages and costs (collectively "Losses") arising from claims, lawsuits and other legal actions brought by a third party against the Indemnified Contributor to the extent caused by the acts or omissions of such Commercial Contributor in connection with its distribution of the Program in a commercial product offering. The obligations in this section do not apply to any claims or Losses relating to any actual or alleged intellectual property infringement. In order to qualify, an Indemnified Contributor must: a) promptly notify the Commercial Contributor in writing of such claim, and b) allow the Commercial Contributor to control, and cooperate with the Commercial Contributor in, the defense and any related settlement negotiations. The Indemnified Contributor may participate in any such claim at its own expense.
+
+For example, a Contributor might include the Program in a commercial product offering, Product X. That Contributor is then a Commercial Contributor. If that Commercial Contributor then makes performance claims, or offers warranties related to Product X, those performance claims and warranties are such Commercial Contributor's responsibility alone. Under this section, the Commercial Contributor would have to defend claims against the other Contributors related to those performance claims and warranties, and if a court requires any other Contributor to pay any damages as a result, the Commercial Contributor must pay those damages.
+
+5. NO WARRANTY
+
+EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely responsible for determining the appropriateness of using and distributing the Program and assumes all risks associated with its exercise of rights under this Agreement , including but not limited to the risks and costs of program errors, compliance with applicable laws, damage to or loss of data, programs or equipment, and unavailability or interruption of operations.
+
+6. DISCLAIMER OF LIABILITY
+
+EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+7. GENERAL
+
+If any provision of this Agreement is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this Agreement, and without further action by the parties hereto, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.
+
+If Recipient institutes patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Program itself (excluding combinations of the Program with other software or hardware) infringes such Recipient's patent(s), then such Recipient's rights granted under Section 2(b) shall terminate as of the date such litigation is filed.
+
+All Recipient's rights under this Agreement shall terminate if it fails to comply with any of the material terms or conditions of this Agreement and does not cure such failure in a reasonable period of time after becoming aware of such noncompliance. If all Recipient's rights under this Agreement terminate, Recipient agrees to cease use and distribution of the Program as soon as reasonably practicable. However, Recipient's obligations under this Agreement and any licenses granted by Recipient relating to the Program shall continue and survive.
+
+Everyone is permitted to copy and distribute copies of this Agreement, but in order to avoid inconsistency the Agreement is copyrighted and may only be modified in the following manner. The Agreement Steward reserves the right to publish new versions (including revisions) of this Agreement from time to time. No one other than the Agreement Steward has the right to modify this Agreement. The Eclipse Foundation is the initial Agreement Steward. The Eclipse Foundation may assign the responsibility to serve as the Agreement Steward to a suitable separate entity. Each new version of the Agreement will be given a distinguishing version number. The Program (including Contributions) may always be distributed subject to the version of the Agreement under which it was received. In addition, after a new version of the Agreement is published, Contributor may elect to distribute the Program (including its Contributions) under the new version. Except as expressly stated in Sections 2(a) and 2(b) above, Recipient receives no rights or licenses to the intellectual property of any Contributor under this Agreement, whether expressly, by implication, estoppel or otherwise. All rights in the Program not expressly granted under this Agreement are reserved.
+
+This Agreement is governed by the laws of the State of New York and the intellectual property laws of the United States of America. No party to this Agreement will bring a legal action under this Agreement more than one year after the cause of action arose. Each party waives its rights to a jury trial in any resulting litigation.
@@ -23,6 +109,7 @@
+
@@ -47,6 +134,7 @@
+
-
-
diff --git a/tools/eclipse/features/com.android.ide.eclipse.ddms/.project b/tools/eclipse/features/com.android.ide.eclipse.ddms/.project
new file mode 100644
index 000000000..f80ff609a
--- /dev/null
+++ b/tools/eclipse/features/com.android.ide.eclipse.ddms/.project
@@ -0,0 +1,17 @@
+
+
+ ddms-feature
+
+
+
+
+
+ org.eclipse.pde.FeatureBuilder
+
+
+
+
+
+ org.eclipse.pde.FeatureNature
+
+
diff --git a/tools/eclipse/features/com.android.ide.eclipse.ddms/build.properties b/tools/eclipse/features/com.android.ide.eclipse.ddms/build.properties
new file mode 100644
index 000000000..64f93a9f0
--- /dev/null
+++ b/tools/eclipse/features/com.android.ide.eclipse.ddms/build.properties
@@ -0,0 +1 @@
+bin.includes = feature.xml
diff --git a/tools/eclipse/features/com.android.ide.eclipse.ddms/feature.xml b/tools/eclipse/features/com.android.ide.eclipse.ddms/feature.xml
new file mode 100644
index 000000000..dfdf98569
--- /dev/null
+++ b/tools/eclipse/features/com.android.ide.eclipse.ddms/feature.xml
@@ -0,0 +1,237 @@
+
+
+
+
+ Android Dalvik Debug Monitor Service
+
+
+
+ Copyright (C) 2007 The Android Open Source Project
+
+
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ 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.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/.classpath b/tools/eclipse/plugins/com.android.ide.eclipse.adt/.classpath
index c3c8c1004..a24fc8721 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/.classpath
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/.classpath
@@ -10,7 +10,7 @@
-
-
+
+
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/META-INF/MANIFEST.MF b/tools/eclipse/plugins/com.android.ide.eclipse.adt/META-INF/MANIFEST.MF
index a464d5c48..3750f6602 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/META-INF/MANIFEST.MF
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/META-INF/MANIFEST.MF
@@ -39,10 +39,12 @@ Require-Bundle: com.android.ide.eclipse.ddms,
org.eclipse.wst.sse.core,
org.eclipse.wst.sse.ui,
org.eclipse.wst.xml.core,
- org.eclipse.wst.xml.ui
+ org.eclipse.wst.xml.ui,
+ org.eclipse.jdt.junit
Eclipse-LazyStart: true
Export-Package: com.android.ide.eclipse.adt,
com.android.ide.eclipse.adt.build;x-friends:="com.android.ide.eclipse.tests",
+ com.android.ide.eclipse.adt.launch;x-friends:="com.android.ide.eclipse.tests",
com.android.ide.eclipse.adt.project;x-friends:="com.android.ide.eclipse.tests",
com.android.ide.eclipse.adt.project.internal;x-friends:="com.android.ide.eclipse.tests",
com.android.ide.eclipse.adt.sdk;x-friends:="com.android.ide.eclipse.tests",
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/icons/new_adt_project.png b/tools/eclipse/plugins/com.android.ide.eclipse.adt/icons/new_adt_project.png
new file mode 100644
index 000000000..0f0e883f0
Binary files /dev/null and b/tools/eclipse/plugins/com.android.ide.eclipse.adt/icons/new_adt_project.png differ
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/icons/new_xml.png b/tools/eclipse/plugins/com.android.ide.eclipse.adt/icons/new_xml.png
new file mode 100644
index 000000000..8273185ed
Binary files /dev/null and b/tools/eclipse/plugins/com.android.ide.eclipse.adt/icons/new_xml.png differ
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml b/tools/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml
index ade464661..39e6dd584 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml
@@ -17,6 +17,14 @@
+
+
+
+
+
@@ -132,8 +140,8 @@
-
+ id="com.android.ide.eclipse.editors.wizards.NewXmlFileWizard">
@@ -337,24 +339,24 @@
name="Debug Android Application"
description="Debug Android Application"
categoryId="org.eclipse.debug.ui.category.run"
- id="com.android.ide.eclipse.adt.debug.launching.LaunchShortcut.debug">
+ id="com.android.ide.eclipse.adt.launch.LaunchShortcut.debug">
+ id="com.android.ide.eclipse.adt.launch.LaunchShortcut.run">
@@ -464,4 +466,48 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtPlugin.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtPlugin.java
index 62bc7edf2..48a21d1d6 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtPlugin.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtPlugin.java
@@ -20,8 +20,7 @@ import com.android.ddmuilib.StackTracePanel;
import com.android.ddmuilib.StackTracePanel.ISourceRevealer;
import com.android.ddmuilib.console.DdmConsole;
import com.android.ddmuilib.console.IDdmConsole;
-import com.android.ide.eclipse.adt.build.DexWrapper;
-import com.android.ide.eclipse.adt.debug.launching.AndroidLaunchController;
+import com.android.ide.eclipse.adt.launch.AndroidLaunchController;
import com.android.ide.eclipse.adt.preferences.BuildPreferencePage;
import com.android.ide.eclipse.adt.project.ProjectHelper;
import com.android.ide.eclipse.adt.project.export.ExportWizard;
@@ -29,6 +28,7 @@ import com.android.ide.eclipse.adt.project.internal.AndroidClasspathContainerIni
import com.android.ide.eclipse.adt.sdk.AndroidTargetParser;
import com.android.ide.eclipse.adt.sdk.LoadStatus;
import com.android.ide.eclipse.adt.sdk.Sdk;
+import com.android.ide.eclipse.adt.sdk.Sdk.ITargetChangeListener;
import com.android.ide.eclipse.common.AndroidConstants;
import com.android.ide.eclipse.common.EclipseUiHelper;
import com.android.ide.eclipse.common.SdkStatsHelper;
@@ -166,11 +166,16 @@ public class AdtPlugin extends AbstractUIPlugin {
/** Load status of the SDK. Any access MUST be in a synchronized(mPostLoadProjects) block */
private LoadStatus mSdkIsLoaded = LoadStatus.LOADING;
/** Project to update once the SDK is loaded.
- * Any access MUST be in a synchronized(mPostLoadProjects) block */
- private final ArrayList mPostLoadProjects = new ArrayList();
+ * Any access MUST be in a synchronized(mPostLoadProjectsToResolve) block */
+ private final ArrayList mPostLoadProjectsToResolve =
+ new ArrayList();
+ /** Project to check validity of cache vs actual once the SDK is loaded.
+ * Any access MUST be in a synchronized(mPostLoadProjectsToResolve) block */
+ private final ArrayList mPostLoadProjectsToCheck = new ArrayList();
private ResourceMonitor mResourceMonitor;
- private ArrayList mResourceRefreshListener = new ArrayList();
+ private ArrayList mTargetChangeListeners =
+ new ArrayList();
/**
* Custom PrintStream for Dx output. This class overrides the method
@@ -306,12 +311,12 @@ public class AdtPlugin extends AbstractUIPlugin {
if (checkSdkLocationAndId()) {
// if sdk if valid, reparse it
- // add the current Android project to the list of projects to be updated
+ // add all the opened Android projects to the list of projects to be updated
// after the SDK is reloaded
- synchronized (mPostLoadProjects) {
+ synchronized (getSdkLockObject()) {
// get the project to refresh.
IJavaProject[] androidProjects = BaseProjectHelper.getAndroidProjects();
- mPostLoadProjects.addAll(Arrays.asList(androidProjects));
+ mPostLoadProjectsToResolve.addAll(Arrays.asList(androidProjects));
}
// parse the SDK resources at the new location
@@ -419,8 +424,6 @@ public class AdtPlugin extends AbstractUIPlugin {
stopEditors();
- DexWrapper.unloadDex();
-
mRed.dispose();
synchronized (AdtPlugin.class) {
sPlugin = null;
@@ -461,21 +464,11 @@ public class AdtPlugin extends AbstractUIPlugin {
return SdkConstants.OS_SDK_TOOLS_FOLDER + AndroidConstants.FN_ADB;
}
- /** Returns the aapt path relative to the sdk folder */
- public static String getOsRelativeAapt() {
- return SdkConstants.OS_SDK_TOOLS_FOLDER + AndroidConstants.FN_AAPT;
- }
-
/** Returns the emulator path relative to the sdk folder */
public static String getOsRelativeEmulator() {
return SdkConstants.OS_SDK_TOOLS_FOLDER + AndroidConstants.FN_EMULATOR;
}
- /** Returns the aidl path relative to the sdk folder */
- public static String getOsRelativeAidl() {
- return SdkConstants.OS_SDK_TOOLS_FOLDER + AndroidConstants.FN_AIDL;
- }
-
/** Returns the absolute adb path */
public static String getOsAbsoluteAdb() {
return getOsSdkFolder() + getOsRelativeAdb();
@@ -487,21 +480,11 @@ public class AdtPlugin extends AbstractUIPlugin {
AndroidConstants.FN_TRACEVIEW;
}
- /** Returns the absolute aapt path */
- public static String getOsAbsoluteAapt() {
- return getOsSdkFolder() + getOsRelativeAapt();
- }
-
/** Returns the absolute emulator path */
public static String getOsAbsoluteEmulator() {
return getOsSdkFolder() + getOsRelativeEmulator();
}
- /** Returns the absolute aidl path */
- public static String getOsAbsoluteAidl() {
- return getOsSdkFolder() + getOsRelativeAidl();
- }
-
/**
* Returns a Url file path to the javaDoc folder.
*/
@@ -869,19 +852,43 @@ public class AdtPlugin extends AbstractUIPlugin {
}
/**
- * Returns whether the Sdk has been loaded. If the SDK has not been loaded, the given
- * project is added to a list of projects to recompile after the SDK is loaded.
+ * Returns whether the Sdk has been loaded.
*/
- public LoadStatus getSdkLoadStatus(IJavaProject project) {
- synchronized (mPostLoadProjects) {
- // only add the project to the list, if we are still loading.
- if (mSdkIsLoaded == LoadStatus.LOADING && project != null) {
- mPostLoadProjects.add(project);
- }
-
+ public final LoadStatus getSdkLoadStatus() {
+ synchronized (getSdkLockObject()) {
return mSdkIsLoaded;
}
}
+
+ /**
+ * Returns the lock object for SDK loading. If you wish to do things while the SDK is loading,
+ * you must synchronize on this object.
+ */
+ public final Object getSdkLockObject() {
+ return mPostLoadProjectsToResolve;
+ }
+
+ /**
+ * Sets the given {@link IJavaProject} to have its target resolved again once the SDK finishes
+ * to load.
+ */
+ public final void setProjectToResolve(IJavaProject javaProject) {
+ synchronized (getSdkLockObject()) {
+ mPostLoadProjectsToResolve.add(javaProject);
+ }
+ }
+
+ /**
+ * Sets the given {@link IJavaProject} to have its target checked for consistency
+ * once the SDK finishes to load. This is used if the target is resolved using cached
+ * information while the SDK is loading.
+ */
+ public final void setProjectToCheck(IJavaProject javaProject) {
+ // only lock on
+ synchronized (getSdkLockObject()) {
+ mPostLoadProjectsToCheck.add(javaProject);
+ }
+ }
/**
* Checks the location of the SDK is valid and if it is, grab the SDK API version
@@ -939,8 +946,6 @@ public class AdtPlugin extends AbstractUIPlugin {
// check the path to various tools we use
String[] filesToCheck = new String[] {
osSdkLocation + getOsRelativeAdb(),
- osSdkLocation + getOsRelativeAapt(),
- osSdkLocation + getOsRelativeAidl(),
osSdkLocation + getOsRelativeEmulator()
};
for (String file : filesToCheck) {
@@ -982,7 +987,7 @@ public class AdtPlugin extends AbstractUIPlugin {
Constants.BUNDLE_VERSION);
Version version = new Version(versionString);
- SdkStatsHelper.pingUsageServer("editors", version); //$NON-NLS-1$
+ SdkStatsHelper.pingUsageServer("adt", version); //$NON-NLS-1$
return Status.OK_STATUS;
} catch (Throwable t) {
@@ -1015,61 +1020,69 @@ public class AdtPlugin extends AbstractUIPlugin {
progress.setTaskName(Messages.AdtPlugin_Parsing_Resources);
- for (IAndroidTarget target : sdk.getTargets()) {
- IStatus status = new AndroidTargetParser(target).run(progress);
- if (status.getCode() != IStatus.OK) {
- synchronized (mPostLoadProjects) {
- mSdkIsLoaded = LoadStatus.FAILED;
- mPostLoadProjects.clear();
+ int n = sdk.getTargets().length;
+ if (n > 0) {
+ int w = 60 / n;
+ for (IAndroidTarget target : sdk.getTargets()) {
+ SubMonitor p2 = progress.newChild(w);
+ IStatus status = new AndroidTargetParser(target).run(p2);
+ if (status.getCode() != IStatus.OK) {
+ synchronized (getSdkLockObject()) {
+ mSdkIsLoaded = LoadStatus.FAILED;
+ mPostLoadProjectsToResolve.clear();
+ }
+ return status;
}
- return status;
}
}
- // FIXME: move this per platform, or somewhere else.
- progress = SubMonitor.convert(monitor,
- Messages.AdtPlugin_Parsing_Resources, 20);
- DexWrapper.unloadDex();
-
- IStatus res = DexWrapper.loadDex(
- mOsSdkLocation + AndroidConstants.OS_SDK_LIBS_DX_JAR);
- if (res != Status.OK_STATUS) {
- synchronized (mPostLoadProjects) {
- mSdkIsLoaded = LoadStatus.FAILED;
- mPostLoadProjects.clear();
- }
- return res;
- }
-
- synchronized (mPostLoadProjects) {
+ synchronized (getSdkLockObject()) {
mSdkIsLoaded = LoadStatus.LOADED;
+ progress.setTaskName("Check Projects");
+
+ // check the projects that need checking.
+ // The method modifies the list (it removes the project that
+ // do not need to be resolved again).
+ AndroidClasspathContainerInitializer.checkProjectsCache(
+ mPostLoadProjectsToCheck);
+
+ mPostLoadProjectsToResolve.addAll(mPostLoadProjectsToCheck);
+
// update the project that needs recompiling.
- if (mPostLoadProjects.size() > 0) {
- IJavaProject[] array = mPostLoadProjects.toArray(
- new IJavaProject[mPostLoadProjects.size()]);
+ if (mPostLoadProjectsToResolve.size() > 0) {
+ IJavaProject[] array = mPostLoadProjectsToResolve.toArray(
+ new IJavaProject[mPostLoadProjectsToResolve.size()]);
AndroidClasspathContainerInitializer.updateProjects(array);
- mPostLoadProjects.clear();
+ mPostLoadProjectsToResolve.clear();
}
+
+ progress.worked(10);
}
}
// Notify resource changed listeners
- progress.subTask("Refresh UI");
- progress.setWorkRemaining(mResourceRefreshListener.size());
+ progress.setTaskName("Refresh UI");
+ progress.setWorkRemaining(mTargetChangeListeners.size());
// Clone the list before iterating, to avoid Concurrent Modification
// exceptions
- List listeners = (List)mResourceRefreshListener.clone();
- for (Runnable listener : listeners) {
- try {
- AdtPlugin.getDisplay().syncExec(listener);
- } catch (Exception e) {
- AdtPlugin.log(e, "ResourceRefreshListener Failed"); //$NON-NLS-1$
- } finally {
- progress.worked(1);
+ final List listeners =
+ (List)mTargetChangeListeners.clone();
+ final SubMonitor progress2 = progress;
+ AdtPlugin.getDisplay().syncExec(new Runnable() {
+ public void run() {
+ for (ITargetChangeListener listener : listeners) {
+ try {
+ listener.onTargetsLoaded();
+ } catch (Exception e) {
+ AdtPlugin.log(e, "Failed to update a TargetChangeListener."); //$NON-NLS-1$
+ } finally {
+ progress2.worked(1);
+ }
+ }
}
- }
+ });
} finally {
if (monitor != null) {
monitor.done();
@@ -1315,12 +1328,42 @@ public class AdtPlugin extends AbstractUIPlugin {
}, IResourceDelta.ADDED | IResourceDelta.CHANGED);
}
- public void addResourceChangedListener(Runnable resourceRefreshListener) {
- mResourceRefreshListener.add(resourceRefreshListener);
+ /**
+ * Adds a new {@link ITargetChangeListener} to be notified when a new SDK is loaded, or when
+ * a project has its target changed.
+ */
+ public void addTargetListener(ITargetChangeListener listener) {
+ mTargetChangeListeners.add(listener);
}
- public void removeResourceChangedListener(Runnable resourceRefreshListener) {
- mResourceRefreshListener.remove(resourceRefreshListener);
+ /**
+ * Removes an existing {@link ITargetChangeListener}.
+ * @see #addTargetListener(ITargetChangeListener)
+ */
+ public void removeTargetListener(ITargetChangeListener listener) {
+ mTargetChangeListeners.remove(listener);
+ }
+
+ /**
+ * Updates all the {@link ITargetChangeListener} that a target has changed for a given project.
+ * Only editors related to that project should reload.
+ */
+ @SuppressWarnings("unchecked")
+ public void updateTargetListener(final IProject project) {
+ final List listeners =
+ (List)mTargetChangeListeners.clone();
+
+ AdtPlugin.getDisplay().asyncExec(new Runnable() {
+ public void run() {
+ for (ITargetChangeListener listener : listeners) {
+ try {
+ listener.onProjectTargetChange(project);
+ } catch (Exception e) {
+ AdtPlugin.log(e, "Failed to update a TargetChangeListener."); //$NON-NLS-1$
+ }
+ }
+ }
+ });
}
public static synchronized OutputStream getErrorStream() {
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/ApkBuilder.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/ApkBuilder.java
index 6743246b5..f8a969e94 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/ApkBuilder.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/ApkBuilder.java
@@ -19,7 +19,7 @@ package com.android.ide.eclipse.adt.build;
import com.android.ide.eclipse.adt.AdtConstants;
import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.adt.project.ProjectHelper;
-import com.android.ide.eclipse.adt.sdk.LoadStatus;
+import com.android.ide.eclipse.adt.sdk.AndroidTargetData;
import com.android.ide.eclipse.adt.sdk.Sdk;
import com.android.ide.eclipse.common.AndroidConstants;
import com.android.ide.eclipse.common.project.BaseProjectHelper;
@@ -67,6 +67,8 @@ import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Map;
+import java.util.Set;
+import java.util.Map.Entry;
public class ApkBuilder extends BaseBuilder {
@@ -181,7 +183,7 @@ public class ApkBuilder extends BaseBuilder {
return mMakeFinalPackage;
}
}
-
+
/**
* {@link IZipEntryFilter} to filter out everything that is not a standard java resources.
* Used in {@link SignedJarBuilder#writeZip(java.io.InputStream, IZipEntryFilter)} when
@@ -201,6 +203,9 @@ public class ApkBuilder extends BaseBuilder {
// get a project object
IProject project = getProject();
+ // Top level check to make sure the build can move forward.
+ abortOnBadSetup(project);
+
// get the list of referenced projects.
IProject[] referencedProjects = ProjectHelper.getReferencedProjects(project);
IJavaProject[] referencedJavaProjects = getJavaProjects(referencedProjects);
@@ -215,6 +220,7 @@ public class ApkBuilder extends BaseBuilder {
// First thing we do is go through the resource delta to not
// lose it if we have to abort the build for any reason.
+ ApkDeltaVisitor dv = null;
if (kind == FULL_BUILD) {
AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project,
Messages.Start_Full_Apk_Build);
@@ -233,22 +239,13 @@ public class ApkBuilder extends BaseBuilder {
mConvertToDex = true;
mBuildFinalPackage = true;
} else {
- ApkDeltaVisitor dv = new ApkDeltaVisitor(this, sourceList, outputFolder);
+ dv = new ApkDeltaVisitor(this, sourceList, outputFolder);
delta.accept(dv);
// save the state
mPackageResources |= dv.getPackageResources();
mConvertToDex |= dv.getConvertToDex();
mBuildFinalPackage |= dv.getMakeFinalPackage();
-
- if (dv.mXmlError) {
- AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project,
- Messages.Xml_Error);
-
- // if there was some XML errors, we just return w/o doing
- // anything since we've put some markers in the files anyway
- return referencedProjects;
- }
}
// also go through the delta for all the referenced projects, until we are forced to
@@ -258,78 +255,28 @@ public class ApkBuilder extends BaseBuilder {
IJavaProject referencedJavaProject = referencedJavaProjects[i];
delta = getDelta(referencedJavaProject.getProject());
if (delta != null) {
- ReferencedProjectDeltaVisitor dv = new ReferencedProjectDeltaVisitor(
+ ReferencedProjectDeltaVisitor refProjectDv = new ReferencedProjectDeltaVisitor(
referencedJavaProject);
- delta.accept(dv);
+ delta.accept(refProjectDv);
// save the state
- mConvertToDex |= dv.needDexConvertion();
- mBuildFinalPackage |= dv.needMakeFinalPackage();
+ mConvertToDex |= refProjectDv.needDexConvertion();
+ mBuildFinalPackage |= refProjectDv.needMakeFinalPackage();
}
}
}
-
- // do some extra check, in case the output files are not present. This
- // will force to recreate them.
- IResource tmp = null;
-
- if (mPackageResources == false && outputFolder != null) {
- tmp = outputFolder.findMember(AndroidConstants.FN_RESOURCES_AP_);
- if (tmp == null || tmp.exists() == false) {
- mPackageResources = true;
- mBuildFinalPackage = true;
- }
- }
- if (mConvertToDex == false && outputFolder != null) {
- tmp = outputFolder.findMember(AndroidConstants.FN_CLASSES_DEX);
- if (tmp == null || tmp.exists() == false) {
- mConvertToDex = true;
- mBuildFinalPackage = true;
- }
- }
-
- // also check the final file!
- String finalPackageName = project.getName() + AndroidConstants.DOT_ANDROID_PACKAGE;
- if (mBuildFinalPackage == false && outputFolder != null) {
- tmp = outputFolder.findMember(finalPackageName);
- if (tmp == null || (tmp instanceof IFile &&
- tmp.exists() == false)) {
- String msg = String.format(Messages.s_Missing_Repackaging, finalPackageName);
- AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project, msg);
- mBuildFinalPackage = true;
- }
- }
-
+
// store the build status in the persistent storage
saveProjectBooleanProperty(PROPERTY_CONVERT_TO_DEX , mConvertToDex);
saveProjectBooleanProperty(PROPERTY_PACKAGE_RESOURCES, mPackageResources);
saveProjectBooleanProperty(PROPERTY_BUILD_APK, mBuildFinalPackage);
- // At this point, we can abort the build if we have to, as we have computed
- // our resource delta and stored the result.
-
- // check if we have finished loading the SDK.
- if (AdtPlugin.getDefault().getSdkLoadStatus(javaProject) != LoadStatus.LOADED) {
- // we exit silently
- return referencedProjects;
- }
-
- // Now check the compiler compliance level, not displaying the error
- // message since this is not the first builder.
- if (ProjectHelper.checkCompilerCompliance(getProject())
- != ProjectHelper.COMPILER_COMPLIANCE_OK) {
- return referencedProjects;
- }
-
- // now check if the project has problem marker already
- if (ProjectHelper.hasError(project, true)) {
- // we found a marker with error severity: we abort the build.
- // Since this is going to happen every time we save a file while
- // errors are remaining, we do not force the display of the console, which
- // would, in most cases, show on top of the Problem view (which is more
- // important in that case).
+ if (dv != null && dv.mXmlError) {
AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project,
- Messages.Project_Has_Errors);
+ Messages.Xml_Error);
+
+ // if there was some XML errors, we just return w/o doing
+ // anything since we've put some markers in the files anyway
return referencedProjects;
}
@@ -350,6 +297,82 @@ public class ApkBuilder extends BaseBuilder {
return referencedProjects;
}
+ // get the extra configs for the project.
+ // The map contains (name, filter) where 'name' is a name to be used in the apk filename,
+ // and filter is the resource filter to be used in the aapt -c parameters to restrict
+ // which resource configurations to package in the apk.
+ Map configs = Sdk.getCurrent().getProjectApkConfigs(project);
+
+ // do some extra check, in case the output files are not present. This
+ // will force to recreate them.
+ IResource tmp = null;
+
+ if (mPackageResources == false) {
+ // check the full resource package
+ tmp = outputFolder.findMember(AndroidConstants.FN_RESOURCES_AP_);
+ if (tmp == null || tmp.exists() == false) {
+ mPackageResources = true;
+ mBuildFinalPackage = true;
+ } else {
+ // if the full package is present, we check the filtered resource packages as well
+ if (configs != null) {
+ Set> entrySet = configs.entrySet();
+
+ for (Entry entry : entrySet) {
+ String filename = String.format(AndroidConstants.FN_RESOURCES_S_AP_,
+ entry.getKey());
+
+ tmp = outputFolder.findMember(filename);
+ if (tmp == null || (tmp instanceof IFile &&
+ tmp.exists() == false)) {
+ String msg = String.format(Messages.s_Missing_Repackaging, filename);
+ AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project, msg);
+ mPackageResources = true;
+ mBuildFinalPackage = true;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ // check classes.dex is present. If not we force to recreate it.
+ if (mConvertToDex == false) {
+ tmp = outputFolder.findMember(AndroidConstants.FN_CLASSES_DEX);
+ if (tmp == null || tmp.exists() == false) {
+ mConvertToDex = true;
+ mBuildFinalPackage = true;
+ }
+ }
+
+ // also check the final file(s)!
+ String finalPackageName = ProjectHelper.getApkFilename(project, null /*config*/);
+ if (mBuildFinalPackage == false) {
+ tmp = outputFolder.findMember(finalPackageName);
+ if (tmp == null || (tmp instanceof IFile &&
+ tmp.exists() == false)) {
+ String msg = String.format(Messages.s_Missing_Repackaging, finalPackageName);
+ AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project, msg);
+ mBuildFinalPackage = true;
+ } else if (configs != null) {
+ // if the full apk is present, we check the filtered apk as well
+ Set> entrySet = configs.entrySet();
+
+ for (Entry entry : entrySet) {
+ String filename = ProjectHelper.getApkFilename(project, entry.getKey());
+
+ tmp = outputFolder.findMember(filename);
+ if (tmp == null || (tmp instanceof IFile &&
+ tmp.exists() == false)) {
+ String msg = String.format(Messages.s_Missing_Repackaging, filename);
+ AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project, msg);
+ mBuildFinalPackage = true;
+ break;
+ }
+ }
+ }
+ }
+
// at this point we know if we need to recreate the temporary apk
// or the dex file, but we don't know if we simply need to recreate them
// because they are missing
@@ -381,9 +404,23 @@ public class ApkBuilder extends BaseBuilder {
// handle already present .apk, and if that one failed as well, the user will be
// notified.
finalPackage.delete();
+
+ if (configs != null) {
+ Set> entrySet = configs.entrySet();
+ for (Entry entry : entrySet) {
+ String packageFilepath = osBinPath + File.separator +
+ ProjectHelper.getApkFilename(project, entry.getKey());
+
+ finalPackage = new File(packageFilepath);
+ finalPackage.delete();
+ }
+ }
// first we check if we need to package the resources.
if (mPackageResources) {
+ // remove some aapt_package only markers.
+ removeMarkersFromContainer(project, AndroidConstants.MARKER_AAPT_PACKAGE);
+
// need to figure out some path before we can execute aapt;
// resource to the AndroidManifest.xml file
@@ -424,13 +461,30 @@ public class ApkBuilder extends BaseBuilder {
osAssetsPath = assetsFolder.getLocation().toOSString();
}
+ // build the default resource package
if (executeAapt(project, osManifestPath, osResPath,
osAssetsPath, osBinPath + File.separator +
- AndroidConstants.FN_RESOURCES_AP_) == false) {
+ AndroidConstants.FN_RESOURCES_AP_, null /*configFilter*/) == false) {
// aapt failed. Whatever files that needed to be marked
// have already been marked. We just return.
return referencedProjects;
}
+
+ // now do the same thing for all the configured resource packages.
+ if (configs != null) {
+ Set> entrySet = configs.entrySet();
+ for (Entry entry : entrySet) {
+ String outPathFormat = osBinPath + File.separator +
+ AndroidConstants.FN_RESOURCES_S_AP_;
+ String outPath = String.format(outPathFormat, entry.getKey());
+ if (executeAapt(project, osManifestPath, osResPath,
+ osAssetsPath, outPath, entry.getValue()) == false) {
+ // aapt failed. Whatever files that needed to be marked
+ // have already been marked. We just return.
+ return referencedProjects;
+ }
+ }
+ }
// build has been done. reset the state of the builder
mPackageResources = false;
@@ -456,25 +510,49 @@ public class ApkBuilder extends BaseBuilder {
}
// now we need to make the final package from the intermediary apk
- // and classes.dex
+ // and classes.dex.
+ // This is the default package with all the resources.
+ String classesDexPath = osBinPath + File.separator + AndroidConstants.FN_CLASSES_DEX;
if (finalPackage(osBinPath + File.separator + AndroidConstants.FN_RESOURCES_AP_,
- osBinPath + File.separator + AndroidConstants.FN_CLASSES_DEX,
- osFinalPackagePath, javaProject, referencedJavaProjects) == false) {
+ classesDexPath,osFinalPackagePath, javaProject,
+ referencedJavaProjects) == false) {
return referencedProjects;
- } else {
- // get the resource to bin
- outputFolder.refreshLocal(IResource.DEPTH_ONE, monitor);
-
- // build has been done. reset the state of the builder
- mBuildFinalPackage = false;
-
- // and store it
- saveProjectBooleanProperty(PROPERTY_BUILD_APK, mBuildFinalPackage);
-
- AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, getProject(),
- "Build Success!");
}
+
+ // now do the same thing for all the configured resource packages.
+ if (configs != null) {
+ String resPathFormat = osBinPath + File.separator +
+ AndroidConstants.FN_RESOURCES_S_AP_;
+
+ Set> entrySet = configs.entrySet();
+ for (Entry entry : entrySet) {
+ // make the filename for the resource package.
+ String resPath = String.format(resPathFormat, entry.getKey());
+
+ // make the filename for the apk to generate
+ String apkOsFilePath = osBinPath + File.separator +
+ ProjectHelper.getApkFilename(project, entry.getKey());
+ if (finalPackage(resPath, classesDexPath, apkOsFilePath, javaProject,
+ referencedJavaProjects) == false) {
+ return referencedProjects;
+ }
+ }
+ }
+
+ // we are done.
+
+ // get the resource to bin
+ outputFolder.refreshLocal(IResource.DEPTH_ONE, monitor);
+
+ // build has been done. reset the state of the builder
+ mBuildFinalPackage = false;
+
+ // and store it
+ saveProjectBooleanProperty(PROPERTY_BUILD_APK, mBuildFinalPackage);
+
+ AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, getProject(),
+ "Build Success!");
}
return referencedProjects;
}
@@ -498,19 +576,27 @@ public class ApkBuilder extends BaseBuilder {
* @param osResPath The path to the res folder
* @param osAssetsPath The path to the assets folder. This can be null.
* @param osOutFilePath The path to the temporary resource file to create.
+ * @param configFilter The configuration filter for the resources to include
+ * (used with -c option, for example "port,en,fr" to include portrait, English and French
+ * resources.)
* @return true if success, false otherwise.
*/
private boolean executeAapt(IProject project, String osManifestPath,
- String osResPath, String osAssetsPath, String osOutFilePath) {
+ String osResPath, String osAssetsPath, String osOutFilePath, String configFilter) {
+ IAndroidTarget target = Sdk.getCurrent().getTarget(project);
// Create the command line.
ArrayList commandArray = new ArrayList();
- commandArray.add(AdtPlugin.getOsAbsoluteAapt());
+ commandArray.add(target.getPath(IAndroidTarget.AAPT));
commandArray.add("package"); //$NON-NLS-1$
commandArray.add("-f");//$NON-NLS-1$
if (AdtPlugin.getBuildVerbosity() == AdtConstants.BUILD_VERBOSE) {
commandArray.add("-v"); //$NON-NLS-1$
}
+ if (configFilter != null) {
+ commandArray.add("-c"); //$NON-NLS-1$
+ commandArray.add(configFilter);
+ }
commandArray.add("-M"); //$NON-NLS-1$
commandArray.add(osManifestPath);
commandArray.add("-S"); //$NON-NLS-1$
@@ -520,8 +606,7 @@ public class ApkBuilder extends BaseBuilder {
commandArray.add(osAssetsPath);
}
commandArray.add("-I"); //$NON-NLS-1$
- commandArray.add(
- Sdk.getCurrent().getTarget(project).getPath(IAndroidTarget.ANDROID_JAR));
+ commandArray.add(target.getPath(IAndroidTarget.ANDROID_JAR));
commandArray.add("-F"); //$NON-NLS-1$
commandArray.add(osOutFilePath);
@@ -599,14 +684,19 @@ public class ApkBuilder extends BaseBuilder {
*/
private boolean executeDx(IJavaProject javaProject, String osBinPath, String osOutFilePath,
IJavaProject[] referencedJavaProjects) throws CoreException {
+ IAndroidTarget target = Sdk.getCurrent().getTarget(javaProject.getProject());
+ AndroidTargetData targetData = Sdk.getCurrent().getTargetData(target);
+ if (targetData == null) {
+ throw new CoreException(new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID,
+ Messages.ApkBuilder_UnableBuild_Dex_Not_loaded));
+ }
+
// get the dex wrapper
- DexWrapper wrapper = DexWrapper.getWrapper();
+ DexWrapper wrapper = targetData.getDexWrapper();
if (wrapper == null) {
- if (DexWrapper.getStatus() == LoadStatus.FAILED) {
- throw new CoreException(new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID,
- Messages.ApkBuilder_UnableBuild_Dex_Not_loaded));
- }
+ throw new CoreException(new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID,
+ Messages.ApkBuilder_UnableBuild_Dex_Not_loaded));
}
// make sure dx use the proper output streams.
@@ -876,9 +966,10 @@ public class ApkBuilder extends BaseBuilder {
* @param javaProject the javaProject object.
* @param referencedJavaProjects the java projects that this project references.
* @throws IOException
+ * @throws CoreException
*/
private void writeStandardResources(SignedJarBuilder jarBuilder, IJavaProject javaProject,
- IJavaProject[] referencedJavaProjects) throws IOException {
+ IJavaProject[] referencedJavaProjects) throws IOException, CoreException {
IWorkspace ws = ResourcesPlugin.getWorkspace();
IWorkspaceRoot wsRoot = ws.getRoot();
@@ -888,7 +979,9 @@ public class ApkBuilder extends BaseBuilder {
writeStandardProjectResources(jarBuilder, javaProject, wsRoot, list);
for (IJavaProject referencedJavaProject : referencedJavaProjects) {
- writeStandardProjectResources(jarBuilder, referencedJavaProject, wsRoot, list);
+ if (referencedJavaProject.getProject().hasNature(AndroidConstants.NATURE)) {
+ writeStandardProjectResources(jarBuilder, referencedJavaProject, wsRoot, list);
+ }
}
}
@@ -977,7 +1070,9 @@ public class ApkBuilder extends BaseBuilder {
}
/**
- * Returns the list of the output folders for the specified {@link IJavaProject} objects.
+ * Returns the list of the output folders for the specified {@link IJavaProject} objects, if
+ * they are Android projects.
+ *
* @param referencedJavaProjects the java projects.
* @return an array, always. Can be empty.
* @throws CoreException
@@ -989,19 +1084,21 @@ public class ApkBuilder extends BaseBuilder {
IWorkspaceRoot wsRoot = ws.getRoot();
for (IJavaProject javaProject : referencedJavaProjects) {
- // get the output folder
- IPath path = null;
- try {
- path = javaProject.getOutputLocation();
- } catch (JavaModelException e) {
- continue;
- }
-
- IResource outputResource = wsRoot.findMember(path);
- if (outputResource != null && outputResource.getType() == IResource.FOLDER) {
- String outputOsPath = outputResource.getLocation().toOSString();
-
- list.add(outputOsPath);
+ if (javaProject.getProject().hasNature(AndroidConstants.NATURE)) {
+ // get the output folder
+ IPath path = null;
+ try {
+ path = javaProject.getOutputLocation();
+ } catch (JavaModelException e) {
+ continue;
+ }
+
+ IResource outputResource = wsRoot.findMember(path);
+ if (outputResource != null && outputResource.getType() == IResource.FOLDER) {
+ String outputOsPath = outputResource.getLocation().toOSString();
+
+ list.add(outputOsPath);
+ }
}
}
@@ -1026,7 +1123,7 @@ public class ApkBuilder extends BaseBuilder {
return list.toArray(new IJavaProject[list.size()]);
}
-
+
/**
* Checks a {@link IFile} to make sure it should be packaged as standard resources.
* @param file the IFile representing the file.
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/ApkDeltaVisitor.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/ApkDeltaVisitor.java
index aec703d1a..5d6793a92 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/ApkDeltaVisitor.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/ApkDeltaVisitor.java
@@ -192,11 +192,16 @@ public class ApkDeltaVisitor extends BaseDeltaVisitor
IPath parentPath = path.removeLastSegments(1);
if (mOutputPath.equals(parentPath)) {
String resourceName = resource.getName();
+ // check if classes.dex was removed
if (resourceName.equalsIgnoreCase(AndroidConstants.FN_CLASSES_DEX)) {
mConvertToDex = true;
mMakeFinalPackage = true;
} else if (resourceName.equalsIgnoreCase(
- AndroidConstants.FN_RESOURCES_AP_)) {
+ AndroidConstants.FN_RESOURCES_AP_) ||
+ AndroidConstants.PATTERN_RESOURCES_S_AP_.matcher(
+ resourceName).matches()) {
+ // or if the default resources.ap_ or a configured version
+ // (resources-###.ap_) was removed.
mPackageResources = true;
mMakeFinalPackage = true;
}
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/BaseBuilder.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/BaseBuilder.java
index 534c12343..c3d5ba671 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/BaseBuilder.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/BaseBuilder.java
@@ -19,6 +19,7 @@ package com.android.ide.eclipse.adt.build;
import com.android.ide.eclipse.adt.AdtConstants;
import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.adt.project.ProjectHelper;
+import com.android.ide.eclipse.adt.sdk.LoadStatus;
import com.android.ide.eclipse.common.AndroidConstants;
import com.android.ide.eclipse.common.project.BaseProjectHelper;
import com.android.ide.eclipse.common.project.XmlErrorHandler;
@@ -26,6 +27,7 @@ import com.android.ide.eclipse.common.project.XmlErrorHandler.XmlErrorListener;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
@@ -34,6 +36,10 @@ import org.eclipse.core.resources.IncrementalProjectBuilder;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
@@ -144,6 +150,15 @@ abstract class BaseBuilder extends IncrementalProjectBuilder {
private final static Pattern sPattern8Line1 = Pattern.compile(
"^(invalid resource directory name): (.*)$"); //$NON-NLS-1$
+ /**
+ * 2 line aapt error
+ * "ERROR: Invalid configuration: foo"
+ * " ^^^"
+ * There's no need to parse the 2nd line.
+ */
+ private final static Pattern sPattern9Line1 = Pattern.compile(
+ "^Invalid configuration: (.+)$"); //$NON-NLS-1$
+
/** SAX Parser factory. */
private SAXParserFactory mParserFactory;
@@ -435,8 +450,8 @@ abstract class BaseBuilder extends IncrementalProjectBuilder {
String location = m.group(1);
// check the values and attempt to mark the file.
- if (checkAndMark(location, lineStr, msg, osRoot,
- project, IMarker.SEVERITY_ERROR) == false) {
+ if (checkAndMark(location, lineStr, msg, osRoot, project,
+ AndroidConstants.MARKER_AAPT_COMPILE, IMarker.SEVERITY_ERROR) == false) {
return true;
}
continue;
@@ -460,7 +475,7 @@ abstract class BaseBuilder extends IncrementalProjectBuilder {
// display the error
if (checkAndMark(location, null, msg, osRoot, project,
- IMarker.SEVERITY_ERROR) == false) {
+ AndroidConstants.MARKER_AAPT_COMPILE, IMarker.SEVERITY_ERROR) == false) {
return true;
}
@@ -483,8 +498,8 @@ abstract class BaseBuilder extends IncrementalProjectBuilder {
String lineStr = m.group(2);
// check the values and attempt to mark the file.
- if (checkAndMark(location, lineStr, msg, osRoot,
- project, IMarker.SEVERITY_ERROR) == false) {
+ if (checkAndMark(location, lineStr, msg, osRoot, project,
+ AndroidConstants.MARKER_AAPT_COMPILE, IMarker.SEVERITY_ERROR) == false) {
return true;
}
continue;
@@ -497,8 +512,8 @@ abstract class BaseBuilder extends IncrementalProjectBuilder {
String msg = m.group(3);
// check the values and attempt to mark the file.
- if (checkAndMark(location, lineStr, msg, osRoot,
- project, IMarker.SEVERITY_ERROR) == false) {
+ if (checkAndMark(location, lineStr, msg, osRoot, project,
+ AndroidConstants.MARKER_AAPT_COMPILE, IMarker.SEVERITY_ERROR) == false) {
return true;
}
@@ -521,8 +536,8 @@ abstract class BaseBuilder extends IncrementalProjectBuilder {
String lineStr = m.group(2);
// check the values and attempt to mark the file.
- if (checkAndMark(location, lineStr, msg, osRoot,
- project, IMarker.SEVERITY_ERROR) == false) {
+ if (checkAndMark(location, lineStr, msg, osRoot, project,
+ AndroidConstants.MARKER_AAPT_COMPILE, IMarker.SEVERITY_ERROR) == false) {
return true;
}
@@ -537,8 +552,8 @@ abstract class BaseBuilder extends IncrementalProjectBuilder {
String msg = m.group(3);
// check the values and attempt to mark the file.
- if (checkAndMark(location, lineStr, msg, osRoot,
- project,IMarker.SEVERITY_WARNING) == false) {
+ if (checkAndMark(location, lineStr, msg, osRoot, project,
+ AndroidConstants.MARKER_AAPT_COMPILE, IMarker.SEVERITY_WARNING) == false) {
return true;
}
@@ -553,8 +568,8 @@ abstract class BaseBuilder extends IncrementalProjectBuilder {
String msg = m.group(3);
// check the values and attempt to mark the file.
- if (checkAndMark(location, lineStr, msg, osRoot,
- project, IMarker.SEVERITY_ERROR) == false) {
+ if (checkAndMark(location, lineStr, msg, osRoot, project,
+ AndroidConstants.MARKER_AAPT_COMPILE, IMarker.SEVERITY_ERROR) == false) {
return true;
}
@@ -569,7 +584,25 @@ abstract class BaseBuilder extends IncrementalProjectBuilder {
// check the values and attempt to mark the file.
if (checkAndMark(location, null, msg, osRoot, project,
- IMarker.SEVERITY_ERROR) == false) {
+ AndroidConstants.MARKER_AAPT_COMPILE, IMarker.SEVERITY_ERROR) == false) {
+ return true;
+ }
+
+ // success, go to the next line
+ continue;
+ }
+
+ m = sPattern9Line1.matcher(p);
+ if (m.matches()) {
+ String badConfig = m.group(1);
+ String msg = String.format("APK Configuration filter '%1$s' is invalid", badConfig);
+
+ // skip the next line
+ i++;
+
+ // check the values and attempt to mark the file.
+ if (checkAndMark(null /*location*/, null, msg, osRoot, project,
+ AndroidConstants.MARKER_AAPT_PACKAGE, IMarker.SEVERITY_ERROR) == false) {
return true;
}
@@ -654,23 +687,25 @@ abstract class BaseBuilder extends IncrementalProjectBuilder {
/**
* Check if the parameters gotten from the error output are valid, and mark
* the file with an AAPT marker.
- * @param location
+ * @param location the full OS path of the error file. If null, the project is marked
* @param lineStr
* @param message
* @param root The root directory of the project, in OS specific format.
* @param project
+ * @param markerId The marker id to put.
* @param severity The severity of the marker to put (IMarker.SEVERITY_*)
- * @return true if the parameters were valid and the file was marked
- * sucessfully.
+ * @return true if the parameters were valid and the file was marked successfully.
*
* @see IMarker
*/
private final boolean checkAndMark(String location, String lineStr,
- String message, String root, IProject project, int severity) {
+ String message, String root, IProject project, String markerId, int severity) {
// check this is in fact a file
- File f = new File(location);
- if (f.exists() == false) {
- return false;
+ if (location != null) {
+ File f = new File(location);
+ if (f.exists() == false) {
+ return false;
+ }
}
// get the line number
@@ -687,16 +722,18 @@ abstract class BaseBuilder extends IncrementalProjectBuilder {
}
// add the marker
- IResource f2 = getResourceFromFullPath(location, root, project);
- if (f2 == null) {
- return false;
+ IResource f2 = project;
+ if (location != null) {
+ f2 = getResourceFromFullPath(location, root, project);
+ if (f2 == null) {
+ return false;
+ }
}
// check if there's a similar marker already, since aapt is launched twice
boolean markerAlreadyExists = false;
try {
- IMarker[] markers = f2.findMarkers(AndroidConstants.MARKER_AAPT, true,
- IResource.DEPTH_ZERO);
+ IMarker[] markers = f2.findMarkers(markerId, true, IResource.DEPTH_ZERO);
for (IMarker marker : markers) {
int tmpLine = marker.getAttribute(IMarker.LINE_NUMBER, -1);
@@ -727,10 +764,10 @@ abstract class BaseBuilder extends IncrementalProjectBuilder {
if (markerAlreadyExists == false) {
if (line != -1) {
- BaseProjectHelper.addMarker(f2, AndroidConstants.MARKER_AAPT, message, line,
+ BaseProjectHelper.addMarker(f2, markerId, message, line,
severity);
} else {
- BaseProjectHelper.addMarker(f2, AndroidConstants.MARKER_AAPT, message, severity);
+ BaseProjectHelper.addMarker(f2, markerId, message, severity);
}
}
@@ -841,4 +878,64 @@ abstract class BaseBuilder extends IncrementalProjectBuilder {
return oslibraryList.toArray(new String[oslibraryList.size()]);
}
+
+ /**
+ * Aborts the build if the SDK/project setups are broken. This does not
+ * display any errors.
+ *
+ * @param project The {@link IJavaProject} being compiled.
+ * @throws CoreException
+ */
+ protected final void abortOnBadSetup(IProject project) throws CoreException {
+ // check if we have finished loading the SDK.
+ if (AdtPlugin.getDefault().getSdkLoadStatus() != LoadStatus.LOADED) {
+ // we exit silently
+ stopBuild("SDK is not loaded yet");
+ }
+
+ // abort if there are TARGET or ADT type markers
+ IMarker[] markers = project.findMarkers(AdtConstants.MARKER_TARGET,
+ false /*includeSubtypes*/, IResource.DEPTH_ZERO);
+
+ if (markers.length > 0) {
+ stopBuild("");
+ }
+
+ markers = project.findMarkers(AdtConstants.MARKER_ADT, false /*includeSubtypes*/,
+ IResource.DEPTH_ZERO);
+
+ if (markers.length > 0) {
+ stopBuild("");
+ }
+ }
+
+ /**
+ * Throws an exception to cancel the build.
+ *
+ * @param error the error message
+ * @param args the printf-style arguments to the error message.
+ * @throws CoreException
+ */
+ protected final void stopBuild(String error, Object... args) throws CoreException {
+ throw new CoreException(new Status(IStatus.CANCEL, AdtPlugin.PLUGIN_ID,
+ String.format(error, args)));
+ }
+
+ /**
+ * Recursively delete all the derived resources.
+ */
+ protected void removeDerivedResources(IResource resource, IProgressMonitor monitor)
+ throws CoreException {
+ if (resource.exists()) {
+ if (resource.isDerived()) {
+ resource.delete(true, new SubProgressMonitor(monitor, 10));
+ } else if (resource.getType() == IResource.FOLDER) {
+ IFolder folder = (IFolder)resource;
+ IResource[] members = folder.members();
+ for (IResource member : members) {
+ removeDerivedResources(member, monitor);
+ }
+ }
+ }
+ }
}
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/DexWrapper.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/DexWrapper.java
index cba8ad76e..65ad4f5be 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/DexWrapper.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/DexWrapper.java
@@ -17,7 +17,6 @@
package com.android.ide.eclipse.adt.build;
import com.android.ide.eclipse.adt.AdtPlugin;
-import com.android.ide.eclipse.adt.sdk.LoadStatus;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
@@ -46,10 +45,6 @@ public final class DexWrapper {
private final static String MAIN_RUN = "run"; //$NON-NLS-1$
- private static DexWrapper sWrapper;
-
- private static LoadStatus sLoadStatus = LoadStatus.LOADING;
-
private Method mRunMethod;
private Constructor> mArgConstructor;
@@ -62,15 +57,16 @@ public final class DexWrapper {
private Field mConsoleErr;
/**
- * Loads the dex library from a file path. The loaded library can be used with the
- * {@link DexWrapper} object returned by {@link #getWrapper()}
+ * Loads the dex library from a file path.
+ *
+ * The loaded library can be used via
+ * {@link DexWrapper#run(String, String[], boolean, PrintStream, PrintStream)}.
+ *
* @param osFilepath the location of the dex.jar file.
* @return an IStatus indicating the result of the load.
*/
- public static synchronized IStatus loadDex(String osFilepath) {
+ public synchronized IStatus loadDex(String osFilepath) {
try {
- sWrapper = null;
-
File f = new File(osFilepath);
if (f.isFile() == false) {
return new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID, String.format(
@@ -86,44 +82,38 @@ public final class DexWrapper {
Class> consoleClass = loader.loadClass(DEX_CONSOLE);
Class> argClass = loader.loadClass(DEX_ARGS);
- sWrapper = new DexWrapper(mainClass, argClass, consoleClass);
-
+ try {
+ // now get the fields/methods we need
+ mRunMethod = mainClass.getMethod(MAIN_RUN, argClass);
+
+ mArgConstructor = argClass.getConstructor();
+ mArgOutName = argClass.getField("outName"); //$NON-NLS-1$
+ mArgJarOutput = argClass.getField("jarOutput"); //$NON-NLS-1$
+ mArgFileNames = argClass.getField("fileNames"); //$NON-NLS-1$
+ mArgVerbose = argClass.getField("verbose"); //$NON-NLS-1$
+
+ mConsoleOut = consoleClass.getField("out"); //$NON-NLS-1$
+ mConsoleErr = consoleClass.getField("err"); //$NON-NLS-1$
+
+ } catch (SecurityException e) {
+ return createErrorStatus(Messages.DexWrapper_SecuryEx_Unable_To_Find_API, e);
+ } catch (NoSuchMethodException e) {
+ return createErrorStatus(Messages.DexWrapper_SecuryEx_Unable_To_Find_Method, e);
+ } catch (NoSuchFieldException e) {
+ return createErrorStatus(Messages.DexWrapper_SecuryEx_Unable_To_Find_Field, e);
+ }
+
return Status.OK_STATUS;
} catch (MalformedURLException e) {
// really this should not happen.
- return createErrorStatus(String.format(Messages.DexWrapper_Failed_to_load_s, osFilepath), e);
+ return createErrorStatus(
+ String.format(Messages.DexWrapper_Failed_to_load_s, osFilepath), e);
} catch (ClassNotFoundException e) {
- return createErrorStatus(String.format(Messages.DexWrapper_Failed_to_load_s, osFilepath), e);
- } catch (CoreException e) {
- return e.getStatus();
- } finally {
- if (sWrapper == null) {
- sLoadStatus = LoadStatus.FAILED;
- } else {
- sLoadStatus = LoadStatus.LOADED;
- }
+ return createErrorStatus(
+ String.format(Messages.DexWrapper_Failed_to_load_s, osFilepath), e);
}
}
- /**
- * Unloads the loaded dex wrapper.
- */
- public static synchronized void unloadDex() {
- sWrapper = null;
- sLoadStatus = LoadStatus.LOADING;
- }
-
- public static synchronized DexWrapper getWrapper() {
- return sWrapper;
- }
-
- /**
- * Returns the {@link LoadStatus}.
- */
- public static synchronized LoadStatus getStatus() {
- return sLoadStatus;
- }
-
/**
* Runs the dex command.
* @param osOutFilePath the OS path to the outputfile (classes.dex
@@ -169,33 +159,6 @@ public final class DexWrapper {
}
}
- private DexWrapper(Class> mainClass, Class> argClass, Class> consoleClass)
- throws CoreException {
- try {
- // now get the fields/methods we need
- mRunMethod = mainClass.getMethod(MAIN_RUN, argClass);
-
- mArgConstructor = argClass.getConstructor();
- mArgOutName = argClass.getField("outName"); //$NON-NLS-1$
- mArgJarOutput = argClass.getField("jarOutput"); //$NON-NLS-1$
- mArgFileNames = argClass.getField("fileNames"); //$NON-NLS-1$
- mArgVerbose = argClass.getField("verbose"); //$NON-NLS-1$
-
- mConsoleOut = consoleClass.getField("out"); //$NON-NLS-1$
- mConsoleErr = consoleClass.getField("err"); //$NON-NLS-1$
-
- } catch (SecurityException e) {
- throw new CoreException(createErrorStatus(
- Messages.DexWrapper_SecuryEx_Unable_To_Find_API, e));
- } catch (NoSuchMethodException e) {
- throw new CoreException(createErrorStatus(
- Messages.DexWrapper_SecuryEx_Unable_To_Find_Method, e));
- } catch (NoSuchFieldException e) {
- throw new CoreException(createErrorStatus(
- Messages.DexWrapper_SecuryEx_Unable_To_Find_Field, e));
- }
- }
-
private static IStatus createErrorStatus(String message, Exception e) {
AdtPlugin.log(e, message);
AdtPlugin.printErrorToConsole(Messages.DexWrapper_Dex_Loader, message);
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/PreCompilerBuilder.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/PreCompilerBuilder.java
index 9fc434880..c5082838a 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/PreCompilerBuilder.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/PreCompilerBuilder.java
@@ -19,17 +19,14 @@ package com.android.ide.eclipse.adt.build;
import com.android.ide.eclipse.adt.AdtConstants;
import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.adt.project.FixLaunchConfig;
-import com.android.ide.eclipse.adt.project.ProjectHelper;
-import com.android.ide.eclipse.adt.sdk.LoadStatus;
import com.android.ide.eclipse.adt.sdk.Sdk;
import com.android.ide.eclipse.common.AndroidConstants;
-import com.android.ide.eclipse.common.project.AndroidManifestHelper;
import com.android.ide.eclipse.common.project.AndroidManifestParser;
import com.android.ide.eclipse.common.project.BaseProjectHelper;
import com.android.ide.eclipse.common.project.XmlErrorHandler.BasicXmlErrorListener;
import com.android.sdklib.IAndroidTarget;
+import com.android.sdklib.SdkConstants;
-import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IMarker;
@@ -37,13 +34,13 @@ import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.resources.IWorkspaceRoot;
-import org.eclipse.core.resources.ResourceAttributes;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
@@ -69,13 +66,8 @@ public class PreCompilerBuilder extends BaseBuilder {
private static final String PROPERTY_PACKAGE = "manifestPackage"; //$NON-NLS-1$
- private static final String PROPERTY_SOURCE_FOLDER =
- "manifestPackageSourceFolder"; //$NON-NLS-1$
-
private static final String PROPERTY_COMPILE_RESOURCES = "compileResources"; //$NON-NLS-1$
-
- static final String PROPERTY_ANDROID_GENERATED = "androidGenerated"; //$NON-NLS-1$
- static final String PROPERTY_ANDROID_CONFLICT = "androidConflict"; //$NON-NLS-1$
+ private static final String PROPERTY_COMPILE_AIDL = "compileAidl"; //$NON-NLS-1$
/**
* Single line aidl error
@@ -84,24 +76,58 @@ public class PreCompilerBuilder extends BaseBuilder {
private static Pattern sAidlPattern1 = Pattern.compile("^(.+?):(\\d+):\\s(.+)$"); //$NON-NLS-1$
/**
- * Compile flag. This is set to true if one of the changed/added/removed
- * file is a resource file. Upon visiting all the delta resources, if
- * this flag is true, then we know we'll have to compile the resources
- * into R.java
+ * Data to temporarly store aidl source file information
*/
- private boolean mCompileResources = false;
+ static class AidlData {
+ IFile aidlFile;
+ IFolder sourceFolder;
+
+ AidlData(IFolder sourceFolder, IFile aidlFile) {
+ this.sourceFolder = sourceFolder;
+ this.aidlFile = aidlFile;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+
+ if (obj instanceof AidlData) {
+ AidlData file = (AidlData)obj;
+ return aidlFile.equals(file.aidlFile) && sourceFolder.equals(file.sourceFolder);
+ }
+
+ return false;
+ }
+ }
+
+ /**
+ * Resource Compile flag. This flag is reset to false after each successful compilation, and
+ * stored in the project persistent properties. This allows the builder to remember its state
+ * when the project is closed/opened.
+ */
+ private boolean mMustCompileResources = false;
/** List of .aidl files found that are modified or new. */
- private final ArrayList mAidlToCompile = new ArrayList();
+ private final ArrayList mAidlToCompile = new ArrayList();
/** List of .aidl files that have been removed. */
- private final ArrayList mAidlToRemove = new ArrayList();
+ private final ArrayList mAidlToRemove = new ArrayList();
/** cache of the java package defined in the manifest */
private String mManifestPackage;
+
+ /** Output folder for generated Java File. Created on the Builder init
+ * @see #startupOnInitialize()
+ */
+ private IFolder mGenFolder;
- /** Source folder containing the java package defined in the manifest. */
- private IFolder mManifestPackageSourceFolder;
+ /**
+ * Progress monitor used at the end of every build to refresh the content of the 'gen' folder
+ * and set the generated files as derived.
+ */
+ private DerivedProgressMonitor mDerivedProgressMonitor;
/**
* Progress monitor waiting the end of the process to set a persistent value
@@ -110,91 +136,36 @@ public class PreCompilerBuilder extends BaseBuilder {
* to be known by eclipse, before we can call resource.setPersistentProperty on
* a new file.
*/
- private static class RefreshProgressMonitor implements IProgressMonitor {
- private boolean mCancelled = false;
- private IFile mNewFile;
- private IFile mSource;
- private boolean mDoneExecuted = false;
- public RefreshProgressMonitor(IFile newFile, IFile source) {
- mNewFile = newFile;
- mSource = source;
- }
-
- public void beginTask(String name, int totalWork) {
- }
-
- public void done() {
- if (mDoneExecuted == false) {
- mDoneExecuted = true;
- if (mNewFile.exists()) {
- ProjectHelper.saveResourceProperty(mNewFile, PROPERTY_ANDROID_GENERATED,
- mSource);
- try {
- mNewFile.setDerived(true);
- } catch (CoreException e) {
- // This really shouldn't happen since we check that the resource exist.
- // Worst case scenario, the resource isn't marked as derived.
- }
- }
- }
- }
-
- public void internalWorked(double work) {
- }
-
- public boolean isCanceled() {
- return mCancelled;
- }
-
- public void setCanceled(boolean value) {
- mCancelled = value;
- }
-
- public void setTaskName(String name) {
- }
-
- public void subTask(String name) {
- }
-
- public void worked(int work) {
- }
- }
-
- /**
- * Progress Monitor setting up to two files as derived once their parent is refreshed.
- * This is used as ProgressMonitor to refresh the R.java/Manifest.java parent (to display
- * the newly created files in the package explorer).
- */
private static class DerivedProgressMonitor implements IProgressMonitor {
private boolean mCancelled = false;
- private IFile mFile1;
- private IFile mFile2;
- private boolean mDoneExecuted = false;
- public DerivedProgressMonitor(IFile file1, IFile file2) {
- mFile1 = file1;
- mFile2 = file2;
+ private final ArrayList mFileList = new ArrayList();
+ private boolean mDone = false;
+ public DerivedProgressMonitor() {
+ }
+
+ void addFile(IFile file) {
+ mFileList.add(file);
+ }
+
+ void reset() {
+ mFileList.clear();
+ mDone = false;
}
public void beginTask(String name, int totalWork) {
}
public void done() {
- if (mDoneExecuted == false) {
- if (mFile1 != null && mFile1.exists()) {
- mDoneExecuted = true;
- try {
- mFile1.setDerived(true);
- } catch (CoreException e) {
- // This really shouldn't happen since we check that the resource edit.
- // Worst case scenario, the resource isn't marked as derived.
- }
- }
- if (mFile2 != null && mFile2.exists()) {
- try {
- mFile2.setDerived(true);
- } catch (CoreException e) {
- // This really shouldn't happen since we check that the resource edit.
- // Worst case scenario, the resource isn't marked as derived.
+ if (mDone == false) {
+ mDone = true;
+ for (IFile file : mFileList) {
+ if (file.exists()) {
+ try {
+ file.setDerived(true);
+ } catch (CoreException e) {
+ // This really shouldn't happen since we check that the resource exist.
+ // Worst case scenario, the resource isn't marked as derived.
+ }
}
}
}
@@ -224,344 +195,312 @@ public class PreCompilerBuilder extends BaseBuilder {
public PreCompilerBuilder() {
super();
}
-
+
// build() returns a list of project from which this project depends for future compilation.
@SuppressWarnings("unchecked") //$NON-NLS-1$
@Override
protected IProject[] build(int kind, Map args, IProgressMonitor monitor)
throws CoreException {
- // First thing we do is go through the resource delta to not
- // lose it if we have to abort the build for any reason.
+ try {
+ mDerivedProgressMonitor.reset();
- // get the project objects
- IProject project = getProject();
- IJavaProject javaProject = JavaCore.create(project);
-
- // now we need to get the classpath list
- ArrayList sourceList = BaseProjectHelper.getSourceClasspaths(javaProject);
-
- PreCompilerDeltaVisitor dv = null;
- String javaPackage = null;
-
- if (kind == FULL_BUILD) {
- AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project,
- Messages.Start_Full_Pre_Compiler);
- mCompileResources = true;
- buildAidlCompilationList(project, sourceList);
- } else {
- AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project,
- Messages.Start_Inc_Pre_Compiler);
-
- // Go through the resources and see if something changed.
- // Even if the mCompileResources flag is true from a previously aborted
- // build, we need to go through the Resource delta to get a possible
- // list of aidl files to compile/remove.
- IResourceDelta delta = getDelta(project);
- if (delta == null) {
- mCompileResources = true;
- buildAidlCompilationList(project, sourceList);
+ // First thing we do is go through the resource delta to not
+ // lose it if we have to abort the build for any reason.
+
+ // get the project objects
+ IProject project = getProject();
+
+ // Top level check to make sure the build can move forward.
+ abortOnBadSetup(project);
+
+ IJavaProject javaProject = JavaCore.create(project);
+ IAndroidTarget projectTarget = Sdk.getCurrent().getTarget(project);
+
+ // now we need to get the classpath list
+ ArrayList sourceFolderPathList = BaseProjectHelper.getSourceClasspaths(
+ javaProject);
+
+ PreCompilerDeltaVisitor dv = null;
+ String javaPackage = null;
+
+ if (kind == FULL_BUILD) {
+ AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project,
+ Messages.Start_Full_Pre_Compiler);
+ mMustCompileResources = true;
+ buildAidlCompilationList(project, sourceFolderPathList);
} else {
- dv = new PreCompilerDeltaVisitor(this, sourceList);
- delta.accept(dv);
-
- // record the state
- mCompileResources |= dv.getCompileResources();
-
- // handle aidl modification
- if (dv.getFullAidlRecompilation()) {
- buildAidlCompilationList(project, sourceList);
+ AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project,
+ Messages.Start_Inc_Pre_Compiler);
+
+ // Go through the resources and see if something changed.
+ // Even if the mCompileResources flag is true from a previously aborted
+ // build, we need to go through the Resource delta to get a possible
+ // list of aidl files to compile/remove.
+ IResourceDelta delta = getDelta(project);
+ if (delta == null) {
+ mMustCompileResources = true;
+ buildAidlCompilationList(project, sourceFolderPathList);
} else {
+ dv = new PreCompilerDeltaVisitor(this, sourceFolderPathList);
+ delta.accept(dv);
+
+ // record the state
+ mMustCompileResources |= dv.getCompileResources();
+
+ // handle aidl modification, and update mMustCompileAidl
mergeAidlFileModifications(dv.getAidlToCompile(),
dv.getAidlToRemove());
+
+ // get the java package from the visitor
+ javaPackage = dv.getManifestPackage();
}
-
- // if there was some XML errors, we just return w/o doing
- // anything since we've put some markers in the files anyway.
- if (dv.mXmlError) {
- AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project,
- Messages.Xml_Error);
-
- return null;
- }
-
- // get the java package from the visitor
- javaPackage = dv.getManifestPackage();
}
- }
-
- // store the build status in the persistent storage
- saveProjectBooleanProperty(PROPERTY_COMPILE_RESOURCES , mCompileResources);
- // TODO also needs to store the list of aidl to compile/remove
-
- // At this point we have stored what needs to be build, so we can
- // do some high level test and abort if needed.
-
- // check if we have finished loading the SDK.
- if (AdtPlugin.getDefault().getSdkLoadStatus(javaProject) != LoadStatus.LOADED) {
- // we exit silently
- return null;
- }
-
- // check the compiler compliance level, not displaying the error message
- // since this is not the first builder.
- if (ProjectHelper.checkCompilerCompliance(getProject())
- != ProjectHelper.COMPILER_COMPLIANCE_OK) {
- AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project,
- Messages.Compiler_Compliance_Error);
- return null;
- }
-
- // Check that the SDK directory has been setup.
- String osSdkFolder = AdtPlugin.getOsSdkFolder();
-
- if (osSdkFolder == null || osSdkFolder.length() == 0) {
- AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project,
- Messages.No_SDK_Setup_Error);
- markProject(AdtConstants.MARKER_ADT, Messages.No_SDK_Setup_Error,
- IMarker.SEVERITY_ERROR);
- return null;
- }
-
- IAndroidTarget projectTarget = Sdk.getCurrent().getTarget(project);
- if (projectTarget == null) {
- // no target. error has been output by the container initializer: exit silently.
- return null;
- }
-
- // get the manifest file
- IFile manifest = AndroidManifestHelper.getManifest(project);
-
- if (manifest == null) {
- String msg = String.format(Messages.s_File_Missing,
- AndroidConstants.FN_ANDROID_MANIFEST);
- AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project, msg);
- markProject(AdtConstants.MARKER_ADT, msg, IMarker.SEVERITY_ERROR);
- return null;
- }
-
- // lets check the XML of the manifest first, if that hasn't been done by the
- // resource delta visitor yet.
- if (dv == null || dv.getCheckedManifestXml() == false) {
- BasicXmlErrorListener errorListener = new BasicXmlErrorListener();
- AndroidManifestParser parser = BaseProjectHelper.parseManifestForError(manifest,
- errorListener);
-
- if (errorListener.mHasXmlError == true) {
- // there was an error in the manifest, its file has been marked,
- // by the XmlErrorHandler.
- // We return;
- String msg = String.format(Messages.s_Contains_Xml_Error,
+
+ // store the build status in the persistent storage
+ saveProjectBooleanProperty(PROPERTY_COMPILE_RESOURCES , mMustCompileResources);
+
+ // if there was some XML errors, we just return w/o doing
+ // anything since we've put some markers in the files anyway.
+ if (dv != null && dv.mXmlError) {
+ AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project,
+ Messages.Xml_Error);
+
+ // This interrupts the build. The next builders will not run.
+ stopBuild(Messages.Xml_Error);
+ }
+
+
+ // get the manifest file
+ IFile manifest = AndroidManifestParser.getManifest(project);
+
+ if (manifest == null) {
+ String msg = String.format(Messages.s_File_Missing,
AndroidConstants.FN_ANDROID_MANIFEST);
AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project, msg);
- return null;
+ markProject(AdtConstants.MARKER_ADT, msg, IMarker.SEVERITY_ERROR);
+
+ // This interrupts the build. The next builders will not run.
+ stopBuild(msg);
}
-
- // get the java package from the parser
- javaPackage = parser.getPackage();
- }
-
- if (javaPackage == null || javaPackage.length() == 0) {
- // looks like the AndroidManifest file isn't valid.
- String msg = String.format(Messages.s_Doesnt_Declare_Package_Error,
- AndroidConstants.FN_ANDROID_MANIFEST);
- AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project,
- msg);
- return null;
- }
-
- // at this point we have the java package. We need to make sure it's not a different package
- // than the previous one that were built.
- if (javaPackage.equals(mManifestPackage) == false) {
- // The manifest package has changed, the user may want to update
- // the launch configuration
- if (mManifestPackage != null) {
+
+ // lets check the XML of the manifest first, if that hasn't been done by the
+ // resource delta visitor yet.
+ if (dv == null || dv.getCheckedManifestXml() == false) {
+ BasicXmlErrorListener errorListener = new BasicXmlErrorListener();
+ AndroidManifestParser parser = BaseProjectHelper.parseManifestForError(manifest,
+ errorListener);
+
+ if (errorListener.mHasXmlError == true) {
+ // there was an error in the manifest, its file has been marked,
+ // by the XmlErrorHandler.
+ // We return;
+ String msg = String.format(Messages.s_Contains_Xml_Error,
+ AndroidConstants.FN_ANDROID_MANIFEST);
+ AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project, msg);
+
+ // This interrupts the build. The next builders will not run.
+ stopBuild(msg);
+ }
+
+ // get the java package from the parser
+ javaPackage = parser.getPackage();
+ }
+
+ if (javaPackage == null || javaPackage.length() == 0) {
+ // looks like the AndroidManifest file isn't valid.
+ String msg = String.format(Messages.s_Doesnt_Declare_Package_Error,
+ AndroidConstants.FN_ANDROID_MANIFEST);
AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project,
- Messages.Checking_Package_Change);
-
- FixLaunchConfig flc = new FixLaunchConfig(project, mManifestPackage, javaPackage);
- flc.start();
+ msg);
+
+ // This interrupts the build. The next builders will not run.
+ stopBuild(msg);
}
-
- // now we delete the generated classes from their previous location
- deleteObsoleteGeneratedClass(AndroidConstants.FN_RESOURCE_CLASS,
- mManifestPackageSourceFolder, mManifestPackage);
- deleteObsoleteGeneratedClass(AndroidConstants.FN_MANIFEST_CLASS,
- mManifestPackageSourceFolder, mManifestPackage);
-
- // record the new manifest package, and save it.
- mManifestPackage = javaPackage;
- saveProjectStringProperty(PROPERTY_PACKAGE, mManifestPackage);
- }
-
- if (mCompileResources) {
- // we need to figure out where to store the R class.
- // get the parent folder for R.java and update mManifestPackageSourceFolder
- IFolder packageFolder = getManifestPackageFolder(project, sourceList);
-
- // at this point, either we have found the package or not.
- // if we haven't well it's time to tell the user and abort
- if (mManifestPackageSourceFolder == null) {
- // mark the manifest file
- String message = String.format(Messages.Package_s_Doesnt_Exist_Error,
+
+ // at this point we have the java package. We need to make sure it's not a different
+ // package than the previous one that were built.
+ if (javaPackage.equals(mManifestPackage) == false) {
+ // The manifest package has changed, the user may want to update
+ // the launch configuration
+ if (mManifestPackage != null) {
+ AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project,
+ Messages.Checking_Package_Change);
+
+ FixLaunchConfig flc = new FixLaunchConfig(project, mManifestPackage,
+ javaPackage);
+ flc.start();
+ }
+
+ // now we delete the generated classes from their previous location
+ deleteObsoleteGeneratedClass(AndroidConstants.FN_RESOURCE_CLASS,
mManifestPackage);
- BaseProjectHelper.addMarker(manifest, AndroidConstants.MARKER_AAPT, message,
- IMarker.SEVERITY_ERROR);
- AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project, message);
-
- // abort
- return null;
+ deleteObsoleteGeneratedClass(AndroidConstants.FN_MANIFEST_CLASS,
+ mManifestPackage);
+
+ // record the new manifest package, and save it.
+ mManifestPackage = javaPackage;
+ saveProjectStringProperty(PROPERTY_PACKAGE, mManifestPackage);
}
-
-
- // found the folder in which to write the stuff
-
- // get the resource folder
- IFolder resFolder = project.getFolder(AndroidConstants.WS_RESOURCES);
-
- // get the file system path
- IPath outputLocation = mManifestPackageSourceFolder.getLocation();
- IPath resLocation = resFolder.getLocation();
- IPath manifestLocation = manifest.getLocation();
-
- // those locations have to exist for us to do something!
- if (outputLocation != null && resLocation != null
- && manifestLocation != null) {
- String osOutputPath = outputLocation.toOSString();
- String osResPath = resLocation.toOSString();
- String osManifestPath = manifestLocation.toOSString();
-
- // remove the aapt markers
- removeMarkersFromFile(manifest, AndroidConstants.MARKER_AAPT);
- removeMarkersFromContainer(resFolder, AndroidConstants.MARKER_AAPT);
-
- AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project,
- Messages.Preparing_Generated_Files);
-
- // since the R.java file may be already existing in read-only
- // mode we need to make it readable so that aapt can overwrite
- // it
- IFile rJavaFile = packageFolder.getFile(AndroidConstants.FN_RESOURCE_CLASS);
- prepareFileForExternalModification(rJavaFile);
-
- // do the same for the Manifest.java class
- IFile manifestJavaFile = packageFolder.getFile(AndroidConstants.FN_MANIFEST_CLASS);
- prepareFileForExternalModification(manifestJavaFile);
-
- // we actually need to delete the manifest.java as it may become empty and in this
- // case aapt doesn't generate an empty one, but instead doesn't touch it.
- manifestJavaFile.delete(true, null);
-
- // launch aapt: create the command line
- ArrayList