From cc6799af7dfab811460803ea032f621e949c945f Mon Sep 17 00:00:00 2001 From: Brett Chabot Date: Thu, 10 Sep 2009 15:08:58 -0700 Subject: Add runtest test def for cts-webkit. --- testrunner/test_defs.xml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/testrunner/test_defs.xml b/testrunner/test_defs.xml index 6f073bbb1..55ddfdfa7 100644 --- a/testrunner/test_defs.xml +++ b/testrunner/test_defs.xml @@ -291,6 +291,13 @@ See test_defs.xsd for more information. coverage_target="framework" cts="true" /> + + Date: Mon, 21 Sep 2009 17:27:45 -0700 Subject: adding a recorder function to collect output into an xml file; adding getvars and listvars command to MonkeyRunner --- tools/monkeyrunner/src/Android.mk | 24 +- .../com/android/monkeyrunner/MonkeyRecorder.java | 311 ++++++++++++++++++ .../src/com/android/monkeyrunner/MonkeyRunner.java | 348 ++++++++++++++++----- 3 files changed, 608 insertions(+), 75 deletions(-) create mode 100644 tools/monkeyrunner/src/com/android/monkeyrunner/MonkeyRecorder.java diff --git a/tools/monkeyrunner/src/Android.mk b/tools/monkeyrunner/src/Android.mk index 576025a0c..fb6b9c10f 100644 --- a/tools/monkeyrunner/src/Android.mk +++ b/tools/monkeyrunner/src/Android.mk @@ -21,9 +21,31 @@ LOCAL_SRC_FILES := $(call all-subdir-java-files) LOCAL_JAR_MANIFEST := ../etc/manifest.txt LOCAL_JAVA_LIBRARIES := \ ddmlib \ - jython + jython \ + xmlwriter LOCAL_MODULE := monkeyrunner include $(BUILD_HOST_JAVA_LIBRARY) + +# Build ext.jar +# ============================================================ + +ext_dirs := ../../../../external/xmlwriter/src + +ext_src_files := $(call all-java-files-under,$(ext_dirs)) + +# ==== the library ========================================= +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := $(ext_src_files) + +LOCAL_NO_STANDARD_LIBRARIES := true +#LOCAL_JAVA_LIBRARIES := core +#LOCAL_STATIC_JAVA_LIBRARIES := libgoogleclient + +LOCAL_MODULE := xmlwriter + +include $(BUILD_HOST_JAVA_LIBRARY) + diff --git a/tools/monkeyrunner/src/com/android/monkeyrunner/MonkeyRecorder.java b/tools/monkeyrunner/src/com/android/monkeyrunner/MonkeyRecorder.java new file mode 100644 index 000000000..1132733b8 --- /dev/null +++ b/tools/monkeyrunner/src/com/android/monkeyrunner/MonkeyRecorder.java @@ -0,0 +1,311 @@ +/* + * 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.monkeyrunner; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.FileWriter; +import java.io.IOException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; + +import org.jheer.XMLWriter; + +/** + * MonkeyRecorder is a host side class that records the output of scripts that are run. + * It creates a unique directory, puts in an xml file that records each cmd and result. + * It stores every screenshot in this directory. + * When finished, it zips this all up. + * + * Calling Sequence: + * mr = new MonkeyRecorder(scriptName); + * mr.startCommand(); + * [mr.addAttribute(name, value);] + * ... + * [mr.addInput(cmd);] + * [mr.addResults(result, filename);] // filename = "" if no screenshot + * mr.endCommand(); + * mr.addComment(comment); + * mr.startCommand(); + * ... + * mr.endCommand(); + * ... + * mr.close(); + * + * With MonkeyRunner this should output an xml file, -yyyyMMdd-HH:mm:ss.xml, into the + * directory out/-yyyyMMdd-HH:mm:ss with the contents: + * + * + * + * script_name="filename" + * monkeyRunnerVersion="0.2" + * + * + * + * ... + * + * + * dateTime="20090921-17:08:43" + * + * + * + * ... + * + * dateTime="20090921-17:09:44" + * + * + * + * ... + * + * + * And then zip it up with all the screenshots in the file: -yyyyMMdd-HH:mm:ss.zip. + */ + +public class MonkeyRecorder { + + // xml file to store output results in + private static String mXmlFilename; + private static FileWriter mXmlFile; + private static XMLWriter mXmlWriter; + + // unique subdirectory to put results in (screenshots and xml file) + private static String mDirname; + private static ArrayList mScreenShots = new ArrayList(); + + // where we store all the results for all the script runs + private static final String ROOT_DIR = "out"; + + // for getting the date and time in now() + private static final SimpleDateFormat SIMPLE_DATE_TIME_FORMAT = new SimpleDateFormat("yyyyMMdd-HH:mm:ss"); + + /** + * Create a new MonkeyRecorder that records commands and zips up screenshots for submittal + * + * @param scriptName filepath of the monkey script we are running + */ + public MonkeyRecorder(String scriptName) throws IOException { + // Create directory structure to store xml file, images and zips + File scriptFile = new File(scriptName); + scriptName = scriptFile.getName(); // Get rid of path + mDirname = ROOT_DIR + "/" + stripType(scriptName) + "-" + now(); + new File(mDirname).mkdirs(); + + // Initialize xml file + mXmlFilename = stampFilename(stripType(scriptName) + ".xml"); + initXmlFile(scriptName); + } + + // Get the current date and time in a simple string format (used for timestamping filenames) + private static String now() { + return SIMPLE_DATE_TIME_FORMAT.format(Calendar.getInstance().getTime()); + } + + /** + * Initialize the xml file writer + * + * @param scriptName filename (not path) of the monkey script, stored as attribute in the xml file + */ + private static void initXmlFile(String scriptName) throws IOException { + mXmlFile = new FileWriter(mDirname + "/" + mXmlFilename); + mXmlWriter = new XMLWriter(mXmlFile); + mXmlWriter.begin(); + mXmlWriter.comment("Monkey Script Results"); + mXmlWriter.start("script_run"); + mXmlWriter.addAttribute("script_name", scriptName); + } + + /** + * Add a comment to the xml file. + * + * @param comment comment to add to the xml file + */ + public static void addComment(String comment) throws IOException { + mXmlWriter.comment(comment); + } + + /** + * Begin writing a command xml element + */ + public static void startCommand() throws IOException { + mXmlWriter.start("command"); + mXmlWriter.addAttribute("dateTime", now()); + } + + /** + * Write a command name attribute in a command xml element. + * It's add as a sinlge script command could be multiple monkey commands. + * + * @param cmd command sent to the monkey + */ + public static void addInput(String cmd) throws IOException { + String name = "cmd"; + String value = cmd; + mXmlWriter.tag("input", name, value); + } + + /** + * Write a response xml element in a command. + * Attributes include the monkey result, datetime, and possibly screenshot filename + * + * @param result response of the monkey to the command + * @param filename filename of the screen shot (or other file to be included) + */ + public static void addResult(String result, String filename) throws IOException { + int num_args = 2; + String[] names = new String[3]; + String[] values = new String[3]; + names[0] = "result"; + values[0] = result; + names[1] = "dateTime"; + values[1] = now(); + if (filename.length() != 0) { + names[2] = "screenshot"; + values[2] = stampFilename(filename); + addScreenShot(filename); + num_args = 3; + } + mXmlWriter.tag("response", names, values, num_args); + } + + /** + * Add an attribut to an xml element. name="escaped_value" + * + * @param name name of the attribute + * @param value value of the attribute + */ + public static void addAttribute(String name, String value) throws IOException { + mXmlWriter.addAttribute(name, value); + } + + /** + * Add an xml device variable element. name="escaped_value" + * + * @param name name of the variable + * @param value value of the variable + */ + public static void addDeviceVar(String name, String value) throws IOException { + String[] names = {"name", "value"}; + String[] values = {name, value}; + mXmlWriter.tag("device_var", names, values, names.length); + } + + /** + * Move the screenshot to storage and remember you did it so it can be zipped up later. + * + * @param filename file name of the screenshot to be stored (Not path name) + */ + private static void addScreenShot(String filename) { + File file = new File(filename); + String screenShotName = stampFilename(filename); + file.renameTo(new File(mDirname, screenShotName)); + mScreenShots.add(screenShotName); + } + + /** + * Finish writing a command xml element + */ + public static void endCommand() throws IOException { + mXmlWriter.end(); + } + + /** + * Add datetime in front of filetype (the stuff after and including the last infamous '.') + * + * @param filename path of file to be stamped + */ + private static String stampFilename(String filename) { + // + int typeIndex = filename.lastIndexOf('.'); + if (typeIndex == -1) { + return filename + "-" + now(); + } + return filename.substring(0, typeIndex) + "-" + now() + filename.substring(typeIndex); + } + + /** + * Strip out the file type (the stuff after and including the last infamous '.') + * + * @param filename path of file to be stripped of type information + */ + private static String stripType(String filename) { + // + int typeIndex = filename.lastIndexOf('.'); + if (typeIndex == -1) + return filename; + return filename.substring(0, typeIndex); + } + + /** + * Add a signature element + * + * @param filename path of file to be signatured + */ + private static void addMD5Signature(String filename) throws IOException { + String signature = ""; + // find signature... MD5 sig = new MD5(filename); signature = sig.toString(); + String[] names = new String[] { "type", "filename", "signature" }; + String[] values = new String[] { "md5", filename, signature }; + mXmlWriter.tag("Signature", names, values, values.length); + } + + + /** + * Close the monkeyRecorder by closing the xml file and zipping it up with the screenshots. + * + * @param filename path of file to be stripped of type information + */ + public static void close() throws IOException { + // zip up xml file and screenshots into ROOT_DIR. + byte[] buf = new byte[1024]; + String zipFileName = mXmlFilename + ".zip"; + endCommand(); + mXmlFile.close(); + FileOutputStream zipFile = new FileOutputStream(ROOT_DIR + "/" + zipFileName); + ZipOutputStream out = new ZipOutputStream(zipFile); + + // add the xml file + addFileToZip(out, mDirname + "/" + mXmlFilename, buf); + + // Add the screenshots + for (String filename : mScreenShots) { + addFileToZip(out, mDirname + "/" + filename, buf); + } + out.close(); + } + + /** + * Helper function to zip up a file into an open zip archive. + * + * @param zip the stream of the zip archive + * @param filepath the filepath of the file to be added to the zip archive + * @param buf storage place to stage reads of file before zipping + */ + private static void addFileToZip(ZipOutputStream zip, String filepath, byte[] buf) throws IOException { + FileInputStream in = new FileInputStream(filepath); + zip.putNextEntry(new ZipEntry(filepath)); + int len; + while ((len = in.read(buf)) > 0) { + zip.write(buf, 0, len); + } + zip.closeEntry(); + in.close(); + } +} diff --git a/tools/monkeyrunner/src/com/android/monkeyrunner/MonkeyRunner.java b/tools/monkeyrunner/src/com/android/monkeyrunner/MonkeyRunner.java index cbc881c6c..13386c0f0 100644 --- a/tools/monkeyrunner/src/com/android/monkeyrunner/MonkeyRunner.java +++ b/tools/monkeyrunner/src/com/android/monkeyrunner/MonkeyRunner.java @@ -30,18 +30,21 @@ import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; import java.io.IOException; +import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.net.InetAddress; import java.net.Socket; import java.net.UnknownHostException; +import java.util.logging.Level; +import java.util.logging.Logger; import javax.imageio.ImageIO; /** * MonkeyRunner is a host side application to control a monkey instance on a - * device. MonkeyRunner provides some useful helper functions to control the - * device as well as various other methods to help script tests. + * device. MonkeyRunner provides some useful helper functions to control the + * device as well as various other methods to help script tests. */ public class MonkeyRunner { @@ -50,27 +53,45 @@ public class MonkeyRunner { static Socket monkeySocket = null; static IDevice monkeyDevice; - + static BufferedReader monkeyReader; static BufferedWriter monkeyWriter; + static String monkeyResponse; + + static MonkeyRecorder monkeyRecorder; static String scriptName = null; + + // Obtain a suitable logger. +// private static Logger logger = Logger.getLogger("com.android.monkeyrunner"); + private static Logger logger = Logger.global; // delay between key events final static int KEY_INPUT_DELAY = 1000; + + // version of monkey runner + final static String monkeyRunnerVersion = "0.2"; - public static void main(String[] args) { + public static void main(String[] args) throws IOException { + // haven't figure out how to get below INFO...bad parent. Pass -v INFO to turn on logging + logger.setLevel(Level.parse("WARNING")); processOptions(args); + logger.info("initAdb"); initAdbConnection(); + logger.info("openMonkeyConnection"); openMonkeyConnection(); + logger.info("start_script"); start_script(); + logger.info("ScriptRunner.run"); ScriptRunner.run(scriptName); + logger.info("end_script"); end_script(); + logger.info("closeMonkeyConnection"); closeMonkeyConnection(); } @@ -166,13 +187,15 @@ public class MonkeyRunner { try { InetAddress addr = InetAddress.getByName(monkeyServer); monkeySocket = new Socket(addr, monkeyPort); + monkeyWriter = new BufferedWriter(new OutputStreamWriter(monkeySocket.getOutputStream())); + monkeyReader = new BufferedReader(new InputStreamReader(monkeySocket.getInputStream())); } catch (UnknownHostException e) { e.printStackTrace(); } catch(IOException e) { e.printStackTrace(); } } - + /** * Close tcp session with the monkey on the device * @@ -189,47 +212,59 @@ public class MonkeyRunner { } /** - * This is a house cleaning type routine to run before starting a script. Puts - * the device in a known state. + * This is a house cleaning routine to run before starting a script. Puts + * the device in a known state and starts recording interesting info. */ - public static void start_script() { - press("menu"); - press("menu"); - press("home"); + public static void start_script() throws IOException { + press("menu", false); + press("menu", false); + press("home", false); + + // Start recording the script output, might want md5 signature of file for completeness + monkeyRecorder = new MonkeyRecorder(scriptName); + + // Record what device and version of software we are running on + monkeyRecorder.addAttribute("monkeyRunnerVersion", monkeyRunnerVersion); + addDeviceVars(); + monkeyRecorder.addComment("Script commands"); } - public static void end_script() { - String command = "END"; - sendMonkeyEvent(command); + /** + * This is a house cleaning routine to run after finishing a script. + * Puts the monkey server in a known state and closes the recording. + */ + public static void end_script() throws IOException { + String command = "done"; + sendMonkeyEvent(command, false, false); + + // Stop the recording and zip up the results + monkeyRecorder.close(); } /** This is a method for scripts to launch an activity on the device * * @param name The name of the activity to launch */ - public static void launch_activity(String name) { - try { - System.out.println("Launching: " + name); - monkeyDevice.executeShellCommand("am start -a android.intent.action.MAIN -n " - + name, new NullOutputReceiver()); - } catch (Exception e) { - e.printStackTrace(); - } - } + public static void launch_activity(String name) throws IOException { + System.out.println("Launching: " + name); + recordCommand("Launching: " + name); + monkeyDevice.executeShellCommand("am start -a android.intent.action.MAIN -n " + + name, new NullOutputReceiver()); + // void return, so no response given, just close the command element in the xml file. + monkeyRecorder.endCommand(); + } /** * Grabs the current state of the screen stores it as a png * * @param tag filename or tag descriptor of the screenshot */ - public static void grabscreen(String tag) { + public static void grabscreen(String tag) throws IOException { tag += ".png"; try { Thread.sleep(1000); getDeviceImage(monkeyDevice, tag, false); - } catch (IOException e) { - e.printStackTrace(); } catch (InterruptedException e) { } } @@ -239,9 +274,11 @@ public class MonkeyRunner { * * @param msec msecs to sleep for */ - public static void sleep(int msec) { + public static void sleep(int msec) throws IOException { try { + recordCommand("sleep: " + msec); Thread.sleep(msec); + recordResponse("OK"); } catch (InterruptedException e) { e.printStackTrace(); } @@ -253,12 +290,14 @@ public class MonkeyRunner { * @param x x-coordinate * @param y y-coordinate */ - public static boolean tap(int x, int y) { - String command = "touch down " + x + " " + y + "\r\n" + - "touch up " + x + " " + y + "\r\n"; + public static boolean tap(int x, int y) throws IOException { + String command = "touch down " + x + " " + y + "\r\n" + "touch up " + x + " " + y + "\r\n"; + recordCommand("Tapping: " + x + ", " + y); System.out.println("Tapping: " + x + ", " + y); - return sendMonkeyEvent(command); + boolean result = sendMonkeyEvent(command, true, false); + recordResponse(monkeyResponse); + return result; } /** @@ -266,25 +305,38 @@ public class MonkeyRunner { * * @param key key to press */ - public static boolean press(String key) { - String command = "key down " + key + "\r\n" + - "key up " + key + "\r\n"; + public static boolean press(String key) throws IOException { + return press(key, true); + } - System.out.println("Pressing: " + key); - return sendMonkeyEvent(command); + /** + * Press function for scripts to call on a particular button or key + * + * @param key key to press + * @param print whether to send output to user + */ + private static boolean press(String key, boolean print) throws IOException { + String command = "key down " + key + "\r\n" + "key up " + key + "\r\n"; + + recordCommand("Pressing: " + key); + if (print) + System.out.println("Pressing: " + key); + boolean result = sendMonkeyEvent(command, print, false); + recordResponse(monkeyResponse); + return result; } /** * dpad down function */ - public static boolean down() { + public static boolean down() throws IOException { return press("dpad_down"); } /** * dpad up function */ - public static boolean up() { + public static boolean up() throws IOException { return press("dpad_up"); } @@ -293,8 +345,10 @@ public class MonkeyRunner { * * @param text text to type */ - public static boolean type(String text) { + public static boolean type(String text) throws IOException { System.out.println("Typing: " + text); + recordCommand("Typing: " + text); + for (int i=0; i 2) { + monkeyRecorder.addDeviceVar(name, monkeyResponse.substring(3)); + } else { + // only got OK - good variable but no value + monkeyRecorder.addDeviceVar(name, "null"); + } + } else { + // error returned - couldn't get var value for name... include error return + monkeyRecorder.addDeviceVar(name, monkeyResponse); + } + } else { + // no monkeyResponse - bad variable with no value + monkeyRecorder.addDeviceVar(name, "null"); + } + } + } else { + // it's an error, can't find variable names... + monkeyRecorder.addAttribute("listvar", monkeyResponse); + } + } + /** * Process the command-line options * * @return Returns true if options were parsed with no apparent errors. */ - public static void processOptions(String[] args) { + private static void processOptions(String[] args) { // parse command line parameters. int index = 0; @@ -364,7 +539,7 @@ public class MonkeyRunner { if ("-s".equals(argument)) { if(index == args.length) { - printAndExit("Missing Server after -s", false); + printUsageAndQuit("Missing Server after -s"); } monkeyServer = args[index++]; @@ -372,18 +547,32 @@ public class MonkeyRunner { } else if ("-p".equals(argument)) { // quick check on the next argument. if (index == args.length) { - printAndExit("Missing Server IP after -p", false /* terminate */); + printUsageAndQuit("Missing Server port after -p"); + } + + monkeyPort = Integer.parseInt(args[index++]); + + } else if ("-v".equals(argument)) { + // quick check on the next argument. + if (index == args.length) { + printUsageAndQuit("Missing Log Level after -v"); } + Level level = Level.parse(args[index++]); + logger.setLevel(level); + level = logger.getLevel(); + System.out.println("Log level set to: " + level + "(" + level.intValue() + ")."); + System.out.println("Warning: Log levels below INFO(800) not working currently... parent issues"); + + } else if (argument.startsWith("-")) { + // we have an unrecognized argument. + printUsageAndQuit("Unrecognized argument: " + argument + "."); + monkeyPort = Integer.parseInt(args[index++]); + } else { - // get the filepath of the script to run. + // get the filepath of the script to run. This will be the last undashed argument. scriptName = argument; - - // should not be any other device. - //if (index < args.length) { - // printAndExit("Too many arguments!", false /* terminate */); - //} } } while (index < args.length); } @@ -394,22 +583,29 @@ public class MonkeyRunner { private static void getDeviceImage(IDevice device, String filepath, boolean landscape) throws IOException { RawImage rawImage; + recordCommand("grabscreen"); + System.out.println("Grabbing Screeshot: " + filepath + "."); try { rawImage = device.getScreenshot(); } catch (IOException ioe) { + recordResponse("No frame buffer", ""); printAndExit("Unable to get frame buffer: " + ioe.getMessage(), true /* terminate */); return; } // device/adb not available? - if (rawImage == null) + if (rawImage == null) { + recordResponse("No image", ""); return; - + } + assert rawImage.bpp == 16; BufferedImage image; + + logger.info("Raw Image - height: " + rawImage.height + ", width: " + rawImage.width); if (landscape) { // convert raw data to an Image @@ -458,16 +654,20 @@ public class MonkeyRunner { } if (!ImageIO.write(image, "png", new File(filepath))) { + recordResponse("No png writer", ""); throw new IOException("Failed to find png writer"); } + recordResponse("OK", filepath); } - private static void printUsageAndQuit() { + private static void printUsageAndQuit(String message) { // 80 cols marker: 01234567890123456789012345678901234567890123456789012345678901234567890123456789 + System.out.println(message); System.out.println("Usage: monkeyrunner [options] SCRIPT_FILE"); System.out.println(""); System.out.println(" -s MonkeyServer IP Address."); System.out.println(" -p MonkeyServer TCP Port."); + System.out.println(" -v MonkeyServer Logging level (ALL, FINEST, FINER, FINE, CONFIG, INFO, WARNING, SEVERE, OFF)"); System.out.println(""); System.out.println(""); -- cgit v1.2.3 From 82034fdf1770e948c9a0cca00c91db8333be3500 Mon Sep 17 00:00:00 2001 From: mike ritter Date: Thu, 24 Sep 2009 11:02:44 -0700 Subject: Remove unnecessary preprocessing of monkey commands, fix bug where it didn't recognize '@', '+', '.', etc. Bug #2121341 --- .../com/android/monkeyrunner/MonkeyRecorder.java | 7 +- .../src/com/android/monkeyrunner/MonkeyRunner.java | 123 ++++++++------------- 2 files changed, 52 insertions(+), 78 deletions(-) diff --git a/tools/monkeyrunner/src/com/android/monkeyrunner/MonkeyRecorder.java b/tools/monkeyrunner/src/com/android/monkeyrunner/MonkeyRecorder.java index 1132733b8..efc002b5a 100644 --- a/tools/monkeyrunner/src/com/android/monkeyrunner/MonkeyRecorder.java +++ b/tools/monkeyrunner/src/com/android/monkeyrunner/MonkeyRecorder.java @@ -22,6 +22,7 @@ import java.io.FileOutputStream; import java.io.FileWriter; import java.io.IOException; import java.text.SimpleDateFormat; +import java.util.List; import java.util.ArrayList; import java.util.Calendar; import java.util.zip.ZipEntry; @@ -88,7 +89,7 @@ public class MonkeyRecorder { // unique subdirectory to put results in (screenshots and xml file) private static String mDirname; - private static ArrayList mScreenShots = new ArrayList(); + private static List mScreenShotNames = new ArrayList(); // where we store all the results for all the script runs private static final String ROOT_DIR = "out"; @@ -216,7 +217,7 @@ public class MonkeyRecorder { File file = new File(filename); String screenShotName = stampFilename(filename); file.renameTo(new File(mDirname, screenShotName)); - mScreenShots.add(screenShotName); + mScreenShotNames.add(screenShotName); } /** @@ -285,7 +286,7 @@ public class MonkeyRecorder { addFileToZip(out, mDirname + "/" + mXmlFilename, buf); // Add the screenshots - for (String filename : mScreenShots) { + for (String filename : mScreenShotNames) { addFileToZip(out, mDirname + "/" + filename, buf); } out.close(); diff --git a/tools/monkeyrunner/src/com/android/monkeyrunner/MonkeyRunner.java b/tools/monkeyrunner/src/com/android/monkeyrunner/MonkeyRunner.java index 13386c0f0..07a473917 100644 --- a/tools/monkeyrunner/src/com/android/monkeyrunner/MonkeyRunner.java +++ b/tools/monkeyrunner/src/com/android/monkeyrunner/MonkeyRunner.java @@ -63,14 +63,15 @@ public class MonkeyRunner { static String scriptName = null; // Obtain a suitable logger. -// private static Logger logger = Logger.getLogger("com.android.monkeyrunner"); - private static Logger logger = Logger.global; + private static Logger logger = Logger.getLogger("com.android.monkeyrunner"); // delay between key events final static int KEY_INPUT_DELAY = 1000; // version of monkey runner - final static String monkeyRunnerVersion = "0.2"; + final static String monkeyRunnerVersion = "0.31"; + + // TODO: interface cmd; class xml tags; fix logger; test class/script public static void main(String[] args) throws IOException { @@ -291,12 +292,8 @@ public class MonkeyRunner { * @param y y-coordinate */ public static boolean tap(int x, int y) throws IOException { - String command = "touch down " + x + " " + y + "\r\n" + "touch up " + x + " " + y + "\r\n"; - - recordCommand("Tapping: " + x + ", " + y); - System.out.println("Tapping: " + x + ", " + y); - boolean result = sendMonkeyEvent(command, true, false); - recordResponse(monkeyResponse); + String command = "tap " + x + " " + y; + boolean result = sendMonkeyEvent(command); return result; } @@ -316,13 +313,8 @@ public class MonkeyRunner { * @param print whether to send output to user */ private static boolean press(String key, boolean print) throws IOException { - String command = "key down " + key + "\r\n" + "key up " + key + "\r\n"; - - recordCommand("Pressing: " + key); - if (print) - System.out.println("Pressing: " + key); - boolean result = sendMonkeyEvent(command, print, false); - recordResponse(monkeyResponse); + String command = "press " + key; + boolean result = sendMonkeyEvent(command, print, true); return result; } @@ -346,33 +338,14 @@ public class MonkeyRunner { * @param text text to type */ public static boolean type(String text) throws IOException { - System.out.println("Typing: " + text); - recordCommand("Typing: " + text); - - for (int i=0; i Date: Thu, 17 Sep 2009 17:19:09 -0700 Subject: Fix the skins to do proper dpad rotation in landscape mode. --- emulator/skins/HVGA/layout | 10 ++++++++-- emulator/skins/QVGA/layout | 3 +++ emulator/skins/WQVGA432/layout | 2 ++ emulator/skins/WVGA800/layout | 2 ++ emulator/skins/WVGA854/layout | 2 ++ 5 files changed, 17 insertions(+), 2 deletions(-) diff --git a/emulator/skins/HVGA/layout b/emulator/skins/HVGA/layout index 7117824d2..cc82ddeae 100644 --- a/emulator/skins/HVGA/layout +++ b/emulator/skins/HVGA/layout @@ -18,7 +18,7 @@ parts { y 0 } } - + controls { background { image controls.png @@ -357,7 +357,7 @@ layouts { height 534 color 0xe0e0e0 event EV_SW:0:1 - + part1 { name portrait x 0 @@ -394,6 +394,12 @@ layouts { color 0xe0e0e0 event EV_SW:0:0 + # the framework _always_ assume that the DPad + # has been physically rotated in landscape mode. + # however, with this skin, this is not the case + # + dpad-rotation 3 + part1 { name portrait x 800 diff --git a/emulator/skins/QVGA/layout b/emulator/skins/QVGA/layout index f98e7bd48..96f213388 100644 --- a/emulator/skins/QVGA/layout +++ b/emulator/skins/QVGA/layout @@ -393,6 +393,9 @@ layouts { color 0xe0e0e0 event EV_SW:0:0 + dpad-rotation 3 + + part1 { name portrait x 800 diff --git a/emulator/skins/WQVGA432/layout b/emulator/skins/WQVGA432/layout index 7e52b5320..006e955d3 100644 --- a/emulator/skins/WQVGA432/layout +++ b/emulator/skins/WQVGA432/layout @@ -393,6 +393,8 @@ layouts { color 0xe0e0e0 event EV_SW:0:0 + dpad-rotation 3 + part1 { name portrait x 800 diff --git a/emulator/skins/WVGA800/layout b/emulator/skins/WVGA800/layout index 6037ab8b5..63647f3e4 100644 --- a/emulator/skins/WVGA800/layout +++ b/emulator/skins/WVGA800/layout @@ -394,6 +394,8 @@ layouts { color 0xe0e0e0 event EV_SW:0:0 + dpad-rotation 3 + part1 { name portrait x 900 diff --git a/emulator/skins/WVGA854/layout b/emulator/skins/WVGA854/layout index ab0784d02..b086ddc2f 100644 --- a/emulator/skins/WVGA854/layout +++ b/emulator/skins/WVGA854/layout @@ -394,6 +394,8 @@ layouts { color 0xe0e0e0 event EV_SW:0:0 + dpad-rotation 3 + part1 { name portrait x 900 -- cgit v1.2.3 From 559f26b0a5e80da467d97d42280c22e5044d7f30 Mon Sep 17 00:00:00 2001 From: Xavier Ducrohet Date: Tue, 29 Sep 2009 15:42:46 -0700 Subject: Move to 1.6_r2 (DO NOT MERGE) --- tools/scripts/platform_source.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/scripts/platform_source.properties b/tools/scripts/platform_source.properties index 49dc3ae91..5916933d1 100644 --- a/tools/scripts/platform_source.properties +++ b/tools/scripts/platform_source.properties @@ -1,5 +1,5 @@ Pkg.Desc=Android SDK Platform 1.6_r1 Pkg.UserSrc=false Platform.Version=1.6 -Pkg.Revision=1 +Pkg.Revision=2 AndroidVersion.ApiLevel=4 -- cgit v1.2.3 From fff9ff696709e17ad73ae654e511907282bfc0fd Mon Sep 17 00:00:00 2001 From: Xavier Ducrohet Date: Tue, 29 Sep 2009 18:06:01 -0700 Subject: Merge the new Ant script rules and template from Eclair (do not merge) --- tools/scripts/AndroidManifest.template | 2 +- tools/scripts/AndroidManifest.tests.template | 2 +- tools/scripts/alias_rules.xml | 24 +- tools/scripts/android_rules.xml | 462 ++++++++++++++++++--------- tools/scripts/android_test_rules.xml | 104 ++++++ tools/scripts/build.alias.template | 8 +- tools/scripts/build.template | 35 +- tools/scripts/java_file.template | 2 +- tools/scripts/java_tests_file.template | 8 +- 9 files changed, 456 insertions(+), 191 deletions(-) create mode 100644 tools/scripts/android_test_rules.xml diff --git a/tools/scripts/AndroidManifest.template b/tools/scripts/AndroidManifest.template index 2b06e76ac..9b07072f5 100644 --- a/tools/scripts/AndroidManifest.template +++ b/tools/scripts/AndroidManifest.template @@ -4,7 +4,7 @@ android:versionCode="1" android:versionName="1.0"> - diff --git a/tools/scripts/AndroidManifest.tests.template b/tools/scripts/AndroidManifest.tests.template index 1f7d827fe..c74ff6dbf 100644 --- a/tools/scripts/AndroidManifest.tests.template +++ b/tools/scripts/AndroidManifest.tests.template @@ -17,5 +17,5 @@ --> + android:label="Tests for PACKAGE"/> diff --git a/tools/scripts/alias_rules.xml b/tools/scripts/alias_rules.xml index 570246134..273da45a6 100644 --- a/tools/scripts/alias_rules.xml +++ b/tools/scripts/alias_rules.xml @@ -5,22 +5,22 @@ THIS FILE IS CURRENTLY BROKEN AND SHOULD NOT BE USED. --> - + - + - + - - + + - - + + - + @@ -33,11 +33,11 @@ THIS FILE IS CURRENTLY BROKEN AND SHOULD NOT BE USED. - + - + - + @@ -49,7 +49,7 @@ THIS FILE IS CURRENTLY BROKEN AND SHOULD NOT BE USED. Sending package to default emulator... - + diff --git a/tools/scripts/android_rules.xml b/tools/scripts/android_rules.xml index ad36cbe72..5b327369a 100644 --- a/tools/scripts/android_rules.xml +++ b/tools/scripts/android_rules.xml @@ -6,7 +6,7 @@ com.android.ant.AndroidInitTask The following properties are put in place by the importing task: - android-jar, android-aidl, aapt, aidl, and dx + android.jar, android.aidl, aapt, aidl, and dx Additionnaly, the task sets up the following classpath reference: android.target.classpath @@ -16,184 +16,285 @@ + classpathref="android.antlibs" /> + classpathref="android.antlibs" /> + + - + + + - - - - - + + + + + + + + - - + + + - - + + + - - - - - - - + + + + - - - - - - + + - - - - - + + + + + + + + + + + + + + + + + + + + - - - + + + + + + + + + + + + + + + + + + + + + Converting compiled files and external libraries into ${intermediate.dex.file}... + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + Running zip align on final apk... + + + + + + + + + - - + + + + Installing ${out.debug.package} onto default emulator or device... + + + + + + + + + + + + Creating output directories if needed... - - - - - + + + + + - - + + Generating R.java / Manifest.java from the resources... + - + - + - + - - + + Compiling aidl files into Java classes... - - - - - + + + + + - - + + + + + + - - + destdir="${out.classes.absolute.dir}" + bootclasspathref="android.target.classpath" + verbose="${verbose}" classpath="${extensible.classpath}"> + + - - + - + - - - Converting compiled files and external libraries into ${out-folder}/${dex-file}... - - - - - - + + + - - + Packaging resources - - - - - - - - - - - - + + + - - + + + - - Running zip align on final apk... - - - - - - - Debug Package: ${out-debug-package} + + + + - + + + + Debug Package: ${out.debug.package} - + @@ -201,80 +302,139 @@ - + + No key.store and key.alias properties found in build.properties. - Please sign ${out-unsigned-package} manually + Please sign ${out.unsigned.package} manually and run zipalign from the Android SDK tools. - - + + + addproperty="key.store.password" /> - + addproperty="key.alias.password" /> + + Signing final apk... - - Running zip align on final apk... - - - - - - - Release Package: ${out-release-package} + keypass="${key.alias.password}" + verbose="${verbose}" /> + + + + Release Package: ${out.release.package} - - - Installing ${out-debug-package} onto default emulator... - - - - - + + - - + - + - - Unable to run 'ant unintall', application-package is not defined in build.properties + + + Unable to run 'ant uninstall', manifest.package property is not defined. + - - Uninstalling ${application-package} from the default emulator... + + + + Uninstalling ${manifest.package} from the default emulator or device... - + - + + + + + + + + + + + Instrumenting classes from ${out.absolute.dir}/classes... + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Android Ant Build. Available targets: help: Displays this help. - debug: Builds the application and sign it with a debug key. + clean: Removes output files created by other targets. + compile: Compiles project's .java files into .class files. + debug: Builds the application and signs it with a debug key. release: Builds the application. The generated apk file must be signed before it is published. - install: Installs/reinstall the debug package onto a running + install: Installs/reinstalls the debug package onto a running emulator or device. If the application was previously installed, the signatures must match. - uninstall: uninstall the application from a running emulator or + uninstall: Uninstalls the application from a running emulator or device. diff --git a/tools/scripts/android_test_rules.xml b/tools/scripts/android_test_rules.xml new file mode 100644 index 000000000..78503aef7 --- /dev/null +++ b/tools/scripts/android_test_rules.xml @@ -0,0 +1,104 @@ + + + + + + + + + + + + + + + + + + + + + Running tests ... + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Downloading coverage file into project directory... + + + + + + Extracting coverage report... + + + + + + + + + + + + Cleaning up temporary files... + + + + Saving the report file in ${basedir}/coverage/coverage.html + + + diff --git a/tools/scripts/build.alias.template b/tools/scripts/build.alias.template index 1043af184..d051405ef 100644 --- a/tools/scripts/build.alias.template +++ b/tools/scripts/build.alias.template @@ -8,16 +8,16 @@ THIS FILE IS CURRENTLY BROKEN AND SHOULD NOT BE USED. - + - + - - + diff --git a/tools/scripts/build.template b/tools/scripts/build.template index 1ed385345..3959c570f 100644 --- a/tools/scripts/build.template +++ b/tools/scripts/build.template @@ -2,22 +2,22 @@ - + - + - + - - - - - + + + + + + classpathref="android.antlibs" /> + diff --git a/tools/scripts/java_file.template b/tools/scripts/java_file.template index aeb541f7b..19714a8e3 100644 --- a/tools/scripts/java_file.template +++ b/tools/scripts/java_file.template @@ -3,7 +3,7 @@ package PACKAGE; import android.app.Activity; import android.os.Bundle; -public class ACTIVITY_NAME extends Activity +public class ACTIVITY_CLASS_NAME extends Activity { /** Called when the activity is first created. */ @Override diff --git a/tools/scripts/java_tests_file.template b/tools/scripts/java_tests_file.template index c6fa8736f..08d6f9b5d 100644 --- a/tools/scripts/java_tests_file.template +++ b/tools/scripts/java_tests_file.template @@ -9,13 +9,13 @@ import android.test.ActivityInstrumentationTestCase2; *

* To run this test, you can type: * adb shell am instrument -w \ - * -e class PACKAGE.ACTIVITY_NAMETest \ + * -e class ACTIVITY_FQ_NAME \ * PACKAGE.tests/android.test.InstrumentationTestRunner */ -public class ACTIVITY_NAMETest extends ActivityInstrumentationTestCase2 { +public class ACTIVITY_CLASS_NAME extends ActivityInstrumentationTestCase2 { - public ACTIVITY_NAMETest() { - super("PACKAGE", ACTIVITY_NAME.class); + public ACTIVITY_CLASS_NAME() { + super("PACKAGE", ACTIVITY_TESTED_CLASS_NAME.class); } } \ No newline at end of file -- cgit v1.2.3 From 4dcec84dcebfd7b15b5dc8652ec6f6a8947feacd Mon Sep 17 00:00:00 2001 From: Xavier Ducrohet Date: Fri, 9 Oct 2009 14:51:48 -0700 Subject: Update the build rules and project template to match the ones in eclair (do not merge) This will make the new 1.6 SDKs require the tools from Eclair --- tools/scripts/android_rules.xml | 7 +++++++ tools/scripts/layout.template | 2 +- tools/scripts/strings.template | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/tools/scripts/android_rules.xml b/tools/scripts/android_rules.xml index 5b327369a..675017c0b 100644 --- a/tools/scripts/android_rules.xml +++ b/tools/scripts/android_rules.xml @@ -28,6 +28,11 @@ + + + Installing ${out.debug.package} onto default emulator or device... + @@ -360,6 +366,7 @@ description="Uninstalls the application from a running emulator or device."> Uninstalling ${manifest.package} from the default emulator or device... + diff --git a/tools/scripts/layout.template b/tools/scripts/layout.template index 864e99748..f5e367ccf 100644 --- a/tools/scripts/layout.template +++ b/tools/scripts/layout.template @@ -7,7 +7,7 @@ diff --git a/tools/scripts/strings.template b/tools/scripts/strings.template index acc28e29d..ee5af40eb 100644 --- a/tools/scripts/strings.template +++ b/tools/scripts/strings.template @@ -1,4 +1,4 @@ - ACTIVITY_NAME + ACTIVITY_ENTRY_NAME -- cgit v1.2.3 From 07118d3152b3f261e726158bfdf5cf7556f75b22 Mon Sep 17 00:00:00 2001 From: Xavier Ducrohet Date: Thu, 8 Oct 2009 14:40:12 -0700 Subject: Make WVGA/High skins use a higher VM heap size. (do not merge) This is integrated from the Eclair branch. --- emulator/skins/WVGA800/hardware.ini | 3 ++- emulator/skins/WVGA854/hardware.ini | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/emulator/skins/WVGA800/hardware.ini b/emulator/skins/WVGA800/hardware.ini index 02e9d89ee..9aec915b0 100644 --- a/emulator/skins/WVGA800/hardware.ini +++ b/emulator/skins/WVGA800/hardware.ini @@ -1,2 +1,3 @@ # skin-specific hardware values -hw.lcd.density=240 \ No newline at end of file +hw.lcd.density=240 +vm.heapSize=24 diff --git a/emulator/skins/WVGA854/hardware.ini b/emulator/skins/WVGA854/hardware.ini index 02e9d89ee..9aec915b0 100644 --- a/emulator/skins/WVGA854/hardware.ini +++ b/emulator/skins/WVGA854/hardware.ini @@ -1,2 +1,3 @@ # skin-specific hardware values -hw.lcd.density=240 \ No newline at end of file +hw.lcd.density=240 +vm.heapSize=24 -- cgit v1.2.3 From 249d0927e440a7f02383beb5768efa6007bba071 Mon Sep 17 00:00:00 2001 From: Xavier Ducrohet Date: Tue, 13 Oct 2009 11:09:20 -0700 Subject: Fix platform package description --- tools/scripts/platform_source.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/scripts/platform_source.properties b/tools/scripts/platform_source.properties index 5916933d1..4269133e9 100644 --- a/tools/scripts/platform_source.properties +++ b/tools/scripts/platform_source.properties @@ -1,4 +1,4 @@ -Pkg.Desc=Android SDK Platform 1.6_r1 +Pkg.Desc=Android SDK Platform 1.6_r2 Pkg.UserSrc=false Platform.Version=1.6 Pkg.Revision=2 -- cgit v1.2.3 From 98b17eca90cd8b747f88181192342925bee57dfd Mon Sep 17 00:00:00 2001 From: Dirk Dougherty Date: Fri, 9 Oct 2009 17:15:52 -0700 Subject: doc change only: update gae python server script (tested) Change-Id: I9c02ffe0cec177d0f52d61fcb30f4e85bee31ff7 --- .../scripts/app_engine_server/memcache_zipserve.py | 255 +++++++++++++++++---- 1 file changed, 208 insertions(+), 47 deletions(-) diff --git a/tools/scripts/app_engine_server/memcache_zipserve.py b/tools/scripts/app_engine_server/memcache_zipserve.py index e11cfc544..43826b00a 100644 --- a/tools/scripts/app_engine_server/memcache_zipserve.py +++ b/tools/scripts/app_engine_server/memcache_zipserve.py @@ -37,7 +37,7 @@ import zipfile from google.appengine.api import memcache from google.appengine.ext import webapp from google.appengine.ext.webapp import util - +from time import localtime, strftime def create_handler(zip_files, max_age=None, public=None): """Factory method to create a MemcachedZipHandler instance. @@ -57,7 +57,6 @@ def create_handler(zip_files, max_age=None, public=None): """ # verify argument integrity. If the argument is passed in list format, # convert it to list of lists format - if zip_files and type(zip_files).__name__ == 'list': num_items = len(zip_files) while num_items > 0: @@ -72,7 +71,6 @@ def create_handler(zip_files, max_age=None, public=None): I'm still not sure why this is needed """ - def get(self, name): self.zipfilenames = zip_files self.TrueGet(name) @@ -96,12 +94,15 @@ class MemcachedZipHandler(webapp.RequestHandler): PUBLIC = True # public cache setting CACHE_PREFIX = 'cache://' # memcache key prefix for actual URLs NEG_CACHE_PREFIX = 'noncache://' # memcache key prefix for non-existant URL - - def TrueGet(self, name): + intlString = 'intl/' + validLangs = ['en', 'de', 'es', 'fr','it','ja','zh-CN','zh-TW'] + + def TrueGet(self, reqUri): """The top-level entry point to serving requests. Called 'True' get because it does the work when called from the wrapper - class' get method + class' get method. Some logic is applied to the request to serve files + from an intl//... directory or fall through to the default language. Args: name: URL requested @@ -109,37 +110,88 @@ class MemcachedZipHandler(webapp.RequestHandler): Returns: None """ - name = self.PreprocessUrl(name) - - # see if we have the page in the memcache - resp_data = self.GetFromCache(name) - if resp_data is None: - logging.info('Cache miss for %s', name) - resp_data = self.GetFromNegativeCache(name) - if resp_data is None: - resp_data = self.GetFromStore(name) + langName = 'en' + resetLangCookie = False + urlLangName = None + retry = False + isValidIntl = False + + # Try to retrieve the user's lang pref from the cookie. If there is no + # lang pref cookie in the request, add set-cookie to the response with the + # default value of 'en'. + try: + langName = self.request.cookies['android_developer_pref_lang'] + except KeyError: + resetLangCookie = True + #logging.info('==========================EXCEPTION: NO LANG COOKIE FOUND, USING [%s]', langName) + logging.info('==========================REQ INIT name [%s] langName [%s]', reqUri, langName) + + # Preprocess the req url. If it references a directory or the domain itself, + # append '/index.html' to the url and 302 redirect. Otherwise, continue + # processing the request below. + name = self.PreprocessUrl(reqUri, langName) + if name: + # Do some prep for handling intl requests. Parse the url and validate + # the intl/lang substring, extract the url lang code (urlLangName) and the + # the uri that follows the intl/lang substring(contentUri) + sections = name.split("/", 2) + contentUri = 0 + isIntl = len(sections) > 1 and (sections[0] == "intl") + if isIntl: + isValidIntl = sections[1] in self.validLangs + if isValidIntl: + urlLangName = sections[1] + contentUri = sections[2] + if (langName != urlLangName): + # if the lang code in the request is different from that in + # the cookie, reset the cookie to the url lang value. + langName = urlLangName + resetLangCookie = True + #logging.info('INTL PREP resetting langName to urlLangName [%s]', langName) + #else: + # logging.info('INTL PREP no need to reset langName') + + # Send for processing + if self.isCleanUrl(name, langName, isValidIntl): + # handle a 'clean' request. + # Try to form a response using the actual request url. + if not self.CreateResponse(name, langName, isValidIntl, resetLangCookie): + # If CreateResponse returns False, there was no such document + # in the intl/lang tree. Before going to 404, see if there is an + # English-language version of the doc in the default + # default tree and return it, else go to 404. + self.CreateResponse(contentUri, langName, False, resetLangCookie) + + elif isIntl: + # handle the case where we need to pass through an invalid intl req + # for processing (so as to get 404 as appropriate). This is needed + # because intl urls are passed through clean and retried in English, + # if necessary. + logging.info(' Handling an invalid intl request...') + self.CreateResponse(name, langName, isValidIntl, resetLangCookie) - # IF we have the file, put it in the memcache - # ELSE put it in the negative cache - if resp_data is not None: - self.StoreOrUpdateInCache(name, resp_data) - else: - logging.info('Adding %s to negative cache, serving 404', name) - self.StoreInNegativeCache(name) - self.Write404Error() - return else: - self.Write404Error() - return + # handle the case where we have a non-clean url (usually a non-intl + # url) that we need to interpret in the context of any lang pref + # that is set. Prepend an intl/lang string to the request url and + # send it as a 302 redirect. After the redirect, the subsequent + # request will be handled as a clean url. + self.RedirToIntl(name, self.intlString, langName) - content_type, encoding = mimetypes.guess_type(name) - if content_type: - self.response.headers['Content-Type'] = content_type - self.SetCachingHeaders() - self.response.out.write(resp_data) + def isCleanUrl(self, name, langName, isValidIntl): + """Determine whether to pass an incoming url straight to processing. - def PreprocessUrl(self, name): - """Any preprocessing work on the URL when it comes it. + Args: + name: The incoming URL + + Returns: + boolean: Whether the URL should be sent straight to processing + """ + if (langName == 'en') or isValidIntl or not ('.html' in name) or (not isValidIntl and not langName): + return True + + def PreprocessUrl(self, name, langName): + """Any preprocessing work on the URL when it comes in. Put any work related to interpretting the incoming URL here. For example, this is used to redirect requests for a directory to the index.html file @@ -150,12 +202,9 @@ class MemcachedZipHandler(webapp.RequestHandler): name: The incoming URL Returns: - The processed URL + False if the request was redirected to '/index.html', or + The processed URL, otherwise """ - # handle special case of requesting the domain itself - if not name: - name = 'index.html' - # determine if this is a request for a directory final_path_segment = name final_slash_offset = name.rfind('/') @@ -164,12 +213,123 @@ class MemcachedZipHandler(webapp.RequestHandler): if final_path_segment.find('.') == -1: name = ''.join([name, '/']) - # if this is a directory, redirect to index.html - if name[len(name) - 1:] == '/': - return '%s%s' % (name, 'index.html') + # if this is a directory or the domain itself, redirect to /index.html + if not name or (name[len(name) - 1:] == '/'): + uri = ''.join(['/', name, 'index.html']) + logging.info('--->PREPROCESSING REDIRECT [%s] to [%s] with langName [%s]', name, uri, langName) + self.redirect(uri, False) + return False else: return name + def RedirToIntl(self, name, intlString, langName): + """Redirect an incoming request to the appropriate intl uri. + + Builds the intl/lang string from a base (en) string + and redirects (302) the request to look for a version + of the file in the language that matches the client- + supplied cookie value. + + Args: + name: The incoming, preprocessed URL + + Returns: + The lang-specific URL + """ + builtIntlLangUri = ''.join([intlString, langName, '/', name, '?', self.request.query_string]) + uri = ''.join(['/', builtIntlLangUri]) + logging.info('-->>REDIRECTING %s to %s', name, uri) + self.redirect(uri, False) + return uri + + def CreateResponse(self, name, langName, isValidIntl, resetLangCookie): + """Process the url and form a response, if appropriate. + + Attempts to retrieve the requested file (name) from cache, + negative cache, or store (zip) and form the response. + For intl requests that are not found (in the localized tree), + returns False rather than forming a response, so that + the request can be retried with the base url (this is the + fallthrough to default language). + + For requests that are found, forms the headers and + adds the content to the response entity. If the request was + for an intl (localized) url, also resets the language cookie + to the language specified in the url if needed, to ensure that + the client language and response data remain harmonious. + + Args: + name: The incoming, preprocessed URL + langName: The language id. Used as necessary to reset the + language cookie in the response. + isValidIntl: If present, indicates whether the request is + for a language-specific url + resetLangCookie: Whether the response should reset the + language cookie to 'langName' + + Returns: + True: A response was successfully created for the request + False: No response was created. + """ + # see if we have the page in the memcache + logging.info('PROCESSING %s langName [%s] isValidIntl [%s] resetLang [%s]', + name, langName, isValidIntl, resetLangCookie) + resp_data = self.GetFromCache(name) + if resp_data is None: + logging.info(' Cache miss for %s', name) + resp_data = self.GetFromNegativeCache(name) + if resp_data is None: + resp_data = self.GetFromStore(name) + + # IF we have the file, put it in the memcache + # ELSE put it in the negative cache + if resp_data is not None: + self.StoreOrUpdateInCache(name, resp_data) + elif isValidIntl: + # couldn't find the intl doc. Try to fall through to English. + #logging.info(' Retrying with base uri...') + return False + else: + logging.info(' Adding %s to negative cache, serving 404', name) + self.StoreInNegativeCache(name) + self.Write404Error() + return True + else: + # found it in negative cache + self.Write404Error() + return True + + # found content from cache or store + logging.info('FOUND CLEAN') + if resetLangCookie: + logging.info(' Resetting android_developer_pref_lang cookie to [%s]', + langName) + expireDate = time.mktime(localtime()) + 60 * 60 * 24 * 365 * 10 + self.response.headers.add_header('Set-Cookie', + 'android_developer_pref_lang=%s; path=/; expires=%s' % + (langName, strftime("%a, %d %b %Y %H:%M:%S", localtime(expireDate)))) + mustRevalidate = False + if ('.html' in name): + # revalidate html files -- workaround for cache inconsistencies for + # negotiated responses + mustRevalidate = True + logging.info(' Adding [Vary: Cookie] to response...') + self.response.headers.add_header('Vary', 'Cookie') + content_type, encoding = mimetypes.guess_type(name) + if content_type: + self.response.headers['Content-Type'] = content_type + self.SetCachingHeaders(mustRevalidate) + self.response.out.write(resp_data) + elif (name == 'favicon.ico'): + self.response.headers['Content-Type'] = 'image/x-icon' + self.SetCachingHeaders(mustRevalidate) + self.response.out.write(resp_data) + elif name.endswith('.psd'): + self.response.headers['Content-Type'] = 'application/octet-stream' + self.SetCachingHeaders(mustRevalidate) + self.response.out.write(resp_data) + return True + def GetFromStore(self, file_path): """Retrieve file from zip files. @@ -192,7 +352,7 @@ class MemcachedZipHandler(webapp.RequestHandler): archive_name = self.MapFileToArchive(file_path) if not archive_name: archive_name = file_itr.next()[0] - + while resp_data is None and archive_name: zip_archive = self.LoadZipFile(archive_name) if zip_archive: @@ -326,15 +486,17 @@ class MemcachedZipHandler(webapp.RequestHandler): else: return 0 - def SetCachingHeaders(self): + def SetCachingHeaders(self, revalidate): """Set caching headers for the request.""" max_age = self.MAX_AGE - self.response.headers['Expires'] = email.Utils.formatdate( - time.time() + max_age, usegmt=True) - cache_control = [] + #self.response.headers['Expires'] = email.Utils.formatdate( + # time.time() + max_age, usegmt=True) + cache_control = [] if self.PUBLIC: cache_control.append('public') cache_control.append('max-age=%d' % max_age) + if revalidate: + cache_control.append('must-revalidate') self.response.headers['Cache-Control'] = ', '.join(cache_control) def GetFromCache(self, filename): @@ -401,7 +563,6 @@ class MemcachedZipHandler(webapp.RequestHandler): """ return memcache.get('%s%s' % (self.NEG_CACHE_PREFIX, filename)) - def main(): application = webapp.WSGIApplication([('/([^/]+)/(.*)', MemcachedZipHandler)]) -- cgit v1.2.3 From dd69b12f6e774ac5aecfcb7533884f37276a569b Mon Sep 17 00:00:00 2001 From: Xavier Ducrohet Date: Sun, 8 Nov 2009 15:15:37 -0800 Subject: Add Japanese font to SDK. (do not merge) BUG 2041229 This is integrated from Eclair. --- build/sdk.atree | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/build/sdk.atree b/build/sdk.atree index 16d65de16..bc0a33114 100644 --- a/build/sdk.atree +++ b/build/sdk.atree @@ -171,16 +171,21 @@ docs/service_actions.txt platforms/${PLATFORM_NAME}/data/service_actions.txt docs/categories.txt platforms/${PLATFORM_NAME}/data/categories.txt docs/widgets.txt platforms/${PLATFORM_NAME}/data/widgets.txt framework/layoutlib.jar platforms/${PLATFORM_NAME}/data/layoutlib.jar + +# framework resources for layoutlib frameworks/base/core/res/res platforms/${PLATFORM_NAME}/data/res -frameworks/base/data/fonts/fonts.xml platforms/${PLATFORM_NAME}/data/fonts/fonts.xml -frameworks/base/data/fonts/DroidSans.ttf platforms/${PLATFORM_NAME}/data/fonts/DroidSans.ttf -frameworks/base/data/fonts/DroidSans-Bold.ttf platforms/${PLATFORM_NAME}/data/fonts/DroidSans-Bold.ttf -frameworks/base/data/fonts/DroidSansFallback.ttf platforms/${PLATFORM_NAME}/data/fonts/DroidSansFallback.ttf -frameworks/base/data/fonts/DroidSansMono.ttf platforms/${PLATFORM_NAME}/data/fonts/DroidSansMono.ttf -frameworks/base/data/fonts/DroidSerif-Bold.ttf platforms/${PLATFORM_NAME}/data/fonts/DroidSerif-Bold.ttf + +# fonts for layoutlib. +frameworks/base/data/fonts/fonts.xml platforms/${PLATFORM_NAME}/data/fonts/fonts.xml +frameworks/base/data/fonts/DroidSans.ttf platforms/${PLATFORM_NAME}/data/fonts/DroidSans.ttf +frameworks/base/data/fonts/DroidSans-Bold.ttf platforms/${PLATFORM_NAME}/data/fonts/DroidSans-Bold.ttf +frameworks/base/data/fonts/DroidSansMono.ttf platforms/${PLATFORM_NAME}/data/fonts/DroidSansMono.ttf +frameworks/base/data/fonts/DroidSerif-Bold.ttf platforms/${PLATFORM_NAME}/data/fonts/DroidSerif-Bold.ttf frameworks/base/data/fonts/DroidSerif-BoldItalic.ttf platforms/${PLATFORM_NAME}/data/fonts/DroidSerif-BoldItalic.ttf -frameworks/base/data/fonts/DroidSerif-Italic.ttf platforms/${PLATFORM_NAME}/data/fonts/DroidSerif-Italic.ttf -frameworks/base/data/fonts/DroidSerif-Regular.ttf platforms/${PLATFORM_NAME}/data/fonts/DroidSerif-Regular.ttf +frameworks/base/data/fonts/DroidSerif-Italic.ttf platforms/${PLATFORM_NAME}/data/fonts/DroidSerif-Italic.ttf +frameworks/base/data/fonts/DroidSerif-Regular.ttf platforms/${PLATFORM_NAME}/data/fonts/DroidSerif-Regular.ttf +frameworks/base/data/fonts/DroidSansFallback.ttf platforms/${PLATFORM_NAME}/data/fonts/DroidSansFallback.ttf +frameworks/base/data/fonts/DroidSansJapanese.ttf platforms/${PLATFORM_NAME}/data/fonts/DroidSansJapanese.ttf # empty add-on folder with just a readme development/tools/scripts/README_add-ons.txt add-ons/README.txt -- cgit v1.2.3