-->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="appTitle">"Záloha"</string>
- <string name="title">"Akce není podporována"</string>
- <string name="error">"Tato akce není momentálně podporována."</string>
+ <string name="appTitle" msgid="161410001913116606">"Záloha"</string>
+ <string name="title" msgid="8156274565006125136">"Akce není podporována"</string>
+ <string name="error" msgid="6539615832923362301">"Tato akce není momentálně podporována."</string>
</resources>
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="appTitle">"Reserve"</string>
- <string name="title">"Ikke understøttet handling"</string>
- <string name="error">"Handlingen er ikke understøttet i øjeblikket."</string>
+ <string name="appTitle" msgid="161410001913116606">"Reserve"</string>
+ <string name="title" msgid="8156274565006125136">"Ikke understøttet handling"</string>
+ <string name="error" msgid="6539615832923362301">"Handlingen er ikke understøttet i øjeblikket."</string>
</resources>
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="appTitle">"Fallback"</string>
- <string name="title">"Nicht unterstützte Aktion"</string>
- <string name="error">"Diese Aktion wird zurzeit nicht unterstützt."</string>
+ <string name="appTitle" msgid="161410001913116606">"Fallback"</string>
+ <string name="title" msgid="8156274565006125136">"Nicht unterstützte Aktion"</string>
+ <string name="error" msgid="6539615832923362301">"Diese Aktion wird zurzeit nicht unterstützt."</string>
</resources>
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="appTitle">"Εναλλακτική"</string>
- <string name="title">"Ενέργεια που δεν υποστηρίζεται"</string>
- <string name="error">"Αυτή η ενέργεια δεν υποστηρίζεται αυτήν τη στιγμή."</string>
+ <string name="appTitle" msgid="161410001913116606">"Εναλλακτική"</string>
+ <string name="title" msgid="8156274565006125136">"Ενέργεια που δεν υποστηρίζεται"</string>
+ <string name="error" msgid="6539615832923362301">"Αυτή η ενέργεια δεν υποστηρίζεται αυτήν τη στιγμή."</string>
</resources>
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="appTitle">"Fallback"</string>
- <string name="title">"Acción no admitida"</string>
- <string name="error">"Esa acción no se admite actualmente."</string>
+ <string name="appTitle" msgid="161410001913116606">"Fallback"</string>
+ <string name="title" msgid="8156274565006125136">"Acción no admitida"</string>
+ <string name="error" msgid="6539615832923362301">"Esa acción no se admite actualmente."</string>
</resources>
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="appTitle">"Fallback"</string>
- <string name="title">"Acción no admitida"</string>
- <string name="error">"Esa acción no se admite actualmente."</string>
+ <string name="appTitle" msgid="161410001913116606">"Fallback"</string>
+ <string name="title" msgid="8156274565006125136">"Acción no admitida"</string>
+ <string name="error" msgid="6539615832923362301">"Esa acción no se admite actualmente."</string>
</resources>
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="appTitle">"Application de secours"</string>
- <string name="title">"Action non prise en charge"</string>
- <string name="error">"Cette action n\'est actuellement pas prise en charge."</string>
+ <string name="appTitle" msgid="161410001913116606">"Application de secours"</string>
+ <string name="title" msgid="8156274565006125136">"Action non prise en charge"</string>
+ <string name="error" msgid="6539615832923362301">"Cette action n\'est actuellement pas prise en charge."</string>
</resources>
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="appTitle">"Fallback"</string>
- <string name="title">"Azione non supportata"</string>
- <string name="error">"L\'azione non è al momento supportata."</string>
+ <string name="appTitle" msgid="161410001913116606">"Fallback"</string>
+ <string name="title" msgid="8156274565006125136">"Azione non supportata"</string>
+ <string name="error" msgid="6539615832923362301">"L\'azione non è al momento supportata."</string>
</resources>
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="appTitle">"Fallback"</string>
- <string name="title">"サポートされていない操作"</string>
- <string name="error">"現在サポートされていない操作です。"</string>
+ <string name="appTitle" msgid="161410001913116606">"Fallback"</string>
+ <string name="title" msgid="8156274565006125136">"サポートされていない操作"</string>
+ <string name="error" msgid="6539615832923362301">"現在サポートされていない操作です。"</string>
</resources>
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="appTitle">"폴백"</string>
- <string name="title">"지원되지 않는 작업"</string>
- <string name="error">"이 작업은 현재 지원되지 않습니다."</string>
+ <string name="appTitle" msgid="161410001913116606">"폴백"</string>
+ <string name="title" msgid="8156274565006125136">"지원되지 않는 작업"</string>
+ <string name="error" msgid="6539615832923362301">"이 작업은 현재 지원되지 않습니다."</string>
</resources>
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="appTitle">"Fallback"</string>
- <string name="title">"Ustøttet handling"</string>
- <string name="error">"Denne handlingen er ikke støttet nå."</string>
+ <string name="appTitle" msgid="161410001913116606">"Fallback"</string>
+ <string name="title" msgid="8156274565006125136">"Ustøttet handling"</string>
+ <string name="error" msgid="6539615832923362301">"Denne handlingen er ikke støttet nå."</string>
</resources>
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="appTitle">"Reserve"</string>
- <string name="title">"Niet-ondersteunde actie"</string>
- <string name="error">"Die actie wordt momenteel niet ondersteund."</string>
+ <string name="appTitle" msgid="161410001913116606">"Reserve"</string>
+ <string name="title" msgid="8156274565006125136">"Niet-ondersteunde actie"</string>
+ <string name="error" msgid="6539615832923362301">"Die actie wordt momenteel niet ondersteund."</string>
</resources>
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="appTitle">"Wycofanie"</string>
- <string name="title">"Nieobsługiwana czynność"</string>
- <string name="error">"Ta czynność nie jest aktualnie obsługiwana."</string>
+ <string name="appTitle" msgid="161410001913116606">"Wycofanie"</string>
+ <string name="title" msgid="8156274565006125136">"Nieobsługiwana czynność"</string>
+ <string name="error" msgid="6539615832923362301">"Ta czynność nie jest aktualnie obsługiwana."</string>
</resources>
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="appTitle">"Fallback"</string>
- <string name="title">"Acção não suportada"</string>
- <string name="error">"Esta·acção·ainda·não·é·suportada."</string>
+ <string name="appTitle" msgid="161410001913116606">"Fallback"</string>
+ <string name="title" msgid="8156274565006125136">"Acção não suportada"</string>
+ <string name="error" msgid="6539615832923362301">"Esta acção ainda não é suportada."</string>
</resources>
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="appTitle">"Fallback"</string>
- <string name="title">"Ação não suportada"</string>
- <string name="error">"Essa ação não é suportada no momento."</string>
+ <string name="appTitle" msgid="161410001913116606">"Fallback"</string>
+ <string name="title" msgid="8156274565006125136">"Ação não suportada"</string>
+ <string name="error" msgid="6539615832923362301">"Essa ação não é suportada no momento."</string>
</resources>
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="appTitle">"Переход в обходной режим"</string>
- <string name="title">"Неподдерживаемое действие"</string>
- <string name="error">"В настоящее время это действие не поддерживается."</string>
+ <string name="appTitle" msgid="161410001913116606">"Переход в обходной режим"</string>
+ <string name="title" msgid="8156274565006125136">"Неподдерживаемое действие"</string>
+ <string name="error" msgid="6539615832923362301">"В настоящее время это действие не поддерживается."</string>
</resources>
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="appTitle">"Reserv"</string>
- <string name="title">"Åtgärden stöds inte"</string>
- <string name="error">"Den här åtgärden stöds inte för närvarande."</string>
+ <string name="appTitle" msgid="161410001913116606">"Reserv"</string>
+ <string name="title" msgid="8156274565006125136">"Åtgärden stöds inte"</string>
+ <string name="error" msgid="6539615832923362301">"Den här åtgärden stöds inte för närvarande."</string>
</resources>
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="appTitle">"Fallback"</string>
- <string name="title">"Desteklenmeyen işlem"</string>
- <string name="error">"Bu işlem şu an desteklenmiyor."</string>
+ <string name="appTitle" msgid="161410001913116606">"Fallback"</string>
+ <string name="title" msgid="8156274565006125136">"Desteklenmeyen işlem"</string>
+ <string name="error" msgid="6539615832923362301">"Bu işlem şu an desteklenmiyor."</string>
</resources>
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="appTitle">"后备"</string>
- <string name="title">"不支持此操作"</string>
- <string name="error">"目前不支持该操作。"</string>
+ <string name="appTitle" msgid="161410001913116606">"后备"</string>
+ <string name="title" msgid="8156274565006125136">"不支持此操作"</string>
+ <string name="error" msgid="6539615832923362301">"目前不支持该操作。"</string>
</resources>
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="appTitle">"備用"</string>
- <string name="title">"不支援的操作"</string>
- <string name="error">"目前不支援此操作。"</string>
+ <string name="appTitle" msgid="161410001913116606">"備用"</string>
+ <string name="title" msgid="8156274565006125136">"不支援的操作"</string>
+ <string name="error" msgid="6539615832923362301">"目前不支援此操作。"</string>
</resources>
bin/dmtracedump tools/dmtracedump
bin/hprof-conv tools/hprof-conv
bin/mksdcard tools/mksdcard
+bin/zipalign tools/zipalign
# the uper-jar file that apps link against
out/target/common/obj/PACKAGING/android_jar_intermediates/android.jar platforms/${PLATFORM_NAME}/android.jar
framework/org.eclipse.core.commands_3.4.0.I20080509-2000.jar tools/lib/org.eclipse.core.commands_3.4.0.I20080509-2000.jar
framework/org.eclipse.equinox.common_3.4.0.v20080421-2006.jar tools/lib/org.eclipse.equinox.common_3.4.0.v20080421-2006.jar
framework/org.eclipse.jface_3.4.2.M20090107-0800.jar tools/lib/org.eclipse.jface_3.4.2.M20090107-0800.jar
-
+framework/osgi.jar tools/lib/osgi.jar
sdk/sdk-build.prop platforms/${PLATFORM_NAME}/build.prop
development/tools/scripts/plugin.prop tools/lib/plugin.prop
# sdk scripts
development/tools/scripts/AndroidManifest.template platforms/${PLATFORM_NAME}/templates/AndroidManifest.template
-development/tools/scripts/AndroidManifest.alias.template platforms/${PLATFORM_NAME}/templates/AndroidManifest.alias.template
development/tools/scripts/AndroidManifest.tests.template platforms/${PLATFORM_NAME}/templates/AndroidManifest.tests.template
development/tools/scripts/iml.template platforms/${PLATFORM_NAME}/templates/iml.template
development/tools/scripts/ipr.template platforms/${PLATFORM_NAME}/templates/ipr.template
development/tools/scripts/java_tests_file.template platforms/${PLATFORM_NAME}/templates/java_tests_file.template
development/tools/scripts/layout.template platforms/${PLATFORM_NAME}/templates/layout.template
development/tools/scripts/strings.template platforms/${PLATFORM_NAME}/templates/strings.template
-development/tools/scripts/alias.template platforms/${PLATFORM_NAME}/templates/alias.template
development/tools/scripts/android_rules.xml platforms/${PLATFORM_NAME}/templates/android_rules.xml
-development/tools/scripts/alias_rules.xml platforms/${PLATFORM_NAME}/templates/alias_rules.xml
development/tools/scripts/build.template tools/lib/build.template
-development/tools/scripts/build.alias.template tools/lib/build.alias.template
# emacs support
development/tools/scripts/android.el tools/lib/android.el
development/samples/Snake platforms/${PLATFORM_NAME}/samples/Snake
development/samples/SoftKeyboard platforms/${PLATFORM_NAME}/samples/SoftKeyboard
development/samples/JetBoy platforms/${PLATFORM_NAME}/samples/JetBoy
+development/samples/SearchableDictionary platforms/${PLATFORM_NAME}/samples/SearchableDictionary
# dx
bin/dx platforms/${PLATFORM_NAME}/tools/dx
external/qemu/android/avd/hardware-properties.ini tools/lib/hardware-properties.ini
# emulator skins
-development/emulator/skins/HVGA platforms/${PLATFORM_NAME}/skins/HVGA
-development/emulator/skins/QVGA platforms/${PLATFORM_NAME}/skins/QVGA
-development/emulator/skins/WVGA platforms/${PLATFORM_NAME}/skins/WVGA
+development/emulator/skins/HVGA platforms/${PLATFORM_NAME}/skins/HVGA
+development/emulator/skins/QVGA platforms/${PLATFORM_NAME}/skins/QVGA
+development/emulator/skins/WVGA800 platforms/${PLATFORM_NAME}/skins/WVGA800
+development/emulator/skins/WVGA854 platforms/${PLATFORM_NAME}/skins/WVGA854
# NOTICE files are copied by build/core/Makefile
development/tools/scripts/sdk_files_NOTICE.txt platforms/${PLATFORM_NAME}/templates/NOTICE.txt
TEMP_DIR="$3"
[ -z "$TEMP_DIR" ] && TEMP_DIR=${TMP:-/tmp}
-
function die() {
- echo "Error:" $*
- echo "Aborting"
- exit 1
+ echo "Error:" $*
+ echo "Aborting"
+ exit 1
}
function usage() {
- echo "Usage: ${PROG_NAME} linux_or_mac_sdk.zip output_dir [temp_dir]"
- echo "If temp_dir is not given, \$TMP is used. If that's missing, /tmp is used."
- status
- exit 2
+ local NAME
+ NAME=`basename ${PROG_NAME}`
+ echo "Usage: ${NAME} linux_or_mac_sdk.zip output_dir [temp_dir]"
+ echo "If temp_dir is not given, \$TMP is used. If that's missing, /tmp is used."
+ status
+ exit 2
}
function status() {
- echo "Current values:"
- echo "- Input SDK: ${SDK_ZIP:-missing}"
- echo "- Output dir: ${DIST_DIR:-missing}"
- echo "- Temp dir: ${TEMP_DIR:-missing}"
+ echo "Current values:"
+ echo "- Input SDK: ${SDK_ZIP:-missing}"
+ echo "- Output dir: ${DIST_DIR:-missing}"
+ echo "- Temp dir: ${TEMP_DIR:-missing}"
}
function check() {
make -j 4 emulator || die "Build failed"
# Disable parallel build: it generates "permission denied" issues when
# multiple "ar.exe" are running in parallel.
- make prebuilt adb fastboot aidl aapt dexdump dmtracedump hprof-conv mksdcard sqlite3 \
+ make aapt adb aidl \
+ prebuilt \
+ dexdump dmtracedump \
+ fastboot \
+ hprof-conv \
+ mksdcard \
+ sqlite3 \
+ zipalign \
|| die "Build failed"
}
"Instead found " $THE_PLATFORM
[[ -d "$PLATFORM_TOOLS" ]] || die "Missing folder $PLATFORM_TOOLS."
+ # Package USB Driver
+ if type package_usb_driver 2>&1 | grep -q function ; then
+ package_usb_driver $TEMP_SDK_DIR
+ fi
# Remove obsolete stuff from tools & platform
TOOLS="$TEMP_SDK_DIR/tools"
LIB="$TEMP_SDK_DIR/tools/lib"
rm -v "$TOOLS"/{adb,android,apkbuilder,ddms,dmtracedump,draw9patch,emulator}
- rm -v "$TOOLS"/{hierarchyviewer,hprof-conv,mksdcard,sqlite3,traceview}
+ rm -v "$TOOLS"/{hierarchyviewer,hprof-conv,mksdcard,sqlite3,traceview,zipalign}
rm -v "$LIB"/*/swt.jar
rm -v "$PLATFORM_TOOLS"/{aapt,aidl,dx,dexdump}
# Copy or move platform specific tools to the default platform.
cp -v dalvik/dx/etc/dx.bat "$PLATFORM_TOOLS"/
- # Note: mgwz.dll must be in same folder than aapt.exe
- mv -v "$TOOLS"/{aapt.exe,aidl.exe,dexdump.exe,mgwz.dll} "$PLATFORM_TOOLS"/
+ mv -v "$TOOLS"/{aapt.exe,aidl.exe,dexdump.exe} "$PLATFORM_TOOLS"/
+ # Note: mgwz.dll must be both in SDK/tools for zipalign and in SDK/platform/XYZ/tools/ for aapt
+ cp -v "$TOOLS"/mgwz.dll "$PLATFORM_TOOLS"/
# Fix EOL chars to make window users happy - fix all files at the top level only
# as well as all batch files including those in platforms/<name>/tools/
# Now move the final zip from the temp dest to the final dist dir
mv -v "$TEMP_DIR/$DEST_NAME_ZIP" "$DIST_DIR/$DEST_NAME_ZIP"
+ # We want fastboot and adb (and its DLLs) next to the new SDK
+ for i in fastboot.exe adb.exe AdbWinApi.dll AdbWinUsbApi.dll; do
+ cp -vf out/host/windows-x86/bin/$i "$DIST_DIR"/$i
+ done
+
echo "Done"
echo
echo "Resulting SDK is in $DIST_DIR/$DEST_NAME_ZIP"
-
- # We want fastboot and adb next to the new SDK
- for i in fastboot.exe adb.exe AdbWinApi.dll; do
- mv -vf out/host/windows-x86/bin/$i "$DIST_DIR"/$i
- done
}
check
LOCAL_MODULE := monkey
include $(BUILD_JAVA_LIBRARY)
+################################################################
include $(CLEAR_VARS)
ALL_PREBUILT += $(TARGET_OUT)/bin/monkey
$(TARGET_OUT)/bin/monkey : $(LOCAL_PATH)/monkey | $(ACP)
--- /dev/null
+SIMPLE PROTOCOL FOR AUTOMATED NETWORK CONTROL
+
+The Simple Protocol for Automated Network Control was designed to be a
+low-level way to programmability inject KeyEvents and MotionEvents
+into the input system. The idea is that a process will run on a host
+computer that will support higher-level operations (like conditionals,
+etc.) and will talk (via TCP over ADB) to the device in Simple
+Protocol for Automated Network Control. For security reasons, the
+Monkey only binds to localhost, so you will need to use adb to setup
+port forwarding to actually talk to the device.
+
+INITIAL SETUP
+
+Setup port forwarding from a local port on your machine to a port on
+the device:
+
+$ adb forward tcp:1080 tcp:1080
+
+Start the monkey server
+
+$ adb shell monkey --port 1080
+
+Now you're ready to run commands
+
+COMMAND LIST
+
+Individual commands are separated by newlines. The Monkey will
+respond to every command with a line starting with OK for commands
+that executed without a problem, or a line starting with ERROR for
+commands that had problems being run. For commands that return a
+value, that value is returned on the same line as the OK or ERROR
+response. The value is everything after (but not include) the colon
+on that line. For ERROR values, this could be a message indicating
+what happened. A possible example:
+
+key down menu
+OK
+touch monkey
+ERROR: monkey not a number
+getvar sdk
+OK: donut
+getvar foo
+ERROR: no such var
+
+The complete list of commands follows:
+
+key [down|up] keycode
+
+This command injects KeyEvent's into the input system. The keycode
+parameter refers to the KEYCODE list in the KeyEvent class
+(http://developer.android.com/reference/android/view/KeyEvent.html).
+The format of that parameter is quite flexible. Using the menu key as
+an example, it can be 82 (the integer value of the keycode),
+KEYCODE_MENU (the name of the keycode), or just menu (and the Monkey
+will add the KEYCODE part). Do note that this last part doesn't work
+for things like KEYCODE_1 for obvious reasons.
+
+Note that sending a full button press requires sending both the down
+and the up event for that key
+
+touch [down|up|move] x y
+
+This command injects a MotionEvent into the input system that
+simulates a user touching the touchscreen (or a pointer event). x and
+y specify coordinates on the display (0 0 being the upper left) for
+the touch event to happen. Just like key events, touch events at a
+single location require both a down and an up. To simulate dragging,
+send a "touch down", then a series of "touch move" events (to simulate
+the drag), followed by a "touch up" at the final location.
+
+trackball dx dy
+
+This command injects a MotionEvent into the input system that
+simulates a user using the trackball. dx and dy indicates the amount
+of change in the trackball location (as opposed to exact coordinates
+that the touch events use)
+
+flip [open|close]
+
+This simulates the opening or closing the keyboard (like on dream).
+
+wake
+
+This command will wake the device up from sleep and allow user input.
+
+tap x y
+The tap command is a shortcut for the touch command. It will
+automatically send both the up and the down event.
+
+press keycode
+
+The press command is a shortcut for the key command. The keycode
+paramter works just like the key command and will automatically send
+both the up and the down event.
+
+type string
+
+This command will simulate a user typing the given string on the
+keyboard by generating the proper KeyEvents.
+
+listvar
+
+This command lists all the vars that the monkey knows about. They are
+returned as a whitespace separated list.
+
+getvar varname
+
+This command returns the value of the given var. listvar can be used
+to find out what vars are supported.
+
+quit
+
+Fully quit the monkey and accept no new sessions.
+
+done
+
+Close the current session and allow a new session to connect
+
+OTHER NOTES
+
+There are some convenience features added to allow running without
+needing a host process.
+
+Lines starting with a # character are considered comments. The Monkey
+eats them and returns no indication that it did anything (no ERROR and
+no OK).
+
+You can put the Monkey to sleep by using the "sleep" command with a
+single argument, how many ms to sleep.
--- /dev/null
+# Touch the android
+touch down 160 200
+touch up 160 200
+sleep 1000
+
+# Hit Next
+touch down 300 450
+touch up 300 450
+sleep 1000
+
+# Hit Next
+touch down 300 450
+touch up 300 450
+sleep 1000
+
+# Hit Next
+touch down 300 450
+touch up 300 450
+sleep 1000
+
+# Go down and select the account username
+key down dpad_down
+key up dpad_down
+key down dpad_down
+key up dpad_down
+key down dpad_center
+key up dpad_center
+# account name: bill
+key down b
+key up b
+key down i
+key up i
+key down l
+key up l
+key down l
+key up l
+
+# Go down to the password field
+key down dpad_down
+key up dpad_down
+
+# password: bill
+key down b
+key up b
+key down i
+key up i
+key down l
+key up l
+key down l
+key up l
+
+# Select next
+touch down 300 450
+touch up 300 450
+
+# quit
+quit
* Application that injects random key events and other actions into the system.
*/
public class Monkey {
-
+
/**
* Monkey Debugging/Dev Support
- *
+ *
* All values should be zero when checking in.
*/
private final static int DEBUG_ALLOW_ANY_STARTS = 0;
/** Ignore any not responding timeouts while running? */
private boolean mIgnoreTimeouts;
-
+
/** Ignore security exceptions when launching activities */
/** (The activity launch still fails, but we keep pluggin' away) */
private boolean mIgnoreSecurityExceptions;
-
+
/** Monitor /data/tombstones and stop the monkey if new files appear. */
private boolean mMonitorNativeCrashes;
-
+
/** Send no events. Use with long throttle-time to watch user operations */
private boolean mSendNoEvents;
/** This is set when we would like to abort the running of the monkey. */
private boolean mAbort;
-
+
/** This is set by the ActivityController thread to request collection of ANR trace files */
private boolean mRequestAnrTraces = false;
/** Kill the process after a timeout or crash. */
private boolean mKillProcessAfterError;
-
+
/** Generate hprof reports before/after monkey runs */
private boolean mGenerateHprof;
ArrayList<String> mMainCategories = new ArrayList<String>();
/** Applications we can switch to. */
private ArrayList<ComponentName> mMainApps = new ArrayList<ComponentName>();
-
+
/** The delay between event inputs **/
long mThrottle = 0;
-
+
/** The number of iterations **/
int mCount = 1000;
-
+
/** The random number seed **/
long mSeed = 0;
-
+
/** Dropped-event statistics **/
long mDroppedKeyEvents = 0;
long mDroppedPointerEvents = 0;
/** a filename to the script (if any) **/
private String mScriptFileName = null;
-
+
+ /** a TCP port to listen on for remote commands. */
+ private int mServerPort = -1;
+
private static final File TOMBSTONES_PATH = new File("/data/tombstones");
private HashSet<String> mTombstones = null;
-
- float[] mFactors = new float[MonkeySourceRandom.FACTORZ_COUNT];
+
+ float[] mFactors = new float[MonkeySourceRandom.FACTORZ_COUNT];
MonkeyEventSource mEventSource;
private MonkeyNetworkMonitor mNetworkMonitor = new MonkeyNetworkMonitor();
-
+
+ // information on the current activity.
+ public static Intent currentIntent;
+ public static String currentPackage;
+
/**
* Monitor operations happening in the system.
*/
System.out.println(" // " + (allow ? "Allowing" : "Rejecting")
+ " start of " + intent + " in package " + pkg);
}
+ currentPackage = pkg;
+ currentIntent = intent;
return allow;
}
-
+
public boolean activityResuming(String pkg) {
System.out.println(" // activityResuming(" + pkg + ")");
boolean allow = checkEnteringPackage(pkg) || (DEBUG_ALLOW_ANY_RESTARTS != 0);
if (!allow) {
if (mVerbose > 0) {
System.out.println(" // " + (allow ? "Allowing" : "Rejecting")
- + " resume of package " + pkg);
+ + " resume of package " + pkg);
}
}
+ currentPackage = pkg;
return allow;
}
-
+
private boolean checkEnteringPackage(String pkg) {
if (pkg == null) {
return true;
return mValidPackages.contains(pkg);
}
}
-
+
public boolean appCrashed(String processName, int pid, String shortMsg,
String longMsg, byte[] crashData) {
System.err.println("// CRASH: " + processName + " (pid " + pid
return 1;
}
}
-
+
/**
* Run the procrank tool to insert system status information into the debug report.
*/
private void reportProcRank() {
commandLineReport("procrank", "procrank");
}
-
+
/**
* Run "cat /data/anr/traces.txt". Wait about 5 seconds first, to let the asynchronous
* report writing complete.
private void reportAnrTraces() {
try {
Thread.sleep(5 * 1000);
- } catch (InterruptedException e) {
+ } catch (InterruptedException e) {
}
commandLineReport("anr traces", "cat /data/anr/traces.txt");
}
-
+
/**
* Run "dumpsys meminfo"
- *
+ *
* NOTE: You cannot perform a dumpsys call from the ActivityController callback, as it will
* deadlock. This should only be called from the main loop of the monkey.
*/
private void reportDumpsysMemInfo() {
commandLineReport("meminfo", "dumpsys meminfo");
}
-
+
/**
* Print report from a single command line.
* @param reportName Simple tag that will print before the report and in various annotations.
try {
// Process must be fully qualified here because android.os.Process is used elsewhere
java.lang.Process p = Runtime.getRuntime().exec(command);
-
+
// pipe everything from process stdout -> System.err
InputStream inStream = p.getInputStream();
InputStreamReader inReader = new InputStreamReader(inStream);
while ((s = inBuffer.readLine()) != null) {
System.err.println(s);
}
-
+
int status = p.waitFor();
System.err.println("// " + reportName + " status was " + status);
} catch (Exception e) {
Debug.waitForDebugger();
}
}
-
+
// Default values for some command-line options
mVerbose = 0;
mCount = 1000;
mSeed = 0;
mThrottle = 0;
-
+
// prepare for command-line processing
mArgs = args;
mNextArg = 0;
-
+
//set a positive value, indicating none of the factors is provided yet
for (int i = 0; i < MonkeySourceRandom.FACTORZ_COUNT; i++) {
mFactors[i] = 1.0f;
}
-
+
if (!processOptions()) {
return -1;
}
-
+
// now set up additional data in preparation for launch
if (mMainCategories.size() == 0) {
mMainCategories.add(Intent.CATEGORY_LAUNCHER);
}
}
}
-
+
if (!checkInternalConfiguration()) {
return -2;
}
-
+
if (!getSystemInterfaces()) {
return -3;
}
if (!getMainApps()) {
return -4;
}
-
+
if (mScriptFileName != null) {
// script mode, ignore other options
mEventSource = new MonkeySourceScript(mScriptFileName, mThrottle);
mEventSource.setVerbose(mVerbose);
+ } else if (mServerPort != -1) {
+ try {
+ mEventSource = new MonkeySourceNetwork(mServerPort);
+ } catch (IOException e) {
+ System.out.println("Error binding to network socket.");
+ return -5;
+ }
+ mCount = Integer.MAX_VALUE;
} else {
// random source by default
if (mVerbose >= 2) { // check seeding performance
((MonkeySourceRandom) mEventSource).setFactors(i, mFactors[i]);
}
}
-
+
//in random mode, we start with a random activity
((MonkeySourceRandom) mEventSource).generateActivity();
}
if (!mEventSource.validate()) {
return -5;
}
-
+
if (mScriptFileName != null) {
// in random mode, count is the number of single events
// while in script mode, count is the number of repetition
mCount = mCount * ((MonkeySourceScript) mEventSource)
.getOneRoundEventCount();
}
-
+
// If we're profiling, do it immediately before/after the main monkey loop
if (mGenerateHprof) {
signalPersistentProcesses();
}
-
+
mNetworkMonitor.start();
int crashedAtCycle = runMonkeyCycles();
mNetworkMonitor.stop();
System.out.println("// Generated profiling reports in /data/misc");
}
}
-
+
try {
mAm.setActivityController(null);
mNetworkMonitor.unregister(mAm);
crashedAtCycle = mCount - 1;
}
}
-
+
// report dropped event stats
if (mVerbose > 0) {
System.out.print(":Dropped: keys=");
System.out.print(" flips=");
System.out.println(mDroppedFlipEvents);
}
-
+
// report network stats
mNetworkMonitor.dump();
return 0;
}
}
-
+
/**
* Process the command-line options
- *
+ *
* @return Returns true if options were parsed with no apparent errors.
*/
private boolean processOptions() {
} else if (opt.equals("--hprof")) {
mGenerateHprof = true;
} else if (opt.equals("--pct-touch")) {
- mFactors[MonkeySourceRandom.FACTOR_TOUCH] =
+ mFactors[MonkeySourceRandom.FACTOR_TOUCH] =
-nextOptionLong("touch events percentage");
} else if (opt.equals("--pct-motion")) {
- mFactors[MonkeySourceRandom.FACTOR_MOTION] =
+ mFactors[MonkeySourceRandom.FACTOR_MOTION] =
-nextOptionLong("motion events percentage");
} else if (opt.equals("--pct-trackball")) {
- mFactors[MonkeySourceRandom.FACTOR_TRACKBALL] =
+ mFactors[MonkeySourceRandom.FACTOR_TRACKBALL] =
-nextOptionLong("trackball events percentage");
} else if (opt.equals("--pct-nav")) {
- mFactors[MonkeySourceRandom.FACTOR_NAV] =
+ mFactors[MonkeySourceRandom.FACTOR_NAV] =
-nextOptionLong("nav events percentage");
} else if (opt.equals("--pct-majornav")) {
- mFactors[MonkeySourceRandom.FACTOR_MAJORNAV] =
+ mFactors[MonkeySourceRandom.FACTOR_MAJORNAV] =
-nextOptionLong("major nav events percentage");
} else if (opt.equals("--pct-appswitch")) {
- mFactors[MonkeySourceRandom.FACTOR_APPSWITCH] =
+ mFactors[MonkeySourceRandom.FACTOR_APPSWITCH] =
-nextOptionLong("app switch events percentage");
} else if (opt.equals("--pct-flip")) {
mFactors[MonkeySourceRandom.FACTOR_FLIP] =
-nextOptionLong("keyboard flip percentage");
} else if (opt.equals("--pct-anyevent")) {
- mFactors[MonkeySourceRandom.FACTOR_ANYTHING] =
+ mFactors[MonkeySourceRandom.FACTOR_ANYTHING] =
-nextOptionLong("any events percentage");
} else if (opt.equals("--throttle")) {
mThrottle = nextOptionLong("delay (in milliseconds) to wait between events");
// do nothing - it's caught at the very start of run()
} else if (opt.equals("--dbg-no-events")) {
mSendNoEvents = true;
- } else if (opt.equals("-f")) {
+ } else if (opt.equals("--port")) {
+ mServerPort = (int) nextOptionLong("Server port to listen on for commands");
+ } else if (opt.equals("-f")) {
mScriptFileName = nextOptionData();
} else if (opt.equals("-h")) {
showUsage();
return false;
}
- String countStr = nextArg();
- if (countStr == null) {
- System.err.println("** Error: Count not specified");
- showUsage();
- return false;
- }
+ // If a server port hasn't been specified, we need to specify
+ // a count
+ if (mServerPort == -1) {
+ String countStr = nextArg();
+ if (countStr == null) {
+ System.err.println("** Error: Count not specified");
+ showUsage();
+ return false;
+ }
- try {
- mCount = Integer.parseInt(countStr);
- } catch (NumberFormatException e) {
- System.err.println("** Error: Count is not a number");
- showUsage();
- return false;
+ try {
+ mCount = Integer.parseInt(countStr);
+ } catch (NumberFormatException e) {
+ System.err.println("** Error: Count is not a number");
+ showUsage();
+ return false;
+ }
}
return true;
/**
* Check for any internal configuration (primarily build-time) errors.
- *
+ *
* @return Returns true if ready to rock.
*/
private boolean checkInternalConfiguration() {
/**
* Attach to the required system interfaces.
- *
+ *
* @return Returns true if all system interfaces were available.
*/
private boolean getSystemInterfaces() {
/**
* Using the restrictions provided (categories & packages), generate a list of activities
* that we can actually switch to.
- *
+ *
* @return Returns true if it could successfully build a list of target activities
*/
private boolean getMainApps() {
final int NA = mainApps.size();
for (int a = 0; a < NA; a++) {
ResolveInfo r = mainApps.get(a);
- if (mValidPackages.size() == 0 ||
+ if (mValidPackages.size() == 0 ||
mValidPackages.contains(r.activityInfo.applicationInfo.packageName)) {
if (mVerbose >= 2) { // very verbose
System.out.println("// + Using main activity "
System.out.println("** No activities found to run, monkey aborted.");
return false;
}
-
+
return true;
}
/**
* Run mCount cycles and see if we hit any crashers.
- *
+ *
* TODO: Meta state on keys
- *
+ *
* @return Returns the last cycle which executed. If the value == mCount, no errors detected.
*/
private int runMonkeyCycles() {
} else if (injectCode == MonkeyEvent.INJECT_ERROR_SECURITY_EXCEPTION) {
systemCrashed = !mIgnoreSecurityExceptions;
}
+ } else {
+ // Event Source has signaled that we have no more events to process
+ break;
}
}
-
// If we got this far, we succeeded!
return mCount;
}
/**
* Watch for appearance of new tombstone files, which indicate native crashes.
- *
+ *
* @return Returns true if new files have appeared in the list
*/
private boolean checkNativeCrashes() {
String[] tombstones = TOMBSTONES_PATH.list();
-
+
// shortcut path for usually empty directory, so we don't waste even more objects
if ((tombstones == null) || (tombstones.length == 0)) {
mTombstones = null;
return false;
}
-
+
// use set logic to look for new files
HashSet<String> newStones = new HashSet<String>();
for (String x : tombstones) {
/**
* Return the next command line option. This has a number of special cases which
* closely, but not exactly, follow the POSIX command line options patterns:
- *
+ *
* -- means to stop processing additional options
* -z means option z
* -z ARGS means option z with (non-optional) arguments ARGS
* -zARGS means option z with (optional) arguments ARGS
* --zz means option zz
* --zz ARGS means option zz with (non-optional) arguments ARGS
- *
+ *
* Note that you cannot combine single letter options; -abc != -a -b -c
*
* @return Returns the option string, or null if there are no more options.
mNextArg++;
return data;
}
-
+
/**
* Returns a long converted from the next data argument, with error handling if not available.
- *
+ *
* @param opt The name of the option.
* @return Returns a long converted from the argument.
*/
System.err.println(" [--pct-appswitch PERCENT] [--pct-flip PERCENT]");
System.err.println(" [--pct-anyevent PERCENT]");
System.err.println(" [--wait-dbg] [--dbg-no-events] [-f scriptfile]");
+ System.err.println(" [--port port]");
System.err.println(" [-s SEED] [-v [-v] ...] [--throttle MILLISEC]");
System.err.println(" COUNT");
}
/**
* abstract class for monkey event
*/
-public abstract class MonkeyEvent {
+public abstract class MonkeyEvent {
protected int eventType;
public static final int EVENT_TYPE_KEY = 0;
public static final int EVENT_TYPE_POINTER = 1;
public static final int EVENT_TYPE_ACTIVITY = 3;
public static final int EVENT_TYPE_FLIP = 4; // Keyboard flip
public static final int EVENT_TYPE_THROTTLE = 5;
-
+ public static final int EVENT_TYPE_NOOP = 6;
+
public static final int INJECT_SUCCESS = 1;
public static final int INJECT_FAIL = 0;
// error code for remote exception during injection
- public static final int INJECT_ERROR_REMOTE_EXCEPTION = -1;
+ public static final int INJECT_ERROR_REMOTE_EXCEPTION = -1;
// error code for security exception during injection
public static final int INJECT_ERROR_SECURITY_EXCEPTION = -2;
-
+
public MonkeyEvent(int type) {
eventType = type;
}
-
- /**
+
+ /**
* @return event type
- */
+ */
public int getEventType() {
return eventType;
}
-
+
/**
* @return true if it is safe to throttle after this event, and false otherwise.
*/
public boolean isThrottlable() {
return true;
}
-
-
+
+
/**
* a method for injecting event
* @param iwm wires to current window manager
* @param iam wires to current activity manager
- * @param verbose a log switch
+ * @param verbose a log switch
* @return INJECT_SUCCESS if it goes through, and INJECT_FAIL if it fails
* in the case of exceptions, return its corresponding error code
*/
- public abstract int injectEvent(IWindowManager iwm, IActivityManager iam, int verbose);
+ public abstract int injectEvent(IWindowManager iwm, IActivityManager iam, int verbose);
}
/**
* monkey key event
*/
-public class MonkeyKeyEvent extends MonkeyEvent {
+public class MonkeyKeyEvent extends MonkeyEvent {
private long mDownTime = -1;
private int mMetaState = -1;
private int mAction = -1;
private int mRepeatCount = -1;
private int mDeviceId = -1;
private long mEventTime = -1;
-
+
+ private KeyEvent keyEvent = null;
+
public MonkeyKeyEvent(int action, int keycode) {
super(EVENT_TYPE_KEY);
mAction = action;
mKeyCode = keycode;
}
-
+
+ public MonkeyKeyEvent(KeyEvent e) {
+ super(EVENT_TYPE_KEY);
+ keyEvent = e;
+ }
+
public MonkeyKeyEvent(long downTime, long eventTime, int action,
int code, int repeat, int metaState,
int device, int scancode) {
super(EVENT_TYPE_KEY);
-
+
mAction = action;
mKeyCode = code;
mMetaState = metaState;
mDownTime = downTime;
mEventTime = eventTime;
}
-
+
public int getKeyCode() {
return mKeyCode;
}
-
+
public int getAction() {
return mAction;
}
-
+
public long getDownTime() {
return mDownTime;
}
-
+
public long getEventTime() {
return mEventTime;
}
-
+
public void setDownTime(long downTime) {
mDownTime = downTime;
}
-
+
public void setEventTime(long eventTime) {
mEventTime = eventTime;
}
-
- /**
+
+ /**
* @return the key event
- */
+ */
private KeyEvent getEvent() {
- if (mDeviceId < 0) {
- return new KeyEvent(mAction, mKeyCode);
- }
-
- // for scripts
- return new KeyEvent(mDownTime, mEventTime, mAction,
- mKeyCode, mRepeatCount, mMetaState, mDeviceId, mScancode);
+ if (keyEvent == null) {
+ if (mDeviceId < 0) {
+ keyEvent = new KeyEvent(mAction, mKeyCode);
+ } else {
+ // for scripts
+ keyEvent = new KeyEvent(mDownTime, mEventTime, mAction,
+ mKeyCode, mRepeatCount, mMetaState, mDeviceId, mScancode);
+ }
+ }
+ return keyEvent;
}
@Override
public boolean isThrottlable() {
return (getAction() == KeyEvent.ACTION_UP);
}
-
+
@Override
public int injectEvent(IWindowManager iwm, IActivityManager iam, int verbose) {
if (verbose > 1) {
} catch (RemoteException ex) {
return MonkeyEvent.INJECT_ERROR_REMOTE_EXCEPTION;
}
-
+
return MonkeyEvent.INJECT_SUCCESS;
}
}
--- /dev/null
+/*
+ * 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.commands.monkey;
+
+import java.util.List;
+
+import android.app.IActivityManager;
+import android.view.IWindowManager;
+
+
+/**
+ * monkey noop event (don't do anything).
+ */
+public class MonkeyNoopEvent extends MonkeyEvent {
+
+ public MonkeyNoopEvent() {
+ super(MonkeyEvent.EVENT_TYPE_NOOP);
+ }
+
+ @Override
+ public int injectEvent(IWindowManager iwm, IActivityManager iam, int verbose) {
+ // No real work to do
+ if (verbose > 1) {
+ System.out.println("NOOP");
+ }
+ return MonkeyEvent.INJECT_SUCCESS;
+ }
+}
--- /dev/null
+/*
+ * Copyright 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.commands.monkey;
+
+import android.content.Context;
+import android.os.IPowerManager;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.SystemClock;
+import android.util.Log;
+import android.view.KeyCharacterMap;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.PrintWriter;
+import java.lang.Integer;
+import java.lang.NumberFormatException;
+import java.net.InetAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Queue;
+import java.util.StringTokenizer;
+
+/**
+ * An Event source for getting Monkey Network Script commands from
+ * over the network.
+ */
+public class MonkeySourceNetwork implements MonkeyEventSource {
+ private static final String TAG = "MonkeyStub";
+
+ /**
+ * ReturnValue from the MonkeyCommand that indicates whether the
+ * command was sucessful or not.
+ */
+ public static class MonkeyCommandReturn {
+ private final boolean success;
+ private final String message;
+
+ public MonkeyCommandReturn(boolean success) {
+ this.success = success;
+ this.message = null;
+ }
+
+ public MonkeyCommandReturn(boolean success,
+ String message) {
+ this.success = success;
+ this.message = message;
+ }
+
+ boolean hasMessage() {
+ return message != null;
+ }
+
+ String getMessage() {
+ return message;
+ }
+
+ boolean wasSuccessful() {
+ return success;
+ }
+ }
+
+ public final static MonkeyCommandReturn OK = new MonkeyCommandReturn(true);
+ public final static MonkeyCommandReturn ERROR = new MonkeyCommandReturn(false);
+ public final static MonkeyCommandReturn EARG = new MonkeyCommandReturn(false,
+ "Invalid Argument");
+
+ /**
+ * Interface that MonkeyCommands must implement.
+ */
+ public interface MonkeyCommand {
+ /**
+ * Translate the command line into a sequence of MonkeyEvents.
+ *
+ * @param command the command line.
+ * @param queue the command queue.
+ * @returs MonkeyCommandReturn indicating what happened.
+ */
+ MonkeyCommandReturn translateCommand(List<String> command, CommandQueue queue);
+ }
+
+ /**
+ * Command to simulate closing and opening the keyboard.
+ */
+ private static class FlipCommand implements MonkeyCommand {
+ // flip open
+ // flip closed
+ public MonkeyCommandReturn translateCommand(List<String> command,
+ CommandQueue queue) {
+ if (command.size() > 1) {
+ String direction = command.get(1);
+ if ("open".equals(direction)) {
+ queue.enqueueEvent(new MonkeyFlipEvent(true));
+ return OK;
+ } else if ("close".equals(direction)) {
+ queue.enqueueEvent(new MonkeyFlipEvent(false));
+ return OK;
+ }
+ }
+ return EARG;
+ }
+ }
+
+ /**
+ * Command to send touch events to the input system.
+ */
+ private static class TouchCommand implements MonkeyCommand {
+ // touch [down|up|move] [x] [y]
+ // touch down 120 120
+ // touch move 140 140
+ // touch up 140 140
+ public MonkeyCommandReturn translateCommand(List<String> command,
+ CommandQueue queue) {
+ if (command.size() == 4) {
+ String actionName = command.get(1);
+ int x = 0;
+ int y = 0;
+ try {
+ x = Integer.parseInt(command.get(2));
+ y = Integer.parseInt(command.get(3));
+ } catch (NumberFormatException e) {
+ // Ok, it wasn't a number
+ Log.e(TAG, "Got something that wasn't a number", e);
+ return EARG;
+ }
+
+ // figure out the action
+ int action = -1;
+ if ("down".equals(actionName)) {
+ action = MotionEvent.ACTION_DOWN;
+ } else if ("up".equals(actionName)) {
+ action = MotionEvent.ACTION_UP;
+ } else if ("move".equals(actionName)) {
+ action = MotionEvent.ACTION_MOVE;
+ }
+ if (action == -1) {
+ Log.e(TAG, "Got a bad action: " + actionName);
+ return EARG;
+ }
+
+ queue.enqueueEvent(new MonkeyMotionEvent(MonkeyEvent.EVENT_TYPE_POINTER,
+ -1, action, x, y, 0));
+ return OK;
+ }
+ return EARG;
+ }
+ }
+
+ /**
+ * Command to send Trackball events to the input system.
+ */
+ private static class TrackballCommand implements MonkeyCommand {
+ // trackball [dx] [dy]
+ // trackball 1 0 -- move right
+ // trackball -1 0 -- move left
+ public MonkeyCommandReturn translateCommand(List<String> command,
+ CommandQueue queue) {
+ if (command.size() == 3) {
+ int dx = 0;
+ int dy = 0;
+ try {
+ dx = Integer.parseInt(command.get(1));
+ dy = Integer.parseInt(command.get(2));
+ } catch (NumberFormatException e) {
+ // Ok, it wasn't a number
+ Log.e(TAG, "Got something that wasn't a number", e);
+ return EARG;
+ }
+ queue.enqueueEvent(new MonkeyMotionEvent(MonkeyEvent.EVENT_TYPE_TRACKBALL, -1,
+ MotionEvent.ACTION_MOVE, dx, dy, 0));
+ return OK;
+
+ }
+ return EARG;
+ }
+ }
+
+ /**
+ * Command to send Key events to the input system.
+ */
+ private static class KeyCommand implements MonkeyCommand {
+ // key [down|up] [keycode]
+ // key down 82
+ // key up 82
+ public MonkeyCommandReturn translateCommand(List<String> command,
+ CommandQueue queue) {
+ if (command.size() == 3) {
+ int keyCode = getKeyCode(command.get(2));
+ if (keyCode < 0) {
+ // Ok, you gave us something bad.
+ Log.e(TAG, "Can't find keyname: " + command.get(2));
+ return EARG;
+ }
+ Log.d(TAG, "keycode: " + keyCode);
+ int action = -1;
+ if ("down".equals(command.get(1))) {
+ action = KeyEvent.ACTION_DOWN;
+ } else if ("up".equals(command.get(1))) {
+ action = KeyEvent.ACTION_UP;
+ }
+ if (action == -1) {
+ Log.e(TAG, "got unknown action.");
+ return EARG;
+ }
+ queue.enqueueEvent(new MonkeyKeyEvent(action, keyCode));
+ return OK;
+ }
+ return EARG;
+ }
+ }
+
+ /**
+ * Get an integer keycode value from a given keyname.
+ *
+ * @param keyName the key name to get the code for
+ * @returns the integer keycode value, or -1 on error.
+ */
+ private static int getKeyCode(String keyName) {
+ int keyCode = -1;
+ try {
+ keyCode = Integer.parseInt(keyName);
+ } catch (NumberFormatException e) {
+ // Ok, it wasn't a number, see if we have a
+ // keycode name for it
+ keyCode = MonkeySourceRandom.getKeyCode(keyName);
+ if (keyCode == -1) {
+ // OK, one last ditch effort to find a match.
+ // Build the KEYCODE_STRING from the string
+ // we've been given and see if that key
+ // exists. This would allow you to do "key
+ // down menu", for example.
+ keyCode = MonkeySourceRandom.getKeyCode("KEYCODE_" + keyName.toUpperCase());
+ }
+ }
+ return keyCode;
+ }
+
+ /**
+ * Command to put the Monkey to sleep.
+ */
+ private static class SleepCommand implements MonkeyCommand {
+ // sleep 2000
+ public MonkeyCommandReturn translateCommand(List<String> command,
+ CommandQueue queue) {
+ if (command.size() == 2) {
+ int sleep = -1;
+ String sleepStr = command.get(1);
+ try {
+ sleep = Integer.parseInt(sleepStr);
+ } catch (NumberFormatException e) {
+ Log.e(TAG, "Not a number: " + sleepStr, e);
+ return EARG;
+ }
+ queue.enqueueEvent(new MonkeyThrottleEvent(sleep));
+ return OK;
+ }
+ return EARG;
+ }
+ }
+
+ /**
+ * Command to type a string
+ */
+ private static class TypeCommand implements MonkeyCommand {
+ // wake
+ public MonkeyCommandReturn translateCommand(List<String> command,
+ CommandQueue queue) {
+ if (command.size() == 2) {
+ String str = command.get(1);
+
+ char[] chars = str.toString().toCharArray();
+
+ // Convert the string to an array of KeyEvent's for
+ // the built in keymap.
+ KeyCharacterMap keyCharacterMap = KeyCharacterMap.
+ load(KeyCharacterMap.BUILT_IN_KEYBOARD);
+ KeyEvent[] events = keyCharacterMap.getEvents(chars);
+
+ // enqueue all the events we just got.
+ for (KeyEvent event : events) {
+ queue.enqueueEvent(new MonkeyKeyEvent(event));
+ }
+ return OK;
+ }
+ return EARG;
+ }
+ }
+
+ /**
+ * Command to wake the device up
+ */
+ private static class WakeCommand implements MonkeyCommand {
+ // wake
+ public MonkeyCommandReturn translateCommand(List<String> command,
+ CommandQueue queue) {
+ if (!wake()) {
+ return ERROR;
+ }
+ return OK;
+ }
+ }
+
+ /**
+ * Command to "tap" at a location (Sends a down and up touch
+ * event).
+ */
+ private static class TapCommand implements MonkeyCommand {
+ // tap x y
+ public MonkeyCommandReturn translateCommand(List<String> command,
+ CommandQueue queue) {
+ if (command.size() == 3) {
+ int x = 0;
+ int y = 0;
+ try {
+ x = Integer.parseInt(command.get(1));
+ y = Integer.parseInt(command.get(2));
+ } catch (NumberFormatException e) {
+ // Ok, it wasn't a number
+ Log.e(TAG, "Got something that wasn't a number", e);
+ return EARG;
+ }
+
+ queue.enqueueEvent(new MonkeyMotionEvent(MonkeyEvent.EVENT_TYPE_POINTER,
+ -1, MotionEvent.ACTION_DOWN,
+ x, y, 0));
+ queue.enqueueEvent(new MonkeyMotionEvent(MonkeyEvent.EVENT_TYPE_POINTER,
+ -1, MotionEvent.ACTION_UP,
+ x, y, 0));
+ return OK;
+ }
+ return EARG;
+ }
+ }
+
+ /**
+ * Command to "press" a buttons (Sends an up and down key event.)
+ */
+ private static class PressCommand implements MonkeyCommand {
+ // press keycode
+ public MonkeyCommandReturn translateCommand(List<String> command,
+ CommandQueue queue) {
+ if (command.size() == 2) {
+ int keyCode = getKeyCode(command.get(1));
+ if (keyCode < 0) {
+ // Ok, you gave us something bad.
+ Log.e(TAG, "Can't find keyname: " + command.get(1));
+ return EARG;
+ }
+
+ queue.enqueueEvent(new MonkeyKeyEvent(KeyEvent.ACTION_DOWN, keyCode));
+ queue.enqueueEvent(new MonkeyKeyEvent(KeyEvent.ACTION_UP, keyCode));
+ return OK;
+
+ }
+ return EARG;
+ }
+ }
+
+ /**
+ * Force the device to wake up.
+ *
+ * @return true if woken up OK.
+ */
+ private static final boolean wake() {
+ IPowerManager pm =
+ IPowerManager.Stub.asInterface(ServiceManager.getService(Context.POWER_SERVICE));
+ try {
+ pm.userActivityWithForce(SystemClock.uptimeMillis(), true, true);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Got remote exception", e);
+ return false;
+ }
+ return true;
+ }
+
+ // This maps from command names to command implementations.
+ private static final Map<String, MonkeyCommand> COMMAND_MAP = new HashMap<String, MonkeyCommand>();
+
+ static {
+ // Add in all the commands we support
+ COMMAND_MAP.put("flip", new FlipCommand());
+ COMMAND_MAP.put("touch", new TouchCommand());
+ COMMAND_MAP.put("trackball", new TrackballCommand());
+ COMMAND_MAP.put("key", new KeyCommand());
+ COMMAND_MAP.put("sleep", new SleepCommand());
+ COMMAND_MAP.put("wake", new WakeCommand());
+ COMMAND_MAP.put("tap", new TapCommand());
+ COMMAND_MAP.put("press", new PressCommand());
+ COMMAND_MAP.put("type", new TypeCommand());
+ COMMAND_MAP.put("listvar", new MonkeySourceNetworkVars.ListVarCommand());
+ COMMAND_MAP.put("getvar", new MonkeySourceNetworkVars.GetVarCommand());
+ }
+
+ // QUIT command
+ private static final String QUIT = "quit";
+ // DONE command
+ private static final String DONE = "done";
+
+ // command response strings
+ private static final String OK_STR = "OK";
+ private static final String ERROR_STR = "ERROR";
+
+ public static interface CommandQueue {
+ /**
+ * Enqueue an event to be returned later. This allows a
+ * command to return multiple events. Commands using the
+ * command queue still have to return a valid event from their
+ * translateCommand method. The returned command will be
+ * executed before anything put into the queue.
+ *
+ * @param e the event to be enqueued.
+ */
+ public void enqueueEvent(MonkeyEvent e);
+ };
+
+ // Queue of Events to be processed. This allows commands to push
+ // multiple events into the queue to be processed.
+ private static class CommandQueueImpl implements CommandQueue{
+ private final Queue<MonkeyEvent> queuedEvents = new LinkedList<MonkeyEvent>();
+
+ public void enqueueEvent(MonkeyEvent e) {
+ queuedEvents.offer(e);
+ }
+
+ /**
+ * Get the next queued event to excecute.
+ *
+ * @returns the next event, or null if there aren't any more.
+ */
+ public MonkeyEvent getNextQueuedEvent() {
+ return queuedEvents.poll();
+ }
+ };
+
+ private final CommandQueueImpl commandQueue = new CommandQueueImpl();
+
+ private BufferedReader input;
+ private PrintWriter output;
+ private boolean started = false;
+
+ private ServerSocket serverSocket;
+ private Socket clientSocket;
+
+ public MonkeySourceNetwork(int port) throws IOException {
+ // Only bind this to local host. This means that you can only
+ // talk to the monkey locally, or though adb port forwarding.
+ serverSocket = new ServerSocket(port,
+ 0, // default backlog
+ InetAddress.getLocalHost());
+ }
+
+ /**
+ * Start a network server listening on the specified port. The
+ * network protocol is a line oriented protocol, where each line
+ * is a different command that can be run.
+ *
+ * @param port the port to listen on
+ */
+ private void startServer() throws IOException {
+ clientSocket = serverSocket.accept();
+ // At this point, we have a client connected. Wake the device
+ // up in preparation for doing some commands.
+ wake();
+
+ input = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
+ // auto-flush
+ output = new PrintWriter(clientSocket.getOutputStream(), true);
+ }
+
+ /**
+ * Stop the server from running so it can reconnect a new client.
+ */
+ private void stopServer() throws IOException {
+ clientSocket.close();
+ input.close();
+ output.close();
+ started = false;
+ }
+
+ /**
+ * Helper function for commandLineSplit that replaces quoted
+ * charaters with their real values.
+ *
+ * @param input the string to do replacement on.
+ * @returns the results with the characters replaced.
+ */
+ private static String replaceQuotedChars(String input) {
+ return input.replace("\\\"", "\"");
+ }
+
+ /**
+ * This function splits the given line into String parts. It obey's quoted
+ * strings and returns them as a single part.
+ *
+ * "This is a test" -> returns only one element
+ * This is a test -> returns four elements
+ *
+ * @param line the line to parse
+ * @return the List of elements
+ */
+ private static List<String> commandLineSplit(String line) {
+ ArrayList<String> result = new ArrayList<String>();
+ StringTokenizer tok = new StringTokenizer(line);
+
+ boolean insideQuote = false;
+ StringBuffer quotedWord = new StringBuffer();
+ while (tok.hasMoreTokens()) {
+ String cur = tok.nextToken();
+ if (!insideQuote && cur.startsWith("\"")) {
+ // begin quote
+ quotedWord.append(replaceQuotedChars(cur));
+ insideQuote = true;
+ } else if (insideQuote) {
+ // end quote
+ if (cur.endsWith("\"")) {
+ insideQuote = false;
+ quotedWord.append(" ").append(replaceQuotedChars(cur));
+ String word = quotedWord.toString();
+
+ // trim off the quotes
+ result.add(word.substring(1, word.length() - 1));
+ } else {
+ quotedWord.append(" ").append(replaceQuotedChars(cur));
+ }
+ } else {
+ result.add(replaceQuotedChars(cur));
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Translate the given command line into a MonkeyEvent.
+ *
+ * @param commandLine the full command line given.
+ */
+ private void translateCommand(String commandLine) {
+ Log.d(TAG, "translateCommand: " + commandLine);
+ List<String> parts = commandLineSplit(commandLine);
+ if (parts.size() > 0) {
+ MonkeyCommand command = COMMAND_MAP.get(parts.get(0));
+ if (command != null) {
+ MonkeyCommandReturn ret = command.translateCommand(parts,
+ commandQueue);
+ if (ret.wasSuccessful()) {
+ if (ret.hasMessage()) {
+ returnOk(ret.getMessage());
+ } else {
+ returnOk();
+ }
+ } else {
+ if (ret.hasMessage()) {
+ returnError(ret.getMessage());
+ } else {
+ returnError();
+ }
+ }
+ }
+ }
+ }
+
+ public MonkeyEvent getNextEvent() {
+ if (!started) {
+ try {
+ startServer();
+ } catch (IOException e) {
+ Log.e(TAG, "Got IOException from server", e);
+ return null;
+ }
+ started = true;
+ }
+
+ // Now, get the next command. This call may block, but that's OK
+ try {
+ while (true) {
+ // Check to see if we have any events queued up. If
+ // we do, use those until we have no more. Then get
+ // more input from the user.
+ MonkeyEvent queuedEvent = commandQueue.getNextQueuedEvent();
+ if (queuedEvent != null) {
+ // dispatch the event
+ return queuedEvent;
+ }
+
+ String command = input.readLine();
+ if (command == null) {
+ Log.d(TAG, "Connection dropped.");
+ // Treat this exactly the same as if the user had
+ // ended the session cleanly with a done commant.
+ command = DONE;
+ }
+
+ if (DONE.equals(command)) {
+ // stop the server so it can accept new connections
+ try {
+ stopServer();
+ } catch (IOException e) {
+ Log.e(TAG, "Got IOException shutting down!", e);
+ return null;
+ }
+ // return a noop event so we keep executing the main
+ // loop
+ return new MonkeyNoopEvent();
+ }
+
+ // Do quit checking here
+ if (QUIT.equals(command)) {
+ // then we're done
+ Log.d(TAG, "Quit requested");
+ // let the host know the command ran OK
+ returnOk();
+ return null;
+ }
+
+ // Do comment checking here. Comments aren't a
+ // command, so we don't echo anything back to the
+ // user.
+ if (command.startsWith("#")) {
+ // keep going
+ continue;
+ }
+
+ // Translate the command line. This will handle returning error/ok to the user
+ translateCommand(command);
+ }
+ } catch (IOException e) {
+ Log.e(TAG, "Exception: ", e);
+ return null;
+ }
+ }
+
+ /**
+ * Returns ERROR to the user.
+ */
+ private void returnError() {
+ output.println(ERROR_STR);
+ }
+
+ /**
+ * Returns ERROR to the user.
+ *
+ * @param msg the error message to include
+ */
+ private void returnError(String msg) {
+ output.print(ERROR_STR);
+ output.print(":");
+ output.println(msg);
+ }
+
+ /**
+ * Returns OK to the user.
+ */
+ private void returnOk() {
+ output.println(OK_STR);
+ }
+
+ /**
+ * Returns OK to the user.
+ *
+ * @param returnValue the value to return from this command.
+ */
+ private void returnOk(String returnValue) {
+ output.print(OK_STR);
+ output.print(":");
+ output.println(returnValue);
+ }
+
+ public void setVerbose(int verbose) {
+ // We're not particualy verbose
+ }
+
+ public boolean validate() {
+ // we have no pre-conditions to validate
+ return true;
+ }
+}
--- /dev/null
+/*
+ * Copyright 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.commands.monkey;
+
+import android.os.Build;
+import android.os.SystemClock;
+import android.view.Display;
+import android.view.WindowManagerImpl;
+import android.util.DisplayMetrics;
+
+import com.android.commands.monkey.MonkeySourceNetwork.CommandQueue;
+import com.android.commands.monkey.MonkeySourceNetwork.MonkeyCommand;
+import com.android.commands.monkey.MonkeySourceNetwork.MonkeyCommandReturn;
+
+import java.lang.Integer;
+import java.lang.Float;
+import java.lang.Long;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+
+public class MonkeySourceNetworkVars {
+ /**
+ * Interface to get the value of a var.
+ */
+ private static interface VarGetter {
+ /**
+ * Get the value of the var.
+ * @returns the value of the var.
+ */
+ public String get();
+ }
+
+ private static class StaticVarGetter implements VarGetter {
+ private final String value;
+
+ public StaticVarGetter(String value) {
+ this.value = value;
+ }
+
+ public String get() {
+ return value;
+ }
+ }
+
+ // Use a TreeMap to keep the keys sorted so they get displayed nicely in listvar
+ private static final Map<String, VarGetter> VAR_MAP = new TreeMap<String, VarGetter>();
+
+ static {
+ VAR_MAP.put("build.board", new StaticVarGetter(Build.BOARD));
+ VAR_MAP.put("build.brand", new StaticVarGetter(Build.BRAND));
+ VAR_MAP.put("build.device", new StaticVarGetter(Build.DEVICE));
+ VAR_MAP.put("build.display", new StaticVarGetter(Build.DISPLAY));
+ VAR_MAP.put("build.fingerprint", new StaticVarGetter(Build.FINGERPRINT));
+ VAR_MAP.put("build.host", new StaticVarGetter(Build.HOST));
+ VAR_MAP.put("build.id", new StaticVarGetter(Build.ID));
+ VAR_MAP.put("build.model", new StaticVarGetter(Build.MODEL));
+ VAR_MAP.put("build.product", new StaticVarGetter(Build.PRODUCT));
+ VAR_MAP.put("build.tags", new StaticVarGetter(Build.TAGS));
+ VAR_MAP.put("build.brand", new StaticVarGetter(Long.toString(Build.TIME)));
+ VAR_MAP.put("build.type", new StaticVarGetter(Build.TYPE));
+ VAR_MAP.put("build.user", new StaticVarGetter(Build.USER));
+ VAR_MAP.put("build.cpu_abi", new StaticVarGetter(Build.CPU_ABI));
+ VAR_MAP.put("build.manufacturer", new StaticVarGetter(Build.MANUFACTURER));
+ VAR_MAP.put("build.version.incremental", new StaticVarGetter(Build.VERSION.INCREMENTAL));
+ VAR_MAP.put("build.version.release", new StaticVarGetter(Build.VERSION.RELEASE));
+ VAR_MAP.put("build.version.sdk", new StaticVarGetter(Integer.toString(Build.VERSION.SDK_INT)));
+ VAR_MAP.put("build.version.codename", new StaticVarGetter(Build.VERSION.CODENAME));
+
+ // Display
+ Display display = WindowManagerImpl.getDefault().getDefaultDisplay();
+ VAR_MAP.put("display.width", new StaticVarGetter(Integer.toString(display.getWidth())));
+ VAR_MAP.put("display.height", new StaticVarGetter(Integer.toString(display.getHeight())));
+
+ DisplayMetrics dm = new DisplayMetrics();
+ display.getMetrics(dm);
+ VAR_MAP.put("display.density", new StaticVarGetter(Float.toString(dm.density)));
+
+ // am. note that the current activity information isn't valid
+ // until the first activity gets launched after the monkey has
+ // been started.
+ VAR_MAP.put("am.current.package", new VarGetter() {
+ public String get() {
+ return Monkey.currentPackage;
+ }
+ });
+ VAR_MAP.put("am.current.action", new VarGetter() {
+ public String get() {
+ if (Monkey.currentIntent == null) {
+ return null;
+ }
+ return Monkey.currentIntent.getAction();
+ }
+ });
+ VAR_MAP.put("am.current.comp.class", new VarGetter() {
+ public String get() {
+ if (Monkey.currentIntent == null) {
+ return null;
+ }
+ return Monkey.currentIntent.getComponent().getClassName();
+ }
+ });
+ VAR_MAP.put("am.current.comp.package", new VarGetter() {
+ public String get() {
+ if (Monkey.currentIntent == null) {
+ return null;
+ }
+ return Monkey.currentIntent.getComponent().getPackageName();
+ }
+ });
+ VAR_MAP.put("am.current.data", new VarGetter() {
+ public String get() {
+ if (Monkey.currentIntent == null) {
+ return null;
+ }
+ return Monkey.currentIntent.getDataString();
+ }
+ });
+ VAR_MAP.put("am.current.categories", new VarGetter() {
+ public String get() {
+ if (Monkey.currentIntent == null) {
+ return null;
+ }
+ StringBuffer sb = new StringBuffer();
+ for (String cat : Monkey.currentIntent.getCategories()) {
+ sb.append(cat).append(" ");
+ }
+ return sb.toString();
+ }
+ });
+
+ // clock
+ VAR_MAP.put("clock.realtime", new VarGetter() {
+ public String get() {
+ return Long.toString(SystemClock.elapsedRealtime());
+ }
+ });
+ VAR_MAP.put("clock.uptime", new VarGetter() {
+ public String get() {
+ return Long.toString(SystemClock.uptimeMillis());
+ }
+ });
+ VAR_MAP.put("clock.millis", new VarGetter() {
+ public String get() {
+ return Long.toString(System.currentTimeMillis());
+ }
+ });
+ }
+
+ /**
+ * Command to list the "vars" that the monkey knows about.
+ */
+ public static class ListVarCommand implements MonkeySourceNetwork.MonkeyCommand {
+ // listvar
+ public MonkeyCommandReturn translateCommand(List<String> command,
+ CommandQueue queue) {
+ Set<String> keys = VAR_MAP.keySet();
+ StringBuffer sb = new StringBuffer();
+ for (String key : keys) {
+ sb.append(key).append(" ");
+ }
+ return new MonkeyCommandReturn(true, sb.toString());
+ }
+ }
+
+ /**
+ * Command to get the value of a var.
+ */
+ public static class GetVarCommand implements MonkeyCommand {
+ // getvar varname
+ public MonkeyCommandReturn translateCommand(List<String> command,
+ CommandQueue queue) {
+ if (command.size() == 2) {
+ VarGetter getter = VAR_MAP.get(command.get(1));
+ if (getter == null) {
+ return new MonkeyCommandReturn(false, "unknown var");
+ }
+ return new MonkeyCommandReturn(true, getter.get());
+ }
+ return MonkeySourceNetwork.EARG;
+ }
+ }
+}
/**
* monkey event queue
*/
-public class MonkeySourceRandom implements MonkeyEventSource {
+public class MonkeySourceRandom implements MonkeyEventSource {
/** Key events that move around the UI. */
private static final int[] NAV_KEYS = {
KeyEvent.KEYCODE_DPAD_UP, KeyEvent.KEYCODE_DPAD_DOWN,
/** Nice names for all key events. */
private static final String[] KEY_NAMES = {
"KEYCODE_UNKNOWN",
- "KEYCODE_MENU",
+ "KEYCODE_SOFT_LEFT",
"KEYCODE_SOFT_RIGHT",
"KEYCODE_HOME",
"KEYCODE_BACK",
"KEYCODE_REWIND",
"KEYCODE_FORWARD",
"KEYCODE_MUTE",
-
+
"TAG_LAST_KEYCODE" // EOL. used to keep the lists in sync
};
public static final int FACTOR_SYSOPS = 5;
public static final int FACTOR_APPSWITCH = 6;
public static final int FACTOR_FLIP = 7;
- public static final int FACTOR_ANYTHING = 8;
+ public static final int FACTOR_ANYTHING = 8;
public static final int FACTORZ_COUNT = 9; // should be last+1
-
-
+
+
/** percentages for each type of event. These will be remapped to working
* values after we read any optional values.
- **/
+ **/
private float[] mFactors = new float[FACTORZ_COUNT];
private ArrayList<ComponentName> mMainApps;
private int mEventCount = 0; //total number of events generated so far
private MonkeyEventQueue mQ;
- private Random mRandom;
+ private Random mRandom;
private int mVerbose = 0;
private long mThrottle = 0;
private boolean mKeyboardOpen = false;
- /**
+ /**
* @return the last name in the key list
*/
public static String getLastKeyName() {
return KEY_NAMES[KeyEvent.getMaxKeyCode() + 1];
}
-
+
public static String getKeyName(int keycode) {
return KEY_NAMES[keycode];
}
-
+
+ /**
+ * Looks up the keyCode from a given KEYCODE_NAME. NOTE: This may
+ * be an expensive operation.
+ *
+ * @param keyName the name of the KEYCODE_VALUE to lookup.
+ * @returns the intenger keyCode value, or -1 if not found
+ */
+ public static int getKeyCode(String keyName) {
+ for (int x = 0; x < KEY_NAMES.length; x++) {
+ if (KEY_NAMES[x].equals(keyName)) {
+ return x;
+ }
+ }
+ return -1;
+ }
+
public MonkeySourceRandom(long seed, ArrayList<ComponentName> MainApps, long throttle) {
// default values for random distributions
// note, these are straight percentages, to match user input (cmd line args)
mFactors[FACTOR_APPSWITCH] = 2.0f;
mFactors[FACTOR_FLIP] = 1.0f;
mFactors[FACTOR_ANYTHING] = 15.0f;
-
+
mRandom = new SecureRandom();
mRandom.setSeed((seed == 0) ? -1 : seed);
mMainApps = MainApps;
} else {
defaultSum += mFactors[i];
++defaultCount;
- }
+ }
}
-
+
// if the user request was > 100%, reject it
if (userSum > 100.0f) {
System.err.println("** Event weights > 100%");
return false;
}
-
+
// if the user specified all of the weights, then they need to be 100%
if (defaultCount == 0 && (userSum < 99.9f || userSum > 100.1f)) {
System.err.println("** Event weights != 100%");
return false;
}
-
+
// compute the adjustment necessary
float defaultsTarget = (100.0f - userSum);
float defaultsAdjustment = defaultsTarget / defaultSum;
-
+
// fix all values, by adjusting defaults, or flipping user values back to >0
for (int i = 0; i < FACTORZ_COUNT; ++i) {
if (mFactors[i] <= 0.0f) { // user values are zero or negative
mFactors[i] *= defaultsAdjustment;
}
}
-
+
// if verbose, show factors
-
+
if (mVerbose > 0) {
System.out.println("// Event percentages:");
for (int i = 0; i < FACTORZ_COUNT; ++i) {
System.out.println("// " + i + ": " + mFactors[i] + "%");
}
- }
-
+ }
+
// finally, normalize and convert to running sum
float sum = 0.0f;
for (int i = 0; i < FACTORZ_COUNT; ++i) {
sum += mFactors[i] / 100.0f;
mFactors[i] = sum;
- }
+ }
return true;
}
-
+
/**
* set the factors
- *
+ *
* @param factors: percentages for each type of event
*/
public void setFactors(float factors[]) {
int c = FACTORZ_COUNT;
if (factors.length < c) {
c = factors.length;
- }
+ }
for (int i = 0; i < c; i++)
mFactors[i] = factors[i];
}
-
+
public void setFactors(int index, float v) {
mFactors[index] = v;
}
-
+
/**
* Generates a random motion event. This method counts a down, move, and up as multiple events.
- *
+ *
* TODO: Test & fix the selectors when non-zero percentages
* TODO: Longpress.
* TODO: Fling.
* TODO: More useful than the random walk here would be to pick a single random direction
* and distance, and divvy it up into a random number of segments. (This would serve to
* generate fling gestures, which are important).
- *
+ *
* @param random Random number source for positioning
- * @param motionEvent If false, touch/release. If true, touch/move/release.
- *
+ * @param motionEvent If false, touch/release. If true, touch/move/release.
+ *
*/
private void generateMotionEvent(Random random, boolean motionEvent){
-
+
Display display = WindowManagerImpl.getDefault().getDefaultDisplay();
float x = Math.abs(random.nextInt() % display.getWidth());
if (downAt == -1) {
downAt = eventTime;
}
-
- MonkeyMotionEvent e = new MonkeyMotionEvent(MonkeyEvent.EVENT_TYPE_POINTER,
- downAt, MotionEvent.ACTION_DOWN, x, y, 0);
- e.setIntermediateNote(false);
+
+ MonkeyMotionEvent e = new MonkeyMotionEvent(MonkeyEvent.EVENT_TYPE_POINTER,
+ downAt, MotionEvent.ACTION_DOWN, x, y, 0);
+ e.setIntermediateNote(false);
mQ.addLast(e);
-
+
// sometimes we'll move during the touch
if (motionEvent) {
int count = random.nextInt(10);
// generate some slop in the up event
x = (x + (random.nextInt() % 10)) % display.getWidth();
y = (y + (random.nextInt() % 10)) % display.getHeight();
-
- e = new MonkeyMotionEvent(MonkeyEvent.EVENT_TYPE_POINTER,
- downAt, MotionEvent.ACTION_MOVE, x, y, 0);
- e.setIntermediateNote(true);
+
+ e = new MonkeyMotionEvent(MonkeyEvent.EVENT_TYPE_POINTER,
+ downAt, MotionEvent.ACTION_MOVE, x, y, 0);
+ e.setIntermediateNote(true);
mQ.addLast(e);
}
}
// TODO generate some slop in the up event
- e = new MonkeyMotionEvent(MonkeyEvent.EVENT_TYPE_POINTER,
- downAt, MotionEvent.ACTION_UP, x, y, 0);
- e.setIntermediateNote(false);
+ e = new MonkeyMotionEvent(MonkeyEvent.EVENT_TYPE_POINTER,
+ downAt, MotionEvent.ACTION_UP, x, y, 0);
+ e.setIntermediateNote(false);
mQ.addLast(e);
}
-
+
/**
* Generates a random trackball event. This consists of a sequence of small moves, followed by
* an optional single click.
- *
+ *
* TODO: Longpress.
* TODO: Meta state
* TODO: Parameterize the % clicked
* TODO: More useful than the random walk here would be to pick a single random direction
* and distance, and divvy it up into a random number of segments. (This would serve to
* generate fling gestures, which are important).
- *
+ *
* @param random Random number source for positioning
- *
+ *
*/
private void generateTrackballEvent(Random random) {
Display display = WindowManagerImpl.getDefault().getDefaultDisplay();
// generate a small random step
int dX = random.nextInt(10) - 5;
int dY = random.nextInt(10) - 5;
-
-
- e = new MonkeyMotionEvent(MonkeyEvent.EVENT_TYPE_TRACKBALL, -1,
- MotionEvent.ACTION_MOVE, dX, dY, 0);
- e.setIntermediateNote(i > 0);
+
+
+ e = new MonkeyMotionEvent(MonkeyEvent.EVENT_TYPE_TRACKBALL, -1,
+ MotionEvent.ACTION_MOVE, dX, dY, 0);
+ e.setIntermediateNote(i > 0);
mQ.addLast(e);
}
-
+
// 10% of trackball moves end with a click
if (0 == random.nextInt(10)) {
long downAt = SystemClock.uptimeMillis();
-
-
- e = new MonkeyMotionEvent(MonkeyEvent.EVENT_TYPE_TRACKBALL, downAt,
- MotionEvent.ACTION_DOWN, 0, 0, 0);
- e.setIntermediateNote(true);
+
+
+ e = new MonkeyMotionEvent(MonkeyEvent.EVENT_TYPE_TRACKBALL, downAt,
+ MotionEvent.ACTION_DOWN, 0, 0, 0);
+ e.setIntermediateNote(true);
mQ.addLast(e);
-
-
- e = new MonkeyMotionEvent(MonkeyEvent.EVENT_TYPE_TRACKBALL, downAt,
- MotionEvent.ACTION_UP, 0, 0, 0);
- e.setIntermediateNote(false);
+
+
+ e = new MonkeyMotionEvent(MonkeyEvent.EVENT_TYPE_TRACKBALL, downAt,
+ MotionEvent.ACTION_UP, 0, 0, 0);
+ e.setIntermediateNote(false);
mQ.addLast(e);
- }
+ }
}
-
- /**
+
+ /**
* generate a random event based on mFactor
*/
- private void generateEvents() {
+ private void generateEvents() {
float cls = mRandom.nextFloat();
int lastKey = 0;
boolean touchEvent = cls < mFactors[FACTOR_TOUCH];
boolean motionEvent = !touchEvent && (cls < mFactors[FACTOR_MOTION]);
- if (touchEvent || motionEvent) {
+ if (touchEvent || motionEvent) {
generateMotionEvent(mRandom, motionEvent);
return;
}
-
- if (cls < mFactors[FACTOR_TRACKBALL]) {
+
+ if (cls < mFactors[FACTOR_TRACKBALL]) {
generateTrackballEvent(mRandom);
return;
}
} else {
lastKey = 1 + mRandom.nextInt(KeyEvent.getMaxKeyCode() - 1);
}
-
+
MonkeyKeyEvent e = new MonkeyKeyEvent(KeyEvent.ACTION_DOWN, lastKey);
mQ.addLast(e);
-
+
e = new MonkeyKeyEvent(KeyEvent.ACTION_UP, lastKey);
mQ.addLast(e);
}
-
+
public boolean validate() {
//check factors
return adjustEventFactors();
}
-
+
public void setVerbose(int verbose) {
mVerbose = verbose;
}
-
+
/**
* generate an activity event
*/
mRandom.nextInt(mMainApps.size())));
mQ.addLast(e);
}
-
+
/**
* if the queue is empty, we generate events first
- * @return the first event in the queue
+ * @return the first event in the queue
*/
public MonkeyEvent getNextEvent() {
if (mQ.isEmpty()) {
generateEvents();
- }
- mEventCount++;
- MonkeyEvent e = mQ.getFirst();
- mQ.removeFirst();
+ }
+ mEventCount++;
+ MonkeyEvent e = mQ.getFirst();
+ mQ.removeFirst();
return e;
}
}
<html>
<head>
-<meta http-equiv="refresh" content="0;url=docs/sdk/1.1_r1/index.html">
+<meta http-equiv="refresh" content="0;url=docs/sdk/RELEASENOTES.html">
</head>
<body>
-<a href="docs/sdk/1.1_r1/index.html">click here if you are not redirected</a>
+<a href="docs/sdk/RELEASENOTES.html">click here if you are not redirected</a>
</body>
-</html>
\ No newline at end of file
+</html>
parts {
- device {
+ portrait {
background {
- image device.png
+ image background_port.png
}
+ }
+ landscape {
+ background {
+ image background_land.png
+ }
+ }
+
+ device {
display {
width 320
height 480
- x 31
- y 72
+ x 0
+ y 0
+ }
+ }
+
+ controls {
+ background {
+ image controls.png
}
-
buttons {
soft-left {
- image menu.png
- x 147
- y 555
+ image button.png
+ x 56
+ y 142
}
home {
- image home.png
- x 48
- y 590
+ image button.png
+ x 0
+ y 142
}
back {
- image back.png
- x 286
- y 590
+ image button.png
+ x 112
+ y 142
}
dpad-up {
image arrow_up.png
- x 140
- y 595
+ x 77
+ y 53
}
dpad-down {
image arrow_down.png
- x 140
- y 656
+ x 77
+ y 106
}
dpad-left {
image arrow_left.png
- x 111
- y 598
+ x 53
+ y 53
}
dpad-right {
image arrow_right.png
- x 222
- y 598
+ x 123
+ y 53
}
dpad-center {
image select.png
- x 142
- y 626
+ x 77
+ y 81
}
phone-dial {
- image send.png
- x 48
- y 646
+ image button.png
+ x 0
+ y 71
}
phone-hangup {
- image end.png
- x 286
- y 646
+ image button.png
+ x 168
+ y 71
}
power {
- image power.png
- x -38
- y 52
+ image button.png
+ x 168
+ y 0
}
volume-up {
- image volume_up.png
- x 362
- y 260
+ image button.png
+ x 112
+ y 0
}
volume-down {
- image volume_down.png
- x 362
- y 310
+ image button.png
+ x 56
+ y 0
}
+
+ search {
+ image button.png
+ x 168
+ y 142
+ }
+
}
}
buttons {
1 {
image key.png
- x 0
- y 0
+ x 5
+ y 5
}
2 {
image key.png
- x 37
- y 0
+ x 42
+ y 5
}
3 {
image key.png
- x 74
- y 0
+ x 79
+ y 5
}
4 {
image key.png
- x 111
- y 0
+ x 116
+ y 5
}
5 {
image key.png
- x 148
- y 0
+ x 153
+ y 5
}
6 {
image key.png
- x 185
- y 0
+ x 190
+ y 5
}
7 {
image key.png
- x 222
- y 0
+ x 227
+ y 5
}
8 {
image key.png
- x 259
- y 0
+ x 264
+ y 5
}
9 {
image key.png
- x 296
- y 0
+ x 301
+ y 5
}
0 {
image key.png
- x 333
- y 0
+ x 338
+ y 5
}
q {
image key.png
- x 0
- y 36
+ x 5
+ y 41
}
w {
image key.png
- x 37
- y 36
+ x 42
+ y 41
}
e {
image key.png
- x 74
- y 36
+ x 79
+ y 41
}
r {
image key.png
- x 111
- y 36
+ x 116
+ y 41
}
t {
image key.png
- x 148
- y 36
+ x 153
+ y 41
}
y {
image key.png
- x 185
- y 36
+ x 190
+ y 41
}
u {
image key.png
- x 222
- y 36
+ x 227
+ y 41
}
i {
image key.png
- x 259
- y 36
+ x 264
+ y 41
}
o {
image key.png
- x 296
- y 36
+ x 301
+ y 41
}
p {
image key.png
- x 333
- y 36
+ x 338
+ y 41
}
a {
image key.png
- x 0
- y 72
+ x 5
+ y 77
}
s {
image key.png
- x 37
- y 72
+ x 42
+ y 77
}
d {
image key.png
- x 74
- y 72
+ x 79
+ y 77
}
f {
image key.png
- x 111
- y 72
+ x 116
+ y 77
}
g {
image key.png
- x 148
- y 72
+ x 153
+ y 77
}
h {
image key.png
- x 185
- y 72
+ x 190
+ y 77
}
j {
image key.png
- x 222
- y 72
+ x 227
+ y 77
}
k {
image key.png
- x 259
- y 72
+ x 264
+ y 77
}
l {
image key.png
- x 296
- y 72
+ x 301
+ y 77
}
DEL {
image key.png
- x 333
- y 72
+ x 338
+ y 77
}
CAP {
image key.png
- x 0
- y 108
+ x 5
+ y 113
}
z {
image key.png
- x 37
- y 108
+ x 42
+ y 113
}
x {
image key.png
- x 74
- y 108
+ x 79
+ y 113
}
c {
image key.png
- x 111
- y 108
+ x 116
+ y 113
}
v {
image key.png
- x 148
- y 108
+ x 153
+ y 113
}
b {
image key.png
- x 185
- y 108
+ x 190
+ y 113
}
n {
image key.png
- x 222
- y 108
+ x 227
+ y 113
}
m {
image key.png
- x 259
- y 108
+ x 264
+ y 113
}
PERIOD {
image key.png
- x 296
- y 108
+ x 301
+ y 113
}
ENTER {
image key.png
- x 333
- y 108
+ x 338
+ y 113
}
ALT {
image key.png
- x 0
- y 144
+ x 5
+ y 149
}
SYM {
image key.png
- x 37
- y 144
+ x 42
+ y 149
}
AT {
image key.png
- x 74
- y 144
+ x 79
+ y 149
}
SPACE {
image spacebar.png
- x 111
- y 144
+ x 116
+ y 149
}
SLASH {
image key.png
- x 259
- y 144
+ x 264
+ y 149
}
COMMA {
image key.png
- x 296
- y 144
+ x 301
+ y 149
}
ALT2 {
image key.png
- x 333
- y 144
+ x 338
+ y 149
}
}
layouts {
portrait {
- width 900
- height 730
+ width 791
+ height 534
color 0xe0e0e0
event EV_SW:0:1
-
+
part1 {
- name device
- x 40
- y -18
+ name portrait
+ x 0
+ y 0
}
+
part2 {
+ name landscape
+ x 800
+ y 0
+ }
+
+ part3 {
+ name device
+ x 28
+ y 27
+ }
+ part4 {
+ name controls
+ x 476
+ y 77
+ }
+ part5 {
name keyboard
- x 480
- y 200
+ x 395
+ y 328
}
+
}
landscape {
- width 900
- height 670
+ width 640
+ height 601
color 0xe0e0e0
event EV_SW:0:0
part1 {
+ name portrait
+ x 800
+ y 0
+ }
+
+ part2 {
+ name landscape
+ x 0
+ y 0
+ }
+
+ part3 {
name device
- x 50
- y 440
+ x 80
+ y 349
rotation 3
}
- part2 {
+
+ part4 {
+ name controls
+ x 410
+ y 396
+ }
+
+ part5 {
name keyboard
- x 250
- y 470
+ x 18
+ y 396
}
}
}
parts {
- device {
+ portrait {
+ background {
+ image background_port.png
+ }
+ }
+ landscape {
background {
- image device.png
+ image background_land.png
}
+ }
+
+ device {
display {
width 240
height 320
- x 33
- y 69
+ x 0
+ y 0
+ }
+ }
+
+ controls {
+ background {
+ image controls.png
}
-
buttons {
soft-left {
- image menu.png
- x 131
- y 404
+ image button.png
+ x 56
+ y 142
}
home {
- image home.png
- x 77
- y 404
+ image button.png
+ x 0
+ y 142
}
back {
- image back.png
- x 185
- y 404
+ image button.png
+ x 112
+ y 142
}
dpad-up {
image arrow_up.png
- x 105
- y 463
+ x 77
+ y 53
}
dpad-down {
image arrow_down.png
- x 104
- y 519
+ x 77
+ y 106
}
dpad-left {
image arrow_left.png
- x 96
- y 470
+ x 53
+ y 53
}
dpad-right {
image arrow_right.png
- x 172
- y 470
+ x 123
+ y 53
}
dpad-center {
image select.png
- x 131
- y 492
+ x 77
+ y 81
}
phone-dial {
- image send.png
- x 23
- y 404
+ image button.png
+ x 0
+ y 71
}
phone-hangup {
- image end.png
- x 238
- y 404
+ image button.png
+ x 168
+ y 71
}
power {
- image power.png
- x -16
+ image button.png
+ x 168
y 0
}
volume-up {
- image volume_up.png
- x 289
- y 177
+ image button.png
+ x 112
+ y 0
}
volume-down {
- image volume_down.png
- x 289
- y 233
+ image button.png
+ x 56
+ y 0
+ }
+
+ search {
+ image button.png
+ x 168
+ y 142
}
+
}
}
buttons {
1 {
image key.png
- x 0
- y 0
+ x 5
+ y 5
}
2 {
image key.png
- x 37
- y 0
+ x 42
+ y 5
}
3 {
image key.png
- x 74
- y 0
+ x 79
+ y 5
}
4 {
image key.png
- x 111
- y 0
+ x 116
+ y 5
}
5 {
image key.png
- x 148
- y 0
+ x 153
+ y 5
}
6 {
image key.png
- x 185
- y 0
+ x 190
+ y 5
}
7 {
image key.png
- x 222
- y 0
+ x 227
+ y 5
}
8 {
image key.png
- x 259
- y 0
+ x 264
+ y 5
}
9 {
image key.png
- x 296
- y 0
+ x 301
+ y 5
}
0 {
image key.png
- x 333
- y 0
+ x 338
+ y 5
}
q {
image key.png
- x 0
- y 36
+ x 5
+ y 41
}
w {
image key.png
- x 37
- y 36
+ x 42
+ y 41
}
e {
image key.png
- x 74
- y 36
+ x 79
+ y 41
}
r {
image key.png
- x 111
- y 36
+ x 116
+ y 41
}
t {
image key.png
- x 148
- y 36
+ x 153
+ y 41
}
y {
image key.png
- x 185
- y 36
+ x 190
+ y 41
}
u {
image key.png
- x 222
- y 36
+ x 227
+ y 41
}
i {
image key.png
- x 259
- y 36
+ x 264
+ y 41
}
o {
image key.png
- x 296
- y 36
+ x 301
+ y 41
}
p {
image key.png
- x 333
- y 36
+ x 338
+ y 41
}
a {
image key.png
- x 0
- y 72
+ x 5
+ y 77
}
s {
image key.png
- x 37
- y 72
+ x 42
+ y 77
}
d {
image key.png
- x 74
- y 72
+ x 79
+ y 77
}
f {
image key.png
- x 111
- y 72
+ x 116
+ y 77
}
g {
image key.png
- x 148
- y 72
+ x 153
+ y 77
}
h {
image key.png
- x 185
- y 72
+ x 190
+ y 77
}
j {
image key.png
- x 222
- y 72
+ x 227
+ y 77
}
k {
image key.png
- x 259
- y 72
+ x 264
+ y 77
}
l {
image key.png
- x 296
- y 72
+ x 301
+ y 77
}
DEL {
image key.png
- x 333
- y 72
+ x 338
+ y 77
}
CAP {
image key.png
- x 0
- y 108
+ x 5
+ y 113
}
z {
image key.png
- x 37
- y 108
+ x 42
+ y 113
}
x {
image key.png
- x 74
- y 108
+ x 79
+ y 113
}
c {
image key.png
- x 111
- y 108
+ x 116
+ y 113
}
v {
image key.png
- x 148
- y 108
+ x 153
+ y 113
}
b {
image key.png
- x 185
- y 108
+ x 190
+ y 113
}
n {
image key.png
- x 222
- y 108
+ x 227
+ y 113
}
m {
image key.png
- x 259
- y 108
+ x 264
+ y 113
}
PERIOD {
image key.png
- x 296
- y 108
+ x 301
+ y 113
}
ENTER {
image key.png
- x 333
- y 108
+ x 338
+ y 113
}
ALT {
image key.png
- x 0
- y 144
+ x 5
+ y 149
}
SYM {
image key.png
- x 37
- y 144
+ x 42
+ y 149
}
AT {
image key.png
- x 74
- y 144
+ x 79
+ y 149
}
SPACE {
image spacebar.png
- x 111
- y 144
+ x 116
+ y 149
}
SLASH {
image key.png
- x 259
- y 144
+ x 264
+ y 149
}
COMMA {
image key.png
- x 296
- y 144
+ x 301
+ y 149
}
ALT2 {
image key.png
- x 333
- y 144
+ x 338
+ y 149
}
}
layouts {
portrait {
- width 750
- height 610
+ width 711
+ height 435
color 0xe0e0e0
event EV_SW:0:1
-
+
part1 {
- name device
- x 30
- y 097
+ name portrait
+ x 0
+ y 0
}
+
part2 {
- name keyboard
- x 360
- y 300
+ name landscape
+ x 800
+ y 0
+ }
+
+ part3 {
+ name device
+ x 28
+ y 58
+ }
+ part4 {
+ name controls
+ x 396
+ y 27
+ }
+ part5 {
+ name keyboard
+ x 315
+ y 229
}
}
landscape {
- width 645
- height 575
+ width 640
+ height 522
color 0xe0e0e0
event EV_SW:0:0
part1 {
+ name portrait
+ x 800
+ y 0
+ }
+
+ part2 {
+ name landscape
+ x 0
+ y 0
+ }
+
+ part3 {
name device
- x 10
- y 360
+ x 160
+ y 270
rotation 3
}
- part2 {
+
+ part4 {
+ name controls
+ x 410
+ y 317
+ }
+
+ part5 {
name keyboard
- x 135
- y 380
+ x 18
+ y 317
}
}
}
--- /dev/null
+# skin-specific hardware values
+hw.lcd.density=120
\ No newline at end of file
--- /dev/null
+parts {
+ portrait {
+ background {
+ image background_port.png
+ }
+ }
+ landscape {
+ background {
+ image background_land.png
+ }
+ }
+
+ device {
+ display {
+ width 240
+ height 432
+ x 0
+ y 0
+ }
+ }
+
+ controls {
+ background {
+ image controls.png
+ }
+ buttons {
+ soft-left {
+ image button.png
+ x 56
+ y 142
+ }
+ home {
+ image button.png
+ x 0
+ y 142
+ }
+ back {
+ image button.png
+ x 112
+ y 142
+ }
+ dpad-up {
+ image arrow_up.png
+ x 77
+ y 53
+ }
+ dpad-down {
+ image arrow_down.png
+ x 77
+ y 106
+ }
+ dpad-left {
+ image arrow_left.png
+ x 53
+ y 53
+ }
+ dpad-right {
+ image arrow_right.png
+ x 123
+ y 53
+ }
+ dpad-center {
+ image select.png
+ x 77
+ y 81
+ }
+ phone-dial {
+ image button.png
+ x 0
+ y 71
+ }
+ phone-hangup {
+ image button.png
+ x 168
+ y 71
+ }
+
+ power {
+ image button.png
+ x 168
+ y 0
+ }
+
+ volume-up {
+ image button.png
+ x 112
+ y 0
+ }
+
+ volume-down {
+ image button.png
+ x 56
+ y 0
+ }
+
+ search {
+ image button.png
+ x 168
+ y 142
+ }
+
+ }
+ }
+
+ keyboard {
+ background {
+ image keyboard.png
+ }
+ buttons {
+ 1 {
+ image key.png
+ x 5
+ y 5
+ }
+ 2 {
+ image key.png
+ x 42
+ y 5
+ }
+ 3 {
+ image key.png
+ x 79
+ y 5
+ }
+ 4 {
+ image key.png
+ x 116
+ y 5
+ }
+ 5 {
+ image key.png
+ x 153
+ y 5
+ }
+ 6 {
+ image key.png
+ x 190
+ y 5
+ }
+ 7 {
+ image key.png
+ x 227
+ y 5
+ }
+ 8 {
+ image key.png
+ x 264
+ y 5
+ }
+ 9 {
+ image key.png
+ x 301
+ y 5
+ }
+ 0 {
+ image key.png
+ x 338
+ y 5
+ }
+
+ q {
+ image key.png
+ x 5
+ y 41
+ }
+ w {
+ image key.png
+ x 42
+ y 41
+ }
+ e {
+ image key.png
+ x 79
+ y 41
+ }
+ r {
+ image key.png
+ x 116
+ y 41
+ }
+ t {
+ image key.png
+ x 153
+ y 41
+ }
+ y {
+ image key.png
+ x 190
+ y 41
+ }
+ u {
+ image key.png
+ x 227
+ y 41
+ }
+ i {
+ image key.png
+ x 264
+ y 41
+ }
+ o {
+ image key.png
+ x 301
+ y 41
+ }
+ p {
+ image key.png
+ x 338
+ y 41
+ }
+
+ a {
+ image key.png
+ x 5
+ y 77
+ }
+ s {
+ image key.png
+ x 42
+ y 77
+ }
+ d {
+ image key.png
+ x 79
+ y 77
+ }
+ f {
+ image key.png
+ x 116
+ y 77
+ }
+ g {
+ image key.png
+ x 153
+ y 77
+ }
+ h {
+ image key.png
+ x 190
+ y 77
+ }
+ j {
+ image key.png
+ x 227
+ y 77
+ }
+ k {
+ image key.png
+ x 264
+ y 77
+ }
+ l {
+ image key.png
+ x 301
+ y 77
+ }
+ DEL {
+ image key.png
+ x 338
+ y 77
+ }
+
+ CAP {
+ image key.png
+ x 5
+ y 113
+ }
+ z {
+ image key.png
+ x 42
+ y 113
+ }
+ x {
+ image key.png
+ x 79
+ y 113
+ }
+ c {
+ image key.png
+ x 116
+ y 113
+ }
+ v {
+ image key.png
+ x 153
+ y 113
+ }
+ b {
+ image key.png
+ x 190
+ y 113
+ }
+ n {
+ image key.png
+ x 227
+ y 113
+ }
+ m {
+ image key.png
+ x 264
+ y 113
+ }
+ PERIOD {
+ image key.png
+ x 301
+ y 113
+ }
+ ENTER {
+ image key.png
+ x 338
+ y 113
+ }
+
+ ALT {
+ image key.png
+ x 5
+ y 149
+ }
+ SYM {
+ image key.png
+ x 42
+ y 149
+ }
+ AT {
+ image key.png
+ x 79
+ y 149
+ }
+ SPACE {
+ image spacebar.png
+ x 116
+ y 149
+ }
+ SLASH {
+ image key.png
+ x 264
+ y 149
+ }
+ COMMA {
+ image key.png
+ x 301
+ y 149
+ }
+ ALT2 {
+ image key.png
+ x 338
+ y 149
+ }
+
+ }
+ }
+}
+
+layouts {
+ portrait {
+ width 711
+ height 486
+ color 0xe0e0e0
+ event EV_SW:0:1
+
+ part1 {
+ name portrait
+ x 0
+ y 0
+ }
+
+ part2 {
+ name landscape
+ x 800
+ y 0
+ }
+
+ part3 {
+ name device
+ x 28
+ y 27
+ }
+ part4 {
+ name controls
+ x 396
+ y 53
+ }
+ part5 {
+ name keyboard
+ x 315
+ y 280
+ }
+ }
+
+ landscape {
+ width 640
+ height 522
+ color 0xe0e0e0
+ event EV_SW:0:0
+
+ part1 {
+ name portrait
+ x 800
+ y 0
+ }
+
+ part2 {
+ name landscape
+ x 0
+ y 0
+ }
+
+ part3 {
+ name device
+ x 104
+ y 270
+ rotation 3
+ }
+
+ part4 {
+ name controls
+ x 410
+ y 317
+ }
+
+ part5 {
+ name keyboard
+ x 18
+ y 317
+ }
+ }
+}
+
+keyboard {
+ charmap qwerty2
+}
+
+network {
+ speed full
+ delay none
+}
parts {
- device {
+ portrait {
+ background {
+ image background_port.png
+ }
+ }
+ landscape {
background {
- image device.png
+ image background_land.png
}
+ }
+
+ device {
display {
width 480
height 800
- x 65
- y 135
+ x 0
+ y 0
+ }
+ }
+
+ controls {
+ background {
+ image controls.png
}
-
buttons {
soft-left {
- image menu.png
- x 222
- y 945
+ image button.png
+ x 56
+ y 142
}
home {
- image home.png
- x 106
- y 945
+ image button.png
+ x 0
+ y 142
}
back {
- image back.png
- x 341
- y 945
- }
- search {
- image search.png
- x 460
- y 945
+ image button.png
+ x 112
+ y 142
}
dpad-up {
image arrow_up.png
- x 260
- y 1019
+ x 77
+ y 53
}
dpad-down {
image arrow_down.png
- x 260
- y 1081
+ x 77
+ y 106
}
dpad-left {
image arrow_left.png
- x 223
- y 1023
+ x 53
+ y 53
}
dpad-right {
image arrow_right.png
- x 343
- y 1023
+ x 123
+ y 53
}
dpad-center {
image select.png
- x 259
- y 1049
+ x 77
+ y 81
}
phone-dial {
- image send.png
- x 107
- y 1043
+ image button.png
+ x 0
+ y 71
}
phone-hangup {
- image end.png
- x 458
- y 1043
+ image button.png
+ x 168
+ y 71
}
power {
- image power.png
- x -10
- y 120
+ image button.png
+ x 168
+ y 0
}
volume-up {
- image volume_up.png
- x 570
- y 260
+ image button.png
+ x 112
+ y 0
}
volume-down {
- image volume_down.png
- x 570
- y 310
+ image button.png
+ x 56
+ y 0
}
+
+ search {
+ image button.png
+ x 168
+ y 142
+ }
+
}
}
buttons {
1 {
image key.png
- x 0
- y 0
+ x 5
+ y 5
}
2 {
image key.png
- x 37
- y 0
+ x 42
+ y 5
}
3 {
image key.png
- x 74
- y 0
+ x 79
+ y 5
}
4 {
image key.png
- x 111
- y 0
+ x 116
+ y 5
}
5 {
image key.png
- x 148
- y 0
+ x 153
+ y 5
}
6 {
image key.png
- x 185
- y 0
+ x 190
+ y 5
}
7 {
image key.png
- x 222
- y 0
+ x 227
+ y 5
}
8 {
image key.png
- x 259
- y 0
+ x 264
+ y 5
}
9 {
image key.png
- x 296
- y 0
+ x 301
+ y 5
}
0 {
image key.png
- x 333
- y 0
+ x 338
+ y 5
}
q {
image key.png
- x 0
- y 36
+ x 5
+ y 41
}
w {
image key.png
- x 37
- y 36
+ x 42
+ y 41
}
e {
image key.png
- x 74
- y 36
+ x 79
+ y 41
}
r {
image key.png
- x 111
- y 36
+ x 116
+ y 41
}
t {
image key.png
- x 148
- y 36
+ x 153
+ y 41
}
y {
image key.png
- x 185
- y 36
+ x 190
+ y 41
}
u {
image key.png
- x 222
- y 36
+ x 227
+ y 41
}
i {
image key.png
- x 259
- y 36
+ x 264
+ y 41
}
o {
image key.png
- x 296
- y 36
+ x 301
+ y 41
}
p {
image key.png
- x 333
- y 36
+ x 338
+ y 41
}
a {
image key.png
- x 0
- y 72
+ x 5
+ y 77
}
s {
image key.png
- x 37
- y 72
+ x 42
+ y 77
}
d {
image key.png
- x 74
- y 72
+ x 79
+ y 77
}
f {
image key.png
- x 111
- y 72
+ x 116
+ y 77
}
g {
image key.png
- x 148
- y 72
+ x 153
+ y 77
}
h {
image key.png
- x 185
- y 72
+ x 190
+ y 77
}
j {
image key.png
- x 222
- y 72
+ x 227
+ y 77
}
k {
image key.png
- x 259
- y 72
+ x 264
+ y 77
}
l {
image key.png
- x 296
- y 72
+ x 301
+ y 77
}
DEL {
image key.png
- x 333
- y 72
+ x 338
+ y 77
}
CAP {
image key.png
- x 0
- y 108
+ x 5
+ y 113
}
z {
image key.png
- x 37
- y 108
+ x 42
+ y 113
}
x {
image key.png
- x 74
- y 108
+ x 79
+ y 113
}
c {
image key.png
- x 111
- y 108
+ x 116
+ y 113
}
v {
image key.png
- x 148
- y 108
+ x 153
+ y 113
}
b {
image key.png
- x 185
- y 108
+ x 190
+ y 113
}
n {
image key.png
- x 222
- y 108
+ x 227
+ y 113
}
m {
image key.png
- x 259
- y 108
+ x 264
+ y 113
}
PERIOD {
image key.png
- x 296
- y 108
+ x 301
+ y 113
}
ENTER {
image key.png
- x 333
- y 108
+ x 338
+ y 113
}
ALT {
image key.png
- x 0
- y 144
+ x 5
+ y 149
}
SYM {
image key.png
- x 37
- y 144
+ x 42
+ y 149
}
AT {
image key.png
- x 74
- y 144
+ x 79
+ y 149
}
SPACE {
image spacebar.png
- x 111
- y 144
+ x 116
+ y 149
}
SLASH {
image key.png
- x 259
- y 144
+ x 264
+ y 149
}
COMMA {
image key.png
- x 296
- y 144
+ x 301
+ y 149
}
ALT2 {
image key.png
- x 333
- y 144
+ x 338
+ y 149
}
}
layouts {
portrait {
- width 975
- height 1080
- color 0xffffff
+ width 950
+ height 854
+ color 0xe0e0e0
event EV_SW:0:1
-
+
part1 {
- name device
- x 10
- y -70
+ name portrait
+ x 0
+ y 0
}
+
part2 {
+ name landscape
+ x 1000
+ y 0
+ }
+
+ part3 {
+ name device
+ x 27
+ y 27
+ }
+ part4 {
+ name controls
+ x 635
+ y 207
+ }
+ part5 {
name keyboard
- x 600
- y 400
+ x 554
+ y 459
}
+
}
landscape {
- width 1080
- height 810
- color 0xffffff
+ width 853
+ height 761
+ color 0xe0e0e0
event EV_SW:0:0
part1 {
+ name portrait
+ x 900
+ y 0
+ }
+
+ part2 {
+ name landscape
+ x 0
+ y 0
+ }
+
+ part3 {
name device
- x -70
- y 640
+ x 26
+ y 509
rotation 3
}
- part2 {
+
+ part4 {
+ name controls
+ x 539
+ y 556
+ }
+
+ part5 {
name keyboard
- x 250
- y 620
+ x 98
+ y 556
}
}
}
--- /dev/null
+# skin-specific hardware values
+hw.lcd.density=240
\ No newline at end of file
--- /dev/null
+parts {
+ portrait {
+ background {
+ image background_port.png
+ }
+ }
+ landscape {
+ background {
+ image background_land.png
+ }
+ }
+
+ device {
+ display {
+ width 480
+ height 854
+ x 0
+ y 0
+ }
+ }
+
+ controls {
+ background {
+ image controls.png
+ }
+ buttons {
+ soft-left {
+ image button.png
+ x 56
+ y 142
+ }
+ home {
+ image button.png
+ x 0
+ y 142
+ }
+ back {
+ image button.png
+ x 112
+ y 142
+ }
+ dpad-up {
+ image arrow_up.png
+ x 77
+ y 53
+ }
+ dpad-down {
+ image arrow_down.png
+ x 77
+ y 106
+ }
+ dpad-left {
+ image arrow_left.png
+ x 53
+ y 53
+ }
+ dpad-right {
+ image arrow_right.png
+ x 123
+ y 53
+ }
+ dpad-center {
+ image select.png
+ x 77
+ y 81
+ }
+ phone-dial {
+ image button.png
+ x 0
+ y 71
+ }
+ phone-hangup {
+ image button.png
+ x 168
+ y 71
+ }
+
+ power {
+ image button.png
+ x 168
+ y 0
+ }
+
+ volume-up {
+ image button.png
+ x 112
+ y 0
+ }
+
+ volume-down {
+ image button.png
+ x 56
+ y 0
+ }
+
+ search {
+ image button.png
+ x 168
+ y 142
+ }
+
+ }
+ }
+
+ keyboard {
+ background {
+ image keyboard.png
+ }
+ buttons {
+ 1 {
+ image key.png
+ x 5
+ y 5
+ }
+ 2 {
+ image key.png
+ x 42
+ y 5
+ }
+ 3 {
+ image key.png
+ x 79
+ y 5
+ }
+ 4 {
+ image key.png
+ x 116
+ y 5
+ }
+ 5 {
+ image key.png
+ x 153
+ y 5
+ }
+ 6 {
+ image key.png
+ x 190
+ y 5
+ }
+ 7 {
+ image key.png
+ x 227
+ y 5
+ }
+ 8 {
+ image key.png
+ x 264
+ y 5
+ }
+ 9 {
+ image key.png
+ x 301
+ y 5
+ }
+ 0 {
+ image key.png
+ x 338
+ y 5
+ }
+
+ q {
+ image key.png
+ x 5
+ y 41
+ }
+ w {
+ image key.png
+ x 42
+ y 41
+ }
+ e {
+ image key.png
+ x 79
+ y 41
+ }
+ r {
+ image key.png
+ x 116
+ y 41
+ }
+ t {
+ image key.png
+ x 153
+ y 41
+ }
+ y {
+ image key.png
+ x 190
+ y 41
+ }
+ u {
+ image key.png
+ x 227
+ y 41
+ }
+ i {
+ image key.png
+ x 264
+ y 41
+ }
+ o {
+ image key.png
+ x 301
+ y 41
+ }
+ p {
+ image key.png
+ x 338
+ y 41
+ }
+
+ a {
+ image key.png
+ x 5
+ y 77
+ }
+ s {
+ image key.png
+ x 42
+ y 77
+ }
+ d {
+ image key.png
+ x 79
+ y 77
+ }
+ f {
+ image key.png
+ x 116
+ y 77
+ }
+ g {
+ image key.png
+ x 153
+ y 77
+ }
+ h {
+ image key.png
+ x 190
+ y 77
+ }
+ j {
+ image key.png
+ x 227
+ y 77
+ }
+ k {
+ image key.png
+ x 264
+ y 77
+ }
+ l {
+ image key.png
+ x 301
+ y 77
+ }
+ DEL {
+ image key.png
+ x 338
+ y 77
+ }
+
+ CAP {
+ image key.png
+ x 5
+ y 113
+ }
+ z {
+ image key.png
+ x 42
+ y 113
+ }
+ x {
+ image key.png
+ x 79
+ y 113
+ }
+ c {
+ image key.png
+ x 116
+ y 113
+ }
+ v {
+ image key.png
+ x 153
+ y 113
+ }
+ b {
+ image key.png
+ x 190
+ y 113
+ }
+ n {
+ image key.png
+ x 227
+ y 113
+ }
+ m {
+ image key.png
+ x 264
+ y 113
+ }
+ PERIOD {
+ image key.png
+ x 301
+ y 113
+ }
+ ENTER {
+ image key.png
+ x 338
+ y 113
+ }
+
+ ALT {
+ image key.png
+ x 5
+ y 149
+ }
+ SYM {
+ image key.png
+ x 42
+ y 149
+ }
+ AT {
+ image key.png
+ x 79
+ y 149
+ }
+ SPACE {
+ image spacebar.png
+ x 116
+ y 149
+ }
+ SLASH {
+ image key.png
+ x 264
+ y 149
+ }
+ COMMA {
+ image key.png
+ x 301
+ y 149
+ }
+ ALT2 {
+ image key.png
+ x 338
+ y 149
+ }
+
+ }
+ }
+}
+
+layouts {
+ portrait {
+ width 950
+ height 908
+ color 0xe0e0e0
+ event EV_SW:0:1
+
+ part1 {
+ name portrait
+ x 0
+ y 0
+ }
+
+ part2 {
+ name landscape
+ x 1000
+ y 0
+ }
+
+ part3 {
+ name device
+ x 27
+ y 27
+ }
+ part4 {
+ name controls
+ x 635
+ y 234
+ }
+ part5 {
+ name keyboard
+ x 554
+ y 486
+ }
+
+ }
+
+ landscape {
+ width 907
+ height 761
+ color 0xe0e0e0
+ event EV_SW:0:0
+
+ part1 {
+ name portrait
+ x 900
+ y 0
+ }
+
+ part2 {
+ name landscape
+ x 0
+ y 0
+ }
+
+ part3 {
+ name device
+ x 26
+ y 509
+ rotation 3
+ }
+
+ part4 {
+ name controls
+ x 567
+ y 556
+ }
+
+ part5 {
+ name keyboard
+ x 126
+ y 556
+ }
+ }
+}
+
+keyboard {
+ charmap qwerty2
+}
+
+network {
+ speed full
+ delay none
+}
*.sln\r
*.vcproj*\r
+*.aps\r
usb/api.*\r
usb/Debug\r
usb/Release\r
usb/api/obj*\r
usb/api/*.log\r
usb/adb_winapi_test/obj*\r
-usb/adb_winapi_test/*.log
\ No newline at end of file
+usb/adb_winapi_test/*.log\r
+usb/winusb/obj*\r
+usb/winusb/*.log
\ No newline at end of file
AdbWinApi.a
LOCAL_PREBUILT_EXECUTABLES := \
- AdbWinApi.dll
+ AdbWinApi.dll \
+ AdbWinUsbApi.dll
.PHONY : kill-adb
;\r
-; WinUsb installation File.\r
+; Android WinUsb driver installation.\r
;\r
[Version]\r
Signature = "$Windows NT$"\r
Class = AndroidUsbDeviceClass
ClassGuid = {3F966BD9-FA04-4ec5-991C-D326973B5128}
Provider = %ProviderName%\r
-DriverVer = 07/20/2009,2.0.0010.00001
+DriverVer = 08/11/2009,2.0.0010.00002
CatalogFile.NTx86 = androidwinusb86.cat
CatalogFile.NTamd64 = androidwinusba64.cat
\r
;\r
-; This section is required even though we report our driver\r
-; as a standard USB driver. If this section is removed the\r
-; installer will report an error "Required section not found\r
-; in INF file.\r
+; This section seems to be required for WinUsb driver installation.\r
+; If this section is removed the installer will report an error\r
+; "Required section not found in INF file".\r
;\r
[ClassInstall32]\r
Addreg = AndroidWinUsbClassReg\r
HKR,,Icon,,-1\r
\r
[Manufacturer]\r
-%ProviderName% = Google,NTx86,NTamd64\r
+%ProviderName% = Google, NTx86, NTamd64\r
\r
[Google.NTx86]\r
; HTC Dream\r
-%USB\HCT_Dream.DeviceDesc% = USB_Install, USB\VID_0BB4&PID_0C01\r
-%USB\HCT_Dream_Composite.DeviceDesc% = USB_Install, USB\VID_0BB4&PID_0C02&MI_01\r
-%USB\HCT_BootLoader.DeviceDesc% = USB_Install, USB\VID_0BB4&PID_0FFF\r
-; HCT Magic\r
-%USB\HCT_Magic_Composite.DeviceDesc% = USB_Install, USB\VID_0BB4&PID_0C03&MI_01\r
+%SingleAdbInterface% = USB_Install, USB\VID_0BB4&PID_0C01\r
+%CompositeAdbInterface% = USB_Install, USB\VID_0BB4&PID_0C02&MI_01\r
+%SingleBootLoaderInterface% = USB_Install, USB\VID_0BB4&PID_0FFF\r
+; HTC Magic\r
+%CompositeAdbInterface% = USB_Install, USB\VID_0BB4&PID_0C03&MI_01\r
\r
[Google.NTamd64]\r
; HTC Dream\r
-%USB\HCT_Dream.DeviceDesc% = USB_Install, USB\VID_0BB4&PID_0C01\r
-%USB\HCT_Dream_Composite.DeviceDesc% = USB_Install, USB\VID_0BB4&PID_0C02&MI_01\r
-%USB\HCT_BootLoader.DeviceDesc% = USB_Install, USB\VID_0BB4&PID_0FFF\r
-; HCT Magic\r
-%USB\HCT_Magic_Composite.DeviceDesc% = USB_Install, USB\VID_0BB4&PID_0C03&MI_01\r
+%SingleAdbInterface% = USB_Install, USB\VID_0BB4&PID_0C01\r
+%CompositeAdbInterface% = USB_Install, USB\VID_0BB4&PID_0C02&MI_01\r
+%SingleBootLoaderInterface% = USB_Install, USB\VID_0BB4&PID_0FFF\r
+; HTC Magic\r
+%CompositeAdbInterface% = USB_Install, USB\VID_0BB4&PID_0C03&MI_01\r
\r
[USB_Install]\r
-Include=winusb.inf\r
-Needs=WINUSB.NT\r
+Include = winusb.inf\r
+Needs = WINUSB.NT\r
\r
[USB_Install.Services]\r
-Include=winusb.inf\r
-AddService=WinUSB,0x00000002,WinUSB_ServiceInstall\r
+Include = winusb.inf\r
+AddService = WinUSB,0x00000002,WinUSB_ServiceInstall\r
\r
[WinUSB_ServiceInstall]\r
DisplayName = %WinUSB_SvcDesc%\r
ServiceBinary = %12%\WinUSB.sys\r
\r
[USB_Install.Wdf]\r
-KmdfService=WINUSB, WinUSB_Install\r
+KmdfService = WINUSB, WinUSB_Install\r
+\r
[WinUSB_Install]\r
-KmdfLibraryVersion=1.7\r
+KmdfLibraryVersion = 1.7\r
\r
[USB_Install.HW]\r
-AddReg=Dev_AddReg\r
+AddReg = Dev_AddReg\r
\r
[Dev_AddReg]\r
HKR,,DeviceInterfaceGUIDs,0x10000,"{F72FE0D4-CBCB-407d-8814-9ED673D0DD6B}"\r
\r
[USB_Install.CoInstallers]\r
-AddReg=CoInstallers_AddReg\r
-CopyFiles=CoInstallers_CopyFiles\r
+AddReg = CoInstallers_AddReg\r
+CopyFiles = CoInstallers_CopyFiles\r
\r
[CoInstallers_AddReg]\r
HKR,,CoInstallers32,0x00010000,"WdfCoInstaller01007.dll,WdfCoInstaller","WinUSBCoInstaller.dll"\r
2 = %DISK_NAME%,,,\amd64\r
\r
[SourceDisksFiles.x86]\r
-WinUSBCoInstaller.dll=1\r
-WdfCoInstaller01007.dll=1\r
+WinUSBCoInstaller.dll = 1\r
+WdfCoInstaller01007.dll = 1\r
\r
[SourceDisksFiles.amd64]\r
-WinUSBCoInstaller.dll=2\r
-WdfCoInstaller01007.dll=2\r
+WinUSBCoInstaller.dll = 2\r
+WdfCoInstaller01007.dll = 2\r
\r
[Strings]\r
-ProviderName="Google, Inc."\r
-USB\HCT_Dream.DeviceDesc="Android ADB Interface"\r
-USB\HCT_Dream_Composite.DeviceDesc="Android Composite ADB Interface"\r
-USB\HCT_BootLoader.DeviceDesc="Android Bootloader Interface"\r
-USB\HCT_Magic_Composite.DeviceDesc="Android Composite ADB Interface"\r
-WinUSB_SvcDesc="Android USB Driver"\r
-DISK_NAME="Android install disk"\r
-ClassName="Android Phone"\r
+ProviderName = "Google, Inc."\r
+SingleAdbInterface = "Android ADB Interface"\r
+CompositeAdbInterface = "Android Composite ADB Interface"\r
+SingleBootLoaderInterface = "Android Bootloader Interface"\r
+WinUSB_SvcDesc = "Android USB Driver"\r
+DISK_NAME = "Android WinUsb installation disk"\r
+ClassName = "Android Phone"\r
// AdbWinApi.cpp : Implementation of DLL Exports.\r
\r
#include "stdafx.h"\r
+#include "adb_api.h"\r
+#include "adb_winusb_api.h"\r
\r
extern "C" {\r
int _forceCRTManifest;\r
int _forceAtlDllManifest;\r
};\r
\r
+/// References InstantiateWinUsbInterface declared in adb_api.cpp\r
+extern PFN_INSTWINUSBINTERFACE InstantiateWinUsbInterface;\r
+\r
class CAdbWinApiModule : public CAtlDllModuleT< CAdbWinApiModule > {\r
-public:\r
+ public:\r
+ CAdbWinApiModule()\r
+ : CAtlDllModuleT< CAdbWinApiModule >(),\r
+ adbwinusbapi_handle_(NULL),\r
+ is_initialized_(false) {\r
+ }\r
+\r
+ ~CAdbWinApiModule() {\r
+ // Unload AdbWinUsbApi.dll before we exit\r
+ if (NULL != adbwinusbapi_handle_) {\r
+ FreeLibrary(adbwinusbapi_handle_);\r
+ }\r
+ }\r
+\r
+ /** \brief Loads AdbWinUsbApi.dll and caches its InstantiateWinUsbInterface\r
+ export.\r
+\r
+ This method is called from DllMain on DLL_PROCESS_ATTACH event. In this\r
+ method we will check if WINUSB.DLL required by AdbWinUsbApi.dll is\r
+ installed, and if it is we will load AdbWinUsbApi.dll and cache address of\r
+ InstantiateWinUsbInterface routine exported from AdbWinUsbApi.dll\r
+ */\r
+ void AttachToAdbWinUsbApi() {\r
+ // We only need to run this only once.\r
+ if (is_initialized_) {\r
+ return;\r
+ }\r
+\r
+ // Just mark that we have ran initialization.\r
+ is_initialized_ = true;\r
+\r
+ // Before we can load AdbWinUsbApi.dll we must make sure that WINUSB.DLL\r
+ // has been installed. Build path to the file.\r
+ wchar_t path_to_winusb_dll[MAX_PATH+1];\r
+ if (!GetSystemDirectory(path_to_winusb_dll, MAX_PATH)) {\r
+ return;\r
+ }\r
+ wcscat(path_to_winusb_dll, L"\\WINUSB.DLL");\r
+\r
+ if (0xFFFFFFFF == GetFileAttributes(path_to_winusb_dll)) {\r
+ // WINUSB.DLL is not installed. We don't (in fact, can't) load\r
+ // AdbWinUsbApi.dll\r
+ return;\r
+ }\r
+\r
+ // WINUSB.DLL is installed. Lets load AdbWinUsbApi.dll and cache its\r
+ // InstantiateWinUsbInterface export.\r
+ // We require that AdbWinUsbApi.dll is located in the same folder\r
+ // where AdbWinApi.dll and adb.exe are located, so by Windows\r
+ // conventions we can pass just module name, and not the full path.\r
+ adbwinusbapi_handle_ = LoadLibrary(L"AdbWinUsbApi.dll");\r
+ if (NULL != adbwinusbapi_handle_) {\r
+ InstantiateWinUsbInterface = reinterpret_cast<PFN_INSTWINUSBINTERFACE>\r
+ (GetProcAddress(adbwinusbapi_handle_, "InstantiateWinUsbInterface"));\r
+ }\r
+ }\r
+\r
+ protected:\r
+ /// Handle to the loaded AdbWinUsbApi.dll\r
+ HINSTANCE adbwinusbapi_handle_;\r
+\r
+ /// Flags whether or not this module has been initialized.\r
+ bool is_initialized_;\r
};\r
\r
CAdbWinApiModule _AtlModule;\r
extern "C" BOOL WINAPI DllMain(HINSTANCE instance,\r
DWORD reason,\r
LPVOID reserved) {\r
- return _AtlModule.DllMain(reason, reserved); \r
+ // Lets see if we need to initialize InstantiateWinUsbInterface\r
+ // variable. We do that only once, on condition that this DLL is\r
+ // being attached to the process and InstantiateWinUsbInterface\r
+ // address has not been calculated yet.\r
+ if (DLL_PROCESS_ATTACH == reason) {\r
+ _AtlModule.AttachToAdbWinUsbApi();\r
+ }\r
+ return _AtlModule.DllMain(reason, reserved);\r
}\r
$(SDK_LIB_PATH)\wbemuuid.lib \\r
$(SDK_LIB_PATH)\uuid.lib \\r
$(SDK_LIB_PATH)\setupapi.lib \\r
- $(SDK_LIB_PATH)\usbd.lib \\r
- $(SDK_LIB_PATH)\winusb.lib\r
+ $(SDK_LIB_PATH)\usbd.lib\r
\r
!IF "$(DDKBUILDENV)" == "fre"
# Libraries for release (free) builds
# Define source files for AdbWinApi.dll\r
SOURCES = adb_api.cpp \\r
adb_endpoint_object.cpp \\r
- adb_winusb_endpoint_object.cpp \\r
adb_legacy_endpoint_object.cpp \\r
adb_helper_routines.cpp \\r
adb_interface.cpp \\r
- adb_winusb_interface.cpp \\r
adb_legacy_interface.cpp \\r
adb_interface_enum.cpp \\r
adb_io_completion.cpp \\r
- adb_winusb_io_completion.cpp \\r
adb_legacy_io_completion.cpp \\r
adb_object_handle.cpp \\r
AdbWinApi.cpp \\r
#include "adb_object_handle.h"\r
#include "adb_interface_enum.h"\r
#include "adb_interface.h"\r
-#include "adb_winusb_interface.h"\r
#include "adb_legacy_interface.h"\r
#include "adb_endpoint_object.h"\r
#include "adb_io_completion.h"\r
#include "adb_helper_routines.h"\r
+#include "adb_winusb_api.h"\r
+\r
+/** \brief Points to InstantiateWinUsbInterface exported from AdbWinUsbApi.dll.\r
+\r
+ This variable is initialized with the actual address in DllMain routine for\r
+ this DLL on DLL_PROCESS_ATTACH event.\r
+ @see PFN_INSTWINUSBINTERFACE for more information.\r
+*/\r
+PFN_INSTWINUSBINTERFACE InstantiateWinUsbInterface = NULL;\r
\r
ADBAPIHANDLE __cdecl AdbEnumInterfaces(GUID class_id,\r
bool exclude_not_present,\r
ADBAPIHANDLE ret = NULL;\r
\r
try {\r
- // Instantiate object\r
+ // Instantiate interface object, depending on the USB driver type.\r
if (IsLegacyInterface(interface_name)) {\r
+ // We have legacy USB driver underneath us.\r
obj = new AdbLegacyInterfaceObject(interface_name);\r
} else {\r
- obj = new AdbWinUsbInterfaceObject(interface_name);\r
+ // We have WinUsb driver underneath us. Make sure that AdbWinUsbApi.dll\r
+ // is loaded and its InstantiateWinUsbInterface routine address has\r
+ // been cached.\r
+ if (NULL != InstantiateWinUsbInterface) {\r
+ obj = InstantiateWinUsbInterface(interface_name);\r
+ if (NULL == obj) {\r
+ return NULL;\r
+ }\r
+ } else {\r
+ return NULL;\r
+ }\r
}\r
\r
// Create handle for it\r
// as being exported.\r
#ifdef ADBWIN_EXPORTS\r
#define ADBWIN_API EXTERN_C __declspec(dllexport)\r
+#define ADBWIN_API_CLASS __declspec(dllexport)\r
#else\r
#define ADBWIN_API EXTERN_C __declspec(dllimport)\r
+#define ADBWIN_API_CLASS __declspec(dllimport)\r
#endif\r
\r
/** \brief Handle to an API object.\r
This class implement functionality that is common for both, WinUsb and\r
legacy APIs.\r
*/\r
-class AdbEndpointObject : public AdbObjectHandle {\r
+class ADBWIN_API_CLASS AdbEndpointObject : public AdbObjectHandle {\r
public:\r
/** \brief Constructs the object\r
\r
\r
#include "adb_object_handle.h"\r
\r
+// 'AdbInterfaceObject::interface_name_' : class 'std::basic_string<_E,_Tr,_A>'\r
+// needs to have dll-interface to be used by clients of class\r
+// 'AdbInterfaceObject' We're ok with that, since interface_name_ will not\r
+// be referenced by name from outside of this class.\r
+#pragma warning(disable: 4251)\r
/** \brief Encapsulates an interface on our USB device.\r
\r
This is an abstract class that implements functionality common for both,\r
legacy, and WinUsb based interfaces.\r
*/\r
-class AdbInterfaceObject : public AdbObjectHandle {\r
+class ADBWIN_API_CLASS AdbInterfaceObject : public AdbObjectHandle {\r
public:\r
/** \brief Constructs the object.\r
\r
}\r
\r
protected:\r
- /// Name of the USB interface (device name) for this object\r
- std::wstring interface_name_;\r
-\r
/// Cached usb device descriptor\r
USB_DEVICE_DESCRIPTOR usb_device_descriptor_;\r
\r
\r
/// Cached usb interface descriptor\r
USB_INTERFACE_DESCRIPTOR usb_interface_descriptor_;\r
+\r
+ private:\r
+ /// Name of the USB interface (device name) for this object\r
+ std::wstring interface_name_;\r
};\r
+#pragma warning(default: 4251)\r
\r
#endif // ANDROID_USB_API_ADB_INTERFACE_H__\r
like all other handles this handle must be closed after it's no longer\r
needed.\r
*/\r
-class AdbIOCompletion : public AdbObjectHandle {\r
+class ADBWIN_API_CLASS AdbIOCompletion : public AdbObjectHandle {\r
public:\r
/** \brief Constructs the object\r
\r
of the API through a handle.\r
*/\r
\r
+#include "adb_api.h"\r
#include "adb_api_private_defines.h"\r
\r
/** \brief Defines types of internal API objects\r
All API objects that have handles that are sent back to API client must be\r
derived from this class.\r
*/\r
-class AdbObjectHandle {\r
+class ADBWIN_API_CLASS AdbObjectHandle {\r
public:\r
/** \brief Constructs the object\r
\r
--- /dev/null
+/*\r
+ * Copyright (C) 2006 The Android Open Source Project\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+\r
+#ifndef ANDROID_USB_API_ADBWINUSBAPI_H__\r
+#define ANDROID_USB_API_ADBWINUSBAPI_H__\r
+\r
+/** \file\r
+ Contains declarations required to link AdbWinApi and AdbWinUsbApi DLLs.\r
+*/\r
+\r
+/** \brief Function prototype for InstantiateWinUsbInterface routine exported\r
+ from AdbWinUsbApi.dll\r
+\r
+ In order to provide backward compatibility with the systems that still run\r
+ legacy (custom) USB drivers, and have not installed WINUSB.DLL we need to\r
+ split functionality of our ADB API on Windows between two DLLs: AdbWinApi,\r
+ and AdbWinUsbApi. AdbWinApi is fully capable of working on top of the legacy\r
+ driver, but has no traces to WinUsb. AdbWinUsbApi is capable of working on\r
+ top of WinUsb API. We are forced to do this split, because we can have\r
+ dependency on WINUSB.DLL in the DLL that implements legacy API. The problem\r
+ is that customers may have a legacy driver that they don't want to upgrade\r
+ to WinUsb, so they may not have WINUSB.DLL installed on their machines, but\r
+ they still must be able to use ADB. So, the idea behind the split is as\r
+ such. When AdbWinApi.dll is loaded into a process, it will check WINUSB.DLL\r
+ installation (by checking existance of C:\Windows\System32\winusb.dll). If\r
+ WINUSB.DLL is installed, AdbWinApi will also load AdbWinUsbApi.dll (by\r
+ calling LoadLibrary), and will extract address of InstantiateWinUsbInterface\r
+ routine exported from AdbWinUsbApi.dll. Then this routine will be used to\r
+ instantiate AdbInterfaceObject instance on condition that it is confirmed\r
+ that USB driver underneath us is in deed WinUsb.\r
+*/\r
+typedef class AdbInterfaceObject* \\r
+ (__cdecl *PFN_INSTWINUSBINTERFACE)(const wchar_t*);\r
+\r
+#endif // ANDROID_USB_API_ADBWINUSBAPI_H__\r
#include <string>\r
#pragma warning(default: 4201)\r
#pragma warning(disable: 4200)\r
-extern "C" {\r
#include <usbdi.h>\r
-#include <winusb.h>\r
#include <usb100.h>\r
-}\r
\r
#include "resource.h"\r
\r
--- /dev/null
+/*\r
+ * Copyright (C) 2009 The Android Open Source Project\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+\r
+// AdbWinUsbApi.cpp : Implementation of DLL Exports.\r
+\r
+#include "stdafx.h"\r
+#include "adb_winusb_interface.h"\r
+\r
+class CAdbWinApiModule : public CAtlDllModuleT< CAdbWinApiModule > {\r
+public:\r
+};\r
+\r
+CAdbWinApiModule _AtlModule;\r
+\r
+// DLL Entry Point\r
+extern "C" BOOL WINAPI DllMain(HINSTANCE instance,\r
+ DWORD reason,\r
+ LPVOID reserved) {\r
+ return _AtlModule.DllMain(reason, reserved);\r
+}\r
+\r
+/** \brief Instantiates interface instance that uses WinUsb API to communicate\r
+ with USB driver.\r
+\r
+ This is the only exported routine from this DLL. This routine instantiates an\r
+ object of AdbWinUsbInterfaceObject on request from AdbWinApi.dll when it is\r
+ detected that underlying USB driver is WinUsb.sys.\r
+ @param[in] interface_name Name of the interface.\r
+ @return AdbInterfaceObject - casted instance of AdbWinUsbInterfaceObject\r
+ object on success, or NULL on failure with GetLastError providing\r
+ information on an error that occurred.\r
+*/\r
+extern "C" __declspec(dllexport)\r
+AdbInterfaceObject* __cdecl InstantiateWinUsbInterface(\r
+ const wchar_t* interface_name) {\r
+ // Validate parameter.\r
+ if (NULL == interface_name) {\r
+ return NULL;\r
+ }\r
+\r
+ // Instantiate requested object.\r
+ try {\r
+ return new AdbWinUsbInterfaceObject(interface_name);\r
+ } catch (...) {\r
+ // We expect only OOM exceptions here.\r
+ SetLastError(ERROR_OUTOFMEMORY);\r
+ return NULL;\r
+ }\r
+}\r
--- /dev/null
+; AdbWinUsbApi.def : Declares the module parameters.\r
+\r
+LIBRARY "AdbWinUsbApi.DLL"\r
+\r
+EXPORTS\r
--- /dev/null
+/*\r
+ * Copyright (C) 2009 The Android Open Source Project\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+\r
+//Microsoft Visual C++ generated resource script.\r
+//\r
+#include "resource.h"\r
+\r
+#define APSTUDIO_READONLY_SYMBOLS\r
+/////////////////////////////////////////////////////////////////////////////\r
+//\r
+// Generated from the TEXTINCLUDE 2 resource.\r
+//\r
+#include "winres.h"\r
+\r
+/////////////////////////////////////////////////////////////////////////////\r
+#undef APSTUDIO_READONLY_SYMBOLS\r
+\r
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)\r
+LANGUAGE 9, 1\r
+#pragma code_page(1252)\r
+#ifdef APSTUDIO_INVOKED\r
+/////////////////////////////////////////////////////////////////////////////\r
+//\r
+// TEXTINCLUDE\r
+//\r
+\r
+1 TEXTINCLUDE\r
+BEGIN\r
+ "resource.h\0"\r
+END\r
+\r
+2 TEXTINCLUDE\r
+BEGIN\r
+ "#include ""winres.h""\r\n"\r
+ "\0"\r
+END\r
+\r
+#endif // APSTUDIO_INVOKED\r
+\r
+#ifndef _MAC\r
+/////////////////////////////////////////////////////////////////////////////\r
+//\r
+// Version\r
+//\r
+\r
+VS_VERSION_INFO VERSIONINFO\r
+ FILEVERSION 2,0,0,0\r
+ PRODUCTVERSION 2,0,0,0\r
+ FILEFLAGSMASK 0x3fL\r
+#ifdef _DEBUG\r
+ FILEFLAGS 0x1L\r
+#else\r
+ FILEFLAGS 0x0L\r
+#endif\r
+ FILEOS 0x4L\r
+ FILETYPE 0x2L\r
+ FILESUBTYPE 0x0L\r
+BEGIN\r
+ BLOCK "StringFileInfo"\r
+ BEGIN\r
+ BLOCK "040904e4"\r
+ BEGIN\r
+ VALUE "CompanyName", "Google, inc"\r
+ VALUE "FileDescription", "Android ADB API (WinUsb)"\r
+ VALUE "FileVersion", "2.0.0.0"\r
+ VALUE "LegalCopyright", "Copyright (C) 2006 The Android Open Source Project"\r
+ VALUE "InternalName", "AdbWinUsbApi.dll"\r
+ VALUE "OriginalFilename", "AdbWinUsbApi.dll"\r
+ VALUE "ProductName", "Android SDK"\r
+ VALUE "ProductVersion", "2.0.0.0"\r
+ VALUE "OLESelfRegister", ""\r
+ END\r
+ END\r
+ BLOCK "VarFileInfo"\r
+ BEGIN\r
+ VALUE "Translation", 0x0409, 1252\r
+ END\r
+END\r
+\r
+#endif // !_MAC\r
+\r
+/////////////////////////////////////////////////////////////////////////////\r
+//\r
+// String Table\r
+//\r
+\r
+STRINGTABLE\r
+BEGIN\r
+ IDS_PROJNAME "AdbWinUsbApi"\r
+END\r
+\r
+////////////////////////////////////////////////////////////////////////////\r
+\r
+\r
+#endif\r
+\r
+#ifndef APSTUDIO_INVOKED\r
+#endif // not APSTUDIO_INVOKED\r
--- /dev/null
+In order to build AdbWinUsbApi.dll you will need to install Windows Driver Kit,\r
+which can be obtained from Microsoft. Assuming that WDK is installed, you\r
+need to set one of the WDK's build environments, "cd" back into this directory,\r
+and execute "build -cbeEIFZ" to clean and rebuild this project, or you can\r
+execute "build -befEIF" to do a minimal build.\r
+Note that you need to build AdbWinApi.dll (..\api) before you build\r
+AdbWinUsbApi.dll, as it depends on AdbWinApi.lib library.\r
--- /dev/null
+#\r
+# Copyright (C) 2009 The Android Open Source Project\r
+# \r
+# Licensed under the Apache License, Version 2.0 (the "License");\r
+# you may not use this file except in compliance with the License.\r
+# You may obtain a copy of the License at\r
+# \r
+# http://www.apache.org/licenses/LICENSE-2.0\r
+# \r
+# Unless required by applicable law or agreed to in writing, software\r
+# distributed under the License is distributed on an "AS IS" BASIS,\r
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+# See the License for the specific language governing permissions and\r
+# limitations under the License.\r
+#\r
+\r
+#\r
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source\r
+# file to this component. This file merely indirects to the real make file\r
+# that is shared by all the components of NT OS/2\r
+#\r
+!INCLUDE $(NTMAKEENV)\makefile.def\r
--- /dev/null
+/*\r
+ * Copyright (C) 2009 The Android Open Source Project\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+\r
+//{{NO_DEPENDENCIES}}\r
+// Microsoft Visual C++ generated include file.\r
+// Used by AdbWinApi.rc\r
+//\r
+\r
+#define IDS_PROJNAME 100\r
+#define IDR_ADBWINAPI 101\r
+\r
+// Next default values for new objects\r
+//\r
+#ifdef APSTUDIO_INVOKED\r
+#ifndef APSTUDIO_READONLY_SYMBOLS\r
+#define _APS_NEXT_RESOURCE_VALUE 201\r
+#define _APS_NEXT_COMMAND_VALUE 32768\r
+#define _APS_NEXT_CONTROL_VALUE 201\r
+#define _APS_NEXT_SYMED_VALUE 102\r
+#endif\r
+#endif\r
--- /dev/null
+#\r
+# Copyright (C) 2009 The Android Open Source Project\r
+# \r
+# Licensed under the Apache License, Version 2.0 (the "License");\r
+# you may not use this file except in compliance with the License.\r
+# You may obtain a copy of the License at\r
+# \r
+# http://www.apache.org/licenses/LICENSE-2.0\r
+# \r
+# Unless required by applicable law or agreed to in writing, software\r
+# distributed under the License is distributed on an "AS IS" BASIS,\r
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+# See the License for the specific language governing permissions and\r
+# limitations under the License.\r
+#\r
+\r
+TARGETNAME = AdbWinUsbApi\r
+TARGETPATH = obj\r
+TARGETTYPE = DYNLINK\r
+\r
+UMTYPE = windows\r
+DLLDEF = AdbWinUsbApi.def\r
+\r
+# Use statically linked atl libraries:\r
+# - atls.lib for free build\r
+# - atlsd.lib for checked build\r
+USE_STATIC_ATL = 1\r
+# Use ATL v. 7.1\r
+ATL_VER = 71\r
+# Use STL v. 6.0\r
+USE_STL = 1\r
+STL_VER = 60\r
+# Use multithreaded libraries\r
+USE_LIBCMT = 1\r
+\r
+# Include directories\r
+INCLUDES = $(DDK_INC_PATH); \\r
+ $(SDK_INC_PATH); \\r
+ $(CRT_INC_PATH); \\r
+ $(SDK_INC_PATH)\crt; \\r
+ $(CRT_INC_PATH)\atl71; \\r
+ $(SDK_INC_PATH)\crt\stl60\r
+\r
+# Common target libraries\r
+TARGETLIBS = $(SDK_LIB_PATH)\ole32.lib \\r
+ $(SDK_LIB_PATH)\Advapi32.lib \\r
+ $(SDK_LIB_PATH)\Kernel32.lib \\r
+ $(SDK_LIB_PATH)\User32.lib \\r
+ $(SDK_LIB_PATH)\oleaut32.lib \\r
+ $(SDK_LIB_PATH)\wbemuuid.lib \\r
+ $(SDK_LIB_PATH)\uuid.lib \\r
+ $(SDK_LIB_PATH)\setupapi.lib \\r
+ $(SDK_LIB_PATH)\usbd.lib \\r
+ $(SDK_LIB_PATH)\winusb.lib \\r
+ ..\api\obj$(BUILD_ALT_DIR)\i386\AdbWinApi.lib\r
+\r
+!IF "$(DDKBUILDENV)" == "fre"
+# Libraries for release (free) builds
+TARGETLIBS = $(TARGETLIBS) $(ATL_LIB_PATH)\atls.lib\r
+!ELSE\r
+# Libraries for debug (checked) builds
+TARGETLIBS = $(TARGETLIBS) $(ATL_LIB_PATH)\atlsd.lib\r
+!ENDIF\r
+\r
+# Common C defines\r
+C_DEFINES= $(C_DEFINES) -DADBWINUSB_EXPORTS -D_UNICODE \\r
+ -DUNICODE -DWIN32 -D_WINDOWS -D_USRDLL -D_WINDLL\r
+\r
+!IF "$(DDKBUILDENV)" == "fre"
+# C defines for release (free) builds
+C_DEFINES = $(C_DEFINES) -DNDEBUG\r
+!ELSE\r
+# C defines for debug (checked) builds
+C_DEFINES = $(C_DEFINES) -D_DEBUG\r
+!ENDIF\r
+\r
+# Turn on all warnings, and treat warnings as errors\r
+MSC_WARNING_LEVEL = /W4 /Wp64 /WX
+
+# Common C defines\r
+USER_C_FLAGS = $(USER_C_FLAGS) /FD /EHsc /wd4100 /wd4200 /wd4702 /nologo
+
+# Set precompiled header information
+PRECOMPILED_CXX = 1\r
+PRECOMPILED_INCLUDE = stdafx.h\r
+PRECOMPILED_SOURCEFILE = stdafx.cpp\r
+\r
+# Define source files for AdbWinUsbApi.dll\r
+SOURCES = adb_winusb_endpoint_object.cpp \\r
+ adb_winusb_interface.cpp \\r
+ adb_winusb_io_completion.cpp \\r
+ AdbWinUsbApi.cpp \\r
+ AdbWinUsbApi.rc\r
#include "stdafx.h"\r
#include "adb_winusb_endpoint_object.h"\r
#include "adb_winusb_io_completion.h"\r
-#include "adb_helper_routines.h"\r
\r
AdbWinUsbEndpointObject::AdbWinUsbEndpointObject(\r
AdbWinUsbInterfaceObject* parent_interf,\r
AdbWinUsbEndpointObject::~AdbWinUsbEndpointObject() {\r
}\r
\r
+LONG AdbWinUsbEndpointObject::Release() {\r
+ ATLASSERT(ref_count_ > 0);\r
+ LONG ret = InterlockedDecrement(&ref_count_);\r
+ ATLASSERT(ret >= 0);\r
+ if (0 == ret) {\r
+ LastReferenceReleased();\r
+ delete this;\r
+ }\r
+ return ret;\r
+}\r
+\r
ADBAPIHANDLE AdbWinUsbEndpointObject::CommonAsyncReadWrite(\r
bool is_read,\r
void* buffer,\r
encapsulates a handle opened to a WinUsb endpoint on our device.\r
*/\r
\r
-#include "adb_endpoint_object.h"\r
+#include "..\api\adb_endpoint_object.h"\r
#include "adb_winusb_interface.h"\r
\r
/** Class AdbWinUsbEndpointObject encapsulates a handle opened to an endpoint on\r
virtual ~AdbWinUsbEndpointObject();\r
\r
//\r
+ // Virtual overrides\r
+ //\r
+\r
+ public:\r
+ /** \brief Releases the object.\r
+\r
+ If refcount drops to zero as the result of this release, the object is\r
+ destroyed in this method. As a general rule, objects must not be touched\r
+ after this method returns even if returned value is not zero. We override\r
+ this method in order to make sure that objects of this class are deleted\r
+ in contect of the DLL they were created in. The problem is that since\r
+ objects of this class were created in context of AdbWinUsbApi module, they\r
+ are allocated from the heap assigned to that module. Now, if these objects\r
+ are deleted outside of AdbWinUsbApi module, this will lead to the heap\r
+ corruption in the module that deleted these objects. Since all objects of\r
+ this class are deleted in the Release method only, by overriding it we make\r
+ sure that we free memory in the context of the module where it was\r
+ allocated.\r
+ @return Value of the reference counter after object is released in this\r
+ method.\r
+ */\r
+ virtual LONG Release();\r
+\r
+ //\r
// Abstract overrides\r
//\r
\r
ATLASSERT(INVALID_HANDLE_VALUE == usb_device_handle_);\r
}\r
\r
+LONG AdbWinUsbInterfaceObject::Release() {\r
+ ATLASSERT(ref_count_ > 0);\r
+ LONG ret = InterlockedDecrement(&ref_count_);\r
+ ATLASSERT(ret >= 0);\r
+ if (0 == ret) {\r
+ LastReferenceReleased();\r
+ delete this;\r
+ }\r
+ return ret;\r
+}\r
+\r
ADBAPIHANDLE AdbWinUsbInterfaceObject::CreateHandle() {\r
// Open USB device for this inteface Note that WinUsb API\r
// requires the handle to be opened for overlapped I/O.\r
via WinUsb API.\r
*/\r
\r
-#include "adb_interface.h"\r
+#include "..\api\adb_interface.h"\r
\r
/** \brief Encapsulates an interface on our USB device that is accessible\r
via WinUsb API.\r
//\r
\r
public:\r
+ /** \brief Releases the object.\r
+\r
+ If refcount drops to zero as the result of this release, the object is\r
+ destroyed in this method. As a general rule, objects must not be touched\r
+ after this method returns even if returned value is not zero. We override\r
+ this method in order to make sure that objects of this class are deleted\r
+ in contect of the DLL they were created in. The problem is that since\r
+ objects of this class were created in context of AdbWinUsbApi module, they\r
+ are allocated from the heap assigned to that module. Now, if these objects\r
+ are deleted outside of AdbWinUsbApi module, this will lead to the heap\r
+ corruption in the module that deleted these objects. Since all objects of\r
+ this class are deleted in the Release method only, by overriding it we make\r
+ sure that we free memory in the context of the module where it was\r
+ allocated.\r
+ @return Value of the reference counter after object is released in this\r
+ method.\r
+ */\r
+ virtual LONG Release();\r
+\r
/** \brief Creates handle to this object.\r
\r
In this call a handle for this object is generated and object is added\r
AdbWinUsbIOCompletion::~AdbWinUsbIOCompletion() {\r
}\r
\r
+LONG AdbWinUsbIOCompletion::Release() {\r
+ ATLASSERT(ref_count_ > 0);\r
+ LONG ret = InterlockedDecrement(&ref_count_);\r
+ ATLASSERT(ret >= 0);\r
+ if (0 == ret) {\r
+ LastReferenceReleased();\r
+ delete this;\r
+ }\r
+ return ret;\r
+}\r
+\r
bool AdbWinUsbIOCompletion::GetOvelappedIoResult(LPOVERLAPPED ovl_data,\r
ULONG* bytes_transferred,\r
bool wait) {\r
asynchronous I/O requests issued via WinUsb API.\r
*/\r
\r
-#include "adb_io_completion.h"\r
+#include "..\api\adb_io_completion.h"\r
#include "adb_winusb_endpoint_object.h"\r
\r
/** \brief Encapsulates encapsulates a wrapper around OVERLAPPED Win32\r
virtual ~AdbWinUsbIOCompletion();\r
\r
//\r
+ // Virtual overrides\r
+ //\r
+\r
+ public:\r
+ /** \brief Releases the object.\r
+\r
+ If refcount drops to zero as the result of this release, the object is\r
+ destroyed in this method. As a general rule, objects must not be touched\r
+ after this method returns even if returned value is not zero. We override\r
+ this method in order to make sure that objects of this class are deleted\r
+ in contect of the DLL they were created in. The problem is that since\r
+ objects of this class were created in context of AdbWinUsbApi module, they\r
+ are allocated from the heap assigned to that module. Now, if these objects\r
+ are deleted outside of AdbWinUsbApi module, this will lead to the heap\r
+ corruption in the module that deleted these objects. Since all objects of\r
+ this class are deleted in the Release method only, by overriding it we make\r
+ sure that we free memory in the context of the module where it was\r
+ allocated.\r
+ @return Value of the reference counter after object is released in this\r
+ method.\r
+ */\r
+ virtual LONG Release();\r
+\r
+ //\r
// Abstract overrides\r
//\r
\r
--- /dev/null
+/*\r
+ * Copyright (C) 2009 The Android Open Source Project\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+\r
+// stdafx.cpp : source file that includes just the standard includes\r
+// AdbWinUsbApi.pch will be the pre-compiled header\r
+// stdafx.obj will contain the pre-compiled type information\r
+\r
+#include "stdafx.h"\r
--- /dev/null
+/*\r
+ * Copyright (C) 2009 The Android Open Source Project\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+\r
+/** \file\r
+ Visual Studio generated include file for standard system include files, or\r
+ project specific include files that are used frequently, but are changed\r
+ infrequently.\r
+*/\r
+\r
+#pragma once\r
+\r
+#ifndef STRICT\r
+#define STRICT\r
+#endif\r
+\r
+// Modify the following defines if you have to target a platform prior to the ones specified below.\r
+// Refer to MSDN for the latest info on corresponding values for different platforms.\r
+#ifndef WINVER // Allow use of features specific to Windows 95 and Windows NT 4 or later.\r
+#define WINVER 0x0500 // Change this to the appropriate value to target Windows 98 and Windows 2000 or later.\r
+#endif\r
+\r
+#ifndef _WIN32_WINNT // Allow use of features specific to Windows NT 4 or later.\r
+#define _WIN32_WINNT 0x0500 // Change this to the appropriate value to target Windows 2000 or later.\r
+#endif\r
+\r
+#ifndef _WIN32_WINDOWS // Allow use of features specific to Windows 98 or later.\r
+#define _WIN32_WINDOWS 0x0500 // Change this to the appropriate value to target Windows Me or later.\r
+#endif\r
+\r
+#ifndef _WIN32_IE // Allow use of features specific to IE 4.0 or later.\r
+#define _WIN32_IE 0x0501 // Change this to the appropriate value to target IE 5.0 or later.\r
+#endif\r
+\r
+// These defines prevent the MS header files from ejecting #pragma comment\r
+// statements with the manifest information of the used ATL, STL, and CRT\r
+#define _ATL_NOFORCE_MANIFEST\r
+#define _STL_NOFORCE_MANIFEST\r
+#define _CRT_NOFORCE_MANIFEST\r
+\r
+#define _ATL_APARTMENT_THREADED\r
+#define _ATL_NO_AUTOMATIC_NAMESPACE\r
+\r
+#define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS // some CString constructors will be explicit\r
+\r
+// turns off ATL's hiding of some common and often safely ignored warning messages\r
+#define _ATL_ALL_WARNINGS\r
+\r
+// #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers\r
+\r
+#include <windows.h>\r
+#pragma warning(disable: 4702)\r
+#pragma warning(disable: 4201)\r
+#include <atlbase.h>\r
+#include <winioctl.h>\r
+#include <setupapi.h>\r
+#include <vector>\r
+#include <map>\r
+#include <string>\r
+#pragma warning(default: 4201)\r
+#pragma warning(disable: 4200)\r
+#include <winusb.h>\r
+\r
+#include "resource.h"\r
+\r
+using namespace ATL;\r
NDK_ROOT_DIR=`cd $NDK_ROOT_DIR && pwd`
# the release name
-RELEASE=1.5_r2
+RELEASE=1.6_r1
# the package prefix
PREFIX=android-ndk
Android NDK ChangeLog:
-------------------------------------------------------------------------------
-current version
+android-ndk-1.6_r1
IMPORTANT BUG FIXES:
--- /dev/null
+home=true
+@jd:body
+
+<p>
+ Some community information here
+</p>
page.title=Audio
pdk.version=1.0
+doc.type=guide
@jd:body
<div id="qv-wrapper">
page.title=Bluetooth
pdk.version=1.0
+doc.type=guide
@jd:body
<div id="qv-wrapper">
page.title=Bluetooth Process Diagram
pdk.version=1.0
+doc.type=guide
@jd:body
<p>The diagram below offers a process-oriented architectural overview of Android's Bluetooth stack. Click <a href="../bluetooth.html">Bluetooth</a> to return to the Bluetooth overview page.</p>
-<img src="images/androidBluetoothProcessDiagram.jpg">
\ No newline at end of file
+<img src="images/androidBluetoothProcessDiagram.jpg">
page.title=Bring Up
pdk.version=1.0
+doc.type=guide
@jd:body
<p>Once your code is built and you have verified that all necessary directories exist, power on and test your device with basic bring up, as described below. Bring up tests are typically designed to stress certain aspects of your system and allow you to characterize the device's behavior. </p>
disabled
user akmd
group akmd
-</pre>
\ No newline at end of file
+</pre>
page.title=Build Cookbook
pdk.version=1.0
+doc.type=guide
@jd:body
<div id="qv-wrapper">
<td valign="top"></td>
</tr>
-</table>
\ No newline at end of file
+</table>
page.title=Configuring a New Product
pdk.version=1.0
+doc.type=guide
@jd:body
page.title=Android Build System
pdk.version=1.0
+doc.type=guide
@jd:body
<code>make installclean</code> between the two makes to guarantee that
you don't pick up files installed by the previous flavor. <code>make
clean</code> will also suffice, but it takes a lot longer.
-</p>
\ No newline at end of file
+</p>
page.title=Camera
pdk.version=1.0
+doc.type=guide
@jd:body
<div id="qv-wrapper">
page.title=Customization
pdk.version=1.0
+doc.type=guide
@jd:body
<div id="qv-wrapper">
<a name="androidThemesAnimations"></a><h4>Animations</h4>
<p>Android supports configurable animations for window and view transitions. System-level animations are defined in XML in global resource files located in <code>//android/framework/base/core/res/res/anim/</code>.</p>
-
\ No newline at end of file
+
page.title=Dalvik
pdk.version=1.0
+doc.type=guide
@jd:body
<div id="qv-wrapper">
page.title=Debugging with GDB
pdk.version=1.0
+doc.type=guide
@jd:body
<div id="qv-wrapper">
</pre>
<p>Otherwise you need to determine the path of the crashing binary and follow the
steps as mentioned above (for example, <code>gdbclient hoser :5039</code> if
- the <code>hoser</code> command has failed).</p>
\ No newline at end of file
+ the <code>hoser</code> command has failed).</p>
page.title=Debugging Native Code
pdk.version=1.0
+doc.type=guide
@jd:body
page.title=Display Drivers
pdk.version=1.0
+doc.type=guide
@jd:body
page.title=Getting Source Code
+pdk.version=1.0
+doc.type=guide
@jd:body
<a name="toc"/>
page.title=GPS
pdk.version=1.0
+doc.type=guide
@jd:body
<div id="qv-wrapper">
page.title=Providing Heap Memory
+pdk.version=1.0
+doc.type=guide
@jd:body
<div class="navigation" id="top">
page.title=Networking Support
+pdk.version=1.0
+doc.type=guide
@jd:body
<div class="navigation" id="top">
page.title=Android Platform Developer's Guide
pdk.version=1.0
+doc.type=guide
@jd:body
page.title=Instrumentation Framework
+pdk.version=1.0
+doc.type=guide
@jd:body
<a name="toc"/>
page.title=Instrumentation Testing
pdk.version=1.0
+doc.type=guide
@jd:body
<div id="qv-wrapper">
page.title=Source Code Overview
+pdk.version=1.0
+doc.type=guide
@jd:body
<a name="toc"/>
page.title=Keymaps and Keyboard Input
pdk.version=1.0
+doc.type=guide
@jd:body
I/KeyInputQueue( 1548): Device added: id=0x10000, name=partnerxx_keypad, classes=1
I/KeyInputQueue( 1548): Keymap: partnerxx_keypad.kl
</pre>
-<p>The snippet above contains artificial line breaks to maintain a print-friendly document.</p>
\ No newline at end of file
+<p>The snippet above contains artificial line breaks to maintain a print-friendly document.</p>
page.title=Lights
pdk.version=1.0
+doc.type=guide
@jd:body
<div id="qv-wrapper">
page.title=Power Management
pdk.version=1.0
+doc.type=guide
@jd:body
<a name="toc"/>
page.title=Creating Release Keys and Signing Builds
pdk.version=1.0
+doc.type=guide
@jd:body
+
<div id="qv-wrapper">
<div id ="qv">
<h2>In this document</h2>
page.title=Sensors
pdk.version=1.0
+doc.type=guide
@jd:body
<div id="qv-wrapper">
page.title=Host System Setup
+pdk.version=1.0
+doc.type=guide
@jd:body
<a name="toc"/>
page.title=Sim Toolkit Application (STK)
pdk.version=1.0
+doc.type=guide
@jd:body
<div id="qv-wrapper">
</div>
-<p>This document offers a high-level overview of the SIM Toolkit Application for Android 1.0 and is primarily of interest for implementors of the Radio Interface Layer (RIL). The STK is R96 compatible (3GPP TS 11.14 v5.9.0) and complies partially with R99 (3GPP TS 101.267 v8.17.0). See the <a href="androidSTKFeatureList">STK Feature List</a> for the complete feature list. </p>
+<p>This document offers a high-level overview of the SIM Toolkit Application for Android 1.0 and is primarily of interest for implementors of the Radio Interface Layer (RIL). The STK is R96 compatible (3GPP TS 11.14 v5.9.0) and complies partially with R99 (3GPP TS 101.267 v8.17.0). See the <a href="#androidSTKFeatureList">STK Feature List</a> for the complete feature list. </p>.
<p>The Android STK implementation includes three layers:</p>
<ul>
<li> STK RIL: Low-level layer provided by the vendor plus <code>libril</code>.</li>
page.title=Device Requirements
pdk.version=1.0
+doc.type=guide
@jd:body
<p>While Android is designed to support a wide variety of hardware platforms and configurations, this section provides recommended minimum device requirements.</p>
<li>QWERTY keyboard</li>
<li>WiFi</li>
<li>GPS</li>
-</ul>
\ No newline at end of file
+</ul>
page.title=Debugging with tcpdump and other tools
pdk.version=1.0
+doc.type=guide
@jd:body
<h4>On the desktop:</h4>
<ul>
<li> <code>curl</code>: fetch URLs directly to emulate device requests</li>
-</ul>
\ No newline at end of file
+</ul>
page.title=Radio Layer Interface
pdk.version=1.0
+doc.type=guide
@jd:body
<div id="qv-wrapper">
page.title=Wi-Fi
pdk.version=1.0
+doc.type=guide
@jd:body
<div id="qv-wrapper">
--- /dev/null
+home=true
+@jd:body
+
+
+ <div id="mainBodyFixed" align="top">
+ <div id="mainBodyLeft">
+ <h2>Android Open Source Project</h2>
+ <!-- total max width is 520px -->
+ <p> Android is the first free, open source, and fully customizable mobile platform.
+ Android offers a full stack: an operating system, middleware and key mobile applications.
+ It also contains a rich set of APIs that allows third-party developers to develop great
+ applications. </p>
+ </div><!-- end mainBodyLeft -->
+
+ <div id="mainBodyRight">
+ <table id="rightColumn">
+ <tr>
+ <td class="imageCell"><a href="{@docRoot}sdk/index.html"><img src="{@docRoot}assets/images/icon_download.jpg" style="padding:0" /></a></td>
+ <td>
+ <h2 class="green">Download</h2>
+ <p>The Android SDK has the tools, sample code, and docs you need to create great apps. </p>
+ <p><a href="http://developer.android.com">Learn more »</a></p>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2"><div class="seperator"> </div></td>
+ </tr>
+ <tr>
+ <td class="imageCell"><a href="http://www.android.com/market.html"><img src="{@docRoot}assets/images/icon_market.jpg" style="padding:0" /></a></td>
+ <td>
+ <h2 class="green">Publish</h2>
+ <p>Android Market is an open service that lets you distribute your apps to handsets.</p>
+ <p><a href="http://www.android.com/market.html">Learn more »</a></p>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2"><div class="seperator"> </div></td>
+ </tr>
+ <tr>
+ <td class="imageCell"><a href="http://source.android.com"><img src="{@docRoot}assets/images/icon_contribute.jpg" style="padding:0" /></a></td>
+ <td>
+ <h2 class="green">Contribute</h2>
+ <p>Android Open Source Project gives you access to the entire platform source.</p>
+ <p><a href="http://source.android.com">Learn more »</a></p>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2"><div class="seperator"> </div></td>
+ </tr>
+ <tr>
+ <td class="imageCell"><a href="http://www.youtube.com/user/androiddevelopers"><img src="{@docRoot}assets/images/video-droid.png" style="padding:0" /></a></td>
+ <td>
+ <h2 class="green">Watch</h2>
+ <object width="150" height="140"><param name="movie" value="http://www.youtube.com/v/GARMe7Km_gk&hl=en&fs=1"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param><embed src="http://www.youtube.com/v/GARMe7Km_gk&hl=en&fs=1" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="150" height="140"></embed></object>
+ <p style="margin-top:1em"><a href="{@docRoot}videos/index.html">More Android videos »</a></p>
+ </td>
+ </tr>
+
+ </table>
+ </div>
+ </div>
+
+<!--[if lte IE 6]>
+ <style>
+ #arrow-left {
+ margin:0 0 0 5px;
+ }
+ #arrow-right {
+ margin-left:0;
+ }
+ .app-list-container {
+ margin: 37px 0 0 23px;
+ }
+ div#list-clip {
+ width:468px;
+ }
+ </style>
+<![endif]-->
+
+<script type="text/javascript">
+
+// * -- carousel dictionary -- * //
+ /* layout: imgLeft, imgRight, imgTop
+ icon: image for carousel entry. cropped (height:70px, width:90px)
+ name: string for carousel entry
+ img: image for bulletin post. cropped (height: 170, width:230px)
+ title: header for bulletin (optional, insert "" value to skip
+ desc: the bulletin post. must include html tags.
+ */
+
+ var droidList = {
+ 'sdk': {
+ 'layout':"imgLeft",
+ 'icon':"sdk-small.png",
+ 'name':"SDK 1.5 r3",
+ 'img':"sdk-large.png",
+ 'title':"Android 1.5 SDK",
+ 'desc': "<p>Android 1.5 SDK is now available. It includes new APIs for Android 1.5, updated developer tools, multiple platform versions, and a Google APIs add-on.</p><p><a href='{@docRoot}sdk/1.5_r3/index.html'>Download Android 1.5 SDK »</a></p>"
+ },
+
+ 'io': {
+ 'layout':"imgLeft",
+ 'icon':"io-small.png",
+ 'name':"Google I/O",
+ 'img':"io-large.png",
+ 'title':"Google I/O Developer Conference",
+ 'desc': "<p>The Google I/O developer conference took place May 27-28 in San Francisco. If you missed the conference, you can experience the Android sessions by viewing YouTube videos.</p><p><a href='{@docRoot}videos/index.html'>See the sessions from Google I/O »</a></p>"
+ },
+
+ 'mapskey': {
+ 'layout':"imgLeft",
+ 'icon':"maps-small.png",
+ 'name':"Maps API Key",
+ 'img':"maps-large.png",
+ 'title':"Maps API Key",
+ 'desc':"<p>If you're writing an Android application that uses Google Maps (with MapView), you must register your application to obtain a Maps API Key. Without the key, your maps application will not work on Android devices. Obtaining a key requires just a couple of steps.</p><p><a href='http://code.google.com/android/add-ons/google-apis/maps-overview.html'>Learn more »</a></p>"
+ },
+
+ 'devphone': {
+ 'layout':"imgLeft",
+ 'icon':"devphone-small.png",
+ 'name':"Dev Phone 1",
+ 'img':"devphone-large.png",
+ 'title':"Android Dev Phone 1",
+ 'desc': "<p>Run and debug your Android applications directly on this device. Modify and rebuild the Android operating system, and flash it onto the phone. The Android Dev Phone 1 is carrier independent, and available for purchase by any developer registered with <a href='http://market.android.com/publish'>Android Market</a>.</p><p><a href='/guide/developing/device.html#dev-phone-1'>Learn more about the Android Dev Phone 1 »</a></p>"
+ }
+
+ }
+</script>
+<script type="text/javascript" src="{@docRoot}assets/carousel.js"></script>
+<script type="text/javascript">
+ initCarousel("sdk");
+</script>
--- /dev/null
+home=true
+doc.type=licenses
+@jd:body
+
+<div id="mainBodyFixed">
+
+<p>
+ The Android Open Source Project uses a few open source initiative approved open source licenses to enable availability
+ of source code and to accept contributions from individuals and corporations.
+</p>
+
+<p>
+ <b>Android Open Source Project license</b>
+</p>
+
+<p>
+ The preferred license for the Android Open Source Project is Apache 2.0. Apache 2.0 is a commercial and open source
+ friendly open source license. The majority of the Android platform is licensed under the Apache 2.0 license. While
+ the project will strive to adhere to the preferred license, there may be exceptions which will be handled on a case-by-case
+ basis. For example, the Linux kernel patches are under the GPLv2 license with system exceptions, which can be found on kernel.org
+</p>
+
+<p>
+ <b>Contributor License Grants</b>
+</p>
+
+<p>
+ All individual contributors of ideas, code, or documentation to the Android Open Source Project will be required to
+ complete, sign, and submit an Individual Contributor License Grant. The grant can be executed online through the code
+ review tool. The agreement clearly defines the terms under which intellectual property has been contributed to the
+ Android Open Source Project. This license is for your protection as a contributor as well as the protection of the
+ project; it does not change your rights to use your own contributions for any other purpose.
+</p>
+
+<p>
+ For a corporation that has assigned employees to work on the Android Open Source Project, a Corporate Contributor License
+ Grant is available. This version of the Grant allows a corporation to authorize contributions submitted by its designated
+ employees and to grant copyright and patent licenses. Note that a Corporate Contributor License Grant does not remove the
+ need for any developer to sign their own Individual Contributor License Grant as an individual, to cover any of their
+ contributions which are not owned by the corporation signing the Corporate Contributor License Grant.
+</p>
+
+<p>
+ Please note that we based our grants on the ones that the Apache Software Foundation uses, which can be found on its site.
+</p>
+
+</div>
--- /dev/null
+<script type="text/javascript" language="JavaScript">
+<!--
+function nothing() {}
+-->
+</script>
+
+<ul>
+ <li> <h2> Referenced Licenses </h2>
+ <ul>
+ <li><a href="http://www.apache.org/licenses/LICENSE-2.0">Apache 2.0 license</a></li>
+ <li><a href="http://www.kernel.org/pub/linux/kernel/COPYING">GPL v2 license</a></li>
+ </ul>
+ </li>
+
+</ul>
+
+<script type="text/javascript">
+<!--
+ buildToggleLists();
+//-->
+</script>
--- /dev/null
+home=true
+doc.type=releases
+@jd:body
+
+<p>
+ Some release information here
+</p>
--- /dev/null
+<script type="text/javascript" language="JavaScript">
+<!--
+function nothing() {}
+-->
+</script>
+
+<ul>
+ <li> <h2> Android Releases </h2>
+ <ul>
+ <li><a href="xxx">Android 1.0</a></li>
+ <li><a href="xxx">Android 1.1</a></li>
+ <li><a href="xxx">Android 1.5</a></li>
+ </ul>
+ </li>
+
+</ul>
+
+<script type="text/javascript">
+<!--
+ buildToggleLists();
+//-->
+</script>
</intent-filter>
</activity-alias>
+ <activity android:name=".graphics.DensityActivity" android:label="Graphics/Density">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.SAMPLE_CODE" />
+ </intent-filter>
+ </activity>
+
<!-- ************************************* -->
<!-- MEDIA SAMPLES -->
<!-- ************************************* -->
-<p>The API Demos include sample code for many aspects of the Android APIs, from screen layout to Intent resolution.
+<p>The API Demos application includes a variety of small applications
+that illustrate the use of various Android APIs. It includes samples of:
</p>
+<ul>
+ <li>Notifications</li>
+ <li>Alarms</li>
+ <li>Progress Dialogs</li>
+ <li>Intents</li>
+ <li>Menus</li>
+ <li>Search</li>
+ <li>Persistent application state</li>
+ <li>Preferences</li>
+ <li>Background Services</li>
+ <li>App Widgets</li>
+ <li>Voice Recognition</li>
+ <li>And many many more...</li>
+</ul>
-<dl>
- <dt><a href="src/com/example/android/apis/app/index.html">App</a></dt>
- <dd></dd>
+<p>You'll notice that all the samples are included in a single Android project,
+so the application code and other resource files for all samples are batched together.
+To help you find the code that's relevant to you, here's a directory that
+points to the program code for the different topics included in the project:</p>
- <dt><a href="src/com/example/android/apis/content/index.html">Content</a></dt>
- <dd></dd>
+<ul>
+ <li><a href="src/com/example/android/apis/animation/index.html">Animation</a></li>
+
+ <li><a href="src/com/example/android/apis/app/index.html">App</a></li>
+
+ <li><a href="src/com/example/android/apis/appwidget/index.html">App Widgets</a></li>
+
+ <li><a href="src/com/example/android/apis/content/index.html">Content</a></li>
- <dt><a href="src/com/example/android/apis/view/index.html">View</a></dt>
- <dd></dd>
+ <li><a href="src/com/example/android/apis/graphics/index.html">Graphics</a></li>
- <dt><a href="src/com/example/android/apis/graphics/index.html">Graphics</a></dt>
- <dd></dd>
+ <li><a href="src/com/example/android/apis/media/index.html">Media</a></li>
+
+ <li><a href="src/com/example/android/apis/os/index.html">OS</a></li>
- <dt><a href="src/com/example/android/apis/text/index.html">Text</a></dt>
- <dd></dd>
-</dl>
+ <li><a href="src/com/example/android/apis/text/index.html">Text</a></li>
+
+ <li><a href="src/com/example/android/apis/view/index.html">Views</a></li>
+</ul>
# project structure.
# Project target.
-target=android-3
+target=android-4
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="horizontal"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+
+ <ImageView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/reslogo120dpi" />
+
+ <ImageView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/reslogo160dpi" />
+
+ <ImageView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/reslogo240dpi" />
+
+</LinearLayout>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="horizontal"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+
+ <ImageView style="@style/ImageView120dpi" />
+ <ImageView style="@style/ImageView160dpi" />
+ <ImageView style="@style/ImageView240dpi" />
+
+</LinearLayout>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2007 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.
+-->
+
+<resources>
+ <string name="density_title">Density: Large Long</string>
+</resources>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2007 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.
+-->
+
+<resources>
+ <string name="density_title">Density: Large NotLong</string>
+</resources>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2007 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.
+-->
+
+<resources>
+ <string name="density_title">Density: Large</string>
+</resources>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2007 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.
+-->
+
+<resources>
+ <string name="density_title">Density: Long</string>
+</resources>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2007 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.
+-->
+
+<resources>
+ <string name="density_title">Density: Normal Long</string>
+</resources>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2007 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.
+-->
+
+<resources>
+ <string name="density_title">Density: Normal NotLong</string>
+</resources>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2007 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.
+-->
+
+<resources>
+ <string name="density_title">Density: Normal</string>
+</resources>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2007 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.
+-->
+
+<resources>
+ <string name="density_title">Density: NotLong</string>
+</resources>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2007 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.
+-->
+
+<resources>
+ <string name="density_title">Density: Small Long</string>
+</resources>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2007 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.
+-->
+
+<resources>
+ <string name="density_title">Density: Small NotLong</string>
+</resources>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2007 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.
+-->
+
+<resources>
+ <string name="density_title">Density: Small</string>
+</resources>
<string name="hide_me">Hide Me!</string>
+ <string name="density_title">Density: Unknown Screen</string>
+
<!-- ============================ -->
<!-- media examples strings -->
<!-- ============================ -->
<item name="android:textStyle">normal</item>
</style>
+ <style name="ImageView120dpi">
+ <item name="android:src">@drawable/stylogo120dpi</item>
+ <item name="android:layout_width">wrap_content</item>
+ <item name="android:layout_height">wrap_content</item>
+ </style>
+
+ <style name="ImageView160dpi">
+ <item name="android:src">@drawable/stylogo160dpi</item>
+ <item name="android:layout_width">wrap_content</item>
+ <item name="android:layout_height">wrap_content</item>
+ </style>
+
+ <style name="ImageView240dpi">
+ <item name="android:src">@drawable/stylogo240dpi</item>
+ <item name="android:layout_width">wrap_content</item>
+ <item name="android:layout_height">wrap_content</item>
+ </style>
</resources>
package com.example.android.apis;
-import com.example.android.apis.app.DefaultValues;
-
import android.app.Application;
import android.preference.PreferenceManager;
*/
public class ApiDemosApplication extends Application {
+ @Override
public void onCreate() {
/*
* This populates the default values from the preferences XML file. See
PreferenceManager.setDefaultValues(this, R.xml.default_values, false);
}
+ @Override
public void onTerminate() {
}
}
import android.app.NotificationManager;
import android.os.Bundle;
-import java.util.Map;
-
/**
* This activity is run as the click activity for {@link IncomingMessage}.
* When it comes up, it also clears the notification, because the "message"
* has been "read."
*/
public class IncomingMessageView extends Activity {
+ @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.incoming_message_view);
import android.appwidget.AppWidgetProvider;
import android.content.ComponentName;
import android.content.Context;
-import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.SystemClock;
import android.util.Log;
import android.widget.RemoteViews;
-import java.util.ArrayList;
-
// Need the following import to get access to the app resources, since this
// class is in a sub-package.
import com.example.android.apis.R;
// log tag
private static final String TAG = "ExampleAppWidgetProvider";
+ @Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
Log.d(TAG, "onUpdate");
// For each widget that needs an update, get the text that we should display:
}
}
+ @Override
public void onDeleted(Context context, int[] appWidgetIds) {
Log.d(TAG, "onDeleted");
// When the user deletes the widget, delete the preference associated with it.
}
}
+ @Override
public void onEnabled(Context context) {
Log.d(TAG, "onEnabled");
// When the first widget is created, register for the TIMEZONE_CHANGED and TIME_CHANGED
PackageManager.DONT_KILL_APP);
}
+ @Override
public void onDisabled(Context context) {
// When the first widget is created, stop listening for the TIMEZONE_CHANGED and
// TIME_CHANGED broadcasts.
Log.d(TAG, "onDisabled");
- Class clazz = ExampleBroadcastReceiver.class;
PackageManager pm = context.getPackageManager();
pm.setComponentEnabledSetting(
new ComponentName("com.example.android.apis", ".appwidget.ExampleBroadcastReceiver"),
package com.example.android.apis.appwidget;
import android.appwidget.AppWidgetManager;
-import android.appwidget.AppWidgetProvider;
import android.content.BroadcastReceiver;
-import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
-import android.os.SystemClock;
import android.util.Log;
-import android.widget.RemoteViews;
import java.util.ArrayList;
-// Need the following import to get access to the app resources, since this
-// class is in a sub-package.
-import com.example.android.apis.R;
-
/**
* A BroadcastReceiver that listens for updates for the ExampleAppWidgetProvider. This
* BroadcastReceiver starts off disabled, and we only enable it when there is a widget
*/
public class ExampleBroadcastReceiver extends BroadcastReceiver {
+ @Override
public void onReceive(Context context, Intent intent) {
Log.d("ExmampleBroadcastReceiver", "intent=" + intent);
if (action.equals(Intent.ACTION_TIMEZONE_CHANGED)
|| action.equals(Intent.ACTION_TIME_CHANGED)) {
AppWidgetManager gm = AppWidgetManager.getInstance(context);
- ArrayList<Integer> appWidgetIds = new ArrayList();
- ArrayList<String> texts = new ArrayList();
+ ArrayList<Integer> appWidgetIds = new ArrayList<Integer>();
+ ArrayList<String> texts = new ArrayList<String>();
ExampleAppWidgetConfigure.loadAllTitlePrefs(context, appWidgetIds, texts);
// Because the CameraDevice object is not a shared resource, it's very
// important to release it when the activity is paused.
mCamera.stopPreview();
+ mCamera.release();
mCamera = null;
}
package com.example.android.apis.graphics;
-import android.R;
import android.os.Bundle;
import android.app.Dialog;
import android.content.Context;
mInitialColor = initialColor;
}
-
+ @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
OnColorChangedListener l = new OnColorChangedListener() {
--- /dev/null
+/*
+ * 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.example.android.apis.graphics;
+
+//Need the following import to get access to the app resources, since this
+//class is in a sub-package.
+import com.example.android.apis.R;
+
+import android.app.Activity;
+import android.app.Application;
+import android.os.Bundle;
+import android.graphics.BitmapFactory;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+import android.widget.ScrollView;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.util.DisplayMetrics;
+import android.util.Log;
+
+/**
+ * This activity demonstrates various ways density can cause the scaling of
+ * bitmaps and drawables.
+ */
+public class DensityActivity extends Activity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ final LayoutInflater li = (LayoutInflater)getSystemService(
+ LAYOUT_INFLATER_SERVICE);
+
+ this.setTitle(R.string.density_title);
+ LinearLayout root = new LinearLayout(this);
+ root.setOrientation(LinearLayout.VERTICAL);
+
+ LinearLayout layout = new LinearLayout(this);
+ addBitmapDrawable(layout, R.drawable.logo120dpi, true);
+ addBitmapDrawable(layout, R.drawable.logo160dpi, true);
+ addBitmapDrawable(layout, R.drawable.logo240dpi, true);
+ addLabelToRoot(root, "Prescaled bitmap in drawable");
+ addChildToRoot(root, layout);
+
+ layout = new LinearLayout(this);
+ addBitmapDrawable(layout, R.drawable.logo120dpi, false);
+ addBitmapDrawable(layout, R.drawable.logo160dpi, false);
+ addBitmapDrawable(layout, R.drawable.logo240dpi, false);
+ addLabelToRoot(root, "Autoscaled bitmap in drawable");
+ addChildToRoot(root, layout);
+
+ layout = new LinearLayout(this);
+ addResourceDrawable(layout, R.drawable.logo120dpi);
+ addResourceDrawable(layout, R.drawable.logo160dpi);
+ addResourceDrawable(layout, R.drawable.logo240dpi);
+ addLabelToRoot(root, "Prescaled resource drawable");
+ addChildToRoot(root, layout);
+
+ layout = (LinearLayout)li.inflate(R.layout.density_image_views, null);
+ addLabelToRoot(root, "Inflated layout");
+ addChildToRoot(root, layout);
+
+ layout = (LinearLayout)li.inflate(R.layout.density_styled_image_views, null);
+ addLabelToRoot(root, "Inflated styled layout");
+ addChildToRoot(root, layout);
+
+ layout = new LinearLayout(this);
+ addCanvasBitmap(layout, R.drawable.logo120dpi, true);
+ addCanvasBitmap(layout, R.drawable.logo160dpi, true);
+ addCanvasBitmap(layout, R.drawable.logo240dpi, true);
+ addLabelToRoot(root, "Prescaled bitmap");
+ addChildToRoot(root, layout);
+
+ layout = new LinearLayout(this);
+ addCanvasBitmap(layout, R.drawable.logo120dpi, false);
+ addCanvasBitmap(layout, R.drawable.logo160dpi, false);
+ addCanvasBitmap(layout, R.drawable.logo240dpi, false);
+ addLabelToRoot(root, "Autoscaled bitmap");
+ addChildToRoot(root, layout);
+
+ layout = new LinearLayout(this);
+ addResourceDrawable(layout, R.drawable.logonodpi120);
+ addResourceDrawable(layout, R.drawable.logonodpi160);
+ addResourceDrawable(layout, R.drawable.logonodpi240);
+ addLabelToRoot(root, "No-dpi resource drawable");
+ addChildToRoot(root, layout);
+
+ layout = new LinearLayout(this);
+ addNinePatchResourceDrawable(layout, R.drawable.smlnpatch120dpi);
+ addNinePatchResourceDrawable(layout, R.drawable.smlnpatch160dpi);
+ addNinePatchResourceDrawable(layout, R.drawable.smlnpatch240dpi);
+ addLabelToRoot(root, "Prescaled 9-patch resource drawable");
+ addChildToRoot(root, layout);
+
+ setContentView(scrollWrap(root));
+ }
+
+ private View scrollWrap(View view) {
+ ScrollView scroller = new ScrollView(this);
+ scroller.addView(view, new ScrollView.LayoutParams(ScrollView.LayoutParams.FILL_PARENT,
+ ScrollView.LayoutParams.FILL_PARENT));
+ return scroller;
+ }
+
+ private void addLabelToRoot(LinearLayout root, String text) {
+ TextView label = new TextView(this);
+ label.setText(text);
+ root.addView(label, new LinearLayout.LayoutParams(LinearLayout.LayoutParams.FILL_PARENT,
+ LinearLayout.LayoutParams.WRAP_CONTENT));
+ }
+
+ private void addChildToRoot(LinearLayout root, LinearLayout layout) {
+ root.addView(layout, new LinearLayout.LayoutParams(LinearLayout.LayoutParams.FILL_PARENT,
+ LinearLayout.LayoutParams.WRAP_CONTENT));
+ }
+
+ private void addBitmapDrawable(LinearLayout layout, int resource, boolean scale) {
+ Bitmap bitmap;
+ bitmap = loadAndPrintDpi(resource, scale);
+
+ View view = new View(this);
+
+ final BitmapDrawable d = new BitmapDrawable(getResources(), bitmap);
+ if (!scale) d.setTargetDensity(getResources().getDisplayMetrics());
+ view.setBackgroundDrawable(d);
+
+ view.setLayoutParams(new LinearLayout.LayoutParams(d.getIntrinsicWidth(),
+ d.getIntrinsicHeight()));
+ layout.addView(view);
+ }
+
+ private void addResourceDrawable(LinearLayout layout, int resource) {
+ View view = new View(this);
+
+ final Drawable d = getResources().getDrawable(resource);
+ view.setBackgroundDrawable(d);
+
+ view.setLayoutParams(new LinearLayout.LayoutParams(d.getIntrinsicWidth(),
+ d.getIntrinsicHeight()));
+ layout.addView(view);
+ }
+
+ private void addCanvasBitmap(LinearLayout layout, int resource, boolean scale) {
+ Bitmap bitmap;
+ bitmap = loadAndPrintDpi(resource, scale);
+
+ ScaledBitmapView view = new ScaledBitmapView(this, bitmap);
+
+ view.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,
+ LinearLayout.LayoutParams.WRAP_CONTENT));
+ layout.addView(view);
+ }
+
+ private void addNinePatchResourceDrawable(LinearLayout layout, int resource) {
+ View view = new View(this);
+
+ final Drawable d = getResources().getDrawable(resource);
+ view.setBackgroundDrawable(d);
+
+ Log.i("foo", "9-patch #" + Integer.toHexString(resource)
+ + " w=" + d.getIntrinsicWidth() + " h=" + d.getIntrinsicHeight());
+ view.setLayoutParams(new LinearLayout.LayoutParams(
+ d.getIntrinsicWidth()*2, d.getIntrinsicHeight()*2));
+ layout.addView(view);
+ }
+
+ private Bitmap loadAndPrintDpi(int id, boolean scale) {
+ Bitmap bitmap;
+ if (scale) {
+ bitmap = BitmapFactory.decodeResource(getResources(), id);
+ } else {
+ BitmapFactory.Options opts = new BitmapFactory.Options();
+ opts.inScaled = false;
+ bitmap = BitmapFactory.decodeResource(getResources(), id, opts);
+ }
+ return bitmap;
+ }
+
+ private class ScaledBitmapView extends View {
+ private Bitmap mBitmap;
+
+ public ScaledBitmapView(Context context, Bitmap bitmap) {
+ super(context);
+ mBitmap = bitmap;
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ final DisplayMetrics metrics = getResources().getDisplayMetrics();
+ setMeasuredDimension(
+ mBitmap.getScaledWidth(metrics),
+ mBitmap.getScaledHeight(metrics));
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+
+ canvas.drawBitmap(mBitmap, 0.0f, 0.0f, null);
+ }
+ }
+}
public class GLColor {
- public final int red;
- public final int green;
- public final int blue;
- public final int alpha;
-
- public GLColor(int red, int green, int blue, int alpha) {
- this.red = red;
- this.green = green;
- this.blue = blue;
- this.alpha = alpha;
- }
+ public final int red;
+ public final int green;
+ public final int blue;
+ public final int alpha;
- public GLColor(int red, int green, int blue) {
- this.red = red;
- this.green = green;
- this.blue = blue;
- this.alpha = 0x10000;
- }
-
- public boolean equals(Object other) {
- if (other instanceof GLColor) {
- GLColor color = (GLColor)other;
- return (red == color.red && green == color.green &&
- blue == color.blue && alpha == color.alpha);
- }
- return false;
- }
+ public GLColor(int red, int green, int blue, int alpha) {
+ this.red = red;
+ this.green = green;
+ this.blue = blue;
+ this.alpha = alpha;
+ }
+
+ public GLColor(int red, int green, int blue) {
+ this.red = red;
+ this.green = green;
+ this.blue = blue;
+ this.alpha = 0x10000;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other instanceof GLColor) {
+ GLColor color = (GLColor)other;
+ return (red == color.red &&
+ green == color.green &&
+ blue == color.blue &&
+ alpha == color.alpha);
+ }
+ return false;
+ }
}
import java.nio.IntBuffer;
public class GLVertex {
-
- public float x;
- public float y;
- public float z;
- final short index; // index in vertex table
- GLColor color;
-
- GLVertex() {
- this.x = 0;
- this.y = 0;
- this.z = 0;
- this.index = -1;
- }
- GLVertex(float x, float y, float z, int index) {
- this.x = x;
- this.y = y;
- this.z = z;
- this.index = (short)index;
- }
+ public float x;
+ public float y;
+ public float z;
+ final short index; // index in vertex table
+ GLColor color;
- public boolean equals(Object other) {
- if (other instanceof GLVertex) {
- GLVertex v = (GLVertex)other;
- return (x == v.x && y == v.y && z == v.z);
- }
- return false;
- }
+ GLVertex() {
+ this.x = 0;
+ this.y = 0;
+ this.z = 0;
+ this.index = -1;
+ }
+
+ GLVertex(float x, float y, float z, int index) {
+ this.x = x;
+ this.y = y;
+ this.z = z;
+ this.index = (short)index;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other instanceof GLVertex) {
+ GLVertex v = (GLVertex)other;
+ return (x == v.x && y == v.y && z == v.z);
+ }
+ return false;
+ }
static public int toFixed(float x) {
- return (int)(x*65536.0f);
+ return (int)(x * 65536.0f);
}
- public void put(IntBuffer vertexBuffer, IntBuffer colorBuffer) {
- vertexBuffer.put(toFixed(x));
- vertexBuffer.put(toFixed(y));
- vertexBuffer.put(toFixed(z));
- if (color == null) {
- colorBuffer.put(0);
- colorBuffer.put(0);
- colorBuffer.put(0);
- colorBuffer.put(0);
- } else {
- colorBuffer.put(color.red);
- colorBuffer.put(color.green);
- colorBuffer.put(color.blue);
- colorBuffer.put(color.alpha);
- }
- }
-
- public void update(IntBuffer vertexBuffer, M4 transform) {
- // skip to location of vertex in mVertex buffer
- vertexBuffer.position(index * 3);
-
- if (transform == null) {
- vertexBuffer.put(toFixed(x));
- vertexBuffer.put(toFixed(y));
- vertexBuffer.put(toFixed(z));
- } else {
- GLVertex temp = new GLVertex();
- transform.multiply(this, temp);
- vertexBuffer.put(toFixed(temp.x));
- vertexBuffer.put(toFixed(temp.y));
- vertexBuffer.put(toFixed(temp.z));
- }
- }
+ public void put(IntBuffer vertexBuffer, IntBuffer colorBuffer) {
+ vertexBuffer.put(toFixed(x));
+ vertexBuffer.put(toFixed(y));
+ vertexBuffer.put(toFixed(z));
+ if (color == null) {
+ colorBuffer.put(0);
+ colorBuffer.put(0);
+ colorBuffer.put(0);
+ colorBuffer.put(0);
+ } else {
+ colorBuffer.put(color.red);
+ colorBuffer.put(color.green);
+ colorBuffer.put(color.blue);
+ colorBuffer.put(color.alpha);
+ }
+ }
+
+ public void update(IntBuffer vertexBuffer, M4 transform) {
+ // skip to location of vertex in mVertex buffer
+ vertexBuffer.position(index * 3);
+
+ if (transform == null) {
+ vertexBuffer.put(toFixed(x));
+ vertexBuffer.put(toFixed(y));
+ vertexBuffer.put(toFixed(z));
+ } else {
+ GLVertex temp = new GLVertex();
+ transform.multiply(this, temp);
+ vertexBuffer.put(toFixed(temp.x));
+ vertexBuffer.put(toFixed(temp.y));
+ vertexBuffer.put(toFixed(temp.z));
+ }
+ }
}
import android.opengl.GLSurfaceView;
-import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
+/*
+ * 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.example.android.apis.media;
import com.example.android.apis.R;
+/*
+ * 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.example.android.apis.media;
import android.app.Activity;
private TextView tx;
+ @Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
tx = new TextView(this);
+/*
+ * 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.example.android.apis.media;
import com.example.android.apis.R;
-import com.example.android.apis.app.AlarmController;
import android.app.Activity;
-import android.graphics.PixelFormat;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnBufferingUpdateListener;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.widget.Button;
import android.widget.Toast;
*
* Called when the activity is first created.
*/
+ @Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.mediaplayer_2);
+/*
+ * 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.example.android.apis.media;
import com.example.android.apis.R;
import android.app.Activity;
-import android.graphics.PixelFormat;
-import android.net.Uri;
import android.os.Bundle;
-import android.util.Log;
import android.widget.MediaController;
import android.widget.Toast;
import android.widget.VideoView;
import android.app.Activity;
import android.os.Bundle;
-import android.widget.LinearLayout;
-import android.widget.TextView;
/**
* Baseline alignment includes elements within nested vertical
*/
public class BaselineNested1 extends Activity {
-
+ @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
import android.app.Activity;
import android.os.Bundle;
-import android.widget.LinearLayout;
-import android.widget.TextView;
/**
* Baseline alignment includes an element within a nested horizontal
*/
public class BaselineNested2 extends Activity {
-
+ @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
import android.app.Activity;
import android.os.Bundle;
-import android.widget.LinearLayout;
-import android.widget.TextView;
/**
* Baseline alignment includes a {@link android.widget.LinearLayout}
*/
public class BaselineNested3 extends Activity {
-
+ @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
public class Focus2 extends Activity {
+ @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
private Button mTopButton;
private Button mBottomButton;
+ @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
/* (non-Javadoc)
* @see android.view.KeyEvent.Callback#onKeyDown(int, android.view.KeyEvent)
*/
+ @Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
switch(event.getKeyCode()) {
case KeyEvent.KEYCODE_DPAD_UP:
package com.example.android.apis.view;
import android.app.ListActivity;
-import android.content.Context;
import android.os.Bundle;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.app.ListActivity;
import android.os.Bundle;
import android.widget.ArrayAdapter;
-import android.widget.ListView;
-import android.view.View;
public class LayoutAnimation3 extends ListActivity {
@Override
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
-import android.widget.Gallery;
import android.widget.GridView;
import android.widget.ImageView;
import com.example.android.apis.R;
import android.app.Activity;
-import android.content.Intent;
-import android.content.pm.PackageManager;
import android.os.Bundle;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.animation.AlphaAnimation;
-import android.view.animation.Animation;
-import android.view.animation.GridLayoutAnimationController;
-import android.widget.BaseAdapter;
-import android.widget.Gallery;
-import android.widget.GridView;
-import android.widget.ImageView;
-
-import java.util.List;
public class LayoutAnimation7 extends Activity {
@Override
return mStrings.length;
}
+ @Override
public boolean areAllItemsEnabled() {
return false;
}
+ @Override
public boolean isEnabled(int position) {
return !mStrings[position].startsWith("-");
}
--- /dev/null
+<p>This is a sample Home application. The Home application that users use
+to launch applications by default is an ordinary application itself.
+A user can install additional Home applications and pick which one to use as the
+default Home. This is a sample of such an alternative.</p>
+
+<p>This is actually the source of an old incarnation of the default Home application,
+which may look familiar if you played with some of the older pre-1.0 SDKs.</p>
+
+<img alt="" src="../images/HomeSample.png" />
\ No newline at end of file
--- /dev/null
+<p>JetBoy is a sample game that demonstrates the use of the
+<a href="../../../reference/android/media/JetPlayer.html">android.media.JetPlayer</a>
+class to implement an interactive music soundtrack in an application. JetBoy uses
+<a href="../../../guide/topics/media/index.html#jet">JET content created with
+JetCreator</a> and game-generated events fed to JetPlayer
+to adapt the soundtrack to the user actions. Listen to how the melody picks
+up when you start shooting asteroids, how you are congratulated when you
+hit several asteroids in a row, or destroy enough of them in the allotted time.</p>
+
+<p>The JetBoyView.java file in JetBoy illustrates the loading of JET content
+(loading a file, queuing segments), its playback, and how to alter what
+is currently playing (use of clips and mute masks).</p>
+
+<p class="note"><strong>Note:</strong>
+The <code>JETBOY_content/</code> directory is empty in this online presentation of
+the application. For complete access to all the JetBoy files, see the sample code included
+in the SDK, located at <code><sdk>/platforms/<platform>/samples/JetBoy/</code>.
+</p>
+
+<p><strong>See also:</strong><br/>
+<a href="../../../guide/topics/media/jet/jetcreator_manual.html">SONiVOX JETCreator User Manual</a><br/>
+<a href="../../../reference/android/media/JetPlayer.html">JetPlayer class</a></p>
+
+<img alt="" src="../images/JetBoy.png" />
<p>A sample game. Your objective: to land on the moon.
-It demonstrates...
+It demonstrates:
<ul>
-<li>loading and drawing resources
-<li>taking keystrokes
-<li>animating by calling invalidate() from draw()
-<li>handling onPause() in an animation
-<li>and many other goodies...
+<li>Loading and drawing resources</li>
+<li>Taking keystrokes</li>
+<li>Animating by calling invalidate() from draw()</li>
+<li>Handling onPause() in an animation</li>
+<li>And more...</li>
</ul>
</p>
-<img height="220px" width="320px" alt="Lunar Lander Example" class="gallery" src="sample_lunarlander.png" >
\ No newline at end of file
+<img alt="" src="../images/sample_lunarlander.png" >
\ No newline at end of file
<p>A simple note pad application.
-It demonstrates...
+It demonstrates:</p>
<ul>
-<li>using views
-<li>accessing a database
-<li>using an intent to open a new window
-<li>managing activity lifecycle
-<li>and many other goodies...
+<li>Using views</li>
+<li>Accessing a database</li>
+<li>Using an intent to open a new window</li>
+<li>Managing activity lifecycle</li>
+<li>And more...</li>
</ul>
-</p>
-<img alt="Note Pad Example" class="gallery" src="sample_notepad.png" >
-<img alt="Note Pad Example" class="gallery" src="sample_note.png" >
+
+<p class="note">Please notice that this is not the same
+notepad code that's used for the <a href="../../../guide/tutorials/notepad/index.html">Notepad Tutorial</a>.
+They are similar in nature, but there are several differences in implementation — the tutorial
+is slightly more simple. If you're new to
+Android development, we suggest you start with the tutorial, then visit this
+code later to see more sample code.</p>
+
+<img alt="" src="../images/sample_notepad.png" />
+<img alt="" src="../images/sample_note.png" />
--- /dev/null
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := samples
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_SDK_VERSION := current
+
+LOCAL_PACKAGE_NAME := SearchableDictionary
+
+include $(BUILD_PACKAGE)
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.example.android.searchabledict">
+
+ <uses-sdk android:minSdkVersion="4" />
+
+ <application android:label="@string/app_name"
+ android:icon="@drawable/ic_dictionary">
+
+ <!-- The default activity of the app. Can also display search results. -->
+ <activity android:name=".SearchableDictionary"
+ android:label="@string/app_name"
+ android:theme="@android:style/Theme.NoTitleBar">
+
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+
+ <!-- Receives the search request. -->
+ <intent-filter>
+ <action android:name="android.intent.action.SEARCH" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+
+ <!-- Points to searchable meta data. -->
+ <meta-data android:name="android.app.searchable"
+ android:resource="@xml/searchable"/>
+ </activity>
+
+ <!-- Displays the definition of a word. -->
+ <activity android:name=".WordActivity"
+ android:theme="@android:style/Theme.NoTitleBar"/>
+
+ <!-- Provides search suggestions for words and their definitions. -->
+ <provider android:name="DictionaryProvider"
+ android:authorities="dictionary"
+ android:syncable="false" />
+
+ </application>
+</manifest>
--- /dev/null
+<p>A sample application that demonstrates Android's search framework.</p>
+
+<p>This application includes a dictionary of words. By invoking a search inside the app
+(via the device search button or Menu > Search), a local search will be performed
+across the dictionary. As you type, suggestions will appear, which you can select
+to view the full definition. You can also execute the search to view all word definitions
+that match the entered text.</p>
+
+<p>The application also grants content provider privileges to
+Quick Search Box, Android's system-wide search tool. This means that the
+dictionary definitions can be offered as search suggestions outside of the application,
+when text is entered into Quick Search Box.</p>
+
+<p>See also:</p>
+<ul>
+ <li><a href="../../../reference/android/app/SearchManager.html">SearchManager</a></li>
+ <li><a href="../../../reference/android/content/ContentProvider.html">ContentProvider</a></li>
+</ul>
+
+<img src="../images/SearchableDictionary1.png" />
+<img src="../images/SearchableDictionary2.png" />
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent">
+ <TextView
+ android:id="@+id/textField"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/search_instructions"/>
+
+ <ListView
+ android:id="@+id/list"
+ android:layout_width="fill_parent"
+ android:layout_height="0dip"
+ android:layout_weight="1"/>
+</LinearLayout>
+
+
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent">
+ <TextView
+ android:id="@+id/word"
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ android:textSize="30sp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/search_instructions"
+ android:layout_marginLeft="10dip"
+ android:layout_marginTop="5dip" />
+ <TextView
+ android:id="@+id/definition"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/search_instructions"
+ android:layout_marginLeft="10dip" />
+</LinearLayout>
--- /dev/null
+abbey - n. a monastery ruled by an abbot
+abide - v. dwell; inhabit or live in
+abound - v. be abundant or plentiful; exist in large quantities
+absence - n. the state of being absent
+absorb - v. assimilate or take in
+abstinence - n. practice of refraining from indulging an appetite especially alcohol
+absurd - j. inconsistent with reason or logic or common sense
+abundant - j. present in great quantity
+abusive - j. characterized by physical or psychological maltreatment
+academic - j. associated with school or learning
+academy - n. a school for special training
+accept - v. consider or hold as true
+access - v. reach or gain access to
+accessible - j. easily obtained
+acclaim - n. enthusiastic approval
+accommodate - v. provide with something desired or needed
+accompany - v. go or travel along with
+accomplish - v. to gain with effort
+account - v. furnish a justifying analysis or explanation
+accurate - j. conforming exactly or almost exactly to fact or to a standard or performing with total accuracy
+accusation - n. an assertion that someone is guilty of a fault or offence
+accuse - v. blame for, make a claim of wrongdoing or misbehavior against
+acid - j. biting, sarcastic, or scornful
+acknowledge - v. declare to be true or admit the existence or reality or truth of
+acquire - v. come into the possession of something concrete or abstract
+acquisition - n. something acquired or received
+adamant - j. impervious to pleas, persuasion, requests, reason
+adjacent - j. having a common boundary or edge; abutting; touching
+administrator - n. someone who manages a government agency or department
+advent - n. arrival that has been awaited (especially of something momentous)
+adverse - j. contrary to your interests or welfare
+advisory - n. an announcement that usually advises or warns the public of some threat
+advocacy - n. active support of an idea or cause etc.; especially the act of pleading or arguing for something
+advocate - v. speak, plead, or argue in favor of
+affect - n. the conscious subjective aspect of feeling or emotion
+affirmative - j. affirming or giving assent
+aggression - n. violent action that is hostile and usually unprovoked
+airy - j. not practical or realizable; speculative
+album - n. a book of blank pages with pockets or envelopes; for organizing photographs or stamp collections etc
+alcohol - n. a liquor or brew containing alcohol as the active agent
+alien - j. being or from or characteristic of another place or part of the world
+allegiance - n. the loyalty that citizens owe to their country (or subjects to their sovereign)
+alley - n. a narrow street with walls on both sides
+alliance - n. a formal agreement establishing an association or alliance between nations or other groups to achieve a particular aim
+ally - n. an associate who provides cooperation or assistance
+altar - n. a raised structure on which gifts or sacrifices to a god are made
+alter - v. cause to change; make different; cause a transformation
+alternate - j. serving or used in place of another
+alternative - j. serving or used in place of another
+altitude - n. elevation especially above sea level or above the earth's surface
+amateur - j. lacking professional skill or expertise
+ambiguous - j. having more than one possible meaning
+ambitious - j. having a strong desire for success or achievement
+ambivalent - j. uncertain or unable to decide about what course to follow
+analogy - n. drawing a comparison in order to show a similarity in some respect
+analyst - n. someone who is skilled at analyzing data
+analyze - v. break down into components or essential features
+anchor - v. fix firmly and stably
+annual - j. occurring or payable every year
+anonymous - j. having no known name or identity or known source
+antichrist - n. the adversary of Christ (or Christianity) mentioned in the New Testament
+antique - j. made in or typical of earlier times and valued for its age
+anxious - j. causing or fraught with or showing anxiety
+apartheid - n. a social policy or racial segregation involving political and economic and legal discrimination against people who are not Whites
+apocalyptic - j. prophetic of devastation or ultimate doom
+apology - n. an expression of regret at having caused trouble for someone
+apparel - n. clothing in general
+apparent - j. clearly revealed to the mind or the senses or judgment
+appease - v. make peace with
+appropriate - j. suitable for a particular person or place or condition etc
+apt - j. being of striking appropriateness and pertinence
+arbitrary - j. based on or subject to individual discretion or preference or sometimes impulse or caprice
+arcade - n. a covered passageway with shops and stalls on either side
+arrange - v. put into a proper or systematic order
+arrangement - n. an orderly grouping (of things or persons) considered as a unit; the result of arranging
+arrival - n. the act of arriving at a certain place
+arrogance - n. overbearing pride evidenced by a superior manner toward inferiors
+arrogant - j. having or showing feelings of unwarranted importance out of overbearing pride
+articulate - j. expressing yourself easily or characterized by clear expressive language
+assassination - n. murder of a public figure by surprise attack
+assess - v. set or determine the amount of (a payment such as a fine)
+assets - n. anything of material value or usefulness that is owned by a person or company
+asylum - n. a shelter from danger or hardship
+auburn - j. (of hair) colored a moderate reddish-brown
+august - j. profoundly honored
+aura - n. a distinctive but intangible quality surrounding a person or thing
+austere - j. of a stern or strict bearing or demeanor; forbidding in aspect
+authentic - j. not counterfeit or copied
+authenticity - n. undisputed credibility
+authoritarian - n. a person who behaves in a tyrannical manner
+autobiography - n. a biography of yourself
+autonomous - j. existing as an independent entity
+autonomy - n. immunity from arbitrary exercise of authority: political independence
+avid - j. marked by active interest and enthusiasm
+banal - j. repeated too often; over familiar through overuse
+barring - n. the act of excluding someone by a negative vote or veto
+bass - n. the lowest adult male singing voice
+batter - n. a liquid or semiliquid mixture, as of flour, eggs, and milk, used in cooking
+belle - n. a young woman who is the most charming and beautiful of several rivals
+beneficial - j. promoting or enhancing well-being
+benefit - n. something that aids or promotes well-being
+benign - j. not dangerous to health; not recurrent or progressive (especially of a tumor)
+bid - n. a formal proposal to buy at a specified price
+biography - n. an account of the series of events making up a person's life
+biology - n. the science that studies living organisms
+blaze - n. a strong flame that burns brightly
+bleak - j. unpleasantly cold and damp
+bogus - j. fraudulent; having a misleading appearance
+bolster - v. support and strengthen
+bomb - n. an explosive device fused to explode under specific conditions
+bore - n. a person who evokes boredom
+botanical - j. of or relating to plants or botany
+boycott - n. a group's refusal to have commercial dealings with some organization in protest against its policies
+brass - n. an alloy of copper and zinc
+breach - n. an opening (especially a gap in a dike or fortification)
+broadcast - n. message that is transmitted by radio or television
+brokerage - n. the business of a broker; charges a fee to arrange a contract between two parties
+buffet - n. a meal set out on a buffet at which guests help themselves
+bumper - n. a mechanical device consisting of bars at either end of a vehicle to absorb shock and prevent serious damage
+bureau - n. an administrative unit of government
+bureaucracy - n. non elected government officials
+butt - v. to strike, thrust or shove against
+cabinet - n. persons appointed by a head of state to head executive departments of government and act as official advisers
+caliber - n. a degree or grade of excellence or worth
+campaign - n. a series of actions advancing a principle or tending toward a particular end
+canon - n. a rule or especially body of rules or principles generally established as valid and fundamental in a field or art or philosophy
+cardinal - j. serving as an essential component
+caricature - n. a representation of a person that is exaggerated for comic effect
+casual - j. without or seeming to be without plan or method; offhand
+catastrophe - n. an event resulting in great loss and misfortune
+caucus - n. a closed political meeting
+causal - j. involving or constituting a cause; causing
+censure - n. harsh criticism or disapproval
+census - n. a periodic count of the population
+cereal - n. grass whose starchy grains are used as food: wheat; rice; rye; oats; maize; buckwheat; millet
+ceremonial - j. marked by pomp or ceremony or formality
+chaos - n. a state of extreme confusion and disorder
+characteristic - n. a distinguishing quality
+chronic - j. being long-lasting and recurrent or characterized by long suffering
+citadel - n. a stronghold into which people could go for shelter during a battle
+cite - v. refer to for illustration or proof
+clumsy - j. not elegant or graceful in expression
+coalition - n. an organization of people (or countries) involved in a pact or treaty
+coherent - j. marked by an orderly, logical, and aesthetically consistent relation of parts
+coincidence - n. the temporal property of two things happening at the same time
+collapse - v. break down, literally or metaphorically
+colleague - n. an associate that one works with
+collective - j. set up on the principle of collectivism or ownership and production by the workers involved usually under the supervision of a government
+collector - n. a person who collects things
+collision - n. an accident resulting from violent impact of a moving object
+commemorate - v. mark by some ceremony or observation
+commentary - n. a written explanation or criticism or illustration that is added to a book or other textual material
+commission - n. a special group delegated to consider some matter
+commitment - n. the act of binding yourself (intellectually or emotionally) to a course of action
+commodity - n. articles of commerce
+commute - n. a regular journey of some distance to and from your place of work
+comparable - j. able to be compared or worthy of comparison
+comparison - n. the act of examining resemblances
+compassionate - j. showing or having compassion
+compensate - v. make payment to; compensate
+competence - n. the quality of being adequately or well qualified physically and intellectually
+competent - j. properly or sufficiently qualified or capable or efficient
+competitive - j. involving competition or competitiveness
+competitor - n. the contestant you hope to defeat
+complex - j. complicated in structure; consisting of interconnected parts
+component - n. an artifact that is one of the individual parts of which a composite entity is made up; especially a part that can be separated from or attached to a system
+composer - n. someone who composes music as a profession
+comprehensive - j. broad in scope
+concede - v. admit (to a wrongdoing)
+conceive - v. have the idea for
+concession - n. a point conceded or yielded
+confederate - j. united in a group or league
+confidence - n. a state of confident hopefulness that events will be favorable
+confident - j. having or marked by confidence or assurance
+confront - v. oppose, as in hostility or a competition
+conscience - n. a feeling of shame when you do something immoral
+conscious - j. knowing and perceiving; having awareness of surroundings and sensations and thoughts
+consecutive - j. one after the other
+consensus - n. agreement in the judgment or opinion reached by a group as a whole
+conservatism - n. a political or theological orientation advocating the preservation of the best in society and opposing radical changes
+conservative - j. avoiding excess
+consistency - n. a harmonious uniformity or agreement among things or parts
+conspicuous - j. obvious to the eye or mind
+conspiracy - n. a plot to carry out some harmful or illegal act (especially a political plot)
+constituency - n. the body of voters who elect a representative for their area
+consume - v. use up (resources or materials)
+consumer - n. a person who uses goods or services
+consumption - n. the process of taking food into the body through the mouth
+contemplate - v. look at thoughtfully; observe deep in thought
+contemporary - j. belonging to the present time
+contender - n. the contestant you hope to defeat
+contentious - j. inclined or showing an inclination to dispute or disagree, even to engage in law suits
+contingent - j. possible but not certain to occur
+continuous - j. continuing in time or space without interruption
+contradiction - n. opposition between two conflicting forces or ideas
+contradictory - j. unable for both to be true at the same time
+contribution - n. a voluntary gift (as of money or service or ideas) made to some worthwhile cause
+contributor - n. someone who contributes (or promises to contribute) a sum of money
+convenience - n. the quality of being useful and convenient
+conversion - n. the act of changing from one use or function or purpose to another
+convertible - j. designed to be changed from one use or form to another
+conviction - n. an unshakable belief in something without need for proof or evidence
+corporate - j. of or belonging to a corporation
+corps - n. a body of people associated together
+corruption - n. destroying someone's (or some group's) honesty or loyalty; undermining moral integrity
+cosmetic - j. serving an esthetic rather than a useful purpose
+cosmopolitan - j. of worldwide scope or applicability
+counsel - n. something that provides direction or advice as to a decision or course of action
+counterpart - n. a person or thing having the same function or characteristics as another
+courageous - j. able to face and deal with danger or fear without flinching
+course - n. a connected series of events or actions or developments
+courtesy - n. a courteous or respectful or considerate act
+credible - j. appearing to merit belief or acceptance
+critique - n. a serious examination and judgment of something
+crusade - v. exert oneself continuously, vigorously, or obtrusively to gain an end or engage in a crusade for a certain cause or person; be an advocate for
+crush - v. to compress with violence, out of natural shape or condition
+curator - n. the custodian of a collection (as a museum or library)
+curriculum - n. an integrated course of academic studies
+cynical - j. believing the worst of human nature and motives; having a sneering disbelief in e.g. selflessness of others
+cynicism - n. a cynical feeling of distrust
+daring - n. the trait of being willing to undertake things that involve risk or danger
+debacle - n. a sudden and violent collapse
+debut - v. appear for the first time in public
+decay - n. the organic phenomenon of rotting
+decency - n. the quality of conforming to standards of propriety and morality
+decent - j. socially or conventionally correct; refined or virtuous
+decisive - j. characterized by decision and firmness
+decree - n. a legally binding command or decision entered on the court record (as if issued by a court or judge)
+dedication - n. complete and wholehearted fidelity
+default - n. loss due to not showing up
+defendant - n. a person or institution against whom an action is brought in a court of law; the person being sued or accused
+defensive - j. attempting to justify or defend in speech or writing
+defiance - n. a hostile challenge
+definite - j. known for certain
+delicacy - n. something considered choice to eat
+demise - v. transfer by a lease or by a will
+demonstrate - v. establish the validity of something, as by an example, explanation or experiment
+denominator - n. the divisor of a fraction
+deposition - n. (law) a pretrial interrogation of a witness; usually conducted in a lawyer's office
+depression - n. a long-term economic state characterized by unemployment and low prices and low levels of trade and investment
+depth - n. the attribute or quality of being deep, strong, or intense
+derive - v. come from
+descent - n. properties attributable to your ancestry
+desert - n. arid land with little or no vegetation
+designate - v. give an assignment to (a person) to a post, or assign a task to (a person)
+despair - n. a state in which all hope is lost or absent
+desperate - j. showing extreme urgency or intensity especially because of great need or desire
+detect - v. discover or determine the existence, presence, or fact of
+deter - v. try to prevent; show opposition to
+determination - n. the quality of being determined to do or achieve something; firmness of purpose
+deterrent - j. tending to deter
+diagnosis - n. identifying the nature or cause of some phenomenon
+dialogue - n. a discussion intended to produce an agreement
+difference - n. the quality of being unlike or dissimilar
+dignity - n. the quality of being worthy of esteem or respect
+dilemma - n. state of uncertainty or perplexity especially as requiring a choice between equally unfavorable options
+diplomacy - n. subtly skillful handling of a situation
+diplomat - n. a person who deals tactfully with others
+diplomatic - j. using or marked by tact in dealing with sensitive matters or people
+disagree - v. be of different opinions
+disappear - v. become invisible or unnoticeable
+discern - v. detect with the senses
+discipline - n. a system of rules of conduct or method of practice
+discomfort - n. the state of being tense and feeling pain
+discourse - n. extended verbal expression in speech or writing
+discover - v. see for the first time; make a discovery
+discriminate - v. treat differently on the basis of sex or race
+discussion - n. an exchange of views on some topic
+disdain - n. lack of respect accompanied by a feeling of intense dislike
+dishonest - j. deceptive or fraudulent; disposed to cheat or defraud or deceive
+dismal - j. causing dejection
+dismay - v. fill with apprehension or alarm; cause to be unpleasantly surprised
+dismissal - n. the termination of someone's employment (leaving them free to depart)
+disparity - n. inequality or difference in some respect
+disregard - n. willful lack of care and attention
+dissent - n. a difference of opinion
+distant - j. separated in space or coming from or going to a distance
+distinction - n. a distinguishing difference
+distress - n. extreme physical pain
+distrust - v. regard as untrustworthy; regard with suspicion; have no faith or confidence in
+diverse - j. many and different
+diversion - n. an activity that diverts or amuses or stimulates
+diversity - n. noticeable heterogeneity
+divert - v. turn aside; turn away from
+document - n. writing that provides information (especially information of an official nature)
+doe - n. mature female of mammals of which the male is called 'buck'
+domain - n. territory over which rule or control is exercised
+dominance - n. the state that exists when one person or group has power over another
+dominant - j. exercising influence or control
+dominate - v. be in control
+domination - n. social control by dominating
+donate - v. give to a charity or good cause
+donor - n. person who makes a gift of property
+drastic - j. forceful and extreme and rigorous
+drought - n. a shortage of rainfall
+dubious - j. open to doubt or suspicion
+dynamics - n. the branch of mechanics concerned with the forces that cause motions of bodies
+earnest - j. characterized by a firm and humorless belief in the validity of your opinions
+eccentric - n. a person with an unusual or odd personality
+eclectic - j. selecting what seems best of various styles or ideas
+editorial - n. an article giving opinions or perspectives
+effect - n. a phenomenon that follows and is caused by some previous phenomenon
+effective - j. works well as a means or remedy
+efficiency - n. skillfulness in avoiding wasted time and effort
+efficient - j. able to accomplish a purpose; functioning effectively
+elaborate - v. add details, as to an account or idea; clarify the meaning of and discourse in a learned way, usually in writing
+element - n. any of the more than 100 known substances (of which 92 occur naturally) that cannot be separated into simpler substances and that singly or in combination constitute all matter
+eligible - j. qualified for or allowed or worthy of being chosen
+eliminate - v. terminate, end, or take out
+embargo - n. a government order imposing a trade barrier
+emblem - n. a visible symbol representing an abstract idea
+embrace - n. the act of clasping another person in the arms (as in greeting or affection)
+emerge - v. come out into view, as from concealment
+emergence - n. the gradual beginning or coming forth
+eminent - j. of imposing height; especially standing out above others
+emphasis - n. intensity or forcefulness of expression
+emphasize - v. to stress, single out as important
+employee - n. a worker who is hired to perform a job
+employer - n. a person or firm that employs workers
+emulate - v. imitate the function of (another system), as by modifying the hardware or the software
+enact - v. order by virtue of superior authority; decree
+encourage - v. inspire with confidence; give hope or courage to
+encyclopedia - n. a reference work (often in several volumes) containing articles on various topics
+endorse - v. be behind; approve of
+enduring - j. patiently bearing continual wrongs or trouble
+energetic - j. possessing or exerting or displaying energy
+enhance - v. make better or more attractive
+enormous - j. extraordinarily large in size or extent or amount or power or degree
+enthusiastic - j. having or showing great excitement and interest
+entity - n. that which is perceived or known or inferred to have its own distinct existence (living or nonliving)
+epic - j. very imposing or impressive; surpassing the ordinary (especially in size or scale)
+epidemic - n. a widespread outbreak of an infectious disease; many people are infected at the same time
+episode - n. a brief section of a literary or dramatic work that forms part of a connected series
+equilibrium - n. a stable situation in which forces cancel one another
+equity - n. the difference between the market value of a property and the claims held against it
+equivalent - j. being essentially equal to something
+error - n. a wrong action attributable to bad judgment or ignorance or inattention
+esquire - n. a title of respect for a member of the English gentry ranking just below a knight; placed after the name
+essence - n. the central meaning or theme of a speech or literary work
+evangelical - j. relating to or being a Christian church believing in personal conversion and the inerrancy of the Bible especially the 4 Gospels
+evoke - v. call to mind
+evolution - n. a process in which something passes by degrees to a different stage (especially a more advanced or mature stage)
+exceed - v. be greater in scope or size than some standard
+excellent - j. very good; of the highest quality
+excerpt - n. a passage selected from a larger work
+excess - j. more than is needed, desired, or required
+exclude - v. prevent from being included or considered or accepted
+excursion - n. a journey taken for pleasure
+exempt - v. grant relief or an exemption from a rule or requirement to
+existence - n. everything that exists anywhere
+exit - v. move out of or depart from
+exotic - j. strikingly strange or unusual
+expand - v. become larger in size or volume or quantity
+expansion - n. the act of increasing (something) in size or volume or quantity or scope
+expect - v. regard something as probable or likely
+expectancy - n. something expected (as on the basis of a norm)
+expense - n. money spent to perform work and usually reimbursed by an employer
+expertise - n. skillfulness by virtue of possessing special knowledge
+explicit - j. precisely and clearly expressed or readily observable; leaving nothing to implication
+explode - v. drive from the stage by noisy disapproval
+exploit - v. use or manipulate to one's advantage
+explosion - n. the act of exploding or bursting
+explosive - n. a chemical substance that undergoes a rapid chemical change (with the production of gas) on being heated or struck
+exposure - n. vulnerability to the elements; to the action of heat or cold or wind or rain
+expressive - j. characterized by expression
+extension - n. an addition to the length of something
+extensive - j. large in spatial extent or range or scope or quantity
+exterior - n. the outer side or surface of something
+external - j. happening or arising or located outside or beyond some limits or especially surface
+extradition - n. the surrender of an accused or convicted person by one state or country to another (usually under the provisions of a statute or treaty)
+extraordinary - j. beyond what is ordinary or usual; highly unusual or exceptional or remarkable
+extravagant - j. unrestrained, especially with regard to feelings
+exuberant - j. joyously unrestrained
+fabulous - j. extremely pleasing
+facial - j. of or concerning the face
+facility - n. something designed and created to serve a particular function and to afford a particular convenience or service
+faction - n. a dissenting clique
+faulty - j. characterized by errors; not agreeing with a model or not following established rules
+feasible - j. capable of being done with means at hand and circumstances as they are
+felony - n. a serious crime (such as murder or arson)
+feminine - j. associated with women and not with men
+fervor - n. the state of being emotionally aroused and worked up
+fetus - n. an unborn or unhatched vertebrate in the later stages of development showing the main recognizable features of the mature animal
+feud - n. a bitter quarrel between two parties
+feudal - j. of or relating to or characteristic of feudalism
+fidelity - n. the quality of being faithful
+finale - n. the concluding part of any performance
+finite - j. bounded or limited in magnitude or spatial or temporal extent
+fiscal - j. involving financial matters
+flag - n. emblem usually consisting of a rectangular piece of cloth of distinctive design
+flamboyant - j. marked by ostentation but often tasteless
+fleet - n. a group of warships organized as a tactical unit
+flexible - j. able to flex; able to bend easily
+flop - n. a complete failure
+flourish - n. a showy gesture
+foil - n. anything that serves by contrast to call attention to another thing's good qualities
+ford - v. cross a river where it's shallow
+forecast - n. a prediction about how something (as the weather) will develop
+foreign - j. relating to or originating in or characteristic of another place or part of the world
+foresee - v. act in advance of; deal with ahead of time
+formation - n. an arrangement of people or things acting as a unit
+formidable - j. extremely impressive in strength or excellence
+formula - n. directions for making something
+forte - n. an asset of special worth or utility
+forth - a. forward in time or order or degree
+foster - v. promote the growth of
+fragile - j. easily broken or damaged or destroyed
+frantic - j. excessively agitated; distraught with fear or other violent emotion
+fray - v. wear away by rubbing
+frequency - n. the number of occurrences within a given time period
+fringe - n. a social group holding marginal or extreme views
+frivolous - j. not serious in content or attitude or behavior
+frontier - n. a wilderness at the edge of a settled area of a country
+fundamental - j. being or involving basic facts or principles
+further - v. promote the growth of
+futile - j. producing no result or effect
+galaxy - n. (astronomy) a collection of star systems; any of the billions of systems each having many stars and nebulae and dust
+gamble - v. take a risk in the hope of a favorable outcome
+gauge - n. a measuring instrument for measuring and indicating a quantity such as the thickness of wire or the amount of rain etc.
+generate - v. bring into existence
+generic - j. applicable to an entire class or group
+generosity - n. the trait of being willing to give your money or time
+genesis - n. the first book of the Old Testament: tells of Creation; Adam and Eve; Cain and Abel
+gesture - n. motion of hands or body to emphasize or help to express a thought or feeling
+gigantic - j. so exceedingly large or extensive as to suggest a giant or mammoth
+gist - n. the choicest or most essential or most vital part of some idea or experience
+glimpse - n. a brief or incomplete view
+glorious - j. having great beauty and splendor
+grandeur - n. the quality of being magnificent or splendid or grand
+grandiose - j. impressive because of unnecessary largeness or grandeur; used to show disapproval
+grave - j. of great gravity or crucial import; requiring serious thought
+gravity - n. a manner that is serious and solemn
+grief - n. something that causes great unhappiness
+grotesque - j. distorted and unnatural in shape or size; abnormal and hideous
+grove - n. a small growth of trees without underbrush
+guise - n. an artful or simulated semblance
+hack - n. a mediocre and disdained writer
+hale - j. exhibiting or restored to vigorous good health
+handwriting - n. something written by hand
+harbor - v. hold back a thought or feeling about
+hazard - n. a source of danger; a possibility of incurring loss or misfortune
+heir - n. a person who is entitled by law or by the terms of a will to inherit the estate of another
+heritage - n. practices that are handed down from the past by tradition
+hilarious - j. marked by or causing boisterous merriment or convulsive laughter
+hollow - j. not solid; having a space or gap or cavity
+homage - n. respectful deference
+hostility - n. violent action that is hostile and usually unprovoked
+humane - j. marked or motivated by concern with the alleviation of suffering
+humanitarian - n. someone devoted to the promotion of human welfare and to social reforms
+hush - v. become quiet or still; fall silent
+hybrid - n. (genetics) an organism that is the offspring of genetically dissimilar parents or stock; especially offspring produced by breeding plants or animals of different varieties or breeds or species
+hypocrisy - n. insincerity by virtue of pretending to have qualities or beliefs that you do not really have
+hypothesis - n. a tentative insight into the natural world; a concept that is not yet verified but that if true would explain certain facts or phenomena
+hysteria - n. excessive or uncontrollable fear
+icon - n. a conventional religious painting in oil on a small wooden panel; venerated in the Eastern Church
+ideology - n. an orientation that characterizes the thinking of a group or nation
+illusion - n. an erroneous mental representation
+imaginary - j. not based on fact; unreal
+imitation - n. something copied or derived from an original
+immense - j. unusually great in size or amount or degree or especially extent or scope
+immigrant - n. a person who comes to a country where they were not born in order to settle there
+imminent - j. close in time; about to occur
+immoral - j. not adhering to ethical or moral principles
+immune - j. (usually followed by 'to') not affected by a given influence
+impending - j. close in time; about to occur
+implausible - j. having a quality that provokes disbelief
+implicit - j. implied though not directly expressed; inherent in the nature of something
+imply - v. express or state indirectly
+impose - v. compel to behave in a certain way
+improper - j. not suitable or right or appropriate
+impulse - n. a sudden desire
+inadequate - j. not sufficient to meet a need
+incentive - n. a positive motivational influence
+incidence - n. the relative frequency of occurrence of something
+incident - n. a public disturbance
+incidentally - a. of a minor or subordinate nature
+inclined - j. at an angle to the horizontal or vertical position
+incompetence - n. lack of physical or intellectual ability or qualifications
+inconsistent - j. displaying a lack of consistency
+inconvenient - j. not suited to your comfort, purpose or needs
+indefinitely - a. to an indefinite extent; for an indefinite time
+indicator - n. a device for showing the operating condition of some system
+indifferent - j. showing no care or concern in attitude or action
+indigenous - j. originating where it is found
+indulge - v. enjoy to excess
+inefficient - j. not producing desired results; wasteful
+inept - j. generally incompetent and ineffectual
+inevitable - j. incapable of being avoided or prevented
+inexpensive - j. relatively low in price or charging low prices
+infamous - j. known widely and usually unfavorably
+infinite - j. having no limits or boundaries in time or space or extent or magnitude
+influence - n. a power to affect persons or events especially power based on prestige etc
+influential - j. having or exercising influence or power
+influx - n. the process of flowing in
+ingenious - j. showing inventiveness and skill
+inherent - j. in the nature of something though not readily apparent
+injunction - n. (law) a judicial remedy issued in order to prohibit a party from doing or continuing to do a certain activity
+inland - a. towards or into the interior of a region
+insight - n. the clear (and often sudden) understanding of a complex situation
+insistence - n. the state of demanding notice or attention
+inspector - n. a high ranking police officer
+instance - n. an occurrence of something
+instant - n. a very short time
+insufficient - j. of a quantity not able to fulfill a need or requirement
+integral - j. essential to completeness; lacking nothing
+integrity - n. moral soundness
+intellectual - j. appealing to or using the intellect
+intelligence - n. the ability to comprehend; to understand and profit from experience
+intensive - j. characterized by a high degree or intensity; often used as a combining form
+intention - n. an act of intending; a volition that you intend to carry out
+interact - v. act together or towards others or with others
+interim - n. the time between one event, process, or period and another
+intermediate - j. lying between two extremes in time or space or state
+intervene - v. get involved, so as to alter or hinder an action, or through force or threat of force
+intervention - n. the act of intervening (as to mediate a dispute, etc.)
+intimacy - n. close or warm friendship
+intricate - j. having many complexly arranged elements; elaborate
+invasion - n. any entry into an area not previously occupied
+inventive - j. (used of persons or artifacts) marked by independence and creativity in thought or action
+investigator - n. a police officer who investigates crimes
+investor - n. someone who commits capital in order to gain financial returns
+invincible - j. incapable of being overcome or subdued
+invoke - v. cite as an authority; resort to
+involuntary - j. not subject to the control of the will
+involve - v. engage as a participant
+irony - n. incongruity between what might be expected and what actually occurs
+irrational - j. not consistent with or using reason
+irrelevant - j. having no bearing on or connection with the subject at issue
+irresistible - j. impossible to resist; overpowering
+irresponsible - j. showing lack of care for consequences
+judgment - n. the capacity to assess situations or circumstances shrewdly and to draw sound conclusions
+judicial - j. belonging or appropriate to the office of a judge
+juicy - j. lucrative
+junction - n. something that joins or connects
+jurisdiction - n. (law) the right and power to interpret and apply the law
+juror - n. someone who serves (or waits to be called to serve) on a jury
+justification - n. something (such as a fact or circumstance) that shows an action to be reasonable or necessary
+juvenile - j. of or relating to or characteristic of or appropriate for children or young people
+ken - n. range of what one can know or understand
+knight - n. originally a person of noble birth trained to arms and chivalry; today in Great Britain a person honored by the sovereign for personal merit
+knit - n. needlework created by interlacing yarn in a series of connected loops using straight eyeless needles or by machine
+lament - v. regret strongly
+landmark - n. the position of a prominent or well-known object in a particular landscape
+landscape - n. an expanse of scenery that can be seen in a single view
+lapse - n. a break or intermission in the occurrence of something
+laureate - n. someone honored for great achievements; figuratively someone crowned with a laurel wreath
+lavish - j. very generous
+lax - j. lacking in rigor or strictness
+legacy - n. (law) a gift of personal property by will
+legislative - j. relating to a legislature or composed of members of a legislature
+legitimacy - n. lawfulness by virtue of being authorized or in accordance with law
+legitimate - j. in accordance with recognized or accepted standards or principles
+leisure - n. time available for ease and relaxation
+lenient - j. not strict
+levy - v. impose and collect
+liable - j. held legally responsible
+liberalism - n. a political orientation that favors social progress by reform and by changing laws rather than by revolution
+lifelong - j. continuing through life
+lifetime - n. the period during which something is functional (as between birth and death)
+likelihood - n. the probability of a specified outcome
+liking - n. a feeling of pleasure and enjoyment
+liquor - n. an alcoholic beverage that is distilled rather than fermented
+literacy - n. the ability to read and write
+literal - j. avoiding embellishment or exaggeration (used for emphasis)
+literature - n. creative writing of recognized artistic value
+logic - n. the principles that guide reasoning within a given field or situation
+logical - j. capable of thinking and expressing yourself in a clear and consistent manner
+lovable - j. having characteristics that attract love or affection
+lucrative - j. producing a sizeable profit
+ludicrous - j. broadly or extravagantly humorous; resembling farce
+lying - n. the deliberate act of deviating from the truth
+machinery - n. machines or machine systems collectively
+magnet - n. (physics) a device that attracts iron and produces a magnetic field
+magnificent - j. characterized by grandeur
+magnitude - n. the property of relative size or extent (whether large or small)
+maintain - v. state or assert
+maintenance - n. activity involved in maintaining something in good working order
+makeup - n. the way in which someone or something is composed
+mandate - n. an authorization to act given to a representative
+mandatory - j. required by rule
+maneuver - v. act in order to achieve a certain goal
+manifesto - n. a public declaration of intentions (as issued by a political party or government)
+marine - j. native to or inhabiting the sea
+maritime - j. relating to or involving ships or shipping or navigation or seamen
+martial - j. suggesting war or military life
+marvel - v. be amazed at
+massacre - n. the savage and excessive killing of many people
+massive - j. imposing in size or bulk or solidity
+masterpiece - n. the most outstanding work of a creative artist or craftsman
+material - j. concerned with worldly rather than spiritual interests
+maternal - j. relating to or derived from one's mother
+maze - n. complex system of paths or tunnels in which it is easy to get lost
+mechanics - n. the technical aspects of doing something
+medicine - n. something that treats or prevents or alleviates the symptoms of disease
+medieval - j. as if belonging to the Middle Ages; old-fashioned and unenlightened
+mediocre - j. moderate to inferior in quality
+meditation - n. continuous and profound contemplation or musing on a subject or series of subjects of a deep or abstruse nature
+melodrama - n. an extravagant comedy in which action is more salient than characterization
+memorable - j. worth remembering
+menace - v. act in a threatening manner
+mentality - n. a habitual or characteristic mental attitude that determines how you will interpret and respond to situations
+mentor - v. serve as a teacher or trusted counselor
+metal - n. any of several chemical elements that are usually shiny solids
+metaphor - n. a figure of speech in which an expression is used to refer to something that it does not literally denote in order to suggest a similarity
+metric - j. based on the meter as a standard of measurement
+metropolis - n. a large and densely populated urban area; may include several independent administrative districts
+metropolitan - j. relating to or characteristic of a densely populated urban area
+mileage - n. the ratio of the number of miles traveled to the number of gallons of gasoline burned; miles per gallon
+militant - j. disposed to warfare or hard-line policies
+militia - n. civilians trained as soldiers but not part of the regular army
+miniature - j. being on a very small scale
+minimize - v. make small or insignificant
+ministry - n. a government department under the direction of a minister
+minority - n. being or relating to the smaller in number of two parts
+minute - j. infinitely or immeasurably small
+misdemeanor - n. a crime less serious than a felony
+missile - n. a rocket carrying a warhead of conventional or nuclear explosives
+momentum - n. an impelling force or strength
+monarchy - n. an autocracy governed by a monarch who usually inherits the authority
+monastery - n. the residence of a religious community
+monetary - j. relating to or involving money
+monopoly - n. (economics) a market in which there are many buyers but only one seller
+morale - n. the spirit of a group that makes the members want the group to succeed
+morality - n. concern with the distinction between good and evil or right and wrong; right or good conduct
+motto - n. a favorite saying of a sect or political group
+mundane - j. concerned with the world or worldly matters; ordinary
+municipal - j. relating to city government
+muster - v. gather or bring together
+myriad - n. a large indefinite number
+myth - n. a traditional story accepted as history; serves to explain the world view of a people
+mythology - n. myths collectively; the body of stories associated with a culture or institution or person
+narrative - n. a message that tells the particulars of an act or occurrence or course of events; presented in writing or drama or cinema or as a radio or television program
+narrator - n. someone who tells a story
+naturally - a. according to nature; by natural means; without artificial help
+naval - j. connected with or belonging to or used in a navy
+necessary - j. absolutely essential
+necessity - n. anything indispensable
+network - n. an interconnected system of things or people
+neutral - j. having no personal preference
+nevertheless - a. despite anything to the contrary (usually following a concession)
+noisy - j. full of or characterized by loud and nonmusical sounds
+nomination - n. the condition of having been proposed as a suitable candidate for appointment or election
+nominee - n. a politician who is running for public office
+norm - n. a standard or model or pattern regarded as typical
+notorious - j. known widely and usually unfavorably
+nude - n. without clothing (especially in the phrase 'in the nude')
+obesity - n. more than average fatness
+objective - j. undistorted by emotion or personal bias; based on observable phenomena
+observatory - n. a building designed and equipped to observe astronomical phenomena
+obsolete - j. no longer in use
+obstruction - n. something that stands in the way and must be circumvented or surmounted
+obtain - v. come into possession of
+occasion - n. a vaguely specified social event
+odor - n. the sensation that results when olfactory receptors in the nose are stimulated by particular chemicals in gaseous form
+ominous - j. threatening or foreshadowing evil or tragic developments
+operate - v. handle and cause to function
+operator - n. an agent that operates some apparatus or machine
+opinion - n. a personal belief or judgment that is not founded on proof or certainty
+opponent - n. a contestant that you are matched against
+opportunity - n. a possibility due to a favorable combination of circumstances
+optimism - n. a general disposition to expect the best in all things
+option - n. one of a number of things from which only one can be chosen
+oral - j. of or relating to or affecting or for use in the mouth
+ordeal - n. a severe or trying experience
+ornate - j. marked by elaborate rhetoric and elaborated with decorative details
+orthodox - j. adhering to what is commonly accepted
+outbreak - n. a sudden violent spontaneous occurrence (usually of some undesirable condition)
+outcry - n. a loud utterance; often in protest or opposition
+outrage - n. a feeling of righteous anger
+outrageous - j. grossly offensive to decency or morality; causing horror
+outright - a. without reservation or concealment
+overhaul - v. make repairs, renovations, revisions or adjustments to
+oversee - v. watch and direct
+overthrow - n. the termination of a ruler or institution (especially by force)
+overweight - n. the property of excessive fatness
+pact - n. a written agreement between two states or sovereigns
+pageant - n. a rich and spectacular ceremony
+panic - n. an overwhelming feeling of fear and anxiety
+pantheon - n. all the gods of a religion
+paradox - n. (logic) a statement that contradicts itself
+parallel - j. being everywhere equidistant and not intersecting
+parish - n. a local church community
+parliament - n. a legislative assembly in certain countries
+parody - v. make a spoof of or make fun of
+participant - n. someone who takes part in an activity
+participate - v. become a participant; be involved in
+partisan - n. an ardent and enthusiastic supporter of some person or activity
+partition - v. divide into parts, pieces, or sections
+passive - j. lacking in energy or will
+patriotism - n. love of country and willingness to sacrifice for it
+patron - n. someone who supports or champions something
+pavilion - n. large and often sumptuous tent
+peaceful - j. not disturbed by strife or turmoil or war
+pedestrian - j. lacking wit or imagination
+penalty - n. a punishment for a crime or offense
+penchant - n. a strong liking
+pennant - n. a long flag; often tapering
+pension - n. a regular payment to a person that is intended to allow them to subsist without working
+pentagon - n. a five-sided polygon
+perceive - v. to become aware of or conscious of
+perception - n. becoming aware of something via the senses
+perennial - j. lasting an indefinitely long time; suggesting self-renewal
+perform - v. carry out or perform an action
+perjury - n. criminal offense of making false statements under oath
+permanent - j. continuing or enduring without marked change in status or condition or place
+perpetual - j. continuing forever or indefinitely
+persist - v. be persistent, refuse to stop
+personal - j. concerning or affecting a particular person or his or her private life and personality
+personality - n. the complex of all the attributes--behavioral, temperamental, emotional and mental--that characterize a unique individual
+personnel - n. persons collectively in the employ of a business
+perspective - n. the appearance of things relative to one another as determined by their distance from the viewer
+persuade - v. cause somebody to adopt a certain position, belief, or course of action; twist somebody's arm
+pervasive - j. spreading or spread throughout
+petty - j. contemptibly narrow in outlook
+phenomenal - j. exceedingly or unbelievably great
+phenomenon - n. any state or process known through the senses rather than by intuition or reasoning
+philharmonic - j. composing or characteristic of an orchestral group
+philosophy - n. any personal belief about how to live or how to deal with a situation
+physicist - n. a scientist trained in physics
+physics - n. the science of matter and energy and their interactions
+pinch - n. a squeeze with the fingers
+pine - n. straight-grained white to yellowish tree
+pioneer - v. open up and explore a new area
+pivotal - j. being of crucial importance
+plausible - j. apparently reasonable and valid, and truthful
+playful - j. full of fun and high spirits
+playwright - n. someone who writes plays
+plea - n. a humble request for help from someone in authority
+plead - v. appeal or request earnestly
+pleasant - j. affording pleasure; being in harmony with your taste or likings
+plunge - v. fall abruptly
+poetic - j. of or relating to poetry
+poignant - j. arousing emotions; touching
+poised - j. in full control of your faculties
+portfolio - n. a set of pieces of creative work collected to be shown to potential customers or employers
+positive - j. characterized by or displaying affirmation or acceptance or certainty etc.
+possess - v. have as an attribute, knowledge, or skill
+possession - n. the act of having and controlling property
+potent - j. having a strong physiological or chemical effect
+potential - j. existing in possibility
+precedent - n. an example that is used to justify similar occurrences at a later time
+precise - j. sharply exact or accurate or delimited
+precision - n. the quality of being reproducible in amount or performance
+predecessor - n. one who precedes you in time (as in holding a position or office)
+predict - v. make a prediction about; tell in advance
+prediction - n. a statement made about the future
+prefer - v. like better; value more highly
+preference - n. a strong liking
+prejudice - n. a partiality that prevents objective consideration of an issue or situation
+premature - j. too soon or too hasty
+premier - v. be performed for the first time
+premise - v. set forth beforehand, often as an explanation
+preparation - n. the activity of putting or setting in order in advance of some act or purpose
+preposterous - j. incongruous; inviting ridicule
+prescription - n. written instructions from a physician or dentist to a druggist concerning the form and dosage of a drug to be issued to a given patient
+preservation - n. the activity of protecting something from loss or danger
+pretentious - j. making claim to or creating an appearance of (often undeserved) importance or distinction
+prevalent - j. most frequent or common
+prevention - n. the act of preventing
+primer - n. an introductory textbook
+primitive - j. belonging to an early stage of technical development; characterized by simplicity and (often) crudeness
+principal - n. the educator who has executive authority for a school
+principle - n. a basic truth or law or assumption
+principled - j. based on or manifesting objectively defined standards of rightness or morality
+pristine - j. completely free from dirt or contamination
+privilege - n. a special advantage or immunity or benefit not enjoyed by all
+probation - n. (law) a way of dealing with offenders without imprisoning them; a defendant found guilty of a crime is released by the court without imprisonment subject to conditions imposed by the court
+probe - v. question or examine thoroughly and closely
+procedure - n. a process or series of acts especially of a practical or mechanical nature involved in a particular form of work
+proceed - v. move ahead; travel onward in time or space
+productive - j. producing or capable of producing (especially abundantly)
+profession - n. an occupation requiring special education (especially in the liberal arts or sciences)
+professor - n. someone who is a member of the faculty at a college or university
+profile - n. an outline of something (especially a human face as seen from one side)
+progressive - j. favoring or promoting progress
+prohibition - n. the action of prohibiting or inhibiting or forbidding (or an instance thereof)
+prolific - j. bearing in abundance especially offspring
+promenade - n. a public area set aside as a pedestrian walk
+prominence - n. relative importance
+prominent - j. having a quality that thrusts itself into attention
+promoter - n. someone who is an active supporter and advocate
+prone - j. having a tendency (to); often used in combination
+propaganda - n. information that is spread for the purpose of promoting some cause
+prophet - n. someone who speaks by divine inspiration; someone who is an interpreter of the will of God
+protagonist - n. the principal character in a work of fiction
+protection - n. the activity of protecting someone or something
+protective - j. intended or adapted to afford protection of some kind
+protestant - j. of or relating to Protestants or Protestantism
+provincial - j. characteristic of the provinces or their people
+provoke - v. evoke or provoke to appear or occur
+proxy - n. a person authorized to act for another
+prudence - n. knowing how to avoid embarrassment or distress
+psychic - j. outside the sphere of physical science
+pundit - n. someone who has been admitted to membership in a scholarly field
+quake - v. shake with fast, tremulous movements
+qualify - v. make fit or prepared
+quarterly - a. in three month intervals
+radical - j. markedly new or introducing extreme change
+rampant - j. unrestrained and violent
+rapid - j. characterized by speed; moving with or capable of moving with high speed
+rave - v. praise enthusiastically
+reaction - n. a response that reveals a person's feelings or attitude
+readily - a. without much difficulty
+realism - n. the attribute of accepting the facts of life and favoring practicality and literal truth
+recipient - n. a person who receives something
+reckless - j. characterized by careless unconcern
+recognize - v. detect with the senses
+reconcile - v. come to terms
+reconsider - v. consider again; give new consideration to; usually with a view to changing
+recover - v. get or find back; recover the use of
+recruit - v. cause to assemble or enlist in the military
+redemption - n. (theology) the act of delivering from sin or saving from evil
+refer - v. send or direct for treatment, information, or a decision
+reflection - n. the image of something as reflected by a mirror (or other reflective material)
+reform - v. make changes for improvement in order to remove abuse and injustices
+refuge - n. a shelter from danger or hardship
+refusal - n. the act of not accepting something that is offered
+regime - n. the government or governing authority of a political unit
+regional - j. related or limited to a particular region
+reign - v. have sovereign power
+relevant - j. having a bearing on or connection with the subject at issue
+reliant - j. depending on another for support
+reluctance - n. a certain degree of unwillingness
+reluctant - j. not eager
+reminiscent - j. serving to bring to mind
+renaissance - n. the period of European history at the close of the Middle Ages and the rise of the modern world; a cultural rebirth from the 14th through the middle of the 17th centuries
+render - v. give or supply
+renowned - j. widely known and esteemed
+repeal - n. cancel officially
+reproduction - n. the act of making copies
+requisite - n. anything indispensable
+resemblance - n. similarity in appearance or external or superficial details
+resent - v. feel bitter or indignant about
+resist - v. withstand the force of something
+resistance - n. the action of opposing something that you disapprove or disagree with
+resistant - j. impervious to being affected
+resort - v. have recourse to
+resource - n. a source of aid or support that may be drawn upon when needed
+restore - v. bring back into original existence, use, function, or position
+resurrection - n. (New Testament) the rising of Christ on the third day after the Crucifixion
+retrospect - n. contemplation of things past
+retrospective - j. concerned with or related to the past
+revelation - n. communication of knowledge to man by a divine or supernatural agency
+revive - v. be brought back to life, consciousness, or strength
+rhetoric - n. using language effectively to please or persuade
+ridiculous - j. incongruous; absurd; nonsensical
+rigorous - j. demanding strict attention to rules and procedures
+robust - j. sturdy and strong in form, constitution, or construction
+rue - n. sadness associated with some wrong done or some disappointment
+rural - j. living in or characteristic of farming or country life
+rustic - j. characteristic of the fields or country
+sacrifice - v. kill or destroy
+savage - v. criticize harshly or violently
+scholarly - j. characteristic of learning or studying
+scope - n. an area in which something acts or operates or has power or control
+script - n. a written version of a play or other dramatic composition; used in preparing for a performance
+secession - n. formal separation from an alliance or federation
+secondary - j. not of major importance
+secrecy - n. the trait of keeping things secret
+secular - j. not concerned with or devoted to religion
+seize - v. take hold of; grab
+selective - j. characterized by very careful or fastidious choices
+seminar - n. any meeting for an exchange of ideas
+sensation - n. a perception associated with stimulation of a sensory organ
+sensibility - n. refined sensitivity to pleasurable or painful impressions
+sensitive - j. responsive to physical stimuli
+sentence - n. the period of time a prisoner is imprisoned
+sentinel - n. a person employed to keep watch for some anticipated event
+sequel - n. a part added to a book or play that continues and extends it
+sequence - n. serial arrangement in which things follow in logical order or a recurrent pattern
+sergeant - n. any of several noncommissioned officer ranks in the Army or Air Force or Marines ranking above a corporal
+servitude - n. state of subjection to an owner or master or forced labor imposed as punishment
+severely - a. with sternness; in a severe manner
+shallow - j. lacking depth of intellect or knowledge; concerned only with what is obvious
+sheer - j. complete and without restriction or qualification
+shrewd - j. marked by practical hardheaded intelligence
+siege - n. the action of an armed force that surrounds a fortified place and isolates it while continuing to attack
+significance - n. the quality of being important in effect or meaning
+significant - j. important in effect or meaning
+similar - j. having close to the same characteristics
+sinister - j. stemming from evil characteristics or forces; wicked or dishonorable
+skepticism - n. the disbelief in any claims of ultimate knowledge
+slack - j. not tense or taut
+slight - n. a deliberate discourteous act (usually as an expression of anger or disapproval)
+sober - v. become more realistic
+socialism - n. a political theory advocating state ownership of industry
+socialist - j. advocating or following the socialist principles of state ownership
+sociology - n. the study and classification of human societies
+solar - j. relating to or derived from the sun or utilizing the energies of the sun
+soldier - n. an enlisted man or woman who serves in an army
+somber - j. grave or even gloomy in character
+sophisticated - j. having or appealing to those having worldly knowledge and refinement
+souvenir - n. a reminder of past events
+specialty - n. an asset of special worth or utility
+species - n. (biology) taxonomic group whose members can interbreed
+spectator - n. a close observer; someone who looks at something (such as an exhibition of some kind)
+specter - n. a ghostly appearing figure
+spectrum - n. a broad range of related objects or ideas or activities
+speculate - v. consider in an idle or casual way and with an element of doubt or without sufficient reason to reach a conclusion
+spontaneous - j. said or done without having been planned or written in advance
+static - j. showing little if any change
+stature - n. high level of respect gained by impressive development or achievement
+statute - n. an act passed by a legislative body
+stealth - n. avoiding detection by moving carefully
+stimulate - v. cause to be alert and energetic
+stringent - j. demanding strict attention to rules and procedures
+submission - n. something (manuscripts or architectural plans and models or estimates or works of art of all genres etc.) submitted for the judgment of others (as in a competition)
+subsequent - j. following in time or order
+subsidiary - j. functioning in a supporting capacity
+substantive - j. having a firm basis in reality and being therefore important, meaningful, or considerable
+subtle - j. difficult to detect or grasp by the mind or analyze
+successor - n. a person who follows next in order
+summary - n. a brief statement that presents the main points in a concise form
+superb - j. of surpassing excellence
+superficial - j. concerned with or comprehending only what is apparent or obvious; not deep or penetrating emotionally or intellectually
+suppress - v. reduce the incidence or severity of or stop
+surround - v. extend on all sides of simultaneously; encircle
+suspense - n. excited anticipation of an approaching climax
+suspension - n. an interruption in the intensity or amount of something
+suspicious - j. openly distrustful and unwilling to confide
+sympathetic - j. expressing or feeling or resulting from sympathy or compassion or friendly fellow feelings; disposed toward
+symphony - n. a large orchestra; can perform symphonies
+systematic - j. characterized by order and planning
+tactics - n. the branch of military science dealing with detailed maneuvers to achieve objectives set by strategy
+tangible - j. perceptible by the senses especially the sense of touch
+taxation - n. the imposition of taxes; the practice of the government in levying taxes on the subjects of a state
+technique - n. skillfulness in the command of fundamentals deriving from practice and familiarity
+technology - n. the practical application of science to commerce or industry
+telescope - n. a magnifier of images of distant objects
+temporary - j. not permanent; not lasting
+tendency - n. a characteristic likelihood of or natural disposition toward a certain condition or character or effect
+tense - j. taut or rigid; stretched tight
+tentative - j. unsettled in mind or opinion
+tenure - v. give life-time employment to
+terminal - j. being or situated at an end
+territorial - j. displaying territoriality; defending an area from intruders
+testament - n. a profession of belief
+theological - j. of or relating to or concerning the study of religion
+theology - n. the rational and systematic study of religion and its influences and of the nature of religious truth
+theoretical - j. concerned with theories rather than their practical applications
+theorist - n. someone who theorizes (especially in science or art)
+thesis - n. a formal paper advancing a new point of view resulting from research; usually a requirement for an advanced academic degree
+titanic - j. of great force or power
+tolerance - n. willingness to recognize and respect the beliefs or practices of others
+tolerant - j. showing respect for the rights or opinions or practices of others
+tolerate - v. recognize and respect (rights and beliefs of others)
+transcript - n. something that has been transcribed; a written record (usually typewritten) of dictated or recorded speech
+transfer - v. move from one place to another
+transition - v. make or undergo a transition (from one state or system to another)
+translate - v. restate (words) from one language into another language
+transmission - n. the act of sending a message; causing a message to be transmitted
+transparent - j. transmitting light; able to be seen through with clarity
+transplant - n. the act of removing something from one location and introducing it in another location
+tremendous - j. extraordinarily large in size or extent or amount or power or degree
+tribune - n. a protector of the people
+trinity - n. the union of the Father and Son and Holy Ghost in one Godhead
+triple - v. increase threefold
+trivial - j. of little substance or significance
+truthful - j. expressing or given to expressing the truth
+turmoil - n. a violent disturbance
+typical - j. exhibiting the qualities or characteristics that identify a group or kind or category
+ubiquitous - j. being present everywhere at once
+ultimate - j. furthest or highest in degree or order; utmost or extreme
+unanimous - j. acting together as a single undiversified whole
+uncommon - j. not common or ordinarily encountered; unusually great in amount or remarkable in character or kind
+unconscious - j. not conscious; lacking awareness and the capacity for sensory perception as if asleep or dead
+undermine - v. destroy property or hinder normal operations
+unique - j. the single one of its kind
+unlimited - j. having no limits in range or scope
+unprecedented - j. having no previous example; novel
+urban - j. located in or characteristic of a city or city life
+urgency - n. an urgent situation calling for prompt action
+usage - n. the act of using
+utility - n. the quality of being of practical use
+vacuum - n. a region that is devoid of matter
+valid - j. well grounded in logic or truth or having legal force
+variation - n. an artifact that deviates from a norm or standard
+vegetarian - n. eater of fruits and grains and nuts; someone who eats no meat or fish or (often) any animal products
+vegetation - n. all the plant life in a particular region or period
+venerable - j. impressive by reason of age
+verify - v. confirm the truth of
+version - n. something a little different from others of the same type
+vertical - j. at right angles to the plane of the horizon or a base line
+veto - n. the power or right to prohibit or reject a proposed or intended act (especially the power of a chief executive to reject a bill passed by the legislature)
+vigorous - j. strong and active physically or mentally
+violation - n. an act that disregards an agreement or a right
+vista - n. the visual percept of a region
+visual - j. relating to or using sight
+vitality - n. an energetic style
+vogue - n. the popular taste at a given time
+volatile - j. liable to lead to sudden change or violence
+vulnerable - j. capable of being wounded or hurt
+warrant - v. stand behind and guarantee the quality, accuracy, or condition of
+wherever - a. where in the world
+wholly - a. to a complete degree or to the full or entire extent ('whole' is often used informally for 'wholly')
+woo - v. seek someone's favor
+zeal - n. excessive fervor to do something or accomplish some end
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+ <!-- The name of the application. -->
+ <string name="app_name">Searchable Dictionary</string>
+
+ <!-- The label for the search results for this app (see searchable.xml for more details). -->
+ <string name="search_label">Dictionary</string>
+
+ <!-- The menu entry that invokes search. -->
+ <string name="menu_search">Search</string>
+
+ <!-- The description that will show up in the search settings for this source. -->
+ <string name="settings_description">Definitions of words</string>
+
+ <!-- General instructions in the main activity. -->
+ <string name="search_instructions">Press the search key to look up a word</string>
+
+ <!-- Shown above search results when we receive a search request. -->
+ <string name="search_results">Search results for \'<xliff:g id="string">%s</xliff:g>\': </string>
+</resources>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+-->
+
+<!-- The attributes below configure how the search results will work:
+ - the 'label' points to a short description used when searching within the application if
+ 'badge mode' is used as specified with android:searchMode="useLabelAsBadge" (which it is not for
+ this application).
+ - the 'searchSettingsDescription' points to a string that will be displayed underneath the
+ name of this application in the search settings to describe what content will be searched.
+ - 'includeInGlobalSearch' will include this app's search suggestions in Quick Search Box.
+ - 'searchSuggestAuthority' specifies the authority matching the authority of the
+ "DictionaryProvider" specified in the manifest. This means the DictionaryProvider will be
+ queried for search suggestions.
+ - 'searchSuggestIntentAction' the default intent action used in the intent that is launched based
+ on a user cilcking on a search suggestion. This saves us from manually having to fill in the
+ SUGGEST_COLUMN_INTENT_ACTION column for each suggestion returned by the provider.
+ -->
+<searchable xmlns:android="http://schemas.android.com/apk/res/android"
+ android:label="@string/search_label"
+ android:searchSettingsDescription="@string/settings_description"
+ android:includeInGlobalSearch="true"
+ android:searchSuggestAuthority="dictionary"
+ android:searchSuggestIntentAction="android.intent.action.VIEW">
+</searchable>
--- /dev/null
+/*
+ * 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.example.android.searchabledict;
+
+import android.content.res.Resources;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * Contains logic to load the word of words and definitions and find a list of matching words
+ * given a query. Everything is held in memory; this is not a robust way to serve lots of
+ * words and is only for demo purposes.
+ *
+ * You may want to consider using an SQLite database. In practice, you'll want to make sure your
+ * suggestion provider is as efficient as possible, as the system will be taxed while performing
+ * searches across many sources for each keystroke the user enters into Quick Search Box.
+ */
+public class Dictionary {
+
+ public static class Word {
+ public final String word;
+ public final String definition;
+
+ public Word(String word, String definition) {
+ this.word = word;
+ this.definition = definition;
+ }
+ }
+
+ private static final Dictionary sInstance = new Dictionary();
+
+ public static Dictionary getInstance() {
+ return sInstance;
+ }
+
+ private final Map<String, List<Word>> mDict = new ConcurrentHashMap<String, List<Word>>();
+
+ private Dictionary() {
+ }
+
+ private boolean mLoaded = false;
+
+ /**
+ * Loads the words and definitions if they haven't been loaded already.
+ *
+ * @param resources Used to load the file containing the words and definitions.
+ */
+ public synchronized void ensureLoaded(final Resources resources) {
+ if (mLoaded) return;
+
+ new Thread(new Runnable() {
+ public void run() {
+ try {
+ loadWords(resources);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }).start();
+ }
+
+ private synchronized void loadWords(Resources resources) throws IOException {
+ if (mLoaded) return;
+
+ Log.d("dict", "loading words");
+ InputStream inputStream = resources.openRawResource(R.raw.definitions);
+ BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
+
+ try {
+ String line;
+ while((line = reader.readLine()) != null) {
+ String[] strings = TextUtils.split(line, "-");
+ if (strings.length < 2) continue;
+ addWord(strings[0].trim(), strings[1].trim());
+ }
+ } finally {
+ reader.close();
+ }
+ mLoaded = true;
+ }
+
+
+ public List<Word> getMatches(String query) {
+ List<Word> list = mDict.get(query);
+ return list == null ? Collections.EMPTY_LIST : list;
+ }
+
+ private void addWord(String word, String definition) {
+ final Word theWord = new Word(word, definition);
+
+ final int len = word.length();
+ for (int i = 0; i < len; i++) {
+ final String prefix = word.substring(0, len - i);
+ addMatch(prefix, theWord);
+ }
+ }
+
+ private void addMatch(String query, Word word) {
+ List<Word> matches = mDict.get(query);
+ if (matches == null) {
+ matches = new ArrayList<Word>();
+ mDict.put(query, matches);
+ }
+ matches.add(word);
+ }
+}
--- /dev/null
+/*
+ * 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.example.android.searchabledict;
+
+import android.app.SearchManager;
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.content.UriMatcher;
+import android.content.res.Resources;
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.net.Uri;
+import android.text.TextUtils;
+
+import java.util.List;
+
+/**
+ * Provides search suggestions for a list of words and their definitions.
+ */
+public class DictionaryProvider extends ContentProvider {
+
+ public static String AUTHORITY = "dictionary";
+
+ private static final int SEARCH_SUGGEST = 0;
+ private static final int SHORTCUT_REFRESH = 1;
+ private static final UriMatcher sURIMatcher = buildUriMatcher();
+
+ /**
+ * The columns we'll include in our search suggestions. There are others that could be used
+ * to further customize the suggestions, see the docs in {@link SearchManager} for the details
+ * on additional columns that are supported.
+ */
+ private static final String[] COLUMNS = {
+ "_id", // must include this column
+ SearchManager.SUGGEST_COLUMN_TEXT_1,
+ SearchManager.SUGGEST_COLUMN_TEXT_2,
+ SearchManager.SUGGEST_COLUMN_INTENT_DATA,
+ };
+
+
+ /**
+ * Sets up a uri matcher for search suggestion and shortcut refresh queries.
+ */
+ private static UriMatcher buildUriMatcher() {
+ UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);
+ matcher.addURI(AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY, SEARCH_SUGGEST);
+ matcher.addURI(AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY + "/*", SEARCH_SUGGEST);
+ matcher.addURI(AUTHORITY, SearchManager.SUGGEST_URI_PATH_SHORTCUT, SHORTCUT_REFRESH);
+ matcher.addURI(AUTHORITY, SearchManager.SUGGEST_URI_PATH_SHORTCUT + "/*", SHORTCUT_REFRESH);
+ return matcher;
+ }
+
+ @Override
+ public boolean onCreate() {
+ Resources resources = getContext().getResources();
+ Dictionary.getInstance().ensureLoaded(resources);
+ return true;
+ }
+
+ @Override
+ public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
+ String sortOrder) {
+ if (!TextUtils.isEmpty(selection)) {
+ throw new IllegalArgumentException("selection not allowed for " + uri);
+ }
+ if (selectionArgs != null && selectionArgs.length != 0) {
+ throw new IllegalArgumentException("selectionArgs not allowed for " + uri);
+ }
+ if (!TextUtils.isEmpty(sortOrder)) {
+ throw new IllegalArgumentException("sortOrder not allowed for " + uri);
+ }
+ switch (sURIMatcher.match(uri)) {
+ case SEARCH_SUGGEST:
+ String query = null;
+ if (uri.getPathSegments().size() > 1) {
+ query = uri.getLastPathSegment().toLowerCase();
+ }
+ return getSuggestions(query, projection);
+ case SHORTCUT_REFRESH:
+ String shortcutId = null;
+ if (uri.getPathSegments().size() > 1) {
+ shortcutId = uri.getLastPathSegment();
+ }
+ return refreshShortcut(shortcutId, projection);
+ default:
+ throw new IllegalArgumentException("Unknown URL " + uri);
+ }
+ }
+
+ private Cursor getSuggestions(String query, String[] projection) {
+ String processedQuery = query == null ? "" : query.toLowerCase();
+ List<Dictionary.Word> words = Dictionary.getInstance().getMatches(processedQuery);
+
+ MatrixCursor cursor = new MatrixCursor(COLUMNS);
+ for (Dictionary.Word word : words) {
+ cursor.addRow(columnValuesOfWord(word));
+ }
+
+ return cursor;
+ }
+
+ private Object[] columnValuesOfWord(Dictionary.Word word) {
+ return new String[] {
+ word.word, // _id
+ word.word, // text1
+ word.definition, // text2
+ word.word, // intent_data (included when clicking on item)
+ };
+ }
+
+ /**
+ * Note: this is unused as is, but if we included
+ * {@link SearchManager#SUGGEST_COLUMN_SHORTCUT_ID} as a column in our results, we
+ * could expect to receive refresh queries on this uri for the id provided, in which case we
+ * would return a cursor with a single item representing the refreshed suggestion data.
+ */
+ private Cursor refreshShortcut(String shortcutId, String[] projection) {
+ return null;
+ }
+
+ /**
+ * All queries for this provider are for the search suggestion and shortcut refresh mime type.
+ */
+ public String getType(Uri uri) {
+ switch (sURIMatcher.match(uri)) {
+ case SEARCH_SUGGEST:
+ return SearchManager.SUGGEST_MIME_TYPE;
+ case SHORTCUT_REFRESH:
+ return SearchManager.SHORTCUT_MIME_TYPE;
+ default:
+ throw new IllegalArgumentException("Unknown URL " + uri);
+ }
+ }
+
+ public Uri insert(Uri uri, ContentValues values) {
+ throw new UnsupportedOperationException();
+ }
+
+ public int delete(Uri uri, String selection, String[] selectionArgs) {
+ throw new UnsupportedOperationException();
+ }
+
+ public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+ throw new UnsupportedOperationException();
+ }
+}
--- /dev/null
+/*
+ * 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.example.android.searchabledict;
+
+import android.app.Activity;
+import android.app.SearchManager;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.widget.AdapterView;
+import android.widget.BaseAdapter;
+import android.widget.ListView;
+import android.widget.TextView;
+import android.widget.TwoLineListItem;
+
+import java.util.List;
+
+/**
+ * The main activity for the dictionary. Also displays search results triggered by the search
+ * dialog.
+ */
+public class SearchableDictionary extends Activity {
+
+ private static final int MENU_SEARCH = 1;
+
+ private TextView mTextView;
+ private ListView mList;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ Intent intent = getIntent();
+
+ setContentView(R.layout.main);
+ mTextView = (TextView) findViewById(R.id.textField);
+ mList = (ListView) findViewById(R.id.list);
+
+ if (Intent.ACTION_VIEW.equals(intent.getAction())) {
+ // from click on search results
+ Dictionary.getInstance().ensureLoaded(getResources());
+ String word = intent.getDataString();
+ Dictionary.Word theWord = Dictionary.getInstance().getMatches(word).get(0);
+ launchWord(theWord);
+ finish();
+ } else if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
+ String query = intent.getStringExtra(SearchManager.QUERY);
+ mTextView.setText(getString(R.string.search_results, query));
+ WordAdapter wordAdapter = new WordAdapter(Dictionary.getInstance().getMatches(query));
+ mList.setAdapter(wordAdapter);
+ mList.setOnItemClickListener(wordAdapter);
+ }
+
+ Log.d("dict", intent.toString());
+ if (intent.getExtras() != null) {
+ Log.d("dict", intent.getExtras().keySet().toString());
+ }
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ menu.add(0, MENU_SEARCH, 0, R.string.menu_search)
+ .setIcon(android.R.drawable.ic_search_category_default)
+ .setAlphabeticShortcut(SearchManager.MENU_KEY);
+
+ return super.onCreateOptionsMenu(menu);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case MENU_SEARCH:
+ onSearchRequested();
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ private void launchWord(Dictionary.Word theWord) {
+ Intent next = new Intent();
+ next.setClass(this, WordActivity.class);
+ next.putExtra("word", theWord.word);
+ next.putExtra("definition", theWord.definition);
+ startActivity(next);
+ }
+
+ class WordAdapter extends BaseAdapter implements AdapterView.OnItemClickListener {
+
+ private final List<Dictionary.Word> mWords;
+ private final LayoutInflater mInflater;
+
+ public WordAdapter(List<Dictionary.Word> words) {
+ mWords = words;
+ mInflater = (LayoutInflater) SearchableDictionary.this.getSystemService(
+ Context.LAYOUT_INFLATER_SERVICE);
+ }
+
+ public int getCount() {
+ return mWords.size();
+ }
+
+ public Object getItem(int position) {
+ return position;
+ }
+
+ public long getItemId(int position) {
+ return position;
+ }
+
+ public View getView(int position, View convertView, ViewGroup parent) {
+ TwoLineListItem view = (convertView != null) ? (TwoLineListItem) convertView :
+ createView(parent);
+ bindView(view, mWords.get(position));
+ return view;
+ }
+
+ private TwoLineListItem createView(ViewGroup parent) {
+ TwoLineListItem item = (TwoLineListItem) mInflater.inflate(
+ android.R.layout.simple_list_item_2, parent, false);
+ item.getText2().setSingleLine();
+ item.getText2().setEllipsize(TextUtils.TruncateAt.END);
+ return item;
+ }
+
+ private void bindView(TwoLineListItem view, Dictionary.Word word) {
+ view.getText1().setText(word.word);
+ view.getText2().setText(word.definition);
+ }
+
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ launchWord(mWords.get(position));
+ }
+ }
+}
--- /dev/null
+/*
+ * 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.example.android.searchabledict;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.widget.TextView;
+import android.content.Intent;
+
+/**
+ * Displays a word and its definition.
+ */
+public class WordActivity extends Activity {
+
+ private TextView mWord;
+ private TextView mDefinition;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.word);
+
+ mWord = (TextView) findViewById(R.id.word);
+ mDefinition = (TextView) findViewById(R.id.definition);
+
+ Intent intent = getIntent();
+
+ String word = intent.getStringExtra("word");
+ String definition = intent.getStringExtra("definition");
+
+ mWord.setText(word);
+ mDefinition.setText(definition);
+ }
+}
--- /dev/null
+<p>This is an implementation of the classic Game "Snake", in which you control a
+serpent roaming around the garden looking for apples. Be careful, though,
+because when you catch one, not only will you become longer, but you'll move
+faster. Running into yourself or the walls will end the game..</p>
+
+<p>This code demonstrates how to create custom View layouts and
+request draws to the screen. A great example of a basic game that does not
+require a fast framerate.</p>
+
+<img alt="" src="../images/Snake.png" />
--- /dev/null
+<p>This application is an example of writing an input method for a software keyboard.
+This code is focused on simplicity over completeness, so it should in no way be considered
+to be a complete soft keyboard implementation. Its purpose is to provide
+a basic example for how you would get started writing an input method, to
+be fleshed out as appropriate.</p>
+
+<img alt="" src="../images/SoftKeyboard.png" />
\ No newline at end of file
<test name="launchperf"
build_path="development/apps/launchperf"
package="com.android.launchperf"
- class="com.android.launchperf.SimpleActivityLaunchPerformance"
+ runner=".SimpleActivityLaunchPerformance"
coverage_target="framework" />
<!-- targeted framework tests -->
runner="android.test.InstrumentationCtsTestRunner"
coverage_target="framework"
cts="true" />
+
+<test name="cts-gesture"
+ build_path="cts/tests/tests/gesture"
+ package="com.android.cts.gesture"
+ runner="android.test.InstrumentationTestRunner"
+ coverage_target="framework"
+ cts="true" />
<test name="cts-graphics"
build_path="cts/tests"
coverage_target="framework"
cts="true" />
+<test name="cts-telephony"
+ build_path="cts/tests"
+ package="com.android.cts.telephony"
+ runner="android.test.InstrumentationCtsTestRunner"
+ coverage_target="framework"
+ cts="true" />
+
<test name="cts-util"
build_path="cts/tests"
package="com.android.cts.util"
coverage_target="Calendar"
continuous="true" />
-<!-- Make continuous = "true" once the bug 1966269 is fixed -->
<test name="calprov"
build_path="packages/providers/CalendarProvider/tests"
package="com.android.providers.calendar.tests"
- coverage_target="CalendarProvider" />
+ coverage_target="CalendarProvider"
+ continuous="true" />
<test name="camerastress"
build_path="packages/apps/Camera"
description="Android STL."
extra_build_args="ASTL_TESTS=1" />
+<!-- Android Keystore tests -->
+<test-native name="netkeystore_test"
+ build_path="frameworks/base/cmds/keystore/tests"
+ description="Android keystore."
+ extra_build_args="KEYSTORE_TESTS=1" />
+
<!-- pending patch 820
<test-native name="gtest"
build_path="external/gtest"
"""
for f in files:
(name, ext) = os.path.splitext(f)
- if ext == ".cc" or ext == ".cpp":
+ if ext == ".cc" or ext == ".cpp" or ext == ".c":
if re.search("_test$|_test_$|_unittest$|_unittest_$|^test_", name):
logger.SilentLog("Found %s" % f)
test_list.append(str(os.path.join(dirname, f)))
super(string);
}
}
-
+
private static String sPrefsLocation = null;
-
+
/**
* Returns the folder used to store android related files.
* @return an OS specific path, terminated by a separator.
- * @throws AndroidLocationException
+ * @throws AndroidLocationException
*/
public final static String getFolder() throws AndroidLocationException {
if (sPrefsLocation == null) {
String home = findValidPath("ANDROID_SDK_HOME", "user.home", "HOME");
-
+
// if the above failed, we throw an exception.
if (home == null) {
throw new AndroidLocationException(
"Unable to get the home directory. Make sure the user.home property is set up");
} else {
sPrefsLocation = home + File.separator + ".android" + File.separator;
+ }
+ }
- // make sure the folder exists!
- File f = new File(sPrefsLocation);
- if (f.exists() == false) {
- f.mkdir();
- } else if (f.isFile()) {
- throw new AndroidLocationException(sPrefsLocation +
- " is not a directory! This is required to run Android tools.");
- }
+ // make sure the folder exists!
+ File f = new File(sPrefsLocation);
+ if (f.exists() == false) {
+ try {
+ f.mkdir();
+ } catch (SecurityException e) {
+ AndroidLocationException e2 = new AndroidLocationException(String.format(
+ "Unable to create folder '%1$s'. " +
+ "This is the path of preference folder expected by the Android tools.",
+ sPrefsLocation));
+ e2.initCause(e);
+ throw e2;
}
+ } else if (f.isFile()) {
+ throw new AndroidLocationException(sPrefsLocation +
+ " is not a directory! " +
+ "This is the path of preference folder expected by the Android tools.");
}
return sPrefsLocation;
}
}
}
-
+
return null;
}
}
import org.apache.tools.ant.ProjectComponent;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.Path.PathElement;
import java.io.File;
import java.io.FileInputStream;
public class ApkBuilderTask extends Task {
+ // ref id to the <path> object containing all the boot classpaths.
+ private final static String REF_APK_PATH = "android.apks.path";
+
/**
* Class to represent nested elements. Since they all have only one attribute ('path'), the
* same class can be used for all the nested elements (zip, file, sourcefolder, jarfolder,
@Override
public void execute() throws BuildException {
- Project taskProject = getProject();
+ Project antProject = getProject();
ApkBuilderImpl apkBuilder = new ApkBuilderImpl();
apkBuilder.setVerbose(mVerbose);
ApkBuilderImpl.processNativeFolder(offset, f, mNativeLibraries);
}
+ // create the Path item that will contain all the generated APKs
+ // for reuse by other targets (signing/zipaligning)
+ Path path = new Path(antProject);
+
+ // The createApk method uses mBaseName for the base name of the packages (resources
+ // and apk files).
+ // The generated apk file name is
+ // debug: {base}[-{config}]-debug-unaligned.apk
+ // release: {base}[-{config}]-unsigned.apk
+ // Unfortunately for 1.5 projects and before the 'install' ant target expects the name
+ // of the default debug package to be {base}-debug.apk
+ // In order to support those package, we look for the 'out-debug-unaligned-package'
+ // property. If this exist, then we generate {base}[-{config}]-debug-unaligned.apk
+ // otherwise we generate {base}[-{config}]-debug.apk
+ // FIXME: Make apkbuilder export the package name used instead of
+ // having to keep apkbuilder and the rules file in sync
+ String debugPackageSuffix = "-debug-unaligned.apk";
+ if (antProject.getProperty("out-debug-unaligned-package") == null) {
+ debugPackageSuffix = "-debug.apk";
+ }
// first do a full resource package
- createApk(apkBuilder, null /*configName*/, null /*resourceFilter*/);
+ createApk(apkBuilder, null /*configName*/, null /*resourceFilter*/, path,
+ debugPackageSuffix);
// now see if we need to create file with filtered resources.
// Get the project base directory.
- File baseDir = taskProject.getBaseDir();
+ File baseDir = antProject.getBaseDir();
ProjectProperties properties = ProjectProperties.load(baseDir.getAbsolutePath(),
PropertyType.DEFAULT);
if (apkConfigs.size() > 0) {
Set<Entry<String, String>> entrySet = apkConfigs.entrySet();
for (Entry<String, String> entry : entrySet) {
- createApk(apkBuilder, entry.getKey(), entry.getValue());
+ createApk(apkBuilder, entry.getKey(), entry.getValue(), path,
+ debugPackageSuffix);
}
}
+
+ // finally sets the path in the project with a reference
+ antProject.addReference(REF_APK_PATH, path);
+
} catch (FileNotFoundException e) {
throw new BuildException(e);
} catch (IllegalArgumentException e) {
* package will be generated.
* @param resourceFilter the resource configuration filter to pass to aapt (if configName is
* non null)
+ * @param path Ant {@link Path} to which add the generated APKs as {@link PathElement}
+ * @param debugPackageSuffix suffix for the debug packages.
* @throws FileNotFoundException
* @throws ApkCreationException
*/
- private void createApk(ApkBuilderImpl apkBuilder, String configName, String resourceFilter)
+ private void createApk(ApkBuilderImpl apkBuilder, String configName, String resourceFilter,
+ Path path, String debugPackageSuffix)
throws FileNotFoundException, ApkCreationException {
// All the files to be included in the archive have already been prep'ed up, except
// the resource package.
}
if (mSigned) {
- filename = filename + "-debug.apk";
+ filename = filename + debugPackageSuffix;
} else {
filename = filename + "-unsigned.apk";
}
}
}
+ // out File
File f = new File(mOutFolder, filename);
+ // add it to the Path object
+ PathElement element = path.createPathElement();
+ element.setLocation(f);
+
// and generate the apk
apkBuilder.createPackage(f.getAbsoluteFile(), mZipArchives,
mArchiveFiles, mJavaResources, mResourcesJars, mNativeLibraries);
<feature
id="com.android.ide.eclipse.adt"
label="Android Development Tools"
- version="0.9.2.qualifier"
+ version="0.9.3.qualifier"
provider-name="The Android Open Source Project"
plugin="com.android.ide.eclipse.adt">
</copyright>
<license url="http://www.eclipse.org/org/documents/epl-v10.php">
- Note: jcommon-1.0.12.jar is under the BSD license rather than the APL. You can find a copy of the BSD License at http://www.opensource.org/licenses/bsd-license.php
+ Note: kxml2-2.3.0.jar is under the BSD license rather than the EPL. You can find a copy of the BSD License at http://www.opensource.org/licenses/bsd-license.php
-jfreechart-1.0.9.jar and jfreechart-1.0.9-swt.jar are under the LGPL rather than the EPL. You can find a copy of the LGPL at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt. You can get the source code for these two components at http://android.git.kernel.org/pub/jfreechart-1.0.9.zip
-
-
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.
<feature
id="com.android.ide.eclipse.ddms"
label="Android DDMS"
- version="0.9.2.qualifier"
+ version="0.9.3.qualifier"
provider-name="The Android Open Source Project">
<description>
<license url="http://www.apache.org/licenses/LICENSE-2.0">
Note: jcommon-1.0.12.jar is under the BSD license rather than the APL. You can find a copy of the BSD License at http://www.opensource.org/licenses/bsd-license.php
- jfreechart-1.0.9.jar and jfreechart-1.0.9-swt.jar are under the LGPL rather than the EPL. You can find a copy of the LGPL at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt. You can get the source code for these two components at http://android.git.kernel.org/pub/jfreechart-1.0.9.zip
-
-
+ jfreechart-1.0.9.jar and jfreechart-1.0.9-swt.jar are under the LGPL rather than the APL. You can find a copy of the LGPL at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt. You can get the source code for these two components at http://android.git.kernel.org/pub/jfreechart-1.0.9.zip
+
+
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
<feature
id="com.android.ide.eclipse.tests"
label="ADT Tests"
- version="0.9.2.qualifier"
+ version="0.9.3.qualifier"
provider-name="The Android Open Source Project">
<copyright>
<classpathentry excluding="Makefile|resources/" kind="src" path="src"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
- <classpathentry kind="lib" path="jarutils.jar"/>
- <classpathentry kind="lib" path="androidprefs.jar"/>
+ <classpathentry kind="lib" path="jarutils.jar" sourcepath="/JarUtils"/>
+ <classpathentry kind="lib" path="androidprefs.jar" sourcepath="/AndroidPrefs"/>
<classpathentry kind="lib" path="sdkstats.jar" sourcepath="/SdkStatsService"/>
<classpathentry kind="lib" path="kxml2-2.3.0.jar"/>
<classpathentry kind="lib" path="layoutlib_api.jar"/>
Bundle-ManifestVersion: 2
Bundle-Name: Android Development Toolkit
Bundle-SymbolicName: com.android.ide.eclipse.adt;singleton:=true
-Bundle-Version: 0.9.2.qualifier
+Bundle-Version: 0.9.3.qualifier
Bundle-ClassPath: .,
jarutils.jar,
androidprefs.jar,
</menu>
</actionSet>
<actionSet
- description="Android AVD Manager"
+ description="Android AVD and SDK Manager"
id="adt.actionSet.avdManager"
- label="Android AVD Manager"
+ label="Android SDK and AVD Manager"
visible="true">
<action
class="com.android.ide.eclipse.adt.internal.wizards.actions.AvdManagerAction"
icon="icons/avd_manager.png"
id="com.android.ide.eclipse.adt.ui.avdmanager"
- label="Android AVD Manager"
+ label="Android SDK and AVD Manager"
menubarPath="Window/additions"
style="push"
toolbarPath="android_project"
- tooltip="Opens the Android Virtual Device (AVD) Manager">
+ tooltip="Opens the Android Virtual Device (AVD) and SDK Manager">
</action>
</actionSet>
</extension>
/** Returns the adb path relative to the sdk folder */
public static String getOsRelativeAdb() {
- return SdkConstants.OS_SDK_TOOLS_FOLDER + AndroidConstants.FN_ADB;
+ return SdkConstants.OS_SDK_TOOLS_FOLDER + SdkConstants.FN_ADB;
+ }
+
+ /** Returns the zipalign path relative to the sdk folder */
+ public static String getOsRelativeZipAlign() {
+ return SdkConstants.OS_SDK_TOOLS_FOLDER + SdkConstants.FN_ZIPALIGN;
}
/** Returns the emulator path relative to the sdk folder */
public static String getOsRelativeEmulator() {
- return SdkConstants.OS_SDK_TOOLS_FOLDER + AndroidConstants.FN_EMULATOR;
+ return SdkConstants.OS_SDK_TOOLS_FOLDER + SdkConstants.FN_EMULATOR;
}
/** Returns the absolute adb path */
return getOsSdkFolder() + getOsRelativeAdb();
}
+ /** Returns the absolute zipalign path */
+ public static String getOsAbsoluteZipAlign() {
+ return getOsSdkFolder() + getOsRelativeZipAlign();
+ }
+
/** Returns the absolute traceview path */
public static String getOsAbsoluteTraceview() {
return getOsSdkFolder() + SdkConstants.OS_SDK_TOOLS_FOLDER +
public final static Pattern PATTERN_RESOURCES_S_AP_ =
Pattern.compile("resources-.*\\.ap_", Pattern.CASE_INSENSITIVE); //$NON-NLS-1$
- public final static String FN_ADB = SdkConstants.FN_ADB;
-
- public final static String FN_EMULATOR = SdkConstants.FN_EMULATOR;
-
public final static String FN_TRACEVIEW =
(SdkConstants.CURRENT_PLATFORM == SdkConstants.PLATFORM_WINDOWS) ?
"traceview.exe" : "traceview"; //$NON-NLS-1$ //$NON-NLS-2$
// This interrupts the build. The next builders will not run.
stopBuild(msg);
+
+ // TODO: document whether code below that uses manifest (which is now guaranteed
+ // to be null) will actually be executed or not.
}
// lets check the XML of the manifest first, if that hasn't been done by the
if (codename != null) {
// integer minSdk when the target is a preview => fatal error
String msg = String.format(
- "Platform %1$s is a preview and requires appication manifests to set %2$s to '%3$s'",
- codename, ManifestConstants.ATTRIBUTE_MIN_SDK_VERSION,
- codename);
+ "Platform %1$s is a preview and requires appication manifests to set %2$s to '%1$s'",
+ codename, ManifestConstants.ATTRIBUTE_MIN_SDK_VERSION);
AdtPlugin.printErrorToConsole(project, msg);
BaseProjectHelper.addMarker(manifest, AdtConstants.MARKER_ADT, msg,
IMarker.SEVERITY_ERROR);
} else if (minSdkValue < projectVersion.getApiLevel()) {
// integer minSdk is not high enough for the target => warning
String msg = String.format(
- "Manifest min SDK version (%1$d) is lower than project target API level (%2$d)",
+ "Manifest min SDK version (%1$s) is lower than project target API level (%2$d)",
minSdkVersion, projectVersion.getApiLevel());
AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project, msg);
BaseProjectHelper.addMarker(manifest, AdtConstants.MARKER_ADT, msg,
// platform is not a preview => fatal error
String msg = String.format(
"Manifest attribute '%1$s' is set to '%2$s'. Integer is expected.",
- ManifestConstants.ATTRIBUTE_MIN_SDK_VERSION, codename);
+ ManifestConstants.ATTRIBUTE_MIN_SDK_VERSION, minSdkVersion);
AdtPlugin.printErrorToConsole(project, msg);
BaseProjectHelper.addMarker(manifest, AdtConstants.MARKER_ADT, msg,
IMarker.SEVERITY_ERROR);
stopBuild(msg);
}
}
+ } else if (projectTarget.getVersion().isPreview()) {
+ // else the minSdkVersion is not set but we are using a preview target.
+ // Display an error
+ String codename = projectTarget.getVersion().getCodename();
+ String msg = String.format(
+ "Platform %1$s is a preview and requires appication manifests to set %2$s to '%1$s'",
+ codename, ManifestConstants.ATTRIBUTE_MIN_SDK_VERSION);
+ AdtPlugin.printErrorToConsole(project, msg);
+ BaseProjectHelper.addMarker(manifest, AdtConstants.MARKER_ADT, msg,
+ IMarker.SEVERITY_ERROR);
+ stopBuild(msg);
}
if (javaPackage == null || javaPackage.length() == 0) {
// This interrupts the build. The next builders will not run.
stopBuild(msg);
+
+ // TODO: document whether code below that uses javaPackage (which is now guaranteed
+ // to be null) will actually be executed or not.
}
// 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) {
+ if (javaPackage != null && javaPackage.equals(mManifestPackage) == false) {
// The manifest package has changed, the user may want to update
// the launch configuration
if (mManifestPackage != null) {
// get the file system path
IPath outputLocation = mGenFolder.getLocation();
IPath resLocation = resFolder.getLocation();
- IPath manifestLocation = manifest.getLocation();
+ IPath manifestLocation = manifest == null ? null : manifest.getLocation();
// those locations have to exist for us to do something!
if (outputLocation != null && resLocation != null
* Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php
*
* Unless required by applicable law or agreed to in writing, software
import com.android.ide.eclipse.adt.internal.resources.configurations.TouchScreenQualifier;
import com.android.ide.eclipse.adt.internal.resources.configurations.KeyboardStateQualifier.KeyboardState;
import com.android.ide.eclipse.adt.internal.resources.configurations.NavigationMethodQualifier.NavigationMethod;
+import com.android.ide.eclipse.adt.internal.resources.configurations.PixelDensityQualifier.Density;
import com.android.ide.eclipse.adt.internal.resources.configurations.ScreenOrientationQualifier.ScreenOrientation;
import com.android.ide.eclipse.adt.internal.resources.configurations.TextInputMethodQualifier.TextInputMethod;
import com.android.ide.eclipse.adt.internal.resources.configurations.TouchScreenQualifier.TouchScreenType;
import com.android.ide.eclipse.adt.internal.sdk.Sdk;
import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData.LayoutBridge;
import com.android.ide.eclipse.adt.internal.sdk.Sdk.ITargetChangeListener;
-import com.android.ide.eclipse.adt.internal.ui.ConfigurationSelector.DensityVerifier;
import com.android.ide.eclipse.adt.internal.ui.ConfigurationSelector.DimensionVerifier;
import com.android.ide.eclipse.adt.internal.ui.ConfigurationSelector.LanguageRegionVerifier;
import com.android.ide.eclipse.adt.internal.ui.ConfigurationSelector.MobileCodeVerifier;
*/
public class GraphicalLayoutEditor extends AbstractGraphicalLayoutEditor
implements ILayoutReloadListener {
-
+
private final static String THEME_SEPARATOR = "----------"; //$NON-NLS-1$
/** Reference to the layout editor */
private Combo mLanguage;
private Combo mRegion;
private Combo mOrientation;
- private Text mDensity;
+ private Combo mDensity;
private Combo mTouch;
private Combo mKeyboard;
private Combo mTextInput;
public void onTargetsLoaded() {
// because the SDK changed we must reset the configured framework resource.
mConfiguredFrameworkRes = null;
-
+
updateUIFromResources();
mThemeCombo.getParent().layout();
new Label(topParent, SWT.NONE).setText("Density");
mDensityIcon = createControlComposite(topParent, true /* grab_horizontal */);
- mDensity = new Text(mDensityIcon.getParent(), SWT.BORDER);
+ mDensity = new Combo(mDensityIcon.getParent(), SWT.DROP_DOWN | SWT.READ_ONLY);
+ Density[] dValues = Density.values();
+ mDensity.add("(Default)");
+ for (Density value : dValues) {
+ mDensity.add(value.getDisplayValue());
+ }
+ mDensity.select(0);
mDensity.setLayoutData(new GridData(
GridData.HORIZONTAL_ALIGN_FILL | GridData.GRAB_HORIZONTAL));
- mDensity.addVerifyListener(new DensityVerifier());
mDensity.addSelectionListener(new SelectionAdapter() {
- @Override
- public void widgetDefaultSelected(SelectionEvent e) {
- onDensityChange();
- }
- });
- mDensity.addModifyListener(new ModifyListener() {
- public void modifyText(ModifyEvent e) {
- onDensityChange();
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ onDensityChange();
}
});
@Override
public void widgetSelected(SelectionEvent e) {
onNavigationChange();
- }
+ }
});
Composite labelParent = new Composite(topParent, SWT.NONE);
mSize1.addSelectionListener(sl);
mSize2.addSelectionListener(sl);
-
+
ModifyListener sizeModifyListener = new ModifyListener() {
public void modifyText(ModifyEvent e) {
onSizeChange();
@Override
public void widgetSelected(SelectionEvent e) {
LayoutCreatorDialog dialog = new LayoutCreatorDialog(mCreateButton.getShell(),
- mEditedFile.getName(), mCurrentConfig);
+ mEditedFile.getName(),
+ Sdk.getCurrent().getTarget(mEditedFile.getProject()), mCurrentConfig);
if (dialog.open() == Dialog.OK) {
final FolderConfiguration config = new FolderConfiguration();
dialog.getConfiguration(config);
-
+
createAlternateLayout(config);
}
}
@Override
protected PaletteRoot getPaletteRoot() {
mPaletteRoot = PaletteFactory.createPaletteRoot(mPaletteRoot,
- mLayoutEditor.getTargetData());
+ mLayoutEditor.getTargetData());
return mPaletteRoot;
}
getCommandStack().markSaveLocation();
firePropertyChange(PROP_DIRTY);
}
-
+
@Override
protected void configurePaletteViewer() {
super.configurePaletteViewer();
// the PaletteTemplateEntry held in the PaletteRoot.
TemplateTransferDragSourceListener dragSource =
new TemplateTransferDragSourceListener(getPaletteViewer());
-
+
// Create a drag source on the palette viewer.
// See the drag target associated with the GraphicalViewer in configureGraphicalViewer.
getPaletteViewer().addDragSourceListener(dragSource);
viewer.setEditPartFactory(new UiElementsEditPartFactory(mParent.getDisplay()));
viewer.setRootEditPart(new ScalableFreeformRootEditPart());
- // Disable the following -- we don't drag *from* the GraphicalViewer yet:
+ // Disable the following -- we don't drag *from* the GraphicalViewer yet:
// viewer.addDragSourceListener(new TemplateTransferDragSourceListener(viewer));
-
+
viewer.addDropTargetListener(new DropListener(viewer));
}
-
+
class DropListener extends TemplateTransferDropTargetListener {
public DropListener(EditPartViewer viewer) {
super(viewer);
public Object getObjectType() {
return template;
}
-
+
};
}
}
input.toString());
}
}
-
+
/* (non-javadoc)
* Sets the graphicalViewer for this EditorPart.
* @param viewer the graphical viewer
/**
* Used by LayoutEditor.UiEditorActions.selectUiNode to select a new UI Node
* created by {@link ElementCreateCommand#execute()}.
- *
+ *
* @param uiNodeModel The {@link UiElementNode} to select.
*/
@Override
void selectModel(UiElementNode uiNodeModel) {
GraphicalViewer viewer = getGraphicalViewer();
-
+
// Give focus to the graphical viewer (in case the outline has it)
viewer.getControl().forceFocus();
-
+
Object editPart = viewer.getEditPartRegistry().get(uiNodeModel);
-
+
if (editPart instanceof EditPart) {
viewer.select((EditPart)editPart);
}
MenuManager menuManager = new MenuManager();
menuManager.setRemoveAllWhenShown(true);
menuManager.addMenuListener(new ActionMenuListener(viewer));
-
+
return menuManager;
}
}
}
}
-
+
if (selected.size() > 0) {
doCreateMenuAction(manager, mViewer, selected);
}
}
}
-
+
private void doCreateMenuAction(IMenuManager manager,
final GraphicalViewer viewer,
final ArrayList<UiElementNode> selected) {
// Append "add" and "remove" actions. They do the same thing as the add/remove
// buttons on the side.
IconFactory factory = IconFactory.getInstance();
-
+
final UiEditorActions uiActions = mLayoutEditor.getUiEditorActions();
// "Add" makes sense only if there's 0 or 1 item selected since the
});
manager.add(new Separator());
-
+
manager.add(new Action("Up", factory.getImageDescriptor("up")) { //$NON-NLS-2$
@Override
public void run() {
}
});
}
-
- }
+
+ }
/**
* Sets the UI for the edition of a new file.
void editNewFile(FolderConfiguration configuration) {
// update the configuration UI
setConfiguration(configuration, true /*force*/);
-
+
// enable the create button if the current and edited config are not equals
mCreateButton.setEnabled(mEditedConfig.equals(mCurrentConfig) == false);
}
-
+
public Rectangle getBounds() {
ScreenOrientation orientation = null;
if (mOrientation.getSelectionIndex() == 0) {
return new Rectangle(0, 0, s1, s1);
}
}
-
+
/**
* Renders an Android View described by a {@link ViewElementDescriptor}.
* <p/>This uses the <code>wrap_content</code> mode for both <code>layout_width</code> and
if (mEditedFile == null) {
return null;
}
-
+
IAndroidTarget target = Sdk.getCurrent().getTarget(mEditedFile.getProject());
if (target == null) {
return null;
}
-
+
AndroidTargetData data = Sdk.getCurrent().getTargetData(target);
if (data == null) {
return null;
}
-
+
LayoutBridge bridge = data.getLayoutBridge();
if (bridge.bridge != null) { // bridge can never be null.
// get the project resource values based on the current config
mConfiguredProjectRes = projectRes.getConfiguredResources(mCurrentConfig);
}
-
+
configuredProjectResources = mConfiguredProjectRes;
} else {
// we absolutely need a Map of configured project resources.
int height = result.getRootView().getBottom();
Raster raster = largeImage.getData(new java.awt.Rectangle(width, height));
int[] imageDataBuffer = ((DataBufferInt)raster.getDataBuffer()).getData();
-
+
ImageData imageData = new ImageData(width, height, 32,
new PaletteData(0x00FF0000, 0x0000FF00, 0x000000FF));
imageData.setPixels(0, 0, imageDataBuffer.length, imageDataBuffer, 0);
-
+
return imageData;
}
}
mNeedsXmlReload = true;
}
}
-
+
/**
* Actually performs the XML reload
* @see #onXmlModelChanged()
private void doXmlReload(boolean force) {
if (force || mNeedsXmlReload) {
GraphicalViewer viewer = getGraphicalViewer();
-
+
// try to preserve the selection before changing the content
SelectionManager selMan = viewer.getSelectionManager();
ISelection selection = selMan.getSelection();
-
+
try {
viewer.setContents(getModel());
} finally {
selMan.setSelection(selection);
}
-
+
mNeedsXmlReload = false;
}
}
mDensityIcon.setImage(mMatchImage);
PixelDensityQualifier densityQualifier = config.getPixelDensityQualifier();
if (densityQualifier != null) {
- mDensity.setText(String.format("%1$d", densityQualifier.getValue()));
+ mDensity.select(
+ Density.getIndex(densityQualifier.getValue()) + 1);
mCurrentConfig.setPixelDensityQualifier(densityQualifier);
} else if (force) {
- mDensity.setText(""); //$NON-NLS-1$
+ mOrientation.select(0);
mCurrentConfig.setPixelDensityQualifier(null);
- } else if (mDensity.getText().length() > 0) {
+ } else if (mDensity.getSelectionIndex() != 0) {
mDensityIcon.setImage(mWarningImage);
}
// update the string showing the folder name
String current = config.toDisplayString();
mCurrentLayoutLabel.setText(current != null ? current : "(Default)");
-
+
mDisableUpdates = false;
}
-
+
/**
* Displays an error icon in front of all the non-null qualifiers.
*/
if (countryQualifier != null) {
mCountryIcon.setImage(mErrorImage);
}
-
+
mNetworkIcon.setImage(mMatchImage);
NetworkCodeQualifier networkQualifier = mCurrentConfig.getNetworkCodeQualifier();
if (networkQualifier != null) {
mNetworkIcon.setImage(mErrorImage);
}
-
+
mLanguageIcon.setImage(mMatchImage);
LanguageQualifier languageQualifier = mCurrentConfig.getLanguageQualifier();
if (languageQualifier != null) {
mLanguageIcon.setImage(mErrorImage);
}
-
+
mRegionIcon.setImage(mMatchImage);
RegionQualifier regionQualifier = mCurrentConfig.getRegionQualifier();
if (regionQualifier != null) {
mRegionIcon.setImage(mErrorImage);
}
-
+
mOrientationIcon.setImage(mMatchImage);
ScreenOrientationQualifier orientationQualifier =
mCurrentConfig.getScreenOrientationQualifier();
if (orientationQualifier != null) {
mOrientationIcon.setImage(mErrorImage);
}
-
+
mDensityIcon.setImage(mMatchImage);
PixelDensityQualifier densityQualifier = mCurrentConfig.getPixelDensityQualifier();
if (densityQualifier != null) {
mDensityIcon.setImage(mErrorImage);
}
-
+
mTouchIcon.setImage(mMatchImage);
TouchScreenQualifier touchQualifier = mCurrentConfig.getTouchTypeQualifier();
if (touchQualifier != null) {
mTouchIcon.setImage(mErrorImage);
}
-
+
mKeyboardIcon.setImage(mMatchImage);
KeyboardStateQualifier keyboardQualifier = mCurrentConfig.getKeyboardStateQualifier();
if (keyboardQualifier != null) {
if (inputQualifier != null) {
mTextInputIcon.setImage(mErrorImage);
}
-
+
mNavigationIcon.setImage(mMatchImage);
NavigationMethodQualifier navigationQualifiter =
mCurrentConfig.getNavigationMethodQualifier();
if (navigationQualifiter != null) {
mNavigationIcon.setImage(mErrorImage);
}
-
+
mSizeIcon.setImage(mMatchImage);
ScreenDimensionQualifier sizeQualifier = mCurrentConfig.getScreenDimensionQualifier();
if (sizeQualifier != null) {
mSizeIcon.setImage(mErrorImage);
}
-
+
// update the string showing the folder name
String current = mCurrentConfig.toDisplayString();
mCurrentLayoutLabel.setText(current != null ? current : "(Default)");
UiDocumentNode getModel() {
return mLayoutEditor.getUiRootNode();
}
-
+
@Override
void reloadPalette() {
PaletteFactory.createPaletteRoot(mPaletteRoot, mLayoutEditor.getTargetData());
}
private void onDensityChange() {
- // because mDensity triggers onDensityChange at each modification, calling setText()
- // will trigger notifications, and we don't want that.
- if (mDisableUpdates == true) {
- return;
- }
-
- // update the current config
- String value = mDensity.getText();
-
- // empty string, means no qualifier.
- if (value.length() == 0) {
- mCurrentConfig.setPixelDensityQualifier(null);
+ int index = mDensity.getSelectionIndex();
+ if (index != 0) {
+ mCurrentConfig.setPixelDensityQualifier((new PixelDensityQualifier(
+ Density.getByIndex(index-1))));
} else {
- try {
- PixelDensityQualifier qualifier = PixelDensityQualifier.getQualifier(
- PixelDensityQualifier.getFolderSegment(Integer.parseInt(value)));
- if (qualifier != null) {
- mCurrentConfig.setPixelDensityQualifier(qualifier);
- } else {
- // Failure! Looks like the value is wrong (for instance a one letter string).
- // We do nothing in this case.
- return;
- }
- } catch (NumberFormatException e) {
- // Looks like the code is not a number. This should not happen since the text
- // field has a VerifyListener that prevents it.
- // We do nothing in this case.
- mDensityIcon.setImage(mErrorImage);
- return;
- }
+ mCurrentConfig.setPixelDensityQualifier(null);
}
// look for a file to open/create
if (mEditedFile == null || mEditedConfig == null) {
return;
}
-
+
// get the resources of the file's project.
ProjectResources resources = ResourceManager.getInstance().getProjectResources(
mEditedFile.getProject());
-
+
// from the resources, look for a matching file
ResourceFile match = null;
if (resources != null) {
// update the configuration icons with the new edited config.
setConfiguration(mEditedConfig, false /*force*/);
-
+
// enable the create button if the current and edited config are not equals
mCreateButton.setEnabled(mEditedConfig.equals(mCurrentConfig) == false);
} else {
// update the configuration icons with the new edited config.
displayConfigError();
-
+
// enable the Create button
mCreateButton.setEnabled(true);
String message = String.format(
"No resources match the configuration\n \n\t%1$s\n \nChange the configuration or create:\n \n\tres/%2$s/%3$s\n \nYou can also click the 'Create' button above.",
mCurrentConfig.toDisplayString(),
- mCurrentConfig.getFolderName(ResourceFolderType.LAYOUT),
+ mCurrentConfig.getFolderName(ResourceFolderType.LAYOUT,
+ Sdk.getCurrent().getTarget(mEditedFile.getProject())),
mEditedFile.getName());
showErrorInEditor(message);
}
int themeIndex = mThemeCombo.getSelectionIndex();
if (themeIndex != -1) {
String theme = mThemeCombo.getItem(themeIndex);
-
+
if (theme.equals(THEME_SEPARATOR)) {
mThemeCombo.select(0);
}
showErrorInEditor("The project target is not set.");
return;
}
-
+
AndroidTargetData data = currentSdk.getTargetData(target);
if (data == null) {
// It can happen that the workspace refreshes while the SDK is loading its
if (bridge.bridge != null) { // bridge can never be null.
ResourceManager resManager = ResourceManager.getInstance();
-
+
ProjectResources projectRes = resManager.getProjectResources(iProject);
if (projectRes == null) {
return;
}
-
+
// get the resources of the file's project.
if (mConfiguredProjectRes == null) {
// make sure they are loaded
projectRes.loadAll();
-
+
// get the project resource values based on the current config
mConfiguredProjectRes = projectRes.getConfiguredResources(mCurrentConfig);
}
-
+
// get the framework resources
Map<String, Map<String, IResourceValue>> frameworkResources =
getConfiguredFrameworkResources();
-
+
if (mConfiguredProjectRes != null && frameworkResources != null) {
if (mProjectCallback == null) {
mProjectCallback = new ProjectCallback(
bridge.classLoader, projectRes, iProject);
}
-
+
if (mLogger == null) {
mLogger = new ILayoutLog() {
public void error(String message) {
AdtPlugin.printErrorToConsole(mEditedFile.getName(), message);
}
-
+
public void error(Throwable error) {
String message = error.getMessage();
if (message == null) {
message = error.getClass().getName();
}
-
+
PrintStream ps = new PrintStream(AdtPlugin.getErrorStream());
error.printStackTrace(ps);
}
-
+
public void warning(String message) {
AdtPlugin.printToConsole(mEditedFile.getName(), message);
}
};
}
-
+
// get the selected theme
int themeIndex = mThemeCombo.getSelectionIndex();
if (themeIndex != -1) {
String theme = mThemeCombo.getItem(themeIndex);
-
+
// Compute the layout
UiElementPullParser parser = new UiElementPullParser(getModel());
Rectangle rect = getBounds();
boolean isProjectTheme = themeIndex >= mPlatformThemeCount;
// FIXME pass the density/dpi from somewhere (resource config or skin).
+ // For now, get it from the config
+ int density = Density.MEDIUM.getDpiValue();
+ PixelDensityQualifier qual = mCurrentConfig.getPixelDensityQualifier();
+ if (qual != null) {
+ int d = qual.getValue().getDpiValue();
+ if (d > 0) {
+ density = d;
+ }
+ }
+
ILayoutResult result = computeLayout(bridge, parser,
iProject /* projectKey */,
- rect.width, rect.height, 160, 160.f, 160.f,
+ rect.width, rect.height, density, density, density,
theme, isProjectTheme,
mConfiguredProjectRes, frameworkResources, mProjectCallback,
mLogger);
// update the UiElementNode with the layout info.
if (result.getSuccess() == ILayoutResult.SUCCESS) {
model.setEditData(result.getImage());
-
+
updateNodeWithBounds(result.getRootView());
} else {
String message = result.getErrorMessage();
-
+
// Reset the edit data for all the nodes.
resetNodeBounds(model);
-
+
if (message != null) {
// set the error in the top element.
model.setEditData(message);
}
}
-
+
model.refreshUi();
}
}
// clear the cache in the bridge in case a bitmap/9-patch changed.
IAndroidTarget target = Sdk.getCurrent().getTarget(mEditedFile.getProject());
if (target != null) {
-
+
AndroidTargetData data = Sdk.getCurrent().getTargetData(target);
if (data != null) {
LayoutBridge bridge = data.getLayoutBridge();
-
+
if (bridge.bridge != null) {
bridge.bridge.clearCaches(mEditedFile.getProject());
}
ProjectResources frameworkProject = getFrameworkResources();
mDisableUpdates = true;
-
+
// Reset stuff
int selection = mThemeCombo.getSelectionIndex();
mThemeCombo.removeAll();
mPlatformThemeCount = 0;
mLanguage.removeAll();
-
+
Set<String> languages = new HashSet<String>();
ArrayList<String> themes = new ArrayList<String>();
-
+
// get the themes, and languages from the Framework.
if (frameworkProject != null) {
// get the configured resources for the framework
Map<String, Map<String, IResourceValue>> frameworResources =
getConfiguredFrameworkResources();
-
+
if (frameworResources != null) {
// get the styles.
Map<String, IResourceValue> styles = frameworResources.get(
ResourceType.STYLE.getName());
-
-
+
+
// collect the themes out of all the styles.
for (IResourceValue value : styles.values()) {
String name = value.getName();
// sort them and add them to the combo
Collections.sort(themes);
-
+
for (String theme : themes) {
mThemeCombo.add(theme);
}
-
+
mPlatformThemeCount = themes.size();
themes.clear();
}
languages.addAll(frameworkLanguages);
}
}
-
+
// now get the themes and languages from the project.
ProjectResources project = null;
if (mEditedFile != null) {
// in cases where the opened file is not linked to a project, this could be null.
if (project != null) {
- // get the configured resources for the project
+ // get the configured resources for the project
if (mConfiguredProjectRes == null) {
// make sure they are loaded
project.loadAll();
// get the project resource values based on the current config
mConfiguredProjectRes = project.getConfiguredResources(mCurrentConfig);
}
-
+
if (mConfiguredProjectRes != null) {
// get the styles.
Map<String, IResourceValue> styleMap = mConfiguredProjectRes.get(
ResourceType.STYLE.getName());
-
+
if (styleMap != null) {
// collect the themes out of all the styles, ie styles that extend,
// directly or indirectly a platform theme.
if (mPlatformThemeCount > 0 && themes.size() > 0) {
mThemeCombo.add(THEME_SEPARATOR);
}
-
+
Collections.sort(themes);
-
+
for (String theme : themes) {
mThemeCombo.add(theme);
}
for (String language : languages) {
mLanguage.add(language);
}
-
+
mDisableUpdates = false;
// and update the Region UI based on the current language
private boolean isTheme(IResourceValue value, Map<String, IResourceValue> styleMap) {
if (value instanceof IStyleResourceValue) {
IStyleResourceValue style = (IStyleResourceValue)value;
-
+
boolean frameworkStyle = false;
String parentStyle = style.getParentStyle();
if (parentStyle == null) {
if (parentStyle.startsWith("@")) {
parentStyle = parentStyle.substring(1);
}
-
+
// check for framework identifier.
if (parentStyle.startsWith("android:")) {
frameworkStyle = true;
parentStyle = parentStyle.substring("android:".length());
}
-
+
// at this point we could have the format style/<name>. we want only the name
if (parentStyle.startsWith("style/")) {
parentStyle = parentStyle.substring("style/".length());
mDisableUpdates = false;
}
}
-
+
private Map<String, Map<String, IResourceValue>> getConfiguredFrameworkResources() {
if (mConfiguredFrameworkRes == null) {
ProjectResources frameworkRes = getFrameworkResources();
// get the framework resource values based on the current config
mConfiguredFrameworkRes = frameworkRes.getConfiguredResources(mCurrentConfig);
}
-
+
return mConfiguredFrameworkRes;
}
@Override
protected IStatus run(IProgressMonitor monitor) {
// get the folder name
- String folderName = config.getFolderName(ResourceFolderType.LAYOUT);
+ String folderName = config.getFolderName(ResourceFolderType.LAYOUT,
+ Sdk.getCurrent().getTarget(mEditedFile.getProject()));
try {
-
+
// look to see if it exists.
// get the res folder
IFolder res = (IFolder)mEditedFile.getParent().getParent();
String path = res.getLocation().toOSString();
-
+
File newLayoutFolder = new File(path + File.separator + folderName);
if (newLayoutFolder.isFile()) {
// this should not happen since aapt would have complained
// happen.
String message = String.format("File 'res/%1$s' is in the way!",
folderName);
-
+
AdtPlugin.displayError("Layout Creation", message);
-
+
return new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID, message);
} else if (newLayoutFolder.exists() == false) {
// create it.
newLayoutFolder.mkdir();
}
-
+
// now create the file
File newLayoutFile = new File(newLayoutFolder.getAbsolutePath() +
File.separator + mEditedFile.getName());
newLayoutFile.createNewFile();
-
+
InputStream input = mEditedFile.getContents();
-
+
FileOutputStream fos = new FileOutputStream(newLayoutFile);
-
+
byte[] data = new byte[512];
int count;
while ((count = input.read(data)) != -1) {
fos.write(data, 0, count);
}
-
+
input.close();
fos.close();
-
+
// refreshes the res folder to show up the new
// layout folder (if needed) and the file.
// We use a progress monitor to catch the end of the refresh
String message = String.format(
"Failed to create File 'res/%1$s/%2$s' : %3$s",
folderName, mEditedFile.getName(), e2.getMessage());
-
+
AdtPlugin.displayError("Layout Creation", message);
-
+
return new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID,
message, e2);
} catch (CoreException e2) {
String message = String.format(
"Failed to create File 'res/%1$s/%2$s' : %3$s",
folderName, mEditedFile.getName(), e2.getMessage());
-
+
AdtPlugin.displayError("Layout Creation", message);
return e2.getStatus();
}
-
+
return Status.OK_STATUS;
}
}.schedule();
}
-
+
/**
* Returns a {@link ProjectResources} for the framework resources.
* @return the framework resources or null if not found.
Sdk currentSdk = Sdk.getCurrent();
if (currentSdk != null) {
IAndroidTarget target = currentSdk.getTarget(mEditedFile.getProject());
-
+
if (target != null) {
AndroidTargetData data = currentSdk.getTargetData(target);
-
+
if (data != null) {
return data.getFrameworkResources();
}
return null;
}
-
+
/**
* Computes a layout by calling the correct computeLayout method of ILayoutBridge based on
* the implementation API level.
Map<String, Map<String, IResourceValue>> projectResources,
Map<String, Map<String, IResourceValue>> frameworkResources,
IProjectCallback projectCallback, ILayoutLog logger) {
-
+
if (bridge.apiLevel >= 3) {
// newer api with boolean for separation of project/framework theme,
// and density support.
return bridge.bridge.computeLayout(layoutDescription,
- projectKey, screenWidth, screenHeight, density, xdpi, ydpi,
+ projectKey, screenWidth, screenHeight, density, xdpi, ydpi,
themeName, isProjectTheme,
projectResources, frameworkResources, projectCallback,
logger);
import com.android.ide.eclipse.adt.internal.resources.manager.ResourceFolderType;
import com.android.ide.eclipse.adt.internal.ui.ConfigurationSelector;
import com.android.ide.eclipse.adt.internal.ui.ConfigurationSelector.ConfigurationState;
+import com.android.sdklib.IAndroidTarget;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.TrayDialog;
private ConfigurationSelector mSelector;
private Composite mStatusComposite;
- private Label mStatusLabel;
+ private Label mStatusLabel;
private Label mStatusImage;
private final FolderConfiguration mConfig = new FolderConfiguration();
private final String mFileName;
+ private final IAndroidTarget mTarget;
/**
* Creates a dialog, and init the UI from a {@link FolderConfiguration}.
* @param parentShell the parent {@link Shell}.
* @param config The starting configuration.
*/
- LayoutCreatorDialog(Shell parentShell, String fileName, FolderConfiguration config) {
+ LayoutCreatorDialog(Shell parentShell, String fileName, IAndroidTarget target,
+ FolderConfiguration config) {
super(parentShell);
- mFileName = fileName;
- // FIXME: add some data to know what configurations already exist.
+ mFileName = fileName;
+ mTarget = target;
+
+ // FIXME: add some data to know what configurations already exist.
mConfig.set(config);
}
new Label(top, SWT.NONE).setText(
String.format("Configuration for the alternate version of %1$s", mFileName));
-
+
mSelector = new ConfigurationSelector(top);
mSelector.setConfiguration(mConfig);
-
+
// parent's layout is a GridLayout as specified in the javadoc.
GridData gd = new GridData();
gd.widthHint = ConfigurationSelector.WIDTH_HINT;
gd.heightHint = ConfigurationSelector.HEIGHT_HINT;
mSelector.setLayoutData(gd);
-
+
// add a listener to check on the validity of the FolderConfiguration as
// they are built.
mSelector.setOnChangeListener(new Runnable() {
public void run() {
ConfigurationState state = mSelector.getState();
-
+
switch (state) {
case OK:
mSelector.getConfiguration(mConfig);
return top;
}
-
+
public void getConfiguration(FolderConfiguration config) {
config.set(mConfig);
}
-
+
/**
* resets the status label to show the file that will be created.
*/
private void resetStatus() {
mStatusLabel.setText(String.format("New File: res/%1$s/%2$s",
- mConfig.getFolderName(ResourceFolderType.LAYOUT), mFileName));
+ mConfig.getFolderName(ResourceFolderType.LAYOUT, mTarget), mFileName));
}
}
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.IDialogConstants;
-import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.viewers.ILabelProviderListener;
import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.ITableLabelProvider;
private final static int ICON_WIDTH = 16;
- private final static String PREFS_COL_SERIAL = "deviceChooser.serial"; //$NON-NLS-1$
- private final static String PREFS_COL_STATE = "deviceChooser.state"; //$NON-NLS-1$
- private final static String PREFS_COL_AVD = "deviceChooser.avd"; //$NON-NLS-1$
- private final static String PREFS_COL_TARGET = "deviceChooser.target"; //$NON-NLS-1$
- private final static String PREFS_COL_DEBUG = "deviceChooser.debug"; //$NON-NLS-1$
-
private Table mDeviceTable;
private TableViewer mViewer;
private AvdSelector mPreferredAvdSelector;
layout.marginLeft = 30;
offsetComp.setLayout(layout);
- IPreferenceStore store = AdtPlugin.getDefault().getPreferenceStore();
mDeviceTable = new Table(offsetComp, SWT.SINGLE | SWT.FULL_SELECTION | SWT.BORDER);
GridData gd;
mDeviceTable.setLayoutData(gd = new GridData(GridData.FILL_BOTH));
TableHelper.createTableColumn(mDeviceTable, "Serial Number",
SWT.LEFT, "AAA+AAAAAAAAAAAAAAAAAAA", //$NON-NLS-1$
- PREFS_COL_SERIAL, store);
+ null /* prefs name */, null /* prefs store */);
TableHelper.createTableColumn(mDeviceTable, "AVD Name",
- SWT.LEFT, "engineering", //$NON-NLS-1$
- PREFS_COL_AVD, store);
+ SWT.LEFT, "AAAAAAAAAAAAAAAAAAA", //$NON-NLS-1$
+ null /* prefs name */, null /* prefs store */);
TableHelper.createTableColumn(mDeviceTable, "Target",
SWT.LEFT, "AAA+Android 9.9.9", //$NON-NLS-1$
- PREFS_COL_TARGET, store);
+ null /* prefs name */, null /* prefs store */);
TableHelper.createTableColumn(mDeviceTable, "Debug",
SWT.LEFT, "Debug", //$NON-NLS-1$
- PREFS_COL_DEBUG, store);
+ null /* prefs name */, null /* prefs store */);
TableHelper.createTableColumn(mDeviceTable, "State",
SWT.LEFT, "bootloader", //$NON-NLS-1$
- PREFS_COL_STATE, store);
+ null /* prefs name */, null /* prefs store */);
// create the viewer for it
mViewer = new TableViewer(mDeviceTable);
* Export helper for project.
*/
public final class ExportHelper {
-
+
private static IExportCallback sCallback;
public interface IExportCallback {
void startExportWizard(IProject project);
}
-
+
public static void setCallback(IExportCallback callback) {
sCallback = callback;
}
-
+
public static void startExportWizard(IProject project) {
if (sCallback != null) {
sCallback.startExportWizard(project);
IFolder outputFolder = BaseProjectHelper.getOutputFolder(project);
if (outputFolder != null) {
IPath binLocation = outputFolder.getLocation();
-
+
// make the full path to the package
String fileName = project.getName() + AndroidConstants.DOT_ANDROID_PACKAGE;
-
+
File file = new File(binLocation.toOSString() + File.separator + fileName);
-
+
if (file.exists() == false || file.isFile() == false) {
- MessageDialog.openInformation(Display.getCurrent().getActiveShell(),
+ MessageDialog.openError(Display.getCurrent().getActiveShell(),
"Android IDE Plug-in",
String.format("Failed to export %1$s: %2$s doesn't exist!",
project.getName(), file.getPath()));
return;
}
-
+
// ok now pop up the file save window
FileDialog fileDialog = new FileDialog(shell, SWT.SAVE);
-
+
fileDialog.setText("Export Project");
fileDialog.setFileName(fileName);
-
+
String saveLocation = fileDialog.open();
if (saveLocation != null) {
// get the stream from the original file
-
+
ZipInputStream zis = null;
ZipOutputStream zos = null;
FileInputStream input = null;
// pass
}
}
-
- MessageDialog.openInformation(shell, "Android IDE Plug-in",
+
+ MessageDialog.openError(shell, "Android IDE Plug-in",
String.format("Failed to export %1$s: %2$s doesn't exist!",
project.getName(), file.getPath()));
return;
try {
ZipEntry entry;
-
+
byte[] buffer = new byte[4096];
while ((entry = zis.getNextEntry()) != null) {
String name = entry.getName();
-
+
// do not take directories or anything inside the META-INF folder since
// we want to strip the signature.
if (entry.isDirectory() || name.startsWith("META-INF/")) { //$NON-NL1$
continue;
}
-
+
ZipEntry newEntry;
-
+
// Preserve the STORED method of the input entry.
if (entry.getMethod() == JarEntry.STORED) {
newEntry = new JarEntry(entry);
// Create a new entry so that the compressed len is recomputed.
newEntry = new JarEntry(name);
}
-
+
// add the entry to the jar archive
zos.putNextEntry(newEntry);
// read the content of the entry from the input stream, and write it into the archive.
- int count;
+ int count;
while ((count = zis.read(buffer)) != -1) {
zos.write(buffer, 0, count);
}
// close the entry for this file
zos.closeEntry();
zis.closeEntry();
-
}
-
+
} catch (IOException e) {
- MessageDialog.openInformation(shell, "Android IDE Plug-in",
+ MessageDialog.openError(shell, "Android IDE Plug-in",
String.format("Failed to export %1$s: %2$s",
project.getName(), e.getMessage()));
} finally {
// pass
}
}
+
+ // this is unsigned export. Let's tell the developers to run zip align
+ MessageDialog.openWarning(shell, "Android IDE Plug-in", String.format(
+ "An unsigned package of the application was saved at\n%1$s\n\n" +
+ "Before publishing the application you will need to:\n" +
+ "- Sign the application with your release key,\n" +
+ "- run zipalign on the signed package. ZipAlign is located in <SDK>/tools/\n\n" +
+ "Aligning applications allows Android to use application resources\n" +
+ "more efficiently.", saveLocation));
+
}
} else {
- MessageDialog.openInformation(shell, "Android IDE Plug-in",
+ MessageDialog.openError(shell, "Android IDE Plug-in",
String.format("Failed to export %1$s: Could not get project output location",
project.getName()));
}
// recreate the res path from the current configuration
mConfigSelector.getConfiguration(mTempConfig);
StringBuffer sb = new StringBuffer(RES_FOLDER_ABS);
- sb.append(mTempConfig.getFolderName(ResourceFolderType.VALUES));
+ sb.append(mTempConfig.getFolderName(ResourceFolderType.VALUES, mProject));
sb.append('/');
String newPath = sb.toString();
append("\">"). //$NON-NLS-1$
append(tokenString).
append("</string>\n"); //$NON-NLS-1$
- content.append("<resources>\n"); //$NON-NLS-1$
+ content.append("</resources>\n"); //$NON-NLS-1$
edit = new InsertEdit(0, content.toString());
editGroup = new TextEditGroup("Create <string> in new XML file", edit);
package com.android.ide.eclipse.adt.internal.resources.configurations;
import com.android.ide.eclipse.adt.internal.editors.IconFactory;
+import com.android.sdklib.IAndroidTarget;
import org.eclipse.swt.graphics.Image;
private final static Pattern sCountryCodePattern = Pattern.compile("^mcc(\\d{3})$");//$NON-NLS-1$
private int mCode = DEFAULT_CODE;
-
+
public static final String NAME = "Mobile Country Code";
-
+
/**
* Creates and returns a qualifier from the given folder segment. If the segment is incorrect,
* <code>null</code> is returned.
// looks like the string we extracted wasn't a valid number.
return null;
}
-
+
CountryCodeQualifier qualifier = new CountryCodeQualifier();
qualifier.mCode = code;
return qualifier;
}
-
+
return null;
}
-
+
/**
* Returns the folder name segment for the given value. This is equivalent to calling
* {@link #toString()} on a {@link CountryCodeQualifier} object.
if (code != DEFAULT_CODE && code >= 100 && code <=999) { // code is 3 digit.) {
return String.format("mcc%1$d", code); //$NON-NLS-1$
}
-
+
return ""; //$NON-NLS-1$
}
-
+
public int getCode() {
return mCode;
}
-
+
@Override
public String getName() {
return NAME;
}
-
+
@Override
public String getShortName() {
return "Country Code";
}
-
+
@Override
public Image getIcon() {
return IconFactory.getInstance().getIcon("mcc"); //$NON-NLS-1$
}
-
+
@Override
public boolean isValid() {
return mCode != DEFAULT_CODE;
config.setCountryCodeQualifier(qualifier);
return true;
}
-
+
return false;
}
-
+
@Override
public boolean equals(Object qualifier) {
if (qualifier instanceof CountryCodeQualifier) {
return mCode == ((CountryCodeQualifier)qualifier).mCode;
}
-
+
return false;
}
-
+
@Override
public int hashCode() {
return mCode;
}
-
+
/**
* Returns the string used to represent this qualifier in the folder name.
*/
@Override
- public String toString() {
+ public String getFolderSegment(IAndroidTarget target) {
return getFolderSegment(mCode);
}
if (mCode != DEFAULT_CODE) {
return String.format("MCC %1$d", mCode);
}
-
+
return ""; //$NON-NLS-1$
}
}
package com.android.ide.eclipse.adt.internal.resources.configurations;
import com.android.ide.eclipse.adt.internal.resources.manager.ResourceFolderType;
+import com.android.ide.eclipse.adt.internal.sdk.Sdk;
+import com.android.sdklib.IAndroidTarget;
+
+import org.eclipse.core.resources.IProject;
/**
public final static String QUALIFIER_SEP = "-"; //$NON-NLS-1$
private final ResourceQualifier[] mQualifiers = new ResourceQualifier[INDEX_COUNT];
-
- private final static int INDEX_COUNTRY_CODE = 0;
- private final static int INDEX_NETWORK_CODE = 1;
- private final static int INDEX_LANGUAGE = 2;
- private final static int INDEX_REGION = 3;
- private final static int INDEX_SCREEN_ORIENTATION = 4;
- private final static int INDEX_PIXEL_DENSITY = 5;
- private final static int INDEX_TOUCH_TYPE = 6;
- private final static int INDEX_KEYBOARD_STATE = 7;
- private final static int INDEX_TEXT_INPUT_METHOD = 8;
- private final static int INDEX_NAVIGATION_METHOD = 9;
- private final static int INDEX_SCREEN_DIMENSION = 10;
- private final static int INDEX_COUNT = 11;
-
+
+ private final static int INDEX_COUNTRY_CODE = 0;
+ private final static int INDEX_NETWORK_CODE = 1;
+ private final static int INDEX_LANGUAGE = 2;
+ private final static int INDEX_REGION = 3;
+ private final static int INDEX_SCREEN_SIZE = 4;
+ private final static int INDEX_SCREEN_RATIO = 5;
+ private final static int INDEX_SCREEN_ORIENTATION = 6;
+ private final static int INDEX_PIXEL_DENSITY = 7;
+ private final static int INDEX_TOUCH_TYPE = 8;
+ private final static int INDEX_KEYBOARD_STATE = 9;
+ private final static int INDEX_TEXT_INPUT_METHOD = 10;
+ private final static int INDEX_NAVIGATION_METHOD = 11;
+ private final static int INDEX_SCREEN_DIMENSION = 12;
+ private final static int INDEX_VERSION = 13;
+ private final static int INDEX_COUNT = 14;
+
/**
* Sets the config from the qualifiers of a given <var>config</var>.
* @param config
}
}
}
-
+
/**
* Returns the first invalid qualifier, or <code>null<code> if they are all valid (or if none
* exists).
return mQualifiers[i];
}
}
-
+
// all allocated qualifiers are valid, we return null.
return null;
}
-
+
/**
* Returns whether the Region qualifier is valid. Region qualifier can only be present if a
* Language qualifier is present as well.
return true;
}
-
+
/**
* Adds a qualifier to the {@link FolderConfiguration}
* @param qualifier the {@link ResourceQualifier} to add.
mQualifiers[INDEX_LANGUAGE] = qualifier;
} else if (qualifier instanceof RegionQualifier) {
mQualifiers[INDEX_REGION] = qualifier;
+ } else if (qualifier instanceof ScreenSizeQualifier) {
+ mQualifiers[INDEX_SCREEN_SIZE] = qualifier;
+ } else if (qualifier instanceof ScreenRatioQualifier) {
+ mQualifiers[INDEX_SCREEN_RATIO] = qualifier;
} else if (qualifier instanceof ScreenOrientationQualifier) {
mQualifiers[INDEX_SCREEN_ORIENTATION] = qualifier;
} else if (qualifier instanceof PixelDensityQualifier) {
mQualifiers[INDEX_NAVIGATION_METHOD] = qualifier;
} else if (qualifier instanceof ScreenDimensionQualifier) {
mQualifiers[INDEX_SCREEN_DIMENSION] = qualifier;
+ } else if (qualifier instanceof VersionQualifier) {
+ mQualifiers[INDEX_VERSION] = qualifier;
}
}
-
+
/**
* Removes a given qualifier from the {@link FolderConfiguration}.
* @param qualifier the {@link ResourceQualifier} to remove.
}
}
}
-
+
public void setCountryCodeQualifier(CountryCodeQualifier qualifier) {
mQualifiers[INDEX_COUNTRY_CODE] = qualifier;
}
return (RegionQualifier)mQualifiers[INDEX_REGION];
}
+ public void setScreenSizeQualifier(ScreenSizeQualifier qualifier) {
+ mQualifiers[INDEX_SCREEN_SIZE] = qualifier;
+ }
+
+ public ScreenSizeQualifier getScreenSizeQualifier() {
+ return (ScreenSizeQualifier)mQualifiers[INDEX_SCREEN_SIZE];
+ }
+
+ public void setScreenRatioQualifier(ScreenRatioQualifier qualifier) {
+ mQualifiers[INDEX_SCREEN_RATIO] = qualifier;
+ }
+
+ public ScreenRatioQualifier getScreenRatioQualifier() {
+ return (ScreenRatioQualifier)mQualifiers[INDEX_SCREEN_RATIO];
+ }
+
public void setScreenOrientationQualifier(ScreenOrientationQualifier qualifier) {
mQualifiers[INDEX_SCREEN_ORIENTATION] = qualifier;
}
public TextInputMethodQualifier getTextInputMethodQualifier() {
return (TextInputMethodQualifier)mQualifiers[INDEX_TEXT_INPUT_METHOD];
}
-
+
public void setNavigationMethodQualifier(NavigationMethodQualifier qualifier) {
mQualifiers[INDEX_NAVIGATION_METHOD] = qualifier;
}
public NavigationMethodQualifier getNavigationMethodQualifier() {
return (NavigationMethodQualifier)mQualifiers[INDEX_NAVIGATION_METHOD];
}
-
+
public void setScreenDimensionQualifier(ScreenDimensionQualifier qualifier) {
mQualifiers[INDEX_SCREEN_DIMENSION] = qualifier;
}
return (ScreenDimensionQualifier)mQualifiers[INDEX_SCREEN_DIMENSION];
}
+ public void setVersionQualifier(VersionQualifier qualifier) {
+ mQualifiers[INDEX_VERSION] = qualifier;
+ }
+
+ public VersionQualifier getVersionQualifier() {
+ return (VersionQualifier)mQualifiers[INDEX_VERSION];
+ }
+
/**
* Returns whether an object is equals to the receiver.
*/
if (obj == this) {
return true;
}
-
+
if (obj instanceof FolderConfiguration) {
FolderConfiguration fc = (FolderConfiguration)obj;
for (int i = 0 ; i < INDEX_COUNT ; i++) {
return true;
}
-
+
return false;
}
public int hashCode() {
return toString().hashCode();
}
-
+
/**
* Returns whether the Configuration has only default values.
*/
return false;
}
}
-
+
return true;
}
-
+
/**
* Returns the name of a folder with the configuration.
*/
- public String getFolderName(ResourceFolderType folder) {
+ public String getFolderName(ResourceFolderType folder, IAndroidTarget target) {
StringBuilder result = new StringBuilder(folder.getName());
-
+
for (ResourceQualifier qualifier : mQualifiers) {
if (qualifier != null) {
result.append(QUALIFIER_SEP);
- result.append(qualifier.toString());
+ result.append(qualifier.getFolderSegment(target));
}
}
-
+
return result.toString();
}
-
+
/**
- * Returns a string valid for usage in a folder name, or <code>null</code> if the configuration
- * is default.
+ * Returns the name of a folder with the configuration.
*/
- @Override
- public String toString() {
- StringBuilder result = null;
-
- for (ResourceQualifier irq : mQualifiers) {
- if (irq != null) {
- if (result == null) {
- result = new StringBuilder();
- } else {
- result.append(QUALIFIER_SEP);
- }
- result.append(irq.toString());
+ public String getFolderName(ResourceFolderType folder, IProject project) {
+ IAndroidTarget target = null;
+ if (project != null) {
+ Sdk currentSdk = Sdk.getCurrent();
+ if (currentSdk != null) {
+ target = currentSdk.getTarget(project);
}
}
-
- if (result != null) {
- return result.toString();
- } else {
- return null;
- }
+
+ return getFolderName(folder, target);
}
-
+
+ /**
+ * Returns {@link #toDisplayString()}.
+ */
+ @Override
+ public String toString() {
+ return toDisplayString();
+ }
+
/**
* Returns a string valid for display purpose.
*/
StringBuilder result = null;
int index = 0;
ResourceQualifier qualifier = null;
-
+
// pre- language/region qualifiers
while (index < INDEX_LANGUAGE) {
qualifier = mQualifiers[index++];
result.append(", "); //$NON-NLS-1$
}
result.append(qualifier.getStringValue());
-
+
}
}
-
+
// process the language/region qualifier in a custom way, if there are both non null.
if (mQualifiers[INDEX_LANGUAGE] != null && mQualifiers[INDEX_REGION] != null) {
String language = mQualifiers[INDEX_LANGUAGE].getStringValue();
result.append(", "); //$NON-NLS-1$
}
result.append(String.format("%s_%s", language, region)); //$NON-NLS-1$
-
+
index += 2;
}
-
+
// post language/region qualifiers.
while (index < INDEX_COUNT) {
qualifier = mQualifiers[index++];
result.append(", "); //$NON-NLS-1$
}
result.append(qualifier.getStringValue());
-
+
}
}
}
return -1;
}
-
+
// now we compare the qualifiers
for (int i = 0 ; i < INDEX_COUNT; i++) {
ResourceQualifier qualifier1 = mQualifiers[i];
ResourceQualifier qualifier2 = folderConfig.mQualifiers[i];
-
+
if (qualifier1 == null) {
if (qualifier2 == null) {
continue;
return 1;
} else {
int result = qualifier1.compareTo(qualifier2);
-
+
if (result == 0) {
continue;
}
-
+
return result;
}
}
}
-
+
// if we arrive here, all the qualifier matches
return 0;
}
*/
public int match(FolderConfiguration referenceConfig) {
int matchCount = 0;
-
+
for (int i = 0 ; i < INDEX_COUNT ; i++) {
ResourceQualifier testQualifier = mQualifiers[i];
ResourceQualifier referenceQualifier = referenceConfig.mQualifiers[i];
-
+
// we only care if testQualifier is non null. If it's null, it's a match but
// without increasing the matchCount.
if (testQualifier != null) {
} else if (testQualifier.equals(referenceQualifier) == false) {
return -1;
}
-
+
// the qualifier match, increment the count
matchCount++;
}
return i;
}
}
-
+
return -1;
}
-
+
/**
* Create default qualifiers.
*/
mQualifiers[INDEX_NETWORK_CODE] = new NetworkCodeQualifier();
mQualifiers[INDEX_LANGUAGE] = new LanguageQualifier();
mQualifiers[INDEX_REGION] = new RegionQualifier();
+ mQualifiers[INDEX_SCREEN_SIZE] = new ScreenSizeQualifier();
+ mQualifiers[INDEX_SCREEN_RATIO] = new ScreenRatioQualifier();
mQualifiers[INDEX_SCREEN_ORIENTATION] = new ScreenOrientationQualifier();
mQualifiers[INDEX_PIXEL_DENSITY] = new PixelDensityQualifier();
mQualifiers[INDEX_TOUCH_TYPE] = new TouchScreenQualifier();
mQualifiers[INDEX_TEXT_INPUT_METHOD] = new TextInputMethodQualifier();
mQualifiers[INDEX_NAVIGATION_METHOD] = new NavigationMethodQualifier();
mQualifiers[INDEX_SCREEN_DIMENSION] = new ScreenDimensionQualifier();
+ mQualifiers[INDEX_VERSION] = new VersionQualifier();
}
/**
count++;
}
}
-
+
ResourceQualifier[] array = new ResourceQualifier[count];
int index = 0;
for (int i = 0 ; i < INDEX_COUNT ; i++) {
array[index++] = mQualifiers[i];
}
}
-
+
return array;
}
}
package com.android.ide.eclipse.adt.internal.resources.configurations;
import com.android.ide.eclipse.adt.internal.editors.IconFactory;
+import com.android.sdklib.IAndroidTarget;
import org.eclipse.swt.graphics.Image;
* Resource Qualifier for keyboard state.
*/
public final class KeyboardStateQualifier extends ResourceQualifier {
-
+
public static final String NAME = "Keyboard State";
private KeyboardState mValue = null;
public static enum KeyboardState {
EXPOSED("keysexposed", "Exposed"), //$NON-NLS-1$
HIDDEN("keyshidden", "Hidden"); //$NON-NLS-1$
-
+
private String mValue;
private String mDisplayValue;
-
+
private KeyboardState(String value, String displayValue) {
mValue = value;
mDisplayValue = displayValue;
}
-
+
/**
* Returns the enum for matching the provided qualifier value.
* @param value The qualifier value.
return orient;
}
}
-
+
return null;
}
public String getValue() {
return mValue;
}
-
+
public String getDisplayValue() {
return mDisplayValue;
}
-
+
public static int getIndex(KeyboardState value) {
int i = 0;
for (KeyboardState input : values()) {
if (value == input) {
return i;
}
-
+
i++;
}
public KeyboardState getValue() {
return mValue;
}
-
+
@Override
public String getName() {
return NAME;
}
-
+
@Override
public String getShortName() {
return "Keyboard";
}
-
+
@Override
public Image getIcon() {
return IconFactory.getInstance().getIcon("keyboard"); //$NON-NLS-1$
}
-
+
@Override
public boolean isValid() {
return mValue != null;
}
-
+
@Override
public boolean checkAndSet(String value, FolderConfiguration config) {
KeyboardState orientation = KeyboardState.getEnum(value);
config.setKeyboardStateQualifier(qualifier);
return true;
}
-
+
return false;
}
-
+
@Override
public boolean equals(Object qualifier) {
if (qualifier instanceof KeyboardStateQualifier) {
if (mValue != null) {
return mValue.hashCode();
}
-
+
return 0;
}
-
+
/**
* Returns the string used to represent this qualifier in the folder name.
*/
@Override
- public String toString() {
+ public String getFolderSegment(IAndroidTarget target) {
if (mValue != null) {
return mValue.getValue();
}
-
+
return ""; //$NON-NLS-1$
}
if (mValue != null) {
return mValue.getDisplayValue();
}
-
+
return ""; //$NON-NLS-1$
}
}
package com.android.ide.eclipse.adt.internal.resources.configurations;
import com.android.ide.eclipse.adt.internal.editors.IconFactory;
+import com.android.sdklib.IAndroidTarget;
import org.eclipse.swt.graphics.Image;
private final static Pattern sLanguagePattern = Pattern.compile("^[a-z]{2}$"); //$NON-NLS-1$
public static final String NAME = "Language";
-
+
private String mValue;
-
+
/**
* Creates and returns a qualifier from the given folder segment. If the segment is incorrect,
* <code>null</code> is returned.
if (sLanguagePattern.matcher(segment).matches()) {
LanguageQualifier qualifier = new LanguageQualifier();
qualifier.mValue = segment;
-
+
return qualifier;
}
return null;
}
-
+
/**
* Returns the folder name segment for the given value. This is equivalent to calling
* {@link #toString()} on a {@link LanguageQualifier} object.
if (sLanguagePattern.matcher(segment).matches()) {
return segment;
}
-
+
return null;
}
if (mValue != null) {
return mValue;
}
-
+
return ""; //$NON-NLS-1$
}
-
+
@Override
public String getName() {
return NAME;
}
-
+
@Override
public String getShortName() {
return NAME;
}
-
+
@Override
public Image getIcon() {
return IconFactory.getInstance().getIcon("language"); //$NON-NLS-1$
}
-
+
@Override
public boolean isValid() {
return mValue != null;
config.setLanguageQualifier(qualifier);
return true;
}
-
+
return false;
}
-
+
@Override
public boolean equals(Object qualifier) {
if (qualifier instanceof LanguageQualifier) {
}
return mValue.equals(((LanguageQualifier)qualifier).mValue);
}
-
+
return false;
}
if (mValue != null) {
return mValue.hashCode();
}
-
+
return 0;
}
-
+
/**
* Returns the string used to represent this qualifier in the folder name.
*/
@Override
- public String toString() {
+ public String getFolderSegment(IAndroidTarget target) {
if (mValue != null) {
return getFolderSegment(mValue);
}
if (mValue != null) {
return mValue;
}
-
+
return ""; //$NON-NLS-1$
}
}
package com.android.ide.eclipse.adt.internal.resources.configurations;
import com.android.ide.eclipse.adt.internal.editors.IconFactory;
+import com.android.sdklib.IAndroidTarget;
import org.eclipse.swt.graphics.Image;
* Resource Qualifier for Navigation Method.
*/
public final class NavigationMethodQualifier extends ResourceQualifier {
-
+
public static final String NAME = "Navigation Method";
private NavigationMethod mValue;
TRACKBALL("trackball", "Trackball"), //$NON-NLS-1$
WHEEL("wheel", "Wheel"), //$NON-NLS-1$
NONAV("nonav", "No Navigation"); //$NON-NLS-1$
-
+
private String mValue;
private String mDisplay;
-
+
private NavigationMethod(String value, String display) {
mValue = value;
mDisplay = display;
}
-
+
/**
* Returns the enum for matching the provided qualifier value.
* @param value The qualifier value.
return orient;
}
}
-
+
return null;
}
-
+
public String getValue() {
return mValue;
}
-
+
public String getDisplayValue() {
return mDisplay;
}
if (nav == value) {
return i;
}
-
+
i++;
}
return null;
}
}
-
+
public NavigationMethodQualifier() {
// pass
}
public NavigationMethod getValue() {
return mValue;
}
-
+
@Override
public String getName() {
return NAME;
}
-
+
@Override
public String getShortName() {
return "Navigation";
}
-
+
@Override
public Image getIcon() {
return IconFactory.getInstance().getIcon("navpad"); //$NON-NLS-1$
config.setNavigationMethodQualifier(qualifier);
return true;
}
-
+
return false;
}
-
+
@Override
public boolean equals(Object qualifier) {
if (qualifier instanceof NavigationMethodQualifier) {
return mValue == ((NavigationMethodQualifier)qualifier).mValue;
}
-
+
return false;
}
if (mValue != null) {
return mValue.hashCode();
}
-
+
return 0;
}
-
+
/**
* Returns the string used to represent this qualifier in the folder name.
*/
@Override
- public String toString() {
+ public String getFolderSegment(IAndroidTarget target) {
if (mValue != null) {
return mValue.getValue();
}
-
+
return ""; //$NON-NLS-1$
}
if (mValue != null) {
return mValue.getDisplayValue();
}
-
+
return ""; //$NON-NLS-1$
}
}
package com.android.ide.eclipse.adt.internal.resources.configurations;
import com.android.ide.eclipse.adt.internal.editors.IconFactory;
+import com.android.sdklib.IAndroidTarget;
import org.eclipse.swt.graphics.Image;
private final static Pattern sNetworkCodePattern = Pattern.compile("^mnc(\\d{1,3})$"); //$NON-NLS-1$
private int mCode = DEFAULT_CODE;
-
+
public final static String NAME = "Mobile Network Code";
-
+
/**
* Creates and returns a qualifier from the given folder segment. If the segment is incorrect,
* <code>null</code> is returned.
// looks like the string we extracted wasn't a valid number.
return null;
}
-
+
NetworkCodeQualifier qualifier = new NetworkCodeQualifier();
qualifier.mCode = code;
return qualifier;
if (code != DEFAULT_CODE && code >= 1 && code <= 999) { // code is 1-3 digit.
return String.format("mnc%1$d", code); //$NON-NLS-1$
}
-
+
return ""; //$NON-NLS-1$
}
public int getCode() {
return mCode;
}
-
+
@Override
public String getName() {
return NAME;
}
-
+
@Override
public String getShortName() {
return "Network Code";
}
-
+
@Override
public Image getIcon() {
return IconFactory.getInstance().getIcon("mnc"); //$NON-NLS-1$
}
-
+
@Override
public boolean isValid() {
return mCode != DEFAULT_CODE;
// looks like the string we extracted wasn't a valid number.
return false;
}
-
+
NetworkCodeQualifier qualifier = new NetworkCodeQualifier();
qualifier.mCode = code;
config.setNetworkCodeQualifier(qualifier);
return true;
}
-
+
return false;
}
-
+
@Override
public boolean equals(Object qualifier) {
if (qualifier instanceof NetworkCodeQualifier) {
return mCode == ((NetworkCodeQualifier)qualifier).mCode;
}
-
+
return false;
}
public int hashCode() {
return mCode;
}
-
+
/**
* Returns the string used to represent this qualifier in the folder name.
*/
@Override
- public String toString() {
+ public String getFolderSegment(IAndroidTarget target) {
return getFolderSegment(mCode);
}
if (mCode != DEFAULT_CODE) {
return String.format("MNC %1$d", mCode);
}
-
+
return ""; //$NON-NLS-1$
}
}
package com.android.ide.eclipse.adt.internal.resources.configurations;
import com.android.ide.eclipse.adt.internal.editors.IconFactory;
+import com.android.sdklib.AndroidVersion;
+import com.android.sdklib.IAndroidTarget;
import org.eclipse.swt.graphics.Image;
* Resource Qualifier for Screen Pixel Density.
*/
public final class PixelDensityQualifier extends ResourceQualifier {
- /** Default pixel density value. This means the property is not set. */
- private final static int DEFAULT_DENSITY = -1;
-
- private final static Pattern sPixelDensityPattern = Pattern.compile("^(\\d+)dpi$");//$NON-NLS-1$
+ private final static Pattern sDensityLegacyPattern = Pattern.compile("^(\\d+)dpi$");//$NON-NLS-1$
public static final String NAME = "Pixel Density";
- private int mValue = DEFAULT_DENSITY;
-
+ private Density mValue = Density.MEDIUM;
+
/**
- * Creates and returns a qualifier from the given folder segment. If the segment is incorrect,
- * <code>null</code> is returned.
- * @param folderSegment the folder segment from which to create a qualifier.
- * @return a new {@link CountryCodeQualifier} object or <code>null</code>
+ * Screen Orientation enum.
*/
- public static PixelDensityQualifier getQualifier(String folderSegment) {
- Matcher m = sPixelDensityPattern.matcher(folderSegment);
- if (m.matches()) {
- String v = m.group(1);
-
- int density = -1;
- try {
- density = Integer.parseInt(v);
- } catch (NumberFormatException e) {
- // looks like the string we extracted wasn't a valid number.
- return null;
+ public static enum Density {
+ HIGH("hdpi", 240, "High Density"), //$NON-NLS-1$
+ MEDIUM("mdpi", 160, "Medium Density"), //$NON-NLS-1$
+ LOW("ldpi", 120, "Low Density"), //$NON-NLS-1$
+ NODPI("nodpi", -1, "No Density"); //$NON-NLS-1$
+
+ private final String mValue;
+ private final String mDisplayValue;
+ private final int mDpiValue;
+
+ private Density(String value, int dpiValue, String displayValue) {
+ mValue = value;
+ mDpiValue = dpiValue;
+ mDisplayValue = displayValue;
+ }
+
+ /**
+ * Returns the enum for matching the provided qualifier value.
+ * @param value The qualifier value.
+ * @return the enum for the qualifier value or null if no matching was found.
+ */
+ static Density getEnum(String value) {
+ for (Density orient : values()) {
+ if (orient.mValue.equals(value)) {
+ return orient;
+ }
}
-
- PixelDensityQualifier qualifier = new PixelDensityQualifier();
- qualifier.mValue = density;
-
- return qualifier;
+
+ return null;
}
- return null;
- }
- /**
- * Returns the folder name segment for the given value. This is equivalent to calling
- * {@link #toString()} on a {@link NetworkCodeQualifier} object.
- * @param value the value of the qualifier, as returned by {@link #getValue()}.
- */
- public static String getFolderSegment(int value) {
- if (value != DEFAULT_DENSITY) {
- return String.format("%1$ddpi", value); //$NON-NLS-1$
+ static Density getLegacyEnum(String value) {
+ Matcher m = sDensityLegacyPattern.matcher(value);
+ if (m.matches()) {
+ String v = m.group(1);
+
+ try {
+ int density = Integer.parseInt(v);
+ for (Density orient : values()) {
+ if (orient.mDpiValue == density) {
+ return orient;
+ }
+ }
+ } catch (NumberFormatException e) {
+ // looks like the string we extracted wasn't a valid number
+ // which really shouldn't happen since the regexp would have failed.
+ }
+ }
+ return null;
}
-
- return ""; //$NON-NLS-1$
+
+ public String getValue() {
+ return mValue;
+ }
+
+ public int getDpiValue() {
+ return mDpiValue;
+ }
+
+ public String getLegacyValue() {
+ if (this != NODPI) {
+ return String.format("%1$ddpi", mDpiValue);
+ }
+
+ return "";
+ }
+
+ public String getDisplayValue() {
+ return mDisplayValue;
+ }
+
+ public static int getIndex(Density value) {
+ int i = 0;
+ for (Density input : values()) {
+ if (value == input) {
+ return i;
+ }
+
+ i++;
+ }
+
+ return -1;
+ }
+
+ public static Density getByIndex(int index) {
+ int i = 0;
+ for (Density value : values()) {
+ if (i == index) {
+ return value;
+ }
+ i++;
+ }
+ return null;
+ }
+ }
+
+ public PixelDensityQualifier() {
+ // pass
+ }
+
+ public PixelDensityQualifier(Density value) {
+ mValue = value;
}
- public int getValue() {
+ public Density getValue() {
return mValue;
}
-
+
@Override
public String getName() {
return NAME;
}
-
+
@Override
public String getShortName() {
return NAME;
}
-
+
@Override
public Image getIcon() {
return IconFactory.getInstance().getIcon("dpi"); //$NON-NLS-1$
}
-
+
@Override
public boolean isValid() {
- return mValue != DEFAULT_DENSITY;
+ return mValue != null;
}
@Override
public boolean checkAndSet(String value, FolderConfiguration config) {
- PixelDensityQualifier qualifier = getQualifier(value);
- if (qualifier != null) {
+ Density density = Density.getEnum(value);
+ if (density == null) {
+ density = Density.getLegacyEnum(value);
+ }
+
+ if (density != null) {
+ PixelDensityQualifier qualifier = new PixelDensityQualifier();
+ qualifier.mValue = density;
config.setPixelDensityQualifier(qualifier);
return true;
}
-
+
return false;
}
-
+
@Override
public boolean equals(Object qualifier) {
if (qualifier instanceof PixelDensityQualifier) {
return mValue == ((PixelDensityQualifier)qualifier).mValue;
}
-
+
return false;
}
@Override
public int hashCode() {
- return mValue;
+ if (mValue != null) {
+ return mValue.hashCode();
+ }
+
+ return 0;
}
-
+
/**
* Returns the string used to represent this qualifier in the folder name.
*/
@Override
- public String toString() {
- return getFolderSegment(mValue);
+ public String getFolderSegment(IAndroidTarget target) {
+ if (mValue != null) {
+ if (target != null) {
+ AndroidVersion version = target.getVersion();
+ if (version.getApiLevel() <= 3 && version.getCodename() == null) {
+ return mValue.getLegacyValue();
+ }
+ }
+ return mValue.getValue();
+ }
+
+ return ""; //$NON-NLS-1$
}
@Override
public String getStringValue() {
- if (mValue != DEFAULT_DENSITY) {
- return String.format("%1$d dpi", mValue);
+ if (mValue != null) {
+ return mValue.getDisplayValue();
}
-
+
return ""; //$NON-NLS-1$
}
}
package com.android.ide.eclipse.adt.internal.resources.configurations;
import com.android.ide.eclipse.adt.internal.editors.IconFactory;
+import com.android.sdklib.IAndroidTarget;
import org.eclipse.swt.graphics.Image;
private final static Pattern sRegionPattern = Pattern.compile("^r([A-Z]{2})$"); //$NON-NLS-1$
public static final String NAME = "Region";
-
+
private String mValue;
-
+
/**
* Creates and returns a qualifier from the given folder segment. If the segment is incorrect,
* <code>null</code> is returned.
}
return null;
}
-
+
/**
* Returns the folder name segment for the given value. This is equivalent to calling
* {@link #toString()} on a {@link RegionQualifier} object.
return segment;
}
}
-
+
return ""; //$NON-NLS-1$
}
if (mValue != null) {
return mValue;
}
-
+
return ""; //$NON-NLS-1$
}
-
+
@Override
public String getName() {
return NAME;
}
-
+
@Override
public String getShortName() {
return NAME;
}
-
+
@Override
public Image getIcon() {
return IconFactory.getInstance().getIcon("region"); //$NON-NLS-1$
config.setRegionQualifier(qualifier);
return true;
}
-
+
return false;
}
-
+
@Override
public boolean equals(Object qualifier) {
if (qualifier instanceof RegionQualifier) {
}
return mValue.equals(((RegionQualifier)qualifier).mValue);
}
-
+
return false;
}
if (mValue != null) {
return mValue.hashCode();
}
-
+
return 0;
}
-
+
/**
* Returns the string used to represent this qualifier in the folder name.
*/
@Override
- public String toString() {
+ public String getFolderSegment(IAndroidTarget target) {
return getFolderSegment(mValue);
}
if (mValue != null) {
return mValue;
}
-
+
return ""; //$NON-NLS-1$
}
}
package com.android.ide.eclipse.adt.internal.resources.configurations;
+import com.android.sdklib.IAndroidTarget;
+
import org.eclipse.swt.graphics.Image;
/**
* <p/>The resource qualifier classes are designed as immutable.
*/
public abstract class ResourceQualifier implements Comparable<ResourceQualifier> {
-
+
/**
* Returns the human readable name of the qualifier.
*/
public abstract String getName();
-
+
/**
* Returns a shorter human readable name for the qualifier.
* @see #getName()
*/
public abstract String getShortName();
-
+
/**
* Returns the icon for the qualifier.
*/
public abstract Image getIcon();
-
+
/**
* Returns whether the qualifier has a valid filter value.
*/
public abstract boolean isValid();
-
+
/**
* Check if the value is valid for this qualifier, and if so sets the value
* into a Folder Configuration.
* @return true if the value was valid and was set.
*/
public abstract boolean checkAndSet(String value, FolderConfiguration config);
-
+
/**
* Returns a string formated to be used in a folder name.
* <p/>This is declared as abstract to force children classes to implement it.
*/
+ public abstract String getFolderSegment(IAndroidTarget target);
+
@Override
- public abstract String toString();
+ public String toString() {
+ return getFolderSegment(null);
+ }
/**
* Returns a string formatted for display purpose.
*/
@Override
public abstract boolean equals(Object object);
-
+
/**
* Returns a hash code value for the object.
* <p/>This is declared as abstract to force children classes to implement it.
package com.android.ide.eclipse.adt.internal.resources.configurations;
import com.android.ide.eclipse.adt.internal.editors.IconFactory;
+import com.android.sdklib.IAndroidTarget;
import org.eclipse.swt.graphics.Image;
* 200 but that'll be Y in landscape and X in portrait.
* Default value is <code>DEFAULT_SIZE</code> */
private int mValue2 = DEFAULT_SIZE;
-
+
public int getValue1() {
return mValue1;
}
public int getValue2() {
return mValue2;
}
-
+
@Override
public String getName() {
return NAME;
}
-
+
@Override
public String getShortName() {
return "Dimension";
}
-
+
@Override
public Image getIcon() {
return IconFactory.getInstance().getIcon("dimension"); //$NON-NLS-1$
if (m.matches()) {
String d1 = m.group(1);
String d2 = m.group(2);
-
+
ScreenDimensionQualifier qualifier = getQualifier(d1, d2);
if (qualifier != null) {
config.setScreenDimensionQualifier(qualifier);
}
return false;
}
-
+
@Override
public boolean equals(Object qualifier) {
if (qualifier instanceof ScreenDimensionQualifier) {
ScreenDimensionQualifier q = (ScreenDimensionQualifier)qualifier;
return (mValue1 == q.mValue1 && mValue2 == q.mValue2);
}
-
+
return false;
}
public int hashCode() {
return toString().hashCode();
}
-
+
public static ScreenDimensionQualifier getQualifier(String size1, String size2) {
try {
int s1 = Integer.parseInt(size1);
int s2 = Integer.parseInt(size2);
-
+
ScreenDimensionQualifier qualifier = new ScreenDimensionQualifier();
if (s1 > s2) {
} catch (NumberFormatException e) {
// looks like the string we extracted wasn't a valid number.
}
-
+
return null;
}
* Returns the string used to represent this qualifier in the folder name.
*/
@Override
- public String toString() {
+ public String getFolderSegment(IAndroidTarget target) {
return String.format("%1$dx%2$d", mValue1, mValue2); //$NON-NLS-1$
}
if (mValue1 != -1 && mValue2 != -1) {
return String.format("%1$dx%2$d", mValue1, mValue2);
}
-
+
return ""; //$NON-NLS-1$
}
}
package com.android.ide.eclipse.adt.internal.resources.configurations;
import com.android.ide.eclipse.adt.internal.editors.IconFactory;
+import com.android.sdklib.IAndroidTarget;
import org.eclipse.swt.graphics.Image;
* Resource Qualifier for Screen Orientation.
*/
public final class ScreenOrientationQualifier extends ResourceQualifier {
-
+
public static final String NAME = "Screen Orientation";
private ScreenOrientation mValue = null;
PORTRAIT("port", "Portrait"), //$NON-NLS-1$
LANDSCAPE("land", "Landscape"), //$NON-NLS-1$
SQUARE("square", "Square"); //$NON-NLS-1$
-
+
private String mValue;
private String mDisplayValue;
-
+
private ScreenOrientation(String value, String displayValue) {
mValue = value;
mDisplayValue = displayValue;
}
-
+
/**
* Returns the enum for matching the provided qualifier value.
* @param value The qualifier value.
public String getValue() {
return mValue;
}
-
+
public String getDisplayValue() {
return mDisplayValue;
}
-
+
public static int getIndex(ScreenOrientation orientation) {
int i = 0;
for (ScreenOrientation orient : values()) {
if (orient == orientation) {
return i;
}
-
+
i++;
}
public ScreenOrientation getValue() {
return mValue;
}
-
+
@Override
public String getName() {
return NAME;
}
-
+
@Override
public String getShortName() {
return "Orientation";
}
-
+
@Override
public Image getIcon() {
return IconFactory.getInstance().getIcon("orientation"); //$NON-NLS-1$
}
-
+
@Override
public boolean isValid() {
return mValue != null;
config.setScreenOrientationQualifier(qualifier);
return true;
}
-
+
return false;
}
-
+
@Override
public boolean equals(Object qualifier) {
if (qualifier instanceof ScreenOrientationQualifier) {
if (mValue != null) {
return mValue.hashCode();
}
-
+
return 0;
}
-
+
/**
* Returns the string used to represent this qualifier in the folder name.
*/
@Override
- public String toString() {
+ public String getFolderSegment(IAndroidTarget target) {
if (mValue != null) {
return mValue.getValue();
}
-
+
return ""; //$NON-NLS-1$
}
if (mValue != null) {
return mValue.getDisplayValue();
}
-
+
return ""; //$NON-NLS-1$
}
}
--- /dev/null
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php
+ *
+ * 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.ide.eclipse.adt.internal.resources.configurations;
+
+import com.android.ide.eclipse.adt.internal.editors.IconFactory;
+import com.android.sdklib.AndroidVersion;
+import com.android.sdklib.IAndroidTarget;
+
+import org.eclipse.swt.graphics.Image;
+
+public class ScreenRatioQualifier extends ResourceQualifier {
+
+ public static final String NAME = "Screen Ratio";
+
+ private ScreenRatio mValue = null;
+
+ /**
+ * Screen Orientation enum.
+ */
+ public static enum ScreenRatio {
+ NOTLONG("notlong", "Not Long"), //$NON-NLS-1$
+ LONG("long", "Long"); //$NON-NLS-1$
+
+ private String mValue;
+ private String mDisplayValue;
+
+ private ScreenRatio(String value, String displayValue) {
+ mValue = value;
+ mDisplayValue = displayValue;
+ }
+
+ /**
+ * Returns the enum for matching the provided qualifier value.
+ * @param value The qualifier value.
+ * @return the enum for the qualifier value or null if no matching was found.
+ */
+ static ScreenRatio getEnum(String value) {
+ for (ScreenRatio orient : values()) {
+ if (orient.mValue.equals(value)) {
+ return orient;
+ }
+ }
+
+ return null;
+ }
+
+ public String getValue() {
+ return mValue;
+ }
+
+ public String getDisplayValue() {
+ return mDisplayValue;
+ }
+
+ public static int getIndex(ScreenRatio orientation) {
+ int i = 0;
+ for (ScreenRatio orient : values()) {
+ if (orient == orientation) {
+ return i;
+ }
+
+ i++;
+ }
+
+ return -1;
+ }
+
+ public static ScreenRatio getByIndex(int index) {
+ int i = 0;
+ for (ScreenRatio orient : values()) {
+ if (i == index) {
+ return orient;
+ }
+ i++;
+ }
+
+ return null;
+ }
+ }
+
+ public ScreenRatioQualifier() {
+ }
+
+ public ScreenRatioQualifier(ScreenRatio value) {
+ mValue = value;
+ }
+
+ public ScreenRatio getValue() {
+ return mValue;
+ }
+
+ @Override
+ public String getName() {
+ return NAME;
+ }
+
+ @Override
+ public String getShortName() {
+ return "Ratio";
+ }
+
+ @Override
+ public Image getIcon() {
+ return IconFactory.getInstance().getIcon("ratio"); //$NON-NLS-1$
+ }
+
+ @Override
+ public boolean isValid() {
+ return mValue != null;
+ }
+
+ @Override
+ public boolean checkAndSet(String value, FolderConfiguration config) {
+ ScreenRatio size = ScreenRatio.getEnum(value);
+ if (size != null) {
+ ScreenRatioQualifier qualifier = new ScreenRatioQualifier(size);
+ config.setScreenRatioQualifier(qualifier);
+ return true;
+ }
+
+ return false;
+ }
+
+ @Override
+ public boolean equals(Object qualifier) {
+ if (qualifier instanceof ScreenRatioQualifier) {
+ return mValue == ((ScreenRatioQualifier)qualifier).mValue;
+ }
+
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ if (mValue != null) {
+ return mValue.hashCode();
+ }
+
+ return 0;
+ }
+
+ /**
+ * Returns the string used to represent this qualifier in the folder name.
+ */
+ @Override
+ public String getFolderSegment(IAndroidTarget target) {
+ if (mValue != null) {
+ if (target == null) {
+ // Default behavior (when target==null) is qualifier is supported
+ return mValue.getValue();
+ }
+
+ AndroidVersion version = target.getVersion();
+ if (version.getApiLevel() >= 4 ||
+ (version.getApiLevel() == 3 && "Donut".equals(version.getCodename()))) {
+ return mValue.getValue();
+ }
+ }
+
+ return ""; //$NON-NLS-1$
+ }
+
+ @Override
+ public String getStringValue() {
+ if (mValue != null) {
+ return mValue.getDisplayValue();
+ }
+
+ return ""; //$NON-NLS-1$
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php
+ *
+ * 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.ide.eclipse.adt.internal.resources.configurations;
+
+import com.android.ide.eclipse.adt.internal.editors.IconFactory;
+import com.android.sdklib.AndroidVersion;
+import com.android.sdklib.IAndroidTarget;
+
+import org.eclipse.swt.graphics.Image;
+
+/**
+ * Resource Qualifier for Screen Size. Size can be "small", "normal", and "large"
+ */
+public class ScreenSizeQualifier extends ResourceQualifier {
+
+ public static final String NAME = "Screen Size";
+
+ private ScreenSize mValue = null;
+
+ /**
+ * Screen Orientation enum.
+ */
+ public static enum ScreenSize {
+ SMALL("small", "Small"), //$NON-NLS-1$
+ NORMAL("normal", "Normal"), //$NON-NLS-1$
+ LARGE("large", "Large"); //$NON-NLS-1$
+
+ private String mValue;
+ private String mDisplayValue;
+
+ private ScreenSize(String value, String displayValue) {
+ mValue = value;
+ mDisplayValue = displayValue;
+ }
+
+ /**
+ * Returns the enum for matching the provided qualifier value.
+ * @param value The qualifier value.
+ * @return the enum for the qualifier value or null if no matching was found.
+ */
+ static ScreenSize getEnum(String value) {
+ for (ScreenSize orient : values()) {
+ if (orient.mValue.equals(value)) {
+ return orient;
+ }
+ }
+
+ return null;
+ }
+
+ public String getValue() {
+ return mValue;
+ }
+
+ public String getDisplayValue() {
+ return mDisplayValue;
+ }
+
+ public static int getIndex(ScreenSize orientation) {
+ int i = 0;
+ for (ScreenSize orient : values()) {
+ if (orient == orientation) {
+ return i;
+ }
+
+ i++;
+ }
+
+ return -1;
+ }
+
+ public static ScreenSize getByIndex(int index) {
+ int i = 0;
+ for (ScreenSize orient : values()) {
+ if (i == index) {
+ return orient;
+ }
+ i++;
+ }
+
+ return null;
+ }
+ }
+
+ public ScreenSizeQualifier() {
+ }
+
+ public ScreenSizeQualifier(ScreenSize value) {
+ mValue = value;
+ }
+
+ public ScreenSize getValue() {
+ return mValue;
+ }
+
+ @Override
+ public String getName() {
+ return NAME;
+ }
+
+ @Override
+ public String getShortName() {
+ return "Size";
+ }
+
+ @Override
+ public Image getIcon() {
+ return IconFactory.getInstance().getIcon("size"); //$NON-NLS-1$
+ }
+
+ @Override
+ public boolean isValid() {
+ return mValue != null;
+ }
+
+ @Override
+ public boolean checkAndSet(String value, FolderConfiguration config) {
+ ScreenSize size = ScreenSize.getEnum(value);
+ if (size != null) {
+ ScreenSizeQualifier qualifier = new ScreenSizeQualifier(size);
+ config.setScreenSizeQualifier(qualifier);
+ return true;
+ }
+
+ return false;
+ }
+
+ @Override
+ public boolean equals(Object qualifier) {
+ if (qualifier instanceof ScreenSizeQualifier) {
+ return mValue == ((ScreenSizeQualifier)qualifier).mValue;
+ }
+
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ if (mValue != null) {
+ return mValue.hashCode();
+ }
+
+ return 0;
+ }
+
+ /**
+ * Returns the string used to represent this qualifier in the folder name.
+ */
+ @Override
+ public String getFolderSegment(IAndroidTarget target) {
+ if (mValue != null) {
+ if (target == null) {
+ // Default behavior (when target==null) is qualifier is supported
+ return mValue.getValue();
+ }
+
+ AndroidVersion version = target.getVersion();
+ if (version.getApiLevel() >= 4 ||
+ (version.getApiLevel() == 3 && "Donut".equals(version.getCodename()))) {
+ return mValue.getValue();
+ }
+ }
+
+ return ""; //$NON-NLS-1$
+ }
+
+ @Override
+ public String getStringValue() {
+ if (mValue != null) {
+ return mValue.getDisplayValue();
+ }
+
+ return ""; //$NON-NLS-1$
+ }
+}
package com.android.ide.eclipse.adt.internal.resources.configurations;
import com.android.ide.eclipse.adt.internal.editors.IconFactory;
+import com.android.sdklib.IAndroidTarget;
import org.eclipse.swt.graphics.Image;
public static final String NAME = "Text Input Method";
private TextInputMethod mValue;
-
+
/**
* Screen Orientation enum.
*/
NOKEY("nokeys", "No Keys"), //$NON-NLS-1$
QWERTY("qwerty", "Qwerty"), //$NON-NLS-1$
TWELVEKEYS("12key", "12 Key"); //$NON-NLS-1$
-
+
private String mValue;
private String mDisplayValue;
-
+
private TextInputMethod(String value, String displayValue) {
mValue = value;
mDisplayValue = displayValue;
}
-
+
/**
* Returns the enum for matching the provided qualifier value.
* @param value The qualifier value.
return orient;
}
}
-
+
return null;
}
public String getValue() {
return mValue;
}
-
+
public String getDisplayValue() {
return mDisplayValue;
}
if (value == input) {
return i;
}
-
+
i++;
}
return null;
}
}
-
+
public TextInputMethodQualifier() {
// pass
}
public TextInputMethod getValue() {
return mValue;
}
-
+
@Override
public String getName() {
return NAME;
}
-
+
@Override
public String getShortName() {
return "Text Input";
}
-
+
@Override
public Image getIcon() {
return IconFactory.getInstance().getIcon("text_input"); //$NON-NLS-1$
config.setTextInputMethodQualifier(qualifier);
return true;
}
-
+
return false;
}
-
+
@Override
public boolean equals(Object qualifier) {
if (qualifier instanceof TextInputMethodQualifier) {
if (mValue != null) {
return mValue.hashCode();
}
-
+
return 0;
}
* Returns the string used to represent this qualifier in the folder name.
*/
@Override
- public String toString() {
+ public String getFolderSegment(IAndroidTarget target) {
if (mValue != null) {
return mValue.getValue();
}
-
+
return ""; //$NON-NLS-1$
}
if (mValue != null) {
return mValue.getDisplayValue();
}
-
+
return ""; //$NON-NLS-1$
}
}
package com.android.ide.eclipse.adt.internal.resources.configurations;
import com.android.ide.eclipse.adt.internal.editors.IconFactory;
+import com.android.sdklib.IAndroidTarget;
import org.eclipse.swt.graphics.Image;
public static final String NAME = "Touch Screen";
private TouchScreenType mValue;
-
+
/**
* Screen Orientation enum.
*/
NOTOUCH("notouch", "No Touch"), //$NON-NLS-1$
STYLUS("stylus", "Stylus"), //$NON-NLS-1$
FINGER("finger", "Finger"); //$NON-NLS-1$
-
+
private String mValue;
private String mDisplayValue;
-
+
private TouchScreenType(String value, String displayValue) {
mValue = value;
mDisplayValue = displayValue;
}
-
+
/**
* Returns the enum for matching the provided qualifier value.
* @param value The qualifier value.
return orient;
}
}
-
+
return null;
}
public String getValue() {
return mValue;
}
-
+
public String getDisplayValue() {
return mDisplayValue;
}
if (t == touch) {
return i;
}
-
+
i++;
}
return null;
}
}
-
+
public TouchScreenQualifier() {
// pass
}
public TouchScreenType getValue() {
return mValue;
}
-
+
@Override
public String getName() {
return NAME;
}
-
+
@Override
public String getShortName() {
return NAME;
}
-
+
@Override
public Image getIcon() {
return IconFactory.getInstance().getIcon("touch"); //$NON-NLS-1$
config.setTouchTypeQualifier(qualifier);
return true;
}
-
+
return false;
}
-
+
@Override
public boolean equals(Object qualifier) {
if (qualifier instanceof TouchScreenQualifier) {
if (mValue != null) {
return mValue.hashCode();
}
-
+
return 0;
}
* Returns the string used to represent this qualifier in the folder name.
*/
@Override
- public String toString() {
+ public String getFolderSegment(IAndroidTarget target) {
if (mValue != null) {
return mValue.getValue();
}
-
+
return ""; //$NON-NLS-1$
}
if (mValue != null) {
return mValue.getDisplayValue();
}
-
+
return ""; //$NON-NLS-1$
}
}
--- /dev/null
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php
+ *
+ * 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.ide.eclipse.adt.internal.resources.configurations;
+
+import com.android.ide.eclipse.adt.internal.editors.IconFactory;
+import com.android.sdklib.AndroidVersion;
+import com.android.sdklib.IAndroidTarget;
+
+import org.eclipse.swt.graphics.Image;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Resource Qualifier for Platform Version.
+ */
+public final class VersionQualifier extends ResourceQualifier {
+ /** Default pixel density value. This means the property is not set. */
+ private final static int DEFAULT_VERSION = -1;
+
+ private final static Pattern sCountryCodePattern = Pattern.compile("^v(\\d+)$");//$NON-NLS-1$
+
+ private int mVersion = DEFAULT_VERSION;
+
+ public static final String NAME = "Platform Version";
+
+ /**
+ * Creates and returns a qualifier from the given folder segment. If the segment is incorrect,
+ * <code>null</code> is returned.
+ * @param segment the folder segment from which to create a qualifier.
+ * @return a new {@link VersionQualifier} object or <code>null</code>
+ */
+ public static VersionQualifier getQualifier(String segment) {
+ Matcher m = sCountryCodePattern.matcher(segment);
+ if (m.matches()) {
+ String v = m.group(1);
+
+ int code = -1;
+ try {
+ code = Integer.parseInt(v);
+ } catch (NumberFormatException e) {
+ // looks like the string we extracted wasn't a valid number.
+ return null;
+ }
+
+ VersionQualifier qualifier = new VersionQualifier();
+ qualifier.mVersion = code;
+ return qualifier;
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns the folder name segment for the given value. This is equivalent to calling
+ * {@link #toString()} on a {@link VersionQualifier} object.
+ * @param version the value of the qualifier, as returned by {@link #getVersion()}.
+ */
+ public static String getFolderSegment(int version) {
+ if (version != DEFAULT_VERSION) {
+ return String.format("v%1$d", version); //$NON-NLS-1$
+ }
+
+ return ""; //$NON-NLS-1$
+ }
+
+ public int getVersion() {
+ return mVersion;
+ }
+
+ @Override
+ public String getName() {
+ return NAME;
+ }
+
+ @Override
+ public String getShortName() {
+ return "Version";
+ }
+
+ @Override
+ public Image getIcon() {
+ return IconFactory.getInstance().getIcon("version"); //$NON-NLS-1$
+ }
+
+ @Override
+ public boolean isValid() {
+ return mVersion != DEFAULT_VERSION;
+ }
+
+ @Override
+ public boolean checkAndSet(String value, FolderConfiguration config) {
+ VersionQualifier qualifier = getQualifier(value);
+ if (qualifier != null) {
+ config.setVersionQualifier(qualifier);
+ return true;
+ }
+
+ return false;
+ }
+
+ @Override
+ public boolean equals(Object qualifier) {
+ if (qualifier instanceof VersionQualifier) {
+ return mVersion == ((VersionQualifier)qualifier).mVersion;
+ }
+
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return mVersion;
+ }
+
+ /**
+ * Returns the string used to represent this qualifier in the folder name.
+ */
+ @Override
+ public String getFolderSegment(IAndroidTarget target) {
+ if (target == null) {
+ // Default behavior (when target==null) is qualifier is supported
+ return getFolderSegment(mVersion);
+ }
+
+ AndroidVersion version = target.getVersion();
+ if (version.getApiLevel() >= 3) {
+ return getFolderSegment(mVersion);
+ }
+
+ return ""; //$NON-NLS-1$
+ }
+
+ @Override
+ public String getStringValue() {
+ if (mVersion != DEFAULT_VERSION) {
+ return String.format("API %1$d", mVersion);
+ }
+
+ return ""; //$NON-NLS-1$
+ }
+}
/** List of the qualifier object helping for the parsing of folder names */
private final ResourceQualifier[] mQualifiers;
-
+
/**
* Map associating project resource with project objects.
*/
private final HashMap<IProject, ProjectResources> mMap =
new HashMap<IProject, ProjectResources>();
-
+
/**
* Sets up the resource manager with the global resource monitor.
* @param monitor The global resource monitor
int mask = IResourceDelta.ADDED | IResourceDelta.REMOVED | IResourceDelta.CHANGED;
monitor.addFolderListener(sThis, mask);
monitor.addFileListener(sThis, mask);
-
+
CompiledResourcesMonitor.setupMonitor(monitor);
}
-
+
/**
* Returns the singleton instance.
*/
public ProjectResources getProjectResources(IProject project) {
return mMap.get(project);
}
-
+
/**
* Processes folder event.
*/
public void folderChanged(IFolder folder, int kind) {
ProjectResources resources;
-
+
final IProject project = folder.getProject();
-
+
try {
if (project.hasNature(AndroidConstants.NATURE) == false) {
return;
// can't get the project nature? return!
return;
}
-
+
switch (kind) {
case IResourceDelta.ADDED:
// checks if the folder is under res.
IPath path = folder.getFullPath();
-
+
// the path will be project/res/<something>
if (path.segmentCount() == 3) {
if (isInResFolder(path)) {
// get the project and its resource object.
resources = mMap.get(project);
-
+
// if it doesn't exist, we create it.
if (resources == null) {
resources = new ProjectResources(false /* isFrameworkRepository */);
break;
}
}
-
+
/* (non-Javadoc)
* Sent when a file changed. Depending on the file being changed, and the type of change (ADDED,
* REMOVED, CHANGED), the file change is processed to update the resource manager data.
- *
+ *
* @param file The file that changed.
* @param markerDeltas The marker deltas for the file.
* @param kind The change kind. This is equivalent to
* {@link IResourceDelta#accept(IResourceDeltaVisitor)}
- *
+ *
* @see IFileListener#fileChanged
*/
public void fileChanged(IFile file, IMarkerDelta[] markerDeltas, int kind) {
ProjectResources resources;
-
+
final IProject project = file.getProject();
-
+
try {
if (project.hasNature(AndroidConstants.NATURE) == false) {
return;
// can't get the project nature? return!
return;
}
-
+
switch (kind) {
case IResourceDelta.ADDED:
// checks if the file is under res/something.
IPath path = file.getFullPath();
-
+
if (path.segmentCount() == 4) {
if (isInResFolder(path)) {
// get the project and its resources
resources = mMap.get(project);
-
+
IContainer container = file.getParent();
if (container instanceof IFolder && resources != null) {
-
+
ResourceFolder folder = resources.getResourceFolder((IFolder)container);
-
+
if (folder != null) {
processFile(new IFileWrapper(file), folder);
}
IContainer container = file.getParent();
if (container instanceof IFolder) {
ResourceFolder resFolder = resources.getResourceFolder((IFolder)container);
-
+
// we get the delete on the folder before the file, so it is possible
// the associated ResourceFolder doesn't exist anymore.
if (resFolder != null) {
IContainer container = file.getParent();
if (container instanceof IFolder) {
ResourceFolder resFolder = resources.getResourceFolder((IFolder)container);
-
+
// we get the delete on the folder before the file, so it is possible
// the associated ResourceFolder doesn't exist anymore.
if (resFolder != null) {
public void projectOpenedWithWorkspace(IProject project) {
createProject(project);
}
-
+
/**
* Returns the {@link ResourceFolder} for the given file or <code>null</code> if none exists.
*/
if (container.getType() == IResource.FOLDER) {
IFolder parent = (IFolder)container;
IProject project = file.getProject();
-
+
ProjectResources resources = getProjectResources(project);
if (resources != null) {
return resources.getResourceFolder(parent);
}
}
-
+
return null;
}
-
+
/**
* Loads and returns the resources for a given {@link IAndroidTarget}
* @param androidTarget the target from which to load the framework resources
*/
public ProjectResources loadFrameworkResources(IAndroidTarget androidTarget) {
String osResourcesPath = androidTarget.getPath(IAndroidTarget.RESOURCES);
-
+
File frameworkRes = new File(osResourcesPath);
if (frameworkRes.isDirectory()) {
ProjectResources resources = new ProjectResources(true /* isFrameworkRepository */);
try {
- File[] files = frameworkRes.listFiles();
- for (File file : files) {
- if (file.isDirectory()) {
- ResourceFolder resFolder = processFolder(new FolderWrapper(file),
- resources);
-
- if (resFolder != null) {
- // now we process the content of the folder
- File[] children = file.listFiles();
-
- for (File childRes : children) {
- if (childRes.isFile()) {
- processFile(new FileWrapper(childRes), resFolder);
- }
- }
- }
-
- }
- }
-
- // now that we have loaded the files, we need to force load the resources from them
- resources.loadAll();
-
+ loadResources(resources, frameworkRes);
return resources;
-
} catch (IOException e) {
// since we test that folders are folders, and files are files, this shouldn't
// happen. We can ignore it.
}
}
-
+
return null;
}
-
+
+ /**
+ * Loads the resources from a folder, and fills the given {@link ProjectResources}.
+ * <p/>
+ * This is mostly a utility method that should not be used to process actual Eclipse projects
+ * (Those are loaded with {@link #createProject(IProject)} for new project or
+ * {@link #processFolder(IAbstractFolder, ProjectResources)} and
+ * {@link #processFile(IAbstractFile, ResourceFolder)} for folder/file modifications)<br>
+ * This method will process files/folders with implementations of {@link IAbstractFile} and
+ * {@link IAbstractFolder} based on {@link File} instead of {@link IFile} and {@link IFolder}
+ * respectively. This is not proper for handling {@link IProject}s.
+ * </p>
+ * This is used to load the framework resources, or to do load project resources when
+ * setting rendering tests.
+ *
+ *
+ * @param resources The {@link ProjectResources} files to load. It is expected that the
+ * framework flag has been properly setup. This is filled up with the content of the folder.
+ * @param folder The folder to read the resources from. This is the top level resource folder
+ * (res/)
+ * @throws IOException
+ */
+ public void loadResources(ProjectResources resources, File folder) throws IOException {
+ File[] files = folder.listFiles();
+ for (File file : files) {
+ if (file.isDirectory()) {
+ ResourceFolder resFolder = processFolder(new FolderWrapper(file),
+ resources);
+
+ if (resFolder != null) {
+ // now we process the content of the folder
+ File[] children = file.listFiles();
+
+ for (File childRes : children) {
+ if (childRes.isFile()) {
+ processFile(new FileWrapper(childRes), resFolder);
+ }
+ }
+ }
+
+ }
+ }
+
+ // now that we have loaded the files, we need to force load the resources from them
+ resources.loadAll();
+ }
+
/**
* Initial project parsing to gather resource info.
* @param project
// can't check the nature of the project? ignore it.
return;
}
-
+
IFolder resourceFolder = project.getFolder(SdkConstants.FD_RESOURCES);
-
+
ProjectResources projectResources = mMap.get(project);
if (projectResources == null) {
projectResources = new ProjectResources(false /* isFrameworkRepository */);
mMap.put(project, projectResources);
}
-
+
if (resourceFolder != null && resourceFolder.exists()) {
try {
IResource[] resources = resourceFolder.members();
-
+
for (IResource res : resources) {
if (res.getType() == IResource.FOLDER) {
IFolder folder = (IFolder)res;
ResourceFolder resFolder = processFolder(new IFolderWrapper(folder),
projectResources);
-
+
if (resFolder != null) {
// now we process the content of the folder
IResource[] files = folder.members();
-
+
for (IResource fileRes : files) {
if (fileRes.getType() == IResource.FILE) {
IFile file = (IFile)fileRes;
-
+
processFile(new IFileWrapper(file), resFolder);
}
}
// Because the order of the qualifier is fixed, we do not reset the first qualifier
// after each sucessful segment.
// If we run out of qualifier before processing all the segments, we fail.
-
+
int qualifierIndex = 0;
int qualifierCount = mQualifiers.length;
-
+
for (int i = 1 ; i < folderSegments.length; i++) {
String seg = folderSegments[i];
if (seg.length() > 0) {
mQualifiers[qualifierIndex].checkAndSet(seg, config) == false) {
qualifierIndex++;
}
-
+
// if we reached the end of the qualifier we didn't find a matching qualifier.
if (qualifierIndex == qualifierCount) {
return null;
}
-
+
} else {
return null;
}
return config;
}
-
+
/**
* Processes a folder and adds it to the list of the project resources.
* @param folder the folder to process
// get the enum for the resource type.
ResourceFolderType type = ResourceFolderType.getTypeByName(folderSegments[0]);
-
+
if (type != null) {
// get the folder configuration.
FolderConfiguration config = getConfig(folderSegments);
-
+
if (config != null) {
ResourceFolder configuredFolder = project.add(type, config, folder);
return configuredFolder;
}
}
-
+
return null;
}
private void processFile(IAbstractFile file, ResourceFolder folder) {
// get the type of the folder
ResourceFolderType type = folder.getType();
-
+
// look for this file if it's already been created
ResourceFile resFile = folder.getFile(file);
-
+
if (resFile != null) {
// invalidate the file
resFile.touch();
} else {
// create a ResourceFile for it.
-
+
// check if that's a single or multi resource type folder. For now we define this by
// the number of possible resource type output by files in the folder. This does
// not make the difference between several resource types from a single file or
// resource. The former is handled by MultiResourceFile properly while we don't
// handle the latter. If we were to add this behavior we'd have to change this call.
ResourceType[] types = FolderTypeRelationship.getRelatedResourceTypes(type);
-
+
if (types.length == 1) {
resFile = new SingleResourceFile(file, folder);
} else {
resFile = new MultiResourceFile(file, folder);
}
-
+
// add it to the folder
folder.addFile(resFile);
}
private boolean isInResFolder(IPath path) {
return SdkConstants.FD_RESOURCES.equalsIgnoreCase(path.segment(1));
}
-
+
/**
* Private constructor to enforce singleton design.
*/
}
/**
+ * Return the {@link AndroidTargetData} for a given {@link IProject}.
+ */
+ public AndroidTargetData getTargetData(IProject project) {
+ synchronized (AdtPlugin.getDefault().getSdkLockObject()) {
+ IAndroidTarget target = getTarget(project);
+ if (target != null) {
+ return getTargetData(target);
+ }
+ }
+
+ return null;
+ }
+
+ /**
* Returns the configuration map for a given project.
* <p/>The Map key are name to be used in the apk filename, while the values are comma separated
* config values. The config value can be passed directly to aapt through the -c option.
import com.android.ide.eclipse.adt.internal.resources.configurations.ResourceQualifier;
import com.android.ide.eclipse.adt.internal.resources.configurations.ScreenDimensionQualifier;
import com.android.ide.eclipse.adt.internal.resources.configurations.ScreenOrientationQualifier;
+import com.android.ide.eclipse.adt.internal.resources.configurations.ScreenRatioQualifier;
+import com.android.ide.eclipse.adt.internal.resources.configurations.ScreenSizeQualifier;
import com.android.ide.eclipse.adt.internal.resources.configurations.TextInputMethodQualifier;
import com.android.ide.eclipse.adt.internal.resources.configurations.TouchScreenQualifier;
+import com.android.ide.eclipse.adt.internal.resources.configurations.VersionQualifier;
import com.android.ide.eclipse.adt.internal.resources.configurations.KeyboardStateQualifier.KeyboardState;
import com.android.ide.eclipse.adt.internal.resources.configurations.NavigationMethodQualifier.NavigationMethod;
+import com.android.ide.eclipse.adt.internal.resources.configurations.PixelDensityQualifier.Density;
import com.android.ide.eclipse.adt.internal.resources.configurations.ScreenOrientationQualifier.ScreenOrientation;
+import com.android.ide.eclipse.adt.internal.resources.configurations.ScreenRatioQualifier.ScreenRatio;
+import com.android.ide.eclipse.adt.internal.resources.configurations.ScreenSizeQualifier.ScreenSize;
import com.android.ide.eclipse.adt.internal.resources.configurations.TextInputMethodQualifier.TextInputMethod;
import com.android.ide.eclipse.adt.internal.resources.configurations.TouchScreenQualifier.TouchScreenType;
import com.android.ide.eclipse.adt.internal.resources.manager.ResourceManager;
* <ul>
* <li>Use {@link #setConfiguration(String)} or {@link #setConfiguration(FolderConfiguration)}.
* <li>Retrieve the configuration using {@link #getConfiguration(FolderConfiguration)}.
- * </ul>
+ * </ul>
*/
public class ConfigurationSelector extends Composite {
-
+
public static final int WIDTH_HINT = 600;
public static final int HEIGHT_HINT = 250;
-
+
private Runnable mOnChangeListener;
private TableViewer mFullTableViewer;
private Button mAddButton;
private Button mRemoveButton;
private StackLayout mStackLayout;
-
+
private boolean mOnRefresh = false;
private final FolderConfiguration mBaseConfiguration = new FolderConfiguration();
private final FolderConfiguration mSelectedConfiguration = new FolderConfiguration();
-
+
private final HashMap<Class<? extends ResourceQualifier>, QualifierEditBase> mUiMap =
new HashMap<Class<? extends ResourceQualifier>, QualifierEditBase>();
private Composite mQualifierEditParent;
-
+
/**
* Basic of {@link VerifyListener} to only accept digits.
*/
}
}
}
-
+
/**
* Implementation of {@link VerifyListener} for Country Code qualifiers.
*/
}
}
}
-
+
/**
* Implementation of {@link VerifyListener} for the Language and Region qualifiers.
*/
e.doit = false;
return;
}
-
+
// check for lower case only.
for (int i = 0 ; i < e.text.length(); i++) {
char letter = e.text.charAt(i);
}
}
}
-
+
/**
* Implementation of {@link VerifyListener} for the Pixel Density qualifier.
*/
public static class DensityVerifier extends DigitVerifier { }
-
+
/**
* Implementation of {@link VerifyListener} for the Screen Dimension qualifier.
*/
public static class DimensionVerifier extends DigitVerifier { }
-
+
/**
* Enum for the state of the configuration being created.
*/
GridLayout gl = new GridLayout(4, false);
gl.marginWidth = gl.marginHeight = 0;
setLayout(gl);
-
+
// first column is the first table
final Table fullTable = new Table(this, SWT.SINGLE | SWT.FULL_SELECTION | SWT.BORDER);
fullTable.setLayoutData(new GridData(GridData.FILL_BOTH));
fullTable.setHeaderVisible(true);
fullTable.setLinesVisible(true);
-
+
// create the column
final TableColumn fullTableColumn = new TableColumn(fullTable, SWT.LEFT);
// set the header
fullTableColumn.setText("Available Qualifiers");
-
+
fullTable.addControlListener(new ControlAdapter() {
@Override
public void controlResized(ControlEvent e) {
if (selection instanceof IStructuredSelection) {
IStructuredSelection structSelection = (IStructuredSelection)selection;
Object first = structSelection.getFirstElement();
-
+
if (first instanceof ResourceQualifier) {
mAddButton.setEnabled(true);
return;
}
}
-
+
mAddButton.setEnabled(false);
}
});
-
+
// 2nd column is the left/right arrow button
Composite buttonComposite = new Composite(this, SWT.NONE);
gl = new GridLayout(1, false);
gl.marginWidth = gl.marginHeight = 0;
buttonComposite.setLayout(gl);
buttonComposite.setLayoutData(new GridData(GridData.FILL_VERTICAL));
-
+
new Composite(buttonComposite, SWT.NONE);
mAddButton = new Button(buttonComposite, SWT.BORDER | SWT.PUSH);
mAddButton.setText("->");
mAddButton.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
- IStructuredSelection selection =
+ IStructuredSelection selection =
(IStructuredSelection)mFullTableViewer.getSelection();
-
+
Object first = selection.getFirstElement();
if (first instanceof ResourceQualifier) {
ResourceQualifier qualifier = (ResourceQualifier)first;
-
+
mBaseConfiguration.removeQualifier(qualifier);
mSelectedConfiguration.addQualifier(qualifier);
-
+
mFullTableViewer.refresh();
mSelectionTableViewer.refresh();
mSelectionTableViewer.setSelection(new StructuredSelection(qualifier), true);
-
+
onChange(false /* keepSelection */);
}
}
mRemoveButton.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
- IStructuredSelection selection =
+ IStructuredSelection selection =
(IStructuredSelection)mSelectionTableViewer.getSelection();
-
+
Object first = selection.getFirstElement();
if (first instanceof ResourceQualifier) {
ResourceQualifier qualifier = (ResourceQualifier)first;
-
+
mSelectedConfiguration.removeQualifier(qualifier);
mBaseConfiguration.addQualifier(qualifier);
mFullTableViewer.refresh();
mSelectionTableViewer.refresh();
-
+
onChange(false /* keepSelection */);
}
}
selectionTable.setLayoutData(new GridData(GridData.FILL_BOTH));
selectionTable.setHeaderVisible(true);
selectionTable.setLinesVisible(true);
-
+
// create the column
final TableColumn selectionTableColumn = new TableColumn(selectionTable, SWT.LEFT);
// set the header
selectionTableColumn.setText("Chosen Qualifiers");
-
+
selectionTable.addControlListener(new ControlAdapter() {
@Override
public void controlResized(ControlEvent e) {
ISelection selection = event.getSelection();
if (selection instanceof IStructuredSelection) {
IStructuredSelection structSelection = (IStructuredSelection)selection;
-
+
if (structSelection.isEmpty() == false) {
Object first = structSelection.getFirstElement();
-
+
if (first instanceof ResourceQualifier) {
mRemoveButton.setEnabled(true);
-
+
QualifierEditBase composite = mUiMap.get(first.getClass());
-
+
if (composite != null) {
composite.setQualifier((ResourceQualifier)first);
}
-
+
mStackLayout.topControl = composite;
mQualifierEditParent.layout();
-
+
return;
}
} else {
mQualifierEditParent.layout();
}
}
-
+
mRemoveButton.setEnabled(false);
}
});
-
- // 4th column is the detail of the selected qualifier
+
+ // 4th column is the detail of the selected qualifier
mQualifierEditParent = new Composite(this, SWT.NONE);
mQualifierEditParent.setLayout(mStackLayout = new StackLayout());
mQualifierEditParent.setLayoutData(new GridData(GridData.FILL_VERTICAL));
-
+
// create the UI for all the qualifiers, and associate them to the ResourceQualifer class.
mUiMap.put(CountryCodeQualifier.class, new MCCEdit(mQualifierEditParent));
mUiMap.put(NetworkCodeQualifier.class, new MNCEdit(mQualifierEditParent));
mUiMap.put(LanguageQualifier.class, new LanguageEdit(mQualifierEditParent));
mUiMap.put(RegionQualifier.class, new RegionEdit(mQualifierEditParent));
+ mUiMap.put(ScreenSizeQualifier.class, new ScreenSizeEdit(mQualifierEditParent));
+ mUiMap.put(ScreenRatioQualifier.class, new ScreenRatioEdit(mQualifierEditParent));
mUiMap.put(ScreenOrientationQualifier.class, new OrientationEdit(mQualifierEditParent));
mUiMap.put(PixelDensityQualifier.class, new PixelDensityEdit(mQualifierEditParent));
mUiMap.put(TouchScreenQualifier.class, new TouchEdit(mQualifierEditParent));
mUiMap.put(TextInputMethodQualifier.class, new TextInputEdit(mQualifierEditParent));
mUiMap.put(NavigationMethodQualifier.class, new NavigationEdit(mQualifierEditParent));
mUiMap.put(ScreenDimensionQualifier.class, new ScreenDimensionEdit(mQualifierEditParent));
+ mUiMap.put(VersionQualifier.class, new VersionEdit(mQualifierEditParent));
}
-
+
/**
* Sets a listener to be notified when the configuration changes.
* @param listener A {@link Runnable} whose <code>run()</code> method is called when the
public void setOnChangeListener(Runnable listener) {
mOnChangeListener = listener;
}
-
+
/**
* Initialize the UI with a given {@link FolderConfiguration}. This must
* be called from the UI thread.
public void setConfiguration(FolderConfiguration config) {
mSelectedConfiguration.set(config);
mSelectionTableViewer.refresh();
-
+
// create the base config, which is the default config minus the qualifiers
// in SelectedConfiguration
mBaseConfiguration.substract(mSelectedConfiguration);
mFullTableViewer.refresh();
}
-
+
/**
* Initialize the UI with the configuration represented by a resource folder name.
* This must be called from the UI thread.
- *
+ *
* @param folderSegments the segments of the folder name,
* split using {@link FolderConfiguration#QUALIFIER_SEP}.
* @return true if success, or false if the folder name is not a valid name.
*/
public boolean setConfiguration(String[] folderSegments) {
FolderConfiguration config = ResourceManager.getInstance().getConfig(folderSegments);
-
+
if (config == null) {
return false;
}
return true;
}
-
+
/**
* Initialize the UI with the configuration represented by a resource folder name.
* This must be called from the UI thread.
return setConfiguration(folderSegments);
}
-
+
/**
* Gets the configuration as setup by the widget.
* @param config the {@link FolderConfiguration} object to be filled with the information
public void getConfiguration(FolderConfiguration config) {
config.set(mSelectedConfiguration);
}
-
+
/**
* Returns the state of the configuration being edited/created.
*/
if (mSelectedConfiguration.getInvalidQualifier() != null) {
return ConfigurationState.INVALID_CONFIG;
}
-
+
if (mSelectedConfiguration.checkRegion() == false) {
return ConfigurationState.REGION_WITHOUT_LANGUAGE;
}
-
+
return ConfigurationState.OK;
}
-
+
/**
* Returns the first invalid qualifier of the configuration being edited/created,
* or <code>null<code> if they are all valid (or if none exists).
}
mSelectionTableViewer.refresh(true);
-
+
if (keepSelection) {
mSelectionTableViewer.setSelection(selection);
mOnRefresh = false;
mOnChangeListener.run();
}
}
-
+
/**
* Content provider around a {@link FolderConfiguration}.
*/
private static class QualifierContentProvider implements IStructuredContentProvider {
-
+
private FolderConfiguration mInput;
public QualifierContentProvider() {
}
}
}
-
+
/**
* Label provider for {@link ResourceQualifier} objects.
*/
private static class QualifierLabelProvider implements ITableLabelProvider {
-
+
private final boolean mShowQualifierValue;
public QualifierLabelProvider(boolean showQualifierValue) {
} else {
return value;
}
-
+
} else {
return ((ResourceQualifier)element).getShortName();
}
return null;
}
-
+
public Image getColumnImage(Object element, int columnIndex) {
// only one column, so we can ignore columnIndex
if (element instanceof ResourceQualifier) {
// pass
}
}
-
+
/**
* Base class for Edit widget for {@link ResourceQualifier}.
*/
new Label(this, SWT.NONE).setText(title);
}
-
+
public abstract void setQualifier(ResourceQualifier qualifier);
}
-
+
/**
* Edit widget for {@link CountryCodeQualifier}.
*/
public MCCEdit(Composite parent) {
super(parent, CountryCodeQualifier.NAME);
-
+
mText = new Text(this, SWT.BORDER);
mText.addVerifyListener(new MobileCodeVerifier());
mText.addModifyListener(new ModifyListener() {
public void modifyText(ModifyEvent e) {
onTextChange();
- }
+ }
});
mText.addFocusListener(new FocusAdapter() {
onTextChange();
}
});
-
+
new Label(this, SWT.NONE).setText("(3 digit code)");
}
-
+
private void onTextChange() {
String value = mText.getText();
-
+
if (value.length() == 0) {
// empty string, means a qualifier with no value.
// Since the qualifier classes are immutable, and we don't want to
mSelectedConfiguration.setCountryCodeQualifier(new CountryCodeQualifier());
}
}
-
+
// notify of change
onChange(true /* keepSelection */);
}
@Override
public void setQualifier(ResourceQualifier qualifier) {
CountryCodeQualifier q = (CountryCodeQualifier)qualifier;
-
+
mText.setText(Integer.toString(q.getCode()));
}
}
public MNCEdit(Composite parent) {
super(parent, NetworkCodeQualifier.NAME);
-
+
mText = new Text(this, SWT.BORDER);
mText.addVerifyListener(new MobileCodeVerifier());
mText.addModifyListener(new ModifyListener() {
new Label(this, SWT.NONE).setText("(1-3 digit code)");
}
-
+
private void onTextChange() {
String value = mText.getText();
-
+
if (value.length() == 0) {
// empty string, means a qualifier with no value.
// Since the qualifier classes are immutable, and we don't want to
mSelectedConfiguration.setNetworkCodeQualifier(new NetworkCodeQualifier());
}
}
-
+
// notify of change
onChange(true /* keepSelection */);
- }
+ }
@Override
public void setQualifier(ResourceQualifier qualifier) {
NetworkCodeQualifier q = (NetworkCodeQualifier)qualifier;
-
+
mText.setText(Integer.toString(q.getCode()));
}
}
-
+
/**
* Edit widget for {@link LanguageQualifier}.
*/
private void onLanguageChange() {
// update the current config
String value = mLanguage.getText();
-
+
if (value.length() == 0) {
// empty string, means no qualifier.
// Since the qualifier classes are immutable, and we don't want to
private void onRegionChange() {
// update the current config
String value = mRegion.getText();
-
+
if (value.length() == 0) {
// empty string, means no qualifier.
// Since the qualifier classes are immutable, and we don't want to
}
}
}
-
+
+ /**
+ * Edit widget for {@link ScreenSizeQualifier}.
+ */
+ private class ScreenSizeEdit extends QualifierEditBase {
+
+ private Combo mSize;
+
+ public ScreenSizeEdit(Composite parent) {
+ super(parent, ScreenSizeQualifier.NAME);
+
+ mSize = new Combo(this, SWT.DROP_DOWN | SWT.READ_ONLY);
+ ScreenSize[] ssValues = ScreenSize.values();
+ for (ScreenSize value : ssValues) {
+ mSize.add(value.getDisplayValue());
+ }
+
+ mSize.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ mSize.addSelectionListener(new SelectionListener() {
+ public void widgetDefaultSelected(SelectionEvent e) {
+ onScreenSizeChange();
+ }
+ public void widgetSelected(SelectionEvent e) {
+ onScreenSizeChange();
+ }
+ });
+ }
+
+ protected void onScreenSizeChange() {
+ // update the current config
+ int index = mSize.getSelectionIndex();
+
+ if (index != -1) {
+ mSelectedConfiguration.setScreenSizeQualifier(new ScreenSizeQualifier(
+ ScreenSize.getByIndex(index)));
+ } else {
+ // empty selection, means no qualifier.
+ // Since the qualifier classes are immutable, and we don't want to
+ // remove the qualifier from the configuration, we create a new default one.
+ mSelectedConfiguration.setScreenSizeQualifier(
+ new ScreenSizeQualifier());
+ }
+
+ // notify of change
+ onChange(true /* keepSelection */);
+ }
+
+ @Override
+ public void setQualifier(ResourceQualifier qualifier) {
+ ScreenSizeQualifier q = (ScreenSizeQualifier)qualifier;
+
+ ScreenSize value = q.getValue();
+ if (value == null) {
+ mSize.clearSelection();
+ } else {
+ mSize.select(ScreenSize.getIndex(value));
+ }
+ }
+ }
+
+ /**
+ * Edit widget for {@link ScreenRatioQualifier}.
+ */
+ private class ScreenRatioEdit extends QualifierEditBase {
+
+ private Combo mSize;
+
+ public ScreenRatioEdit(Composite parent) {
+ super(parent, ScreenRatioQualifier.NAME);
+
+ mSize = new Combo(this, SWT.DROP_DOWN | SWT.READ_ONLY);
+ ScreenRatio[] srValues = ScreenRatio.values();
+ for (ScreenRatio value : srValues) {
+ mSize.add(value.getDisplayValue());
+ }
+
+ mSize.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ mSize.addSelectionListener(new SelectionListener() {
+ public void widgetDefaultSelected(SelectionEvent e) {
+ onScreenRatioChange();
+ }
+ public void widgetSelected(SelectionEvent e) {
+ onScreenRatioChange();
+ }
+ });
+ }
+
+ protected void onScreenRatioChange() {
+ // update the current config
+ int index = mSize.getSelectionIndex();
+
+ if (index != -1) {
+ mSelectedConfiguration.setScreenRatioQualifier(new ScreenRatioQualifier(
+ ScreenRatio.getByIndex(index)));
+ } else {
+ // empty selection, means no qualifier.
+ // Since the qualifier classes are immutable, and we don't want to
+ // remove the qualifier from the configuration, we create a new default one.
+ mSelectedConfiguration.setScreenRatioQualifier(
+ new ScreenRatioQualifier());
+ }
+
+ // notify of change
+ onChange(true /* keepSelection */);
+ }
+
+ @Override
+ public void setQualifier(ResourceQualifier qualifier) {
+ ScreenRatioQualifier q = (ScreenRatioQualifier)qualifier;
+
+ ScreenRatio value = q.getValue();
+ if (value == null) {
+ mSize.clearSelection();
+ } else {
+ mSize.select(ScreenRatio.getIndex(value));
+ }
+ }
+ }
+
/**
* Edit widget for {@link ScreenOrientationQualifier}.
*/
* Edit widget for {@link PixelDensityQualifier}.
*/
private class PixelDensityEdit extends QualifierEditBase {
- private Text mText;
+ private Combo mDensity;
public PixelDensityEdit(Composite parent) {
super(parent, PixelDensityQualifier.NAME);
-
- mText = new Text(this, SWT.BORDER);
- mText.addVerifyListener(new DensityVerifier());
- mText.addModifyListener(new ModifyListener() {
- public void modifyText(ModifyEvent e) {
- onTextChange();
+
+ mDensity = new Combo(this, SWT.DROP_DOWN | SWT.READ_ONLY);
+ Density[] soValues = Density.values();
+ for (Density value : soValues) {
+ mDensity.add(value.getDisplayValue());
+ }
+
+ mDensity.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ mDensity.addSelectionListener(new SelectionListener() {
+ public void widgetDefaultSelected(SelectionEvent e) {
+ onDensityChange();
}
- });
- mText.addFocusListener(new FocusAdapter() {
- @Override
- public void focusLost(FocusEvent e) {
- onTextChange();
+ public void widgetSelected(SelectionEvent e) {
+ onDensityChange();
}
});
+
}
-
- private void onTextChange() {
- String value = mText.getText();
-
- if (value.length() == 0) {
- // empty string, means a qualifier with no value.
+
+ private void onDensityChange() {
+ // update the current config
+ int index = mDensity.getSelectionIndex();
+
+ if (index != -1) {
+ mSelectedConfiguration.setPixelDensityQualifier(new PixelDensityQualifier(
+ Density.getByIndex(index)));
+ } else {
+ // empty selection, means no qualifier.
// Since the qualifier classes are immutable, and we don't want to
// remove the qualifier from the configuration, we create a new default one.
- mSelectedConfiguration.setPixelDensityQualifier(new PixelDensityQualifier());
- } else {
- try {
- PixelDensityQualifier qualifier = PixelDensityQualifier.getQualifier(
- PixelDensityQualifier.getFolderSegment(Integer.parseInt(value)));
- if (qualifier != null) {
- mSelectedConfiguration.setPixelDensityQualifier(qualifier);
- } else {
- // Failure! Looks like the value is wrong
- // (for instance a one letter string).
- // We do nothing in this case.
- return;
- }
- } catch (NumberFormatException nfe) {
- // Looks like the code is not a number. This should not happen since the text
- // field has a VerifyListener that prevents it.
- // We do nothing in this case.
- return;
- }
+ mSelectedConfiguration.setPixelDensityQualifier(
+ new PixelDensityQualifier());
}
-
+
// notify of change
onChange(true /* keepSelection */);
- }
+ }
@Override
public void setQualifier(ResourceQualifier qualifier) {
PixelDensityQualifier q = (PixelDensityQualifier)qualifier;
-
- mText.setText(Integer.toString(q.getValue()));
+
+ Density value = q.getValue();
+ if (value == null) {
+ mDensity.clearSelection();
+ } else {
+ mDensity.select(Density.getIndex(value));
+ }
}
}
}
}
}
-
+
/**
* Edit widget for {@link ScreenDimensionQualifier}.
*/
ModifyListener modifyListener = new ModifyListener() {
public void modifyText(ModifyEvent e) {
onSizeChange();
- }
+ }
};
-
+
FocusAdapter focusListener = new FocusAdapter() {
@Override
public void focusLost(FocusEvent e) {
mSize2.addModifyListener(modifyListener);
mSize2.addFocusListener(focusListener);
}
-
+
private void onSizeChange() {
// update the current config
String size1 = mSize1.getText();
String size2 = mSize2.getText();
-
+
if (size1.length() == 0 || size2.length() == 0) {
// if one of the strings is empty, reset to no qualifier.
// Since the qualifier classes are immutable, and we don't want to
new ScreenDimensionQualifier());
}
}
-
+
// notify of change
onChange(true /* keepSelection */);
}
@Override
public void setQualifier(ResourceQualifier qualifier) {
ScreenDimensionQualifier q = (ScreenDimensionQualifier)qualifier;
-
+
mSize1.setText(Integer.toString(q.getValue1()));
mSize2.setText(Integer.toString(q.getValue2()));
}
}
+
+ /**
+ * Edit widget for {@link VersionQualifier}.
+ */
+ private class VersionEdit extends QualifierEditBase {
+ private Text mText;
+
+ public VersionEdit(Composite parent) {
+ super(parent, VersionQualifier.NAME);
+
+ mText = new Text(this, SWT.BORDER);
+ mText.addVerifyListener(new MobileCodeVerifier());
+ mText.addModifyListener(new ModifyListener() {
+ public void modifyText(ModifyEvent e) {
+ onVersionChange();
+ }
+ });
+ mText.addFocusListener(new FocusAdapter() {
+ @Override
+ public void focusLost(FocusEvent e) {
+ onVersionChange();
+ }
+ });
+
+ new Label(this, SWT.NONE).setText("(Platform API level)");
+ }
+
+ private void onVersionChange() {
+ String value = mText.getText();
+
+ if (value.length() == 0) {
+ // empty string, means a qualifier with no value.
+ // Since the qualifier classes are immutable, and we don't want to
+ // remove the qualifier from the configuration, we create a new default one.
+ mSelectedConfiguration.setVersionQualifier(new VersionQualifier());
+ } else {
+ try {
+ VersionQualifier qualifier = VersionQualifier.getQualifier(
+ VersionQualifier.getFolderSegment(Integer.parseInt(value)));
+ if (qualifier != null) {
+ mSelectedConfiguration.setVersionQualifier(qualifier);
+ } else {
+ // Failure! Looks like the value is wrong
+ mSelectedConfiguration.setVersionQualifier(new VersionQualifier());
+ }
+ } catch (NumberFormatException nfe) {
+ // Looks like the code is not a number. This should not happen since the text
+ // field has a VerifyListener that prevents it.
+ mSelectedConfiguration.setVersionQualifier(new VersionQualifier());
+ }
+ }
+
+ // notify of change
+ onChange(true /* keepSelection */);
+ }
+
+ @Override
+ public void setQualifier(ResourceQualifier qualifier) {
+ VersionQualifier q = (VersionQualifier)qualifier;
+
+ mText.setText(Integer.toString(q.getVersion()));
+ }
+ }
+
}
package com.android.ide.eclipse.adt.internal.wizards.export;
+import com.android.ide.eclipse.adt.AdtConstants;
import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.adt.internal.project.BaseProjectHelper;
import com.android.ide.eclipse.adt.internal.project.ProjectHelper;
import com.android.jarutils.KeystoreHelper;
import com.android.jarutils.SignedJarBuilder;
import com.android.jarutils.DebugKeyProvider.IKeyGenOutput;
-import com.android.jarutils.DebugKeyProvider.KeytoolException;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IncrementalProjectBuilder;
-import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.PlatformUI;
+import java.io.BufferedReader;
+import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
-import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.PrintStream;
import java.lang.reflect.InvocationTargetException;
-import java.security.GeneralSecurityException;
import java.security.KeyStore;
-import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.KeyStore.PrivateKeyEntry;
import java.security.cert.X509Certificate;
import java.util.Map.Entry;
/**
- * Export wizard to export an apk signed with a release key/certificate.
+ * Export wizard to export an apk signed with a release key/certificate.
*/
public final class ExportWizard extends Wizard implements IExportWizard {
private static final String PROJECT_LOGO_LARGE = "icons/android_large.png"; //$NON-NLS-1$
-
+
private static final String PAGE_PROJECT_CHECK = "Page_ProjectCheck"; //$NON-NLS-1$
private static final String PAGE_KEYSTORE_SELECTION = "Page_KeystoreSelection"; //$NON-NLS-1$
private static final String PAGE_KEY_CREATION = "Page_KeyCreation"; //$NON-NLS-1$
private static final String PAGE_KEY_SELECTION = "Page_KeySelection"; //$NON-NLS-1$
private static final String PAGE_KEY_CHECK = "Page_KeyCheck"; //$NON-NLS-1$
-
+
static final String PROPERTY_KEYSTORE = "keystore"; //$NON-NLS-1$
static final String PROPERTY_ALIAS = "alias"; //$NON-NLS-1$
static final String PROPERTY_DESTINATION = "destination"; //$NON-NLS-1$
static final String PROPERTY_FILENAME = "baseFilename"; //$NON-NLS-1$
-
+
static final int APK_FILE_SOURCE = 0;
static final int APK_FILE_DEST = 1;
static final int APK_COUNT = 2;
* Base page class for the ExportWizard page. This class add the {@link #onShow()} callback.
*/
static abstract class ExportWizardPage extends WizardPage {
-
+
/** bit mask constant for project data change event */
protected static final int DATA_PROJECT = 0x001;
/** bit mask constant for keystore data change event */
public void verifyText(VerifyEvent e) {
// verify the characters are valid for password.
int len = e.text.length();
-
+
// first limit to 127 characters max
if (len + ((Text)e.getSource()).getText().length() > 127) {
e.doit = false;
return;
}
-
+
// now only take non control characters
for (int i = 0 ; i < len ; i++) {
if (e.text.charAt(i) < 32) {
}
}
};
-
+
/**
* Bit mask indicating what changed while the page was hidden.
* @see #DATA_PROJECT
* @see #DATA_KEY
*/
protected int mProjectDataChanged = 0;
-
+
ExportWizardPage(String name) {
super(name);
}
-
+
abstract void onShow();
-
+
@Override
public void setVisible(boolean visible) {
super.setVisible(visible);
mProjectDataChanged = 0;
}
}
-
+
final void projectDataChanged(int changeMask) {
mProjectDataChanged |= changeMask;
}
-
+
/**
* Calls {@link #setErrorMessage(String)} and {@link #setPageComplete(boolean)} based on a
* {@link Throwable} object.
*/
protected void onException(Throwable t) {
String message = getExceptionMessage(t);
-
+
setErrorMessage(message);
setPageComplete(false);
}
}
-
+
private ExportWizardPage mPages[] = new ExportWizardPage[5];
private IProject mProject;
setWindowTitle("Export Android Application");
setImageDescriptor();
}
-
+
@Override
public void addPages() {
addPage(mPages[0] = new ProjectCheckPage(this, PAGE_PROJECT_CHECK));
mDestinationParentFolder.getAbsolutePath());
ProjectHelper.saveStringProperty(mProject, PROPERTY_FILENAME,
mApkMap.get(null)[APK_FILE_DEST]);
-
+
// run the export in an UI runnable.
IWorkbench workbench = PlatformUI.getWorkbench();
final boolean[] result = new boolean[1];
} catch (InterruptedException e) {
return false;
}
-
+
return result[0];
}
-
+
private boolean doExport(IProgressMonitor monitor) {
try {
// first we make sure the project is built
output.add(message);
}
});
-
+
if (createdStore == false) {
// keystore creation error!
displayError(output.toArray(new String[output.size()]));
return false;
}
-
+
// keystore is created, now load the private key and certificate.
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
FileInputStream fis = new FileInputStream(mKeystore);
fis.close();
PrivateKeyEntry entry = (KeyStore.PrivateKeyEntry)keyStore.getEntry(
mKeyAlias, new KeyStore.PasswordProtection(mKeyPassword.toCharArray()));
-
+
if (entry != null) {
mPrivateKey = entry.getPrivateKey();
mCertificate = (X509Certificate)entry.getCertificate();
return false;
}
}
-
+
// check the private key/certificate again since it may have been created just above.
if (mPrivateKey != null && mCertificate != null) {
// get the output folder of the project to export.
return false;
}
String outputOsPath = outputIFolder.getLocation().toOSString();
-
+
// now generate the packages.
Set<Entry<String, String[]>> set = mApkMap.entrySet();
+
+ boolean runZipAlign = false;
+ String path = AdtPlugin.getOsAbsoluteZipAlign();
+ File zipalign = new File(path);
+ runZipAlign = zipalign.isFile();
+
for (Entry<String, String[]> entry : set) {
String[] defaultApk = entry.getValue();
String srcFilename = defaultApk[APK_FILE_SOURCE];
String destFilename = defaultApk[APK_FILE_DEST];
+ File destFile;
+ if (runZipAlign) {
+ destFile = File.createTempFile("android", ".apk");
+ } else {
+ destFile = new File(mDestinationParentFolder, destFilename);
+ }
+
- FileOutputStream fos = new FileOutputStream(
- new File(mDestinationParentFolder, destFilename));
+ FileOutputStream fos = new FileOutputStream(destFile);
SignedJarBuilder builder = new SignedJarBuilder(fos, mPrivateKey, mCertificate);
-
+
// get the input file.
FileInputStream fis = new FileInputStream(new File(outputOsPath, srcFilename));
-
+
// add the content of the source file to the output file, and sign it at
// the same time.
try {
builder.writeZip(fis, null /* filter */);
- // close the builder: write the final signature files, and close the archive.
+ // close the builder: write the final signature files,
+ // and close the archive.
builder.close();
+
+ // now zipalign the result
+ if (runZipAlign) {
+ File realDestFile = new File(mDestinationParentFolder, destFilename);
+ String message = zipAlign(path, destFile, realDestFile);
+ if (message != null) {
+ displayError(message);
+ return false;
+ }
+ }
} finally {
try {
fis.close();
}
}
}
+
+ // export success. In case we didn't run ZipAlign we display a warning
+ if (runZipAlign == false) {
+ AdtPlugin.displayWarning("Export Wizard",
+ "The zipalign tool was not found in the SDK.\n\n" +
+ "Please update to the latest SDK and re-export your application\n" +
+ "or run zipalign manually.\n\n" +
+ "Aligning applications allows Android to use application resources\n" +
+ "more efficiently.");
+ }
+
return true;
}
- } catch (FileNotFoundException e) {
- displayError(e);
- } catch (NoSuchAlgorithmException e) {
- displayError(e);
- } catch (IOException e) {
- displayError(e);
- } catch (GeneralSecurityException e) {
- displayError(e);
- } catch (KeytoolException e) {
- displayError(e);
- } catch (CoreException e) {
- displayError(e);
+ } catch (Throwable t) {
+ displayError(t);
}
return false;
}
-
+
@Override
public boolean canFinish() {
// check if we have the apk to resign, the destination location, and either
|| mKeystoreCreationMode || mKeyCreationMode) &&
mDestinationParentFolder != null;
}
-
+
/*
* (non-Javadoc)
* @see org.eclipse.ui.IWorkbenchWizard#init(org.eclipse.ui.IWorkbench, org.eclipse.jface.viewers.IStructuredSelection)
public void init(IWorkbench workbench, IStructuredSelection selection) {
// get the project from the selection
Object selected = selection.getFirstElement();
-
+
if (selected instanceof IProject) {
mProject = (IProject)selected;
} else if (selected instanceof IAdaptable) {
}
}
}
-
+
ExportWizardPage getKeystoreSelectionPage() {
return mKeystoreSelectionPage;
}
-
+
ExportWizardPage getKeyCreationPage() {
return mKeyCreationPage;
}
-
+
ExportWizardPage getKeySelectionPage() {
return mKeySelectionPage;
}
-
+
ExportWizardPage getKeyCheckPage() {
return mKeyCheckPage;
}
ImageDescriptor desc = AdtPlugin.getImageDescriptor(PROJECT_LOGO_LARGE);
setDefaultPageImageDescriptor(desc);
}
-
+
IProject getProject() {
return mProject;
}
-
+
void setProject(IProject project) {
mProject = project;
-
+
updatePageOnChange(ExportWizardPage.DATA_PROJECT);
}
-
+
void setKeystore(String path) {
mKeystore = path;
mPrivateKey = null;
mCertificate = null;
-
+
updatePageOnChange(ExportWizardPage.DATA_KEYSTORE);
}
-
+
String getKeystore() {
return mKeystore;
}
-
+
void setKeystoreCreationMode(boolean createStore) {
mKeystoreCreationMode = createStore;
updatePageOnChange(ExportWizardPage.DATA_KEYSTORE);
}
-
+
boolean getKeystoreCreationMode() {
return mKeystoreCreationMode;
}
-
-
+
+
void setKeystorePassword(String password) {
mKeystorePassword = password;
mPrivateKey = null;
updatePageOnChange(ExportWizardPage.DATA_KEYSTORE);
}
-
+
String getKeystorePassword() {
return mKeystorePassword;
}
mKeyCreationMode = createKey;
updatePageOnChange(ExportWizardPage.DATA_KEY);
}
-
+
boolean getKeyCreationMode() {
return mKeyCreationMode;
}
-
+
void setExistingAliases(List<String> aliases) {
mExistingAliases = aliases;
}
-
+
List<String> getExistingAliases() {
return mExistingAliases;
}
updatePageOnChange(ExportWizardPage.DATA_KEY);
}
-
+
String getKeyAlias() {
return mKeyAlias;
}
updatePageOnChange(ExportWizardPage.DATA_KEY);
}
-
+
String getKeyPassword() {
return mKeyPassword;
}
mValidity = validity;
updatePageOnChange(ExportWizardPage.DATA_KEY);
}
-
+
int getValidity() {
return mValidity;
}
mDName = dName;
updatePageOnChange(ExportWizardPage.DATA_KEY);
}
-
+
String getDName() {
return mDName;
}
page.projectDataChanged(changeMask);
}
}
-
+
private void displayError(String... messages) {
String message = null;
if (messages.length == 1) {
sb.append('\n');
sb.append(messages[i]);
}
-
+
message = sb.toString();
}
AdtPlugin.displayError("Export Wizard", message);
}
-
- private void displayError(Exception e) {
- String message = getExceptionMessage(e);
+
+ private void displayError(Throwable t) {
+ String message = getExceptionMessage(t);
displayError(message);
-
- AdtPlugin.log(e, "Export Wizard Error");
+
+ AdtPlugin.log(t, "Export Wizard Error");
}
-
+
+ /**
+ * Executes zipalign
+ * @param zipAlignPath location of the zipalign too
+ * @param source file to zipalign
+ * @param destination where to write the resulting file
+ * @return null if success, the error otherwise
+ * @throws IOException
+ */
+ private String zipAlign(String zipAlignPath, File source, File destination) throws IOException {
+ // command line: zipaling -f 4 tmp destination
+ String[] command = new String[5];
+ command[0] = zipAlignPath;
+ command[1] = "-f"; //$NON-NLS-1$
+ command[2] = "4"; //$NON-NLS-1$
+ command[3] = source.getAbsolutePath();
+ command[4] = destination.getAbsolutePath();
+
+ Process process = Runtime.getRuntime().exec(command);
+ ArrayList<String> output = new ArrayList<String>();
+ try {
+ if (grabProcessOutput(process, output) != 0) {
+ // build a single message from the array list
+ StringBuilder sb = new StringBuilder("Error while running zipalign:");
+ for (String msg : output) {
+ sb.append('\n');
+ sb.append(msg);
+ }
+
+ return sb.toString();
+ }
+ } catch (InterruptedException e) {
+ // ?
+ }
+ return null;
+ }
+
+ /**
+ * Get the stderr output of a process and return when the process is done.
+ * @param process The process to get the ouput from
+ * @param results The array to store the stderr output
+ * @return the process return code.
+ * @throws InterruptedException
+ */
+ private final int grabProcessOutput(final Process process,
+ final ArrayList<String> results)
+ throws InterruptedException {
+ // Due to the limited buffer size on windows for the standard io (stderr, stdout), we
+ // *need* to read both stdout and stderr all the time. If we don't and a process output
+ // a large amount, this could deadlock the process.
+
+ // read the lines as they come. if null is returned, it's
+ // because the process finished
+ new Thread("") { //$NON-NLS-1$
+ @Override
+ public void run() {
+ // create a buffer to read the stderr output
+ InputStreamReader is = new InputStreamReader(process.getErrorStream());
+ BufferedReader errReader = new BufferedReader(is);
+
+ try {
+ while (true) {
+ String line = errReader.readLine();
+ if (line != null) {
+ results.add(line);
+ } else {
+ break;
+ }
+ }
+ } catch (IOException e) {
+ // do nothing.
+ }
+ }
+ }.start();
+
+ new Thread("") { //$NON-NLS-1$
+ @Override
+ public void run() {
+ InputStreamReader is = new InputStreamReader(process.getInputStream());
+ BufferedReader outReader = new BufferedReader(is);
+
+ IProject project = getProject();
+
+ try {
+ while (true) {
+ String line = outReader.readLine();
+ if (line != null) {
+ AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE,
+ project, line);
+ } else {
+ break;
+ }
+ }
+ } catch (IOException e) {
+ // do nothing.
+ }
+ }
+
+ }.start();
+
+ // get the return code from the process
+ return process.waitFor();
+ }
+
+
+
/**
* Returns the {@link Throwable#getMessage()}. If the {@link Throwable#getMessage()} returns
* <code>null</code>, the method is called again on the cause of the Throwable object.
static String getExceptionMessage(Throwable t) {
String message = t.getMessage();
if (message == null) {
- Throwable cause = t.getCause();
- if (cause != null) {
- return getExceptionMessage(cause);
- }
-
- // no more cause and still no message. display the first exception.
- return t.getClass().getCanonicalName();
+ // no error info? get the stack call to display it
+ // At least that'll give us a better bug report.
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ t.printStackTrace(new PrintStream(baos));
+ message = baos.toString();
}
-
+
return message;
}
}
// enable types based on new API level
enableTypesBasedOnApi();
+ // update the folder name based on API level
+ resetFolderPath(false /*validate*/);
+
// update the Type with the new descriptors.
initializeRootValues();
// The configuration is valid. Reformat the folder path using the canonical
// value from the configuration.
- newPath = RES_FOLDER_ABS + mTempConfig.getFolderName(type.getResFolderType());
+ newPath = RES_FOLDER_ABS + mTempConfig.getFolderName(type.getResFolderType(), mProject);
} else {
// The configuration is invalid. We still update the path but this time
// do it manually on the string.
"^(" + RES_FOLDER_ABS +")[^-]*(.*)", //$NON-NLS-1$ //$NON-NLS-2$
"\\1" + type.getResFolderName() + "\\2"); //$NON-NLS-1$ //$NON-NLS-2$
} else {
- newPath = RES_FOLDER_ABS + mTempConfig.getFolderName(type.getResFolderType());
+ newPath = RES_FOLDER_ABS + mTempConfig.getFolderName(type.getResFolderType(),
+ mProject);
}
}
return;
}
- TypeInfo type = getSelectedType();
-
- if (type != null) {
- mConfigSelector.getConfiguration(mTempConfig);
- StringBuffer sb = new StringBuffer(RES_FOLDER_ABS);
- sb.append(mTempConfig.getFolderName(type.getResFolderType()));
-
- mInternalWsFolderPathUpdate = true;
- mWsFolderPathTextField.setText(sb.toString());
- mInternalWsFolderPathUpdate = false;
-
- validatePage();
- }
+ resetFolderPath(true /*validate*/);
}
}
}
/**
+ * Reset the current Folder path based on the UI selection
+ * @param validate if true, force a call to {@link #validatePage()}.
+ */
+ private void resetFolderPath(boolean validate) {
+ TypeInfo type = getSelectedType();
+
+ if (type != null) {
+ mConfigSelector.getConfiguration(mTempConfig);
+ StringBuffer sb = new StringBuffer(RES_FOLDER_ABS);
+ sb.append(mTempConfig.getFolderName(type.getResFolderType(), mProject));
+
+ mInternalWsFolderPathUpdate = true;
+ mWsFolderPathTextField.setText(sb.toString());
+ mInternalWsFolderPathUpdate = false;
+
+ if (validate) {
+ validatePage();
+ }
+ }
+ }
+
+ /**
* Validates the fields, displays errors and warnings.
* Enables the finish button if there are no errors.
*/
Bundle-ManifestVersion: 2
Bundle-Name: Dalvik Debug Monitor Service
Bundle-SymbolicName: com.android.ide.eclipse.ddms;singleton:=true
-Bundle-Version: 0.9.2.qualifier
+Bundle-Version: 0.9.3.qualifier
Bundle-Activator: com.android.ide.eclipse.ddms.DdmsPlugin
Bundle-Vendor: The Android Open Source Project
Bundle-Localization: plugin
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="lib" path="kxml2-2.3.0.jar"/>
<classpathentry kind="lib" path="/adt/sdklib.jar" sourcepath="/SdkLib"/>
+ <classpathentry kind="lib" path="/adt/layoutlib_api.jar" sourcepath="/layoutlib_api"/>
<classpathentry kind="output" path="bin"/>
</classpath>
Bundle-ManifestVersion: 2
Bundle-Name: Android Plugin Tests
Bundle-SymbolicName: com.android.ide.eclipse.tests
-Bundle-Version: 0.9.2.qualifier
+Bundle-Version: 0.9.3.qualifier
Bundle-Activator: com.android.ide.eclipse.tests.AndroidTestPlugin
Require-Bundle: org.eclipse.ui,
org.eclipse.core.runtime,
Bundle-ActivationPolicy: lazy
Bundle-Vendor: The Android Open Source Project
Bundle-ClassPath: kxml2-2.3.0.jar,
- .
+ .,
+ layoutlib_api.jar,
+ sdklib.jar
This project contains the tests for the Android Eclipse Plugins.
-You can do two things:
-1- Run the full "eclipse plugin" suite
-2- Run independent JUnit tests (not as plugin)
+You can do three things:
+1- Run the unit tests as a full "eclipse plugin" suite
+2- Run the unit tests as independent JUnit tests (not as plugin)
+3. Run the functional tests as a full "eclipse plugin" suite (require a real SDK)
+
+The unit tests are isolated tests that do not require external dependencies such as an SDK.
+The functional tests are higher level tests that may require a real SDK.
------------------------------------------
-1- Running the full "eclipse plugin" suite
+1- Running the unit tests as a full "eclipse plugin" suite
------------------------------------------
Steps to run the test suite:
-A- In Eclipse, import following projects from //device/tools/eclipse/plugins:
+A- In Eclipse, import following projects from development/tools/eclipse/plugins:
- adt-tests
- adt
- - common
- - editors
+ - ddms
B- Create a new "JUnit Plug-in Test" run configuration via the "Run > Open Run Dialog..." menu
Set the launch configuration's data as follows:
-------------------------------------------
-2- Run independent JUnit tests (not plugin)
+2- Run the unit tests as independent JUnit tests (not plugin)
-------------------------------------------
-A- In Eclipse, import following projects from //device/tools/eclipse/plugins:
+A- In Eclipse, import following projects from development/tools/eclipse/plugins:
- adt-tests
- adt
- - common
- - editors
+ - ddms
B- Select the "unittests" source folder, right-click and select
"Run As > JUnit Test" (i.e. not the plugin tests)
fail when run as "JUnit Plugin Tests", due to the extra constraints imposed by
running within an Eclipse plug-in noted in section 1.
+------------------------------------------
+3- Running the functional tests as a full "eclipse plugin" suite
+------------------------------------------
+
+Steps to run the test suite:
+
+A- In Eclipse, import following projects from development/tools/eclipse/plugins:
+ - adt-tests
+ - adt
+ - ddms
+B - Setup an SDK on host machine, that is compatible with the Eclipse ADT plugins under test
+
+C- Create a new "JUnit Plug-in Test" run configuration via the "Run > Open Run Dialog..." menu
+Set the launch configuration's data as follows:
+i. "Test" tab:
+ Select "Run a single test"
+ Project: adt-tests
+ Test class: com.android.ide.eclipse.tests.FuncTests
+ Test runner: JUnit 3
+ii. "Environment" tab:
+ Add a "sdk_home" environment variable, setting its path to the SDK from step B
+
+All other fields can be left with their default values
+
+D. Run the newly created launch configuration
+
+Running the tests will run a secondary instance of Eclipse.
prefs.template,\
unittest.xml,\
kxml2-2.3.0.jar,\
- unittests/data/
+ unittests/data/,\
+ sdklib.jar,\
+ layoutlib_api.jar
/*
* Copyright (C) 2008 The Android Open Source Project
- *
+ *
* Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php
- *
+ *
* 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
import junit.framework.TestCase;
/**
- * Generic superclass for Eclipse Android functional test cases, that provides
+ * Generic superclass for Eclipse Android functional test cases, that provides
* common facilities
*/
public class FuncTestCase extends TestCase {
/**
* Constructor
- *
+ *
* @throws IllegalArgumentException if environment variable "sdk_home" is
* not set
*/
protected FuncTestCase() {
mOsSdkLocation = System.getProperty("sdk_home");
+ if (mOsSdkLocation == null) {
+ mOsSdkLocation = System.getenv("sdk_home");
+ }
if (mOsSdkLocation == null || mOsSdkLocation.length() < 1) {
throw new IllegalArgumentException(
"Environment variable sdk_home is not set");
/*
* Copyright (C) 2008 The Android Open Source Project
- *
+ *
* Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php
- *
+ *
* 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
*/
package com.android.ide.eclipse.tests;
+import com.android.ide.eclipse.tests.functests.layoutRendering.ApiDemosRenderingTest;
import com.android.ide.eclipse.tests.functests.sampleProjects.SampleProjectTest;
import junit.framework.TestSuite;
static final String FUNC_TEST_PACKAGE = "com.android.ide.eclipse.tests.functests";
public FuncTests() {
-
+
}
-
+
/**
* Returns a suite of test cases to be run.
* Needed for JUnit3 compliant command line test runner
*/
public static TestSuite suite() {
TestSuite suite = new TestSuite();
-
+
suite.addTestSuite(SampleProjectTest.class);
-
+ suite.addTestSuite(ApiDemosRenderingTest.class);
+
return suite;
}
--- /dev/null
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php
+ *
+ * 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.ide.eclipse.tests.functests.layoutRendering;
+
+import com.android.ide.eclipse.adt.internal.resources.configurations.FolderConfiguration;
+import com.android.ide.eclipse.adt.internal.resources.manager.ProjectResources;
+import com.android.ide.eclipse.adt.internal.resources.manager.ResourceManager;
+import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData;
+import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetParser;
+import com.android.ide.eclipse.adt.internal.sdk.LoadStatus;
+import com.android.ide.eclipse.adt.internal.sdk.Sdk;
+import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData.LayoutBridge;
+import com.android.ide.eclipse.tests.FuncTestCase;
+import com.android.layoutlib.api.ILayoutResult;
+import com.android.layoutlib.api.IProjectCallback;
+import com.android.layoutlib.api.IResourceValue;
+import com.android.layoutlib.api.IXmlPullParser;
+import com.android.sdklib.IAndroidTarget;
+import com.android.sdklib.SdkConstants;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.kxml2.io.KXmlParser;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.imageio.ImageIO;
+
+public class ApiDemosRenderingTest extends FuncTestCase {
+
+ /**
+ * Custom parser that implements {@link IXmlPullParser} (which itself extends
+ * {@link XmlPullParser}).
+ */
+ private final static class TestParser extends KXmlParser implements IXmlPullParser {
+ /**
+ * Since we're not going to go through the result of the rendering/layout, we can return
+ * null for the View Key.
+ */
+ public Object getViewKey() {
+ return null;
+ }
+ }
+
+ private final static class ProjectCallBack implements IProjectCallback {
+ // resource id counter.
+ // We start at 0x7f000000 to avoid colliding with the framework id
+ // since we have no access to the project R.java and we need to generate them automatically.
+ private int mIdCounter = 0x7f000000;
+
+ // in some cases, the id that getResourceValue(String type, String name) returns
+ // will be sent back to get the type/name. This map stores the id/type/name we generate
+ // to be able to do the reverse resolution.
+ private Map<Integer, String[]> mResourceMap = new HashMap<Integer, String[]>();
+
+ private boolean mCustomViewAttempt = false;
+
+ public String getNamespace() {
+ // TODO: read from the ApiDemos manifest.
+ return "com.example.android.apis";
+ }
+
+ public Integer getResourceValue(String type, String name) {
+ Integer result = ++mIdCounter;
+ mResourceMap.put(result, new String[] { name, type });
+ return result;
+ }
+
+ @SuppressWarnings("unchecked")
+ public Object loadView(String name, Class[] constructorSignature, Object[] constructorArgs)
+ throws ClassNotFoundException, Exception {
+ mCustomViewAttempt = true;
+ return null;
+ }
+
+ public String[] resolveResourceValue(int id) {
+ return mResourceMap.get(id);
+ }
+
+ public String resolveResourceValue(int[] id) {
+ return null;
+ }
+
+ }
+
+ private Sdk mSdk;
+
+ public void testApiDemos() throws IOException, XmlPullParserException {
+ loadSdk();
+ findApiDemos();
+ }
+
+ /**
+ * Loads the {@link Sdk}.
+ */
+ private void loadSdk() {
+ mSdk = Sdk.loadSdk(this.getOsSdkLocation());
+
+ int n = mSdk.getTargets().length;
+ if (n > 0) {
+ for (IAndroidTarget target : mSdk.getTargets()) {
+ IStatus status = new AndroidTargetParser(target).run(new NullProgressMonitor());
+ if (status.getCode() != IStatus.OK) {
+ fail("Failed to parse targets data");
+ }
+ }
+ }
+ }
+
+ private void findApiDemos() throws IOException, XmlPullParserException {
+ IAndroidTarget[] targets = mSdk.getTargets();
+
+ for (IAndroidTarget target : targets) {
+ String path = target.getPath(IAndroidTarget.SAMPLES);
+ File samples = new File(path);
+ if (samples.isDirectory()) {
+ File[] files = samples.listFiles();
+ for (File file : files) {
+ if ("apidemos".equalsIgnoreCase(file.getName())) {
+ testSample(target, file);
+ return;
+ }
+ }
+ }
+ }
+
+ fail("Failed to find ApiDemos!");
+ }
+
+ private void testSample(IAndroidTarget target, File sampleProject) throws IOException, XmlPullParserException {
+ AndroidTargetData data = mSdk.getTargetData(target);
+ if (data == null) {
+ fail("No AndroidData!");
+ }
+
+ LayoutBridge bridge = data.getLayoutBridge();
+ if (bridge.status != LoadStatus.LOADED || bridge.bridge == null) {
+ fail("Fail to load the bridge");
+ }
+
+ File resFolder = new File(sampleProject, SdkConstants.FD_RES);
+ if (resFolder.isDirectory() == false) {
+ fail("Sample project has no res folder!");
+ }
+
+ // look for the layout folder
+ File layoutFolder = new File(resFolder, SdkConstants.FD_LAYOUT);
+ if (layoutFolder.isDirectory() == false) {
+ fail("Sample project has no layout folder!");
+ }
+
+ // first load the project's target framework resource
+ ProjectResources framework = ResourceManager.getInstance().loadFrameworkResources(target);
+
+ // now load the project resources
+ ProjectResources project = new ProjectResources(false /* isFrameworkRepository */);
+ ResourceManager.getInstance().loadResources(project, resFolder);
+
+
+ // Create a folder configuration that will be used for the rendering:
+ FolderConfiguration config = getConfiguration();
+
+ // get the configured resources
+ Map<String, Map<String, IResourceValue>> configuredFramework =
+ framework.getConfiguredResources(config);
+ Map<String, Map<String, IResourceValue>> configuredProject =
+ project.getConfiguredResources(config);
+
+ boolean saveFiles = System.getenv("save_file") != null;
+
+ // loop on the layouts and render them
+ File[] layouts = layoutFolder.listFiles();
+ for (File layout : layouts) {
+ // create a parser for the layout file
+ TestParser parser = new TestParser();
+ parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
+ parser.setInput(new FileReader(layout));
+
+ System.out.println("Rendering " + layout.getName());
+
+ ProjectCallBack projectCallBack = new ProjectCallBack();
+
+ ILayoutResult result = bridge.bridge.computeLayout(
+ parser,
+ null /*projectKey*/,
+ 320,
+ 480,
+ 160, //density
+ 160, //xdpi
+ 160, // ydpi
+ "Theme", //themeName
+ false, //isProjectTheme
+ configuredProject,
+ configuredFramework,
+ projectCallBack,
+ null //logger
+ );
+
+ if (result.getSuccess() != ILayoutResult.SUCCESS) {
+ if (projectCallBack.mCustomViewAttempt == false) {
+ System.out.println("FAILED");
+ fail(String.format("Rendering %1$s: %2$s", layout.getName(),
+ result.getErrorMessage()));
+ } else {
+ System.out.println("Ignore custom views for now");
+ }
+ } else {
+ if (saveFiles) {
+ File tmp = File.createTempFile(layout.getName(), ".png");
+ ImageIO.write(result.getImage(), "png", tmp);
+ }
+ System.out.println("Success!");
+ }
+ }
+ }
+
+ private FolderConfiguration getConfiguration() {
+ FolderConfiguration config = new FolderConfiguration();
+
+ return config;
+ }
+}
import com.android.ide.eclipse.adt.internal.resources.manager.files.IFolderWrapper;
import com.android.ide.eclipse.mock.FileMock;
import com.android.ide.eclipse.mock.FolderMock;
+import com.android.sdklib.IAndroidTarget;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
private static final String SEARCHED_FILENAME = "main.xml"; //$NON-NLS-1$
private static final String MISC1_FILENAME = "foo.xml"; //$NON-NLS-1$
private static final String MISC2_FILENAME = "bar.xml"; //$NON-NLS-1$
-
+
private ProjectResources mResources;
private ResourceQualifier[] mQualifierList;
private FolderConfiguration config4;
@Override
protected void setUp() throws Exception {
super.setUp();
-
+
// create a Resource Manager to get a list of qualifier as instantiated by the real code.
- // Thanks for QualifierListTest we know this contains all the qualifiers.
+ // Thanks for QualifierListTest we know this contains all the qualifiers.
ResourceManager manager = ResourceManager.getInstance();
Field qualifierListField = ResourceManager.class.getDeclaredField("mQualifiers");
assertNotNull(qualifierListField);
qualifierListField.setAccessible(true);
-
+
// get the actual list.
mQualifierList = (ResourceQualifier[])qualifierListField.get(manager);
-
+
// create the project resources.
mResources = new ProjectResources(false /* isFrameworkRepository */);
-
+
// create 2 arrays of IResource. one with the filename being looked up, and one without.
// Since the required API uses IResource, we can use MockFolder for them.
FileMock[] validMemberList = new FileMock[] {
new FileMock(MISC1_FILENAME),
new FileMock(MISC2_FILENAME),
};
-
+
// add multiple ResourceFolder to the project resource.
FolderConfiguration defaultConfig = getConfiguration(
null, // country code
null, // text input
null, // navigation
null); // screen size
-
+
addFolder(mResources, defaultConfig, validMemberList);
-
+
config1 = getConfiguration(
null, // country code
null, // network code
null, // text input
null, // navigation
null); // screen size
-
+
addFolder(mResources, config1, validMemberList);
config2 = getConfiguration(
null, // text input
null, // navigation
null); // screen size
-
+
addFolder(mResources, config2, validMemberList);
config3 = getConfiguration(
null, // text input
null, // navigation
null); // screen size
-
+
addFolder(mResources, config3, validMemberList);
config4 = getConfiguration(
TextInputMethod.QWERTY.getValue(), // text input
NavigationMethod.DPAD.getValue(), // navigation
"480x320"); // screen size
-
+
addFolder(mResources, config4, invalidMemberList);
}
TextInputMethod.QWERTY.getValue(), // text input
NavigationMethod.DPAD.getValue(), // navigation
"480x320"); // screen size
-
+
ResourceFile result = mResources.getMatchingFile(SEARCHED_FILENAME,
ResourceFolderType.LAYOUT, testConfig);
-
+
boolean bresult = result.getFolder().getConfiguration().equals(config3);
assertEquals(bresult, true);
}
*/
private FolderConfiguration getConfiguration(String... qualifierValues) {
FolderConfiguration config = new FolderConfiguration();
-
+
// those must be of the same length
assertEquals(qualifierValues.length, mQualifierList.length);
-
+
int index = 0;
for (ResourceQualifier qualifier : mQualifierList) {
return config;
}
-
+
/**
* Adds a folder to the given {@link ProjectResources} with the given
* {@link FolderConfiguration}. The folder is filled with files from the provided list.
*/
private void addFolder(ProjectResources resources, FolderConfiguration config,
FileMock[] memberList) throws Exception {
-
+
// figure out the folder name based on the configuration
- String folderName = "layout";
- if (config.isDefault() == false) {
- folderName += "-" + config.toString();
- }
-
+ String folderName = config.getFolderName(ResourceFolderType.LAYOUT, (IAndroidTarget)null);
+
// create the folder mock
FolderMock folder = new FolderMock(folderName, memberList);
}
}
- /** Calls ProjectResource.add method via reflection to circumvent access
- * restrictions that are enforced when running in the plug-in environment
+ /** Calls ProjectResource.add method via reflection to circumvent access
+ * restrictions that are enforced when running in the plug-in environment
* ie cannot access package or protected members in a different plug-in, even
* if they are in the same declared package as the accessor
*/
private ResourceFolder _addProjectResourceFolder(ProjectResources resources,
FolderConfiguration config, FolderMock folder) throws Exception {
- Method addMethod = ProjectResources.class.getDeclaredMethod("add",
+ Method addMethod = ProjectResources.class.getDeclaredMethod("add",
ResourceFolderType.class, FolderConfiguration.class,
IAbstractFolder.class);
addMethod.setAccessible(true);
ResourceFolderType.LAYOUT, config, new IFolderWrapper(folder));
return resFolder;
}
-
-
-
}
<description url="https://dl-ssl.google.com/android/eclipse/">
Update Site for Android Development Toolkit
</description>
- <feature url="features/com.android.ide.eclipse.adt_0.9.2.qualifier.jar" id="com.android.ide.eclipse.adt" version="0.9.2.qualifier">
+ <feature url="features/com.android.ide.eclipse.adt_0.9.3.qualifier.jar" id="com.android.ide.eclipse.adt" version="0.9.3.qualifier">
<category name="developer"/>
</feature>
- <feature url="features/com.android.ide.eclipse.ddms_0.9.2.qualifier.jar" id="com.android.ide.eclipse.ddms" version="0.9.2.qualifier">
+ <feature url="features/com.android.ide.eclipse.ddms_0.9.3.qualifier.jar" id="com.android.ide.eclipse.ddms" version="0.9.3.qualifier">
<category name="developer"/>
</feature>
<category-def name="developer" label="Developer Tools">
<description url="https://android.corp.google.com/adt/">
Update Site for Android Development Toolkit
</description>
- <feature url="features/com.android.ide.eclipse.adt_0.9.2.qualifier.jar" id="com.android.ide.eclipse.adt" version="0.9.2.qualifier">
+ <feature url="features/com.android.ide.eclipse.adt_0.9.3.qualifier.jar" id="com.android.ide.eclipse.adt" version="0.9.3.qualifier">
<category name="developer"/>
</feature>
- <feature url="features/com.android.ide.eclipse.ddms_0.9.2.qualifier.jar" id="com.android.ide.eclipse.ddms" version="0.9.2.qualifier">
+ <feature url="features/com.android.ide.eclipse.ddms_0.9.3.qualifier.jar" id="com.android.ide.eclipse.ddms" version="0.9.3.qualifier">
<category name="developer"/>
</feature>
- <feature url="features/com.android.ide.eclipse.tests_0.9.2.qualifier.jar" id="com.android.ide.eclipse.tests" version="0.9.2.qualifier">
+ <feature url="features/com.android.ide.eclipse.tests_0.9.3.qualifier.jar" id="com.android.ide.eclipse.tests" version="0.9.3.qualifier">
<category name="test"/>
</feature>
<category-def name="developer" label="Application Developer Tools">
--- /dev/null
+#
+# 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.
+#
+MONKEYRUNNER_LOCAL_DIR := $(call my-dir)
+include $(MONKEYRUNNER_LOCAL_DIR)/etc/Android.mk
+include $(MONKEYRUNNER_LOCAL_DIR)/src/Android.mk
--- /dev/null
+
+ Copyright (c) 2005-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.
+
+ 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.
+
+
+ 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
+
--- /dev/null
+#
+# 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.
+#
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_PREBUILT_EXECUTABLES := monkeyrunner
+include $(BUILD_HOST_PREBUILT)
--- /dev/null
+Main-Class: com.android.monkeyrunner.MonkeyRunner
--- /dev/null
+#!/bin/sh
+# Copyright 2005-2007, 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.
+
+# Set up prog to be the path of this script, including following symlinks,
+# and set up progdir to be the fully-qualified pathname of its directory.
+prog="$0"
+while [ -h "${prog}" ]; do
+ newProg=`/bin/ls -ld "${prog}"`
+ newProg=`expr "${newProg}" : ".* -> \(.*\)$"`
+ if expr "x${newProg}" : 'x/' >/dev/null; then
+ prog="${newProg}"
+ else
+ progdir=`dirname "${prog}"`
+ prog="${progdir}/${newProg}"
+ fi
+done
+oldwd=`pwd`
+progdir=`dirname "${prog}"`
+cd "${progdir}"
+progdir=`pwd`
+prog="${progdir}"/`basename "${prog}"`
+cd "${oldwd}"
+
+jarfile=monkeyrunner.jar
+frameworkdir="$progdir"
+libdir="$progdir"
+if [ ! -r "$frameworkdir/$jarfile" ]
+then
+ frameworkdir=`dirname "$progdir"`/tools/lib
+ libdir=`dirname "$progdir"`/tools/lib
+fi
+if [ ! -r "$frameworkdir/$jarfile" ]
+then
+ frameworkdir=`dirname "$progdir"`/framework
+ libdir=`dirname "$progdir"`/lib
+fi
+if [ ! -r "$frameworkdir/$jarfile" ]
+then
+ echo `basename "$prog"`": can't find $jarfile"
+ exit 1
+fi
+
+
+# Check args.
+if [ debug = "$1" ]; then
+ # add this in for debugging
+ java_debug=-agentlib:jdwp=transport=dt_socket,server=y,address=8050,suspend=y
+ shift 1
+else
+ java_debug=
+fi
+
+if [ "$OSTYPE" = "cygwin" ] ; then
+ jarpath=`cygpath -w "$frameworkdir/$jarfile"`
+ progdir=`cygpath -w "$progdir"`
+else
+ jarpath="$frameworkdir/$jarfile"
+fi
+
+# need to use "java.ext.dirs" because "-jar" causes classpath to be ignored
+# might need more memory, e.g. -Xmx128M
+exec java -Xmx128M $os_opts $java_debug -Djava.ext.dirs="$frameworkdir" -Djava.library.path="$libdir" -Dcom.android.monkeyrunner.bindir="$progdir" -jar "$jarpath" "$@"
--- /dev/null
+#
+# 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.
+#
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_JAR_MANIFEST := ../etc/manifest.txt
+LOCAL_JAVA_LIBRARIES := \
+ ddmlib \
+ jython
+
+
+LOCAL_MODULE := monkeyrunner
+
+include $(BUILD_HOST_JAVA_LIBRARY)
--- /dev/null
+/*
+ * 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 com.android.ddmlib.AndroidDebugBridge;
+import com.android.ddmlib.IDevice;
+import com.android.ddmlib.Log;
+import com.android.ddmlib.NullOutputReceiver;
+import com.android.ddmlib.RawImage;
+import com.android.ddmlib.Log.ILogOutput;
+import com.android.ddmlib.Log.LogLevel;
+
+import java.awt.image.BufferedImage;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.net.UnknownHostException;
+
+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.
+ */
+public class MonkeyRunner {
+
+ static String monkeyServer = "127.0.0.1";
+ static int monkeyPort = 1080;
+ static Socket monkeySocket = null;
+
+ static IDevice monkeyDevice;
+
+ static BufferedReader monkeyReader;
+ static BufferedWriter monkeyWriter;
+
+ static String scriptName = null;
+
+ // delay between key events
+ final static int KEY_INPUT_DELAY = 1000;
+
+ public static void main(String[] args) {
+
+ processOptions(args);
+
+ initAdbConnection();
+ openMonkeyConnection();
+
+ start_script();
+
+ ScriptRunner.run(scriptName);
+
+ end_script();
+ closeMonkeyConnection();
+ }
+
+ /**
+ * Initialize an adb session with a device connected to the host
+ *
+ */
+ public static void initAdbConnection() {
+ String adbLocation = "adb";
+ boolean device = false;
+ boolean emulator = false;
+ String serial = null;
+
+ AndroidDebugBridge.init(false /* debugger support */);
+
+ try {
+ AndroidDebugBridge bridge = AndroidDebugBridge.createBridge(
+ adbLocation, true /* forceNewBridge */);
+
+ // we can't just ask for the device list right away, as the internal thread getting
+ // them from ADB may not be done getting the first list.
+ // Since we don't really want getDevices() to be blocking, we wait here manually.
+ int count = 0;
+ while (bridge.hasInitialDeviceList() == false) {
+ try {
+ Thread.sleep(100);
+ count++;
+ } catch (InterruptedException e) {
+ // pass
+ }
+
+ // let's not wait > 10 sec.
+ if (count > 100) {
+ System.err.println("Timeout getting device list!");
+ return;
+ }
+ }
+
+ // now get the devices
+ IDevice[] devices = bridge.getDevices();
+
+ if (devices.length == 0) {
+ printAndExit("No devices found!", true /* terminate */);
+ }
+
+ monkeyDevice = null;
+
+ if (emulator || device) {
+ for (IDevice d : devices) {
+ // this test works because emulator and device can't both be true at the same
+ // time.
+ if (d.isEmulator() == emulator) {
+ // if we already found a valid target, we print an error and return.
+ if (monkeyDevice != null) {
+ if (emulator) {
+ printAndExit("Error: more than one emulator launched!",
+ true /* terminate */);
+ } else {
+ printAndExit("Error: more than one device connected!",true /* terminate */);
+ }
+ }
+ monkeyDevice = d;
+ }
+ }
+ } else if (serial != null) {
+ for (IDevice d : devices) {
+ if (serial.equals(d.getSerialNumber())) {
+ monkeyDevice = d;
+ break;
+ }
+ }
+ } else {
+ if (devices.length > 1) {
+ printAndExit("Error: more than one emulator or device available!",
+ true /* terminate */);
+ }
+ monkeyDevice = devices[0];
+ }
+
+ monkeyDevice.createForward(monkeyPort, monkeyPort);
+ String command = "monkey --port " + monkeyPort;
+ monkeyDevice.executeShellCommand(command, new NullOutputReceiver());
+
+ } catch(IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * Open a tcp session over adb with the device to communicate monkey commands
+ */
+ public static void openMonkeyConnection() {
+ try {
+ InetAddress addr = InetAddress.getByName(monkeyServer);
+ monkeySocket = new Socket(addr, monkeyPort);
+ } catch (UnknownHostException e) {
+ e.printStackTrace();
+ } catch(IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * Close tcp session with the monkey on the device
+ *
+ */
+ public static void closeMonkeyConnection() {
+ try {
+ monkeyReader.close();
+ monkeyWriter.close();
+ monkeySocket.close();
+ AndroidDebugBridge.terminate();
+ } catch(IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * This is a house cleaning type routine to run before starting a script. Puts
+ * the device in a known state.
+ */
+ public static void start_script() {
+ press("menu");
+ press("menu");
+ press("home");
+ }
+
+ public static void end_script() {
+ String command = "END";
+ sendMonkeyEvent(command);
+ }
+
+ /** 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();
+ }
+ }
+
+ /**
+ * 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) {
+ tag += ".png";
+
+ try {
+ Thread.sleep(1000);
+ getDeviceImage(monkeyDevice, tag, false);
+ } catch (IOException e) {
+ e.printStackTrace();
+ } catch (InterruptedException e) {
+ }
+ }
+
+ /**
+ * Sleeper method for script to call
+ *
+ * @param msec msecs to sleep for
+ */
+ public static void sleep(int msec) {
+ try {
+ Thread.sleep(msec);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * Tap function for scripts to call at a particular x and y location
+ *
+ * @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";
+
+ System.out.println("Tapping: " + x + ", " + y);
+ return sendMonkeyEvent(command);
+ }
+
+ /**
+ * Press function for scripts to call on a particular button or key
+ *
+ * @param key key to press
+ */
+ public static boolean press(String key) {
+ String command = "key down " + key + "\r\n" +
+ "key up " + key + "\r\n";
+
+ System.out.println("Pressing: " + key);
+ return sendMonkeyEvent(command);
+ }
+
+ /**
+ * dpad down function
+ */
+ public static boolean down() {
+ return press("dpad_down");
+ }
+
+ /**
+ * dpad up function
+ */
+ public static boolean up() {
+ return press("dpad_up");
+ }
+
+ /**
+ * Function to type text on the device
+ *
+ * @param text text to type
+ */
+ public static boolean type(String text) {
+ System.out.println("Typing: " + text);
+ for (int i=0; i<text.length(); i++) {
+ String command = "key down ";
+ char c = text.charAt(i);
+ if(Character.isDigit(c)) {
+ command += "KEYCODE_" + c + "\n" + "key up KEYCODE_" + c + "\n";
+ } else {
+ command = "key down " + c + "\n" + "key up " + c + "\n";
+ }
+
+ if(!sendMonkeyEvent(command)) {
+ System.out.println("\nERROR: Key not set \n");
+ }
+
+ // lets delay a bit after each input to ensure accuracy
+ try {
+ Thread.sleep(KEY_INPUT_DELAY);
+ } catch (InterruptedException e) {
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * This function is the communication bridge between the host and the device.
+ * It sends monkey events and waits for responses over the adb tcp socket.
+ *
+ * @param command the monkey command to send to the device
+ */
+ private static boolean sendMonkeyEvent(String command) {
+ try {
+ monkeyWriter = new BufferedWriter(
+ new OutputStreamWriter(monkeySocket.getOutputStream()));
+ monkeyWriter.write(command);
+ monkeyWriter.flush();
+
+ monkeyReader = new BufferedReader(
+ new InputStreamReader(monkeySocket.getInputStream()));
+ String response = monkeyReader.readLine();
+
+ sleep(1000);
+ System.out.println("MonkeyServer: " + response);
+ if(response.equals("OK"))
+ return true;
+ if(response.equals("ERROR"))
+ return false;
+
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ return false;
+ }
+
+
+ /**
+ * Process the command-line options
+ *
+ * @return Returns true if options were parsed with no apparent errors.
+ */
+ public static void processOptions(String[] args) {
+ // parse command line parameters.
+ int index = 0;
+
+ do {
+ String argument = args[index++];
+
+ if ("-s".equals(argument)) {
+ if(index == args.length) {
+ printAndExit("Missing Server after -s", false);
+ }
+
+ monkeyServer = args[index++];
+
+ } else if ("-p".equals(argument)) {
+ // quick check on the next argument.
+ if (index == args.length) {
+ printAndExit("Missing Server IP after -p", false /* terminate */);
+ }
+
+ monkeyPort = Integer.parseInt(args[index++]);
+ } else {
+ // get the filepath of the script to run.
+ scriptName = argument;
+
+ // should not be any other device.
+ //if (index < args.length) {
+ // printAndExit("Too many arguments!", false /* terminate */);
+ //}
+ }
+ } while (index < args.length);
+ }
+
+ /*
+ * Grab an image from an ADB-connected device.
+ */
+ private static void getDeviceImage(IDevice device, String filepath, boolean landscape)
+ throws IOException {
+ RawImage rawImage;
+
+ try {
+ rawImage = device.getScreenshot();
+ }
+ catch (IOException ioe) {
+ printAndExit("Unable to get frame buffer: " + ioe.getMessage(), true /* terminate */);
+ return;
+ }
+
+ // device/adb not available?
+ if (rawImage == null)
+ return;
+
+ assert rawImage.bpp == 16;
+
+ BufferedImage image;
+
+ if (landscape) {
+ // convert raw data to an Image
+ image = new BufferedImage(rawImage.height, rawImage.width,
+ BufferedImage.TYPE_INT_ARGB);
+
+ byte[] buffer = rawImage.data;
+ int index = 0;
+ for (int y = 0 ; y < rawImage.height ; y++) {
+ for (int x = 0 ; x < rawImage.width ; x++) {
+
+ int value = buffer[index++] & 0x00FF;
+ value |= (buffer[index++] << 8) & 0x0FF00;
+
+ int r = ((value >> 11) & 0x01F) << 3;
+ int g = ((value >> 5) & 0x03F) << 2;
+ int b = ((value >> 0) & 0x01F) << 3;
+
+ value = 0xFF << 24 | r << 16 | g << 8 | b;
+
+ image.setRGB(y, rawImage.width - x - 1, value);
+ }
+ }
+ } else {
+ // convert raw data to an Image
+ image = new BufferedImage(rawImage.width, rawImage.height,
+ BufferedImage.TYPE_INT_ARGB);
+
+ byte[] buffer = rawImage.data;
+ int index = 0;
+ for (int y = 0 ; y < rawImage.height ; y++) {
+ for (int x = 0 ; x < rawImage.width ; x++) {
+
+ int value = buffer[index++] & 0x00FF;
+ value |= (buffer[index++] << 8) & 0x0FF00;
+
+ int r = ((value >> 11) & 0x01F) << 3;
+ int g = ((value >> 5) & 0x03F) << 2;
+ int b = ((value >> 0) & 0x01F) << 3;
+
+ value = 0xFF << 24 | r << 16 | g << 8 | b;
+
+ image.setRGB(x, y, value);
+ }
+ }
+ }
+
+ if (!ImageIO.write(image, "png", new File(filepath))) {
+ throw new IOException("Failed to find png writer");
+ }
+ }
+
+ private static void printUsageAndQuit() {
+ // 80 cols marker: 01234567890123456789012345678901234567890123456789012345678901234567890123456789
+ 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("");
+ System.out.println("");
+
+ System.exit(1);
+ }
+
+ private static void printAndExit(String message, boolean terminate) {
+ System.out.println(message);
+ if (terminate) {
+ AndroidDebugBridge.terminate();
+ }
+ System.exit(1);
+ }
+}
--- /dev/null
+package com.android.monkeyrunner;
+
+import org.python.core.Py;
+import org.python.core.PyObject;
+import org.python.util.PythonInterpreter;
+import org.python.util.InteractiveConsole;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.FileInputStream;
+import java.lang.RuntimeException;
+import java.util.Properties;
+
+
+/**
+ * Runs Jython based scripts.
+ */
+public class ScriptRunner {
+
+ /** The "this" scope object for scripts. */
+ private final Object scope;
+ private final String variable;
+
+ /** Private constructor. */
+ private ScriptRunner(Object scope, String variable) {
+ this.scope = scope;
+ this.variable = variable;
+ }
+
+ /** Creates a new instance for the given scope object. */
+ public static ScriptRunner newInstance(Object scope, String variable) {
+ return new ScriptRunner(scope, variable);
+ }
+
+ /**
+ * Runs the specified Jython script. First runs the initialization script to
+ * preload the appropriate client library version.
+ */
+ public static void run(String scriptfilename) {
+ try {
+ initPython();
+ PythonInterpreter python = new PythonInterpreter();
+
+ python.execfile(scriptfilename);
+ } catch(Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+
+ /** Initialize the python interpreter. */
+ private static void initPython() {
+ Properties props = new Properties();
+ // Default is 'message' which displays sys-package-mgr bloat
+ // Choose one of error,warning,message,comment,debug
+ props.setProperty("python.verbose", "error");
+ props.setProperty("python.path", System.getProperty("java.class.path"));
+ PythonInterpreter.initialize(System.getProperties(), props, new String[] {""});
+ }
+
+ /**
+ * Create and run a console using a new python interpreter for the test
+ * associated with this instance.
+ */
+ public void console() throws IOException {
+ initPython();
+ InteractiveConsole python = new InteractiveConsole();
+ initInterpreter(python, scope, variable);
+ python.interact();
+ }
+
+ /**
+ * Start an interactive python interpreter using the specified set of local
+ * variables. Use this to interrupt a running test script with a prompt:
+ *
+ * @param locals
+ */
+ public static void console(PyObject locals) {
+ initPython();
+ InteractiveConsole python = new InteractiveConsole(locals);
+ python.interact();
+ }
+
+ /**
+ * Initialize a python interpreter.
+ *
+ * @param python
+ * @param scope
+ * @throws IOException
+ */
+ public static void initInterpreter(PythonInterpreter python, Object scope, String variable)
+ throws IOException {
+ // Store the current test case as the this variable
+ python.set(variable, scope);
+ }
+}
<?xml version="1.0" encoding="utf-8"?>
+<!--
+ALIAS PROJECTS ARE CURRENT NOT SUPPORTED.
+THIS FILE IS CURRENTLY BROKEN AND SHOULD NOT BE USED.
+-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="PACKAGE"
android:versionCode="1"
<?xml version="1.0" encoding="utf-8"?>
+<!--
+ALIAS PROJECTS ARE CURRENT NOT SUPPORTED.
+THIS FILE IS CURRENTLY BROKEN AND SHOULD NOT BE USED.
+-->
<alias xmlns:android="http://schemas.android.com/apk/res/android">
<intent android:action="android.intent.action.VIEW"
android:data="ALIASDATA"/>
<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ALIAS PROJECTS ARE CURRENT NOT SUPPORTED.
+THIS FILE IS CURRENTLY BROKEN AND SHOULD NOT BE USED.
+-->
<project name="alias_rules" default="package">
<!-- No user servicable parts below. -->
</condition>
<!-- The final package file to generate -->
+ <property name="out-debug-unaligned-package" value="${out-folder}/${ant.project.name}-debug-unaligned.apk"/>
<property name="out-debug-package" value="${out-folder}/${ant.project.name}-debug.apk"/>
+ <property name="out-unsigned-package" value="${out-folder}/${ant.project.name}-unsigned.apk"/>
+ <property name="out-unaligned-package" value="${out-folder}/${ant.project.name}-unaligned.apk"/>
+ <property name="out-release-package" value="${out-folder}/${ant.project.name}-release.apk"/>
<!-- Tools -->
<condition property="exe" value=".exe" else=""><os family="windows"/></condition>
<property name="adb" value="${android-tools}/adb${exe}"/>
+ <property name="zipalign" value="${android-tools}/zipalign${exe}" />
<!-- rules -->
basename="${ant.project.name}" />
</target>
- <!-- Package the application and sign it with a debug key.
- This is the default target when building. It is used for debug. -->
- <target name="debug" depends="dex, package-resources">
+ <!-- Package the application and (maybe) sign it with a debug key.
+ This requires the property sign.package to be set to true or false. -->
+ <target name="package">
<apkbuilder
outfolder="${out-folder}"
basename="${ant.project.name}"
- signed="true"
- verbose="false">
+ signed="${sign.package}"
+ verbose="true">
<file path="${intermediate-dex}" />
<sourcefolder path="${source-folder}" />
<jarfolder path="${external-libs-folder}" />
</apkbuilder>
</target>
- <!-- Package the application without signing it.
- This allows for the application to be signed later with an official publishing key. -->
- <target name="release" depends="dex, package-resources">
- <apkbuilder
- outfolder="${out-folder}"
- basename="${ant.project.name}"
- signed="false"
- verbose="false">
- <file path="${intermediate-dex}" />
- <sourcefolder path="${source-folder}" />
- <jarfolder path="${external-libs-folder}" />
- <nativefolder path="${native-libs-folder}" />
- </apkbuilder>
- <echo>All generated packages need to be signed with jarsigner before they are published.</echo>
+ <target name="no-sign">
+ <property name="sign.package" value="false" />
</target>
- <!-- Install the package on the default emulator -->
- <target name="install" depends="debug">
- <echo>Installing ${out-debug-package} onto default emulator...</echo>
- <exec executable="${adb}" failonerror="true">
- <arg value="install" />
+ <target name="debug-sign">
+ <property name="sign.package" value="true" />
+ </target>
+
+ <target name="debug" depends="dex, package-resources, debug-sign, package">
+ <echo>Running zip align on final apk...</echo>
+ <exec executable="${zipalign}" failonerror="true">
+ <arg value="-f" />
+ <arg value="4" />
+ <arg path="${out-debug-unaligned-package}" />
<arg path="${out-debug-package}" />
</exec>
+ <echo>Debug Package: ${out-debug-package}</echo>
+ </target>
+
+ <target name="release-package" depends="dex, package-resources, no-sign, package">
</target>
- <target name="reinstall" depends="debug">
+ <target name="release.check">
+ <condition property="release.sign">
+ <and>
+ <isset property="key.store" />
+ <isset property="key.alias" />
+ </and>
+ </condition>
+ </target>
+ <target name="release.nosign" depends="release.check" unless="release.sign">
+ <echo>No key.store and key.alias properties found in build.properties.</echo>
+ <echo>Please sign ${out-unsigned-package} manually</echo>
+ <echo>and run zipalign from the Android SDK tools.</echo>
+ </target>
+
+ <target name="release" depends="release-package, release.nosign" if="release.sign">
+ <!-- get passwords -->
+ <input
+ message="Please enter keystore password (store:${key.store}):"
+ addproperty="key.store.password"/>
+ <input
+ message="Please enter password for alias '${key.alias}':"
+ addproperty="key.alias.password"/>
+ <!-- sign the APK -->
+ <echo>Signing final apk...</echo>
+ <signjar
+ jar="${out-unsigned-package}"
+ signedjar="${out-unaligned-package}"
+ keystore="${key.store}"
+ storepass="${key.store.password}"
+ alias="${key.alias}"
+ keypass="${key.alias.password}"/>
+ <!-- zip align the APK -->
+ <echo>Running zip align on final apk...</echo>
+ <exec executable="${zipalign}" failonerror="true">
+ <arg value="-f" />
+ <arg value="4" />
+ <arg path="${out-unaligned-package}" />
+ <arg path="${out-release-package}" />
+ </exec>
+ <echo>Release Package: ${out-release-package}</echo>
+ </target>
+
+ <!-- Install the package on the default emulator -->
+ <target name="install" depends="debug">
<echo>Installing ${out-debug-package} onto default emulator...</echo>
<exec executable="${adb}" failonerror="true">
<arg value="install" />
</target>
<!-- Uinstall the package from the default emulator -->
- <target name="uninstall">
+ <target name="uninstall.check">
+ <condition property="uninstall.run">
+ <isset property="application-package" />
+ </condition>
+ </target>
+ <target name="uninstall.error" depends="uninstall.check" unless="uninstall.run">
+ <echo>Unable to run 'ant unintall', application-package is not defined in build.properties</echo>
+ </target>
+ <target name="uninstall" depends="uninstall.error" if="uninstall.run">
<echo>Uninstalling ${application-package} from the default emulator...</echo>
<exec executable="${adb}" failonerror="true">
<arg value="uninstall" />
<echo> debug: Builds the application and sign it with a debug key.</echo>
<echo> release: Builds the application. The generated apk file must be</echo>
<echo> signed before it is published.</echo>
- <echo> install: Installs the debug package onto a running emulator or</echo>
- <echo> device. This can only be used if the application has </echo>
- <echo> not yet been installed.</echo>
- <echo> reinstall: Installs the debug package on a running emulator or</echo>
- <echo> device that already has the application.</echo>
- <echo> The signatures must match.</echo>
+ <echo> install: Installs/reinstall the debug package onto a running</echo>
+ <echo> emulator or device.</echo>
+ <echo> If the application was previously installed, the</echo>
+ <echo> signatures must match.</echo>
<echo> uninstall: uninstall the application from a running emulator or</echo>
<echo> device.</echo>
</target>
<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ALIAS PROJECTS ARE CURRENT NOT SUPPORTED.
+THIS FILE IS CURRENTLY BROKEN AND SHOULD NOT BE USED.
+-->
<project name="PROJECT_NAME" default="package">
<!-- The build.properties file can be created by you and is never touched
classpathref="android.antlibs"/>
<!-- Execute the Android Setup task that will setup some properties specific to the target,
- and import the rules files.
- To customize the rules, copy/paste them below the task, and disable import by setting
- the import attribute to false:
- <setup import="false" />
+ and import the build rules files.
+
+ The rules file is imported from
+ <SDK>/platforms/<target_platform>/templates/android_rules.xml
+
+ To customize some build steps for your project:
+ - copy the content of the main node <project> from android_rules.xml
+ - paste it in this build.xml below the <setup /> task.
+ - disable the import by changing the setup task below to <setup import="false" />
This will ensure that the properties are setup correctly but that your customized
- targets are used.
+ build steps are used.
-->
<setup />
</project>
Pkg.UserSrc=false
-Platform.Version=Donut
+Platform.Version=1.6
Pkg.Revision=1
-AndroidVersion.CodeName=Donut
-AndroidVersion.ApiLevel=3
+AndroidVersion.ApiLevel=4
package PACKAGE;
-import android.test.ActivityInstrumentationTestCase;
+import android.test.ActivityInstrumentationTestCase2;
/**
* This is a simple framework for a test of an Application. See
* -e class PACKAGE.ACTIVITY_NAMETest \
* PACKAGE.tests/android.test.InstrumentationTestRunner
*/
-public class ACTIVITY_NAMETest extends ActivityInstrumentationTestCase<ACTIVITY_NAME> {
+public class ACTIVITY_NAMETest extends ActivityInstrumentationTestCase2<ACTIVITY_NAME> {
public ACTIVITY_NAMETest() {
super("PACKAGE", ACTIVITY_NAME.class);
-Pkg.Desc=Android SDK Platform Donut_r1
+Pkg.Desc=Android SDK Platform 1.6_r1
Pkg.UserSrc=false
-Platform.Version=Donut
+Platform.Version=1.6
Pkg.Revision=1
-AndroidVersion.CodeName=Donut
-AndroidVersion.ApiLevel=3
+AndroidVersion.ApiLevel=4
Pkg.UserSrc=false
-Pkg.Revision=1
+Pkg.Revision=2
set tmpdir=%TEMP%\temp-android-tool
xcopy lib\x86 %tmpdir%\lib\x86 /I /E /C /G /R /O /Y /Q > nul
- copy /B /D /Y lib\androidprefs.jar %tmpdir%\lib\ > nul
- copy /B /D /Y lib\org.eclipse.* %tmpdir%\lib\ > nul
- copy /B /D /Y lib\sdk* %tmpdir%\lib\ > nul
+ copy /B /D /Y lib\androidprefs.jar %tmpdir%\lib\ > nul
+ copy /B /D /Y lib\org.eclipse.* %tmpdir%\lib\ > nul
+ copy /B /D /Y lib\sdk* %tmpdir%\lib\ > nul
+ copy /B /D /Y lib\commons-compress* %tmpdir%\lib\ > nul
rem jarpath and swt_path are relative to PWD so we don't need to adjust them, just change dirs.
set toolsdir=%cd%
private Text mProxyServerText;\r
private Text mProxyPortText;\r
private Button mForceHttpCheck;\r
+ private Button mAskAdbRestartCheck;\r
\r
private ModifyListener mSetApplyDirty = new ModifyListener() {\r
public void modifyText(ModifyEvent e) {\r
mProxyServerLabel = new Label(mProxySettingsGroup, SWT.NONE);\r
mProxyServerLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));\r
mProxyServerLabel.setText("HTTP Proxy Server");\r
+ String tooltip = "The DNS name or IP of the HTTP proxy server to use. " +\r
+ "When empty, no HTTP proxy is used.";\r
+ mProxyServerLabel.setToolTipText(tooltip);\r
\r
mProxyServerText = new Text(mProxySettingsGroup, SWT.BORDER);\r
mProxyServerText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));\r
mProxyServerText.addModifyListener(mSetApplyDirty);\r
+ mProxyServerText.setToolTipText(tooltip);\r
\r
mProxyPortLabel = new Label(mProxySettingsGroup, SWT.NONE);\r
mProxyPortLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));\r
mProxyPortLabel.setText("HTTP Proxy Port");\r
+ tooltip = "The port of the HTTP proxy server to use. " +\r
+ "When empty, the default for HTTP or HTTPS is used.";\r
+ mProxyPortLabel.setToolTipText(tooltip);\r
\r
mProxyPortText = new Text(mProxySettingsGroup, SWT.BORDER);\r
mProxyPortText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));\r
mProxyPortText.addModifyListener(mSetApplyDirty);\r
+ mProxyPortText.setToolTipText(tooltip);\r
\r
mMiscGroup = new Group(this, SWT.NONE);\r
mMiscGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));\r
mMiscGroup.setLayout(new GridLayout(2, false));\r
\r
mForceHttpCheck = new Button(mMiscGroup, SWT.CHECK);\r
+ mForceHttpCheck.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1));\r
mForceHttpCheck.setText("Force https://... sources to be fetched using http://...");\r
+ mForceHttpCheck.setToolTipText("If you are not able to connect to the official Android repository " +\r
+ "using HTTPS, enable this setting to force accessing it via HTTP.");\r
mForceHttpCheck.addSelectionListener(new SelectionAdapter() {\r
@Override\r
public void widgetSelected(SelectionEvent e) {\r
}\r
});\r
\r
+ mAskAdbRestartCheck = new Button(mMiscGroup, SWT.CHECK);\r
+ mAskAdbRestartCheck.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1));\r
+ mAskAdbRestartCheck.setText("Ask before restarting ADB");\r
+ mAskAdbRestartCheck.setToolTipText("When checked, the user will be asked for permission " +\r
+ "to restart ADB after updating an addon-on package or a tool package.");\r
+ mAskAdbRestartCheck.addSelectionListener(new SelectionAdapter() {\r
+ @Override\r
+ public void widgetSelected(SelectionEvent e) {\r
+ onForceHttpSelected(); //$hide$\r
+ }\r
+ });\r
+\r
mApplyButton = new Button(this, SWT.NONE);\r
mApplyButton.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));\r
mApplyButton.setText("Save && Apply");\r
mProxyServerText.setText(in_settings.getProperty(KEY_HTTP_PROXY_HOST, "")); //$NON-NLS-1$\r
mProxyPortText.setText( in_settings.getProperty(KEY_HTTP_PROXY_PORT, "")); //$NON-NLS-1$\r
mForceHttpCheck.setSelection(Boolean.parseBoolean(in_settings.getProperty(KEY_FORCE_HTTP)));\r
+ mAskAdbRestartCheck.setSelection(Boolean.parseBoolean(in_settings.getProperty(KEY_ASK_ADB_RESTART)));\r
\r
// We loaded fresh settings so there's nothing dirty to apply\r
mApplyButton.setEnabled(false);\r
out_settings.setProperty(KEY_HTTP_PROXY_PORT, mProxyPortText.getText());\r
out_settings.setProperty(KEY_FORCE_HTTP,\r
Boolean.toString(mForceHttpCheck.getSelection()));\r
+ out_settings.setProperty(KEY_ASK_ADB_RESTART,\r
+ Boolean.toString(mAskAdbRestartCheck.getSelection()));\r
}\r
\r
/**\r
return true;
}
- // if the receiver has optional libraries, then the target is only compatible if the
- // vendor and name are the same
- if (mLibraries.length != 0 &&
- (mVendor.equals(target.getVendor()) == false ||
- mName.equals(target.getName()) == false)) {
- return false;
+ /*
+ * The method javadoc indicates:
+ * Returns whether the given target is compatible with the receiver.
+ * <p/>A target is considered compatible if applications developed for the receiver can
+ * run on the given target.
+ */
+
+ // The receiver is an add-on. There are 2 big use cases: The add-on has libraries
+ // or the add-on doesn't (in which case we consider it a platform).
+ if (mLibraries.length == 0) {
+ return mBasePlatform.isCompatibleBaseFor(target);
+ } else {
+ // the only targets that can run the receiver are the same add-on in the same or later
+ // versions.
+ // first check: vendor/name
+ if (mVendor.equals(target.getVendor()) == false ||
+ mName.equals(target.getName()) == false) {
+ return false;
+ }
+
+ // now check the version. At this point since we checked the add-on part,
+ // we can revert to the basic check on version/codename which are done by the
+ // base platform already.
+ return mBasePlatform.isCompatibleBaseFor(target);
}
- return mBasePlatform.equals(target);
}
public String hashString() {
} else if (obj instanceof String) {
// if we have a code name, this must match.
if (mCodename != null) {
- return mCodename.equals((String)obj);
+ return mCodename.equals(obj);
}
// else we try to convert to a int and compare to the api level
int apiDiff = mVersion.getApiLevel() - target.getVersion().getApiLevel();
if (mVersion.getCodename() != null && apiDiff == 0) {
- if (target.getVersionName() == null) {
+ if (target.getVersion().getCodename() == null) {
return +1; // preview showed last
}
return mVersion.getCodename().compareTo(target.getVersion().getCodename());
public final static String FN_EMULATOR = (CURRENT_PLATFORM == PLATFORM_WINDOWS) ?
"emulator.exe" : "emulator"; //$NON-NLS-1$ //$NON-NLS-2$
+ /** zipalign executable (with extension for the current OS) */
+ public final static String FN_ZIPALIGN = (CURRENT_PLATFORM == PLATFORM_WINDOWS) ?
+ "zipalign.exe" : "zipalign"; //$NON-NLS-1$ //$NON-NLS-2$
+
/* Folder Names for Android Projects . */
/** Resources folder name, i.e. "res". */
replaceAvd(avdInfo, info);
// update the ini file
- createAvdIniFile(avdInfo);
+ createAvdIniFile(info);
}
if (newName != null) {
defaultProperties.setAndroidTarget(target);
defaultProperties.save();
- // create an empty build.properties
+ // create a build.properties file with just the application package
ProjectProperties buildProperties = ProjectProperties.create(folderPath,
PropertyType.BUILD);
+ buildProperties.setProperty(ProjectProperties.PROPERTY_APP_PACKAGE, packageName);
buildProperties.save();
// create the map for place-holders of values to replace in the templates
import java.io.File;
import java.io.FileOutputStream;
-import java.io.FileWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.HashMap;
public final static String PROPERTY_TARGET = "target";
public final static String PROPERTY_APK_CONFIGS = "apk-configurations";
public final static String PROPERTY_SDK = "sdk-location";
+ public final static String PROPERTY_APP_PACKAGE = "application-package";
public static enum PropertyType {
BUILD("build.properties", BUILD_HEADER),
"# This file must be checked in Version Control Systems, as it is\n" +
"# integral to the build system of your project.\n" +
"\n" +
- "# The name of your application package as defined in the manifest.\n" +
- "# Used by the 'uninstall' rule.\n"+
- "#application-package=com.example.myproject\n" +
+ "# This file is only used by the Ant script.\n" +
"\n" +
- "# The name of the source folder.\n" +
- "#source-folder=src\n" +
+ "# You can use this to override default values such as\n" +
+ "# 'source-folder' for the location of your java source folder and\n" +
+ "# 'out-folder' for the location of your output folder.\n" +
"\n" +
- "# The name of the output folder.\n" +
- "#out-folder=bin\n" +
+ "# You can also use it define how the release builds are signed by declaring\n" +
+ "# the following properties:\n" +
+ "# 'key.store' for the location of your keystore and\n" +
+ "# 'key.alias' for the name of the key to use.\n" +
+ "# The password will be asked during the build when you use the 'release' target.\n" +
"\n";
private final static Map<String, String> COMMENT_MAP = new HashMap<String, String>();
"# location of the SDK. This is only used by Ant\n" +
"# For customization when using a Version Control System, please read the\n" +
"# header note.\n");
+ COMMENT_MAP.put(PROPERTY_APP_PACKAGE,
+ "# The name of your application package as defined in the manifest.\n" +
+ "# Used by the 'uninstall' rule.\n");
}
private final String mProjectFolderOsPath;
super.saveProperties(props);\r
\r
mVersion.saveProperties(props);\r
- props.setProperty(PROP_NAME, mName);\r
- props.setProperty(PROP_VENDOR, mVendor);\r
+ if (mName != null) {\r
+ props.setProperty(PROP_NAME, mName);\r
+ }\r
+ if (mVendor != null) {\r
+ props.setProperty(PROP_VENDOR, mVendor);\r
+ }\r
}\r
\r
/**\r
/** Returns a long description for an {@link IDescription}. */\r
@Override\r
public String getLongDescription() {\r
- return String.format("%1$s.\n%2$s",\r
+ return String.format("%1$s,\nRevision %2$d.",\r
getShortDescription(),\r
- super.getLongDescription());\r
+ getRevision());\r
}\r
\r
/**\r
import org.apache.commons.compress.archivers.zip.ZipFile;\r
\r
import java.io.File;\r
+import java.io.FileNotFoundException;\r
import java.io.FileOutputStream;\r
import java.io.IOException;\r
import java.io.InputStream;\r
\r
return true;\r
\r
+ } catch (FileNotFoundException e) {\r
+ // The FNF message is just the URL. Make it a bit more useful.\r
+ monitor.setResult("File not found: %1$s", e.getMessage());\r
+\r
} catch (Exception e) {\r
monitor.setResult(e.getMessage());\r
\r
}\r
\r
// Swap the old folder by the new one.\r
+ File renameFailedForDir = null;\r
if (destFolder.isDirectory()) {\r
renamedDestFolder = findTempFolder(osSdkRoot, pkgKind, "old"); //$NON-NLS-1$\r
if (renamedDestFolder == null) {\r
if (!destFolder.renameTo(renamedDestFolder)) {\r
monitor.setResult("Failed to rename directory %1$s to %2$s",\r
destFolder.getPath(), renamedDestFolder.getPath());\r
- return false;\r
-\r
+ renameFailedForDir = destFolder;\r
}\r
}\r
\r
- if (!unzipDestFolder.renameTo(destFolder)) {\r
+ if (renameFailedForDir == null && !unzipDestFolder.renameTo(destFolder)) {\r
monitor.setResult("Failed to rename directory %1$s to %2$s",\r
unzipDestFolder.getPath(), destFolder.getPath());\r
+ renameFailedForDir = unzipDestFolder;\r
+ }\r
+\r
+ if (renameFailedForDir != null) {\r
+ if (SdkConstants.CURRENT_PLATFORM == SdkConstants.PLATFORM_WINDOWS) {\r
+ monitor.setResult(\r
+ "-= Warning ! =-\n" +\r
+ "A folder failed to be renamed or moved. On Windows this " +\r
+ "typically means that a program is using that folder (for example " +\r
+ "Windows Explorer.) Please close all running programs that may be " +\r
+ "locking the directory '%1$s' and try again.",\r
+ renameFailedForDir.getPath());\r
+ }\r
return false;\r
}\r
\r
\r
byte[] buf = new byte[65536];\r
\r
- Enumeration<ZipArchiveEntry> entries =\r
- (Enumeration<ZipArchiveEntry>)zipFile.getEntries();\r
+ Enumeration<ZipArchiveEntry> entries = zipFile.getEntries();\r
while (entries.hasMoreElements()) {\r
ZipArchiveEntry entry = entries.nextElement();\r
\r
/** Returns a long description for an {@link IDescription}. */\r
@Override\r
public String getLongDescription() {\r
- return String.format("%1$s.\n%2$s",\r
+ return String.format("%1$s,\nRevision %2$d.",\r
getShortDescription(),\r
- super.getLongDescription());\r
+ getRevision());\r
}\r
\r
/**\r
\r
AndroidVersion replacementVersion = replacementDoc.getVersion();\r
\r
- // the new doc is an update if the api level is higher\r
+ // the new doc is an update if the api level is higher (no matter the codename on either)\r
if (replacementVersion.getApiLevel() > mVersion.getApiLevel()) {\r
return UpdateInfo.UPDATE;\r
}\r
\r
- // if it's the exactly same (including codename), we check the revision\r
- if (replacementVersion.equals(mVersion) &&\r
- replacementPackage.getRevision() > this.getRevision()) {\r
- return UpdateInfo.UPDATE;\r
- }\r
-\r
- // else we check if they have the same api level and the new one is a preview, in which\r
- // case it's also an update (since preview have the api level of the _previous_ version.\r
- if (replacementVersion.getApiLevel() == mVersion.getApiLevel() &&\r
- replacementVersion.isPreview()) {\r
- return UpdateInfo.UPDATE;\r
+ // Check if they're the same exact (api and codename)\r
+ if (replacementVersion.equals(mVersion)) {\r
+ // exact same version, so check the revision level\r
+ if (replacementPackage.getRevision() > this.getRevision()) {\r
+ return UpdateInfo.UPDATE;\r
+ }\r
+ } else {\r
+ // not the same version? we check if they have the same api level and the new one\r
+ // is a preview, in which case it's also an update (since preview have the api level\r
+ // of the _previous_ version.)\r
+ if (replacementVersion.getApiLevel() == mVersion.getApiLevel() &&\r
+ replacementVersion.isPreview()) {\r
+ return UpdateInfo.UPDATE;\r
+ }\r
}\r
\r
// not an upgrade but not incompatible either.\r
*/\r
public class ExtraPackage extends Package {\r
\r
- private static final String PROP_PATH = "Extra.Path"; //$NON-NLS-1$\r
+ private static final String PROP_PATH = "Extra.Path"; //$NON-NLS-1$\r
+ private static final String PROP_MIN_TOOLS_REV = "Extra.MinToolsRev"; //$NON-NLS-1$\r
\r
+ /**\r
+ * The install folder name. It must be a single-segment path.\r
+ * The paths "add-ons", "platforms", "tools" and "docs" are reserved and cannot be used.\r
+ * This limitation cannot be written in the XML Schema and must be enforced here by using\r
+ * the method {@link #isPathValid()} *before* installing the package.\r
+ */\r
private final String mPath;\r
\r
/**\r
+ * The minimal revision of the tools package required by this extra package, if > 0,\r
+ * or {@link #MIN_TOOLS_REV_NOT_SPECIFIED} if there is no such requirement.\r
+ */\r
+ private final int mMinToolsRevision;\r
+\r
+ /**\r
+ * The value of {@link #mMinToolsRevision} when the {@link SdkRepository#NODE_MIN_TOOLS_REV}\r
+ * was not specified in the XML source.\r
+ */\r
+ public static final int MIN_TOOLS_REV_NOT_SPECIFIED = 0;\r
+\r
+ /**\r
* Creates a new tool package from the attributes and elements of the given XML node.\r
* <p/>\r
* This constructor should throw an exception if the package cannot be created.\r
ExtraPackage(RepoSource source, Node packageNode, Map<String,String> licenses) {\r
super(source, packageNode, licenses);\r
mPath = XmlParserUtils.getXmlString(packageNode, SdkRepository.NODE_PATH);\r
+ mMinToolsRevision = XmlParserUtils.getXmlInt(packageNode, SdkRepository.NODE_MIN_TOOLS_REV,\r
+ MIN_TOOLS_REV_NOT_SPECIFIED);\r
}\r
\r
/**\r
archiveOsPath);\r
// The path argument comes before whatever could be in the properties\r
mPath = path != null ? path : getProperty(props, PROP_PATH, path);\r
+\r
+ mMinToolsRevision = Integer.parseInt(getProperty(props, PROP_MIN_TOOLS_REV,\r
+ Integer.toString(MIN_TOOLS_REV_NOT_SPECIFIED)));\r
}\r
\r
/**\r
super.saveProperties(props);\r
\r
props.setProperty(PROP_PATH, mPath);\r
+\r
+ if (mMinToolsRevision != MIN_TOOLS_REV_NOT_SPECIFIED) {\r
+ props.setProperty(PROP_MIN_TOOLS_REV, Integer.toString(mMinToolsRevision));\r
+ }\r
}\r
\r
/**\r
return mPath;\r
}\r
\r
+ /**\r
+ * The minimal revision of the tools package required by this extra package, if > 0,\r
+ * or {@link #MIN_TOOLS_REV_NOT_SPECIFIED} if there is no such requirement.\r
+ */\r
+ public int getMinToolsRevision() {\r
+ return mMinToolsRevision;\r
+ }\r
+\r
/** Returns a short description for an {@link IDescription}. */\r
@Override\r
public String getShortDescription() {\r
- return String.format("Extra %1$s package, revision %2$d", getPath(), getRevision());\r
+ String name = getPath();\r
+ if (name != null) {\r
+ // Uniformize all spaces in the name and upper case words.\r
+\r
+ name = name.replaceAll("[ _\t\f-]+", " "); //$NON-NLS-1$ //$NON-NLS-2$\r
+\r
+ // Look at all lower case characters in range [1..n-1] and replace them by an upper\r
+ // case if they are preceded by a space. Also upper cases the first character of the\r
+ // string.\r
+ boolean changed = false;\r
+ char[] chars = name.toCharArray();\r
+ for (int n = chars.length - 1, i = 0; i < n; i++) {\r
+ if (Character.isLowerCase(chars[i]) && (i == 0 || chars[i - 1] == ' ')) {\r
+ chars[i] = Character.toUpperCase(chars[i]);\r
+ changed = true;\r
+ }\r
+ }\r
+ if (changed) {\r
+ name = new String(chars);\r
+ }\r
+ }\r
+\r
+ String s = String.format("%1$s package, revision %2$d",\r
+ name,\r
+ getRevision());\r
+\r
+ if (mMinToolsRevision != MIN_TOOLS_REV_NOT_SPECIFIED) {\r
+ s += String.format(" (tools rev: %1$d)", mMinToolsRevision);\r
+ }\r
+\r
+ return s;\r
}\r
\r
/** Returns a long description for an {@link IDescription}. */\r
@Override\r
public String getLongDescription() {\r
- return String.format("Extra %1$s package, revision %2$d.\n%3$s",\r
+ String s = String.format("Extra %1$s package, revision %2$d",\r
getPath(),\r
- getRevision(),\r
- super.getLongDescription());\r
+ getRevision());\r
+\r
+ if (mMinToolsRevision != MIN_TOOLS_REV_NOT_SPECIFIED) {\r
+ s += String.format(" (min tools rev.: %1$d)", mMinToolsRevision);\r
+ }\r
+\r
+ s += ".";\r
+\r
+ return s;\r
}\r
\r
/**\r
}\r
\r
/**\r
- * Returns the packages found by the last call to {@link #parseSdk(String, SdkManager, ISdkLog)}.\r
+ * Returns the packages found by the last call to\r
+ * {@link #parseSdk(String, SdkManager, ISdkLog)}.\r
+ * <p/>\r
+ * This returns initially returns null.\r
+ * Once the parseSdk() method has been called, this returns a possibly empty but non-null array.\r
*/\r
public Package[] getPackages() {\r
return mPackages;\r
*/\r
public abstract class Package implements IDescription {\r
\r
- private static final String PROP_REVISION = "Pkg.Revision"; //$NON-NLS-1$\r
- private static final String PROP_LICENSE = "Pkg.License"; //$NON-NLS-1$\r
- private static final String PROP_DESC = "Pkg.Desc"; //$NON-NLS-1$\r
- private static final String PROP_DESC_URL = "Pkg.DescUrl"; //$NON-NLS-1$\r
- private static final String PROP_SOURCE_URL = "Pkg.SourceUrl"; //$NON-NLS-1$\r
- private static final String PROP_USER_SOURCE = "Pkg.UserSrc"; //$NON-NLS-1$\r
+ private static final String PROP_REVISION = "Pkg.Revision"; //$NON-NLS-1$\r
+ private static final String PROP_LICENSE = "Pkg.License"; //$NON-NLS-1$\r
+ private static final String PROP_DESC = "Pkg.Desc"; //$NON-NLS-1$\r
+ private static final String PROP_DESC_URL = "Pkg.DescUrl"; //$NON-NLS-1$\r
+ private static final String PROP_RELEASE_NOTE = "Pkg.RelNote"; //$NON-NLS-1$\r
+ private static final String PROP_RELEASE_URL = "Pkg.RelNoteUrl"; //$NON-NLS-1$\r
+ private static final String PROP_SOURCE_URL = "Pkg.SourceUrl"; //$NON-NLS-1$\r
+ private static final String PROP_USER_SOURCE = "Pkg.UserSrc"; //$NON-NLS-1$\r
private final int mRevision;\r
private final String mLicense;\r
private final String mDescription;\r
private final String mDescUrl;\r
+ private final String mReleaseNote;\r
+ private final String mReleaseUrl;\r
private final Archive[] mArchives;\r
private final RepoSource mSource;\r
\r
mRevision = XmlParserUtils.getXmlInt (packageNode, SdkRepository.NODE_REVISION, 0);\r
mDescription = XmlParserUtils.getXmlString(packageNode, SdkRepository.NODE_DESCRIPTION);\r
mDescUrl = XmlParserUtils.getXmlString(packageNode, SdkRepository.NODE_DESC_URL);\r
+ mReleaseNote = XmlParserUtils.getXmlString(packageNode, SdkRepository.NODE_RELEASE_NOTE);\r
+ mReleaseUrl = XmlParserUtils.getXmlString(packageNode, SdkRepository.NODE_RELEASE_URL);\r
\r
mLicense = parseLicense(packageNode, licenses);\r
mArchives = parseArchives(XmlParserUtils.getFirstChild(\r
Arch archiveArch,\r
String archiveOsPath) {\r
\r
+ if (description == null) {\r
+ description = "";\r
+ }\r
+ if (descUrl == null) {\r
+ descUrl = "";\r
+ }\r
+\r
mRevision = Integer.parseInt(getProperty(props, PROP_REVISION, Integer.toString(revision)));\r
- mLicense = getProperty(props, PROP_LICENSE, license);\r
- mDescription = getProperty(props, PROP_DESC, description);\r
- mDescUrl = getProperty(props, PROP_DESC_URL, descUrl);\r
+ mLicense = getProperty(props, PROP_LICENSE, license);\r
+ mDescription = getProperty(props, PROP_DESC, description);\r
+ mDescUrl = getProperty(props, PROP_DESC_URL, descUrl);\r
+ mReleaseNote = getProperty(props, PROP_RELEASE_NOTE, "");\r
+ mReleaseUrl = getProperty(props, PROP_RELEASE_URL, "");\r
\r
// If source is null and we can find a source URL in the properties, generate\r
// a dummy source just to store the URL. This allows us to easily remember where\r
*/\r
void saveProperties(Properties props) {\r
props.setProperty(PROP_REVISION, Integer.toString(mRevision));\r
- props.setProperty(PROP_LICENSE, mLicense);\r
- props.setProperty(PROP_DESC, mDescription);\r
- props.setProperty(PROP_DESC_URL, mDescUrl);\r
+ if (mLicense != null && mLicense.length() > 0) {\r
+ props.setProperty(PROP_LICENSE, mLicense);\r
+ }\r
+\r
+ if (mDescription != null && mDescription.length() > 0) {\r
+ props.setProperty(PROP_DESC, mDescription);\r
+ }\r
+ if (mDescUrl != null && mDescUrl.length() > 0) {\r
+ props.setProperty(PROP_DESC_URL, mDescUrl);\r
+ }\r
+\r
+ if (mReleaseNote != null && mReleaseNote.length() > 0) {\r
+ props.setProperty(PROP_RELEASE_NOTE, mReleaseNote);\r
+ }\r
+ if (mReleaseUrl != null && mReleaseUrl.length() > 0) {\r
+ props.setProperty(PROP_RELEASE_URL, mReleaseUrl);\r
+ }\r
\r
if (mSource != null) {\r
props.setProperty(PROP_SOURCE_URL, mSource.getUrl());\r
}\r
\r
/**\r
+ * Returns the optional release note for all packages (platform, add-on, tool, doc) or\r
+ * for a lib. Can be empty but not null.\r
+ */\r
+ public String getReleaseNote() {\r
+ return mReleaseNote;\r
+ }\r
+\r
+ /**\r
+ * Returns the optional release note URL for all packages (platform, add-on, tool, doc).\r
+ * Can be empty but not null.\r
+ */\r
+ public String getReleaseNoteUrl() {\r
+ return mReleaseUrl;\r
+ }\r
+\r
+ /**\r
* Returns the archives defined in this package.\r
* Can be an empty array but not null.\r
*/\r
* Can be empty but not null.\r
*/\r
public String getLongDescription() {\r
- return String.format("%1$s\nRevision %2$d", getDescription(), getRevision());\r
+ StringBuilder sb = new StringBuilder();\r
+\r
+ String s = getDescription();\r
+ if (s != null) {\r
+ sb.append(s);\r
+ }\r
+ if (sb.length() > 0) {\r
+ sb.append("\n");\r
+ }\r
+\r
+ sb.append(String.format("Revision %1$d", getRevision()));\r
+\r
+ s = getDescUrl();\r
+ if (s != null && s.length() > 0) {\r
+ sb.append(String.format("\n\nMore information at %1$s", s));\r
+ }\r
+\r
+ s = getReleaseNote();\r
+ if (s != null && s.length() > 0) {\r
+ sb.append("\n\nRelease note:\n").append(s);\r
+ }\r
+\r
+ s = getReleaseNoteUrl();\r
+ if (s != null && s.length() > 0) {\r
+ sb.append("\nRelease note URL: ").append(s);\r
+ }\r
+\r
+ return sb.toString();\r
}\r
\r
/**\r
* current one, it's not an update.\r
*\r
* @param replacementPackage The potential replacement package.\r
- * @return\r
+ * @return One of the {@link UpdateInfo} values.\r
*\r
* @see #sameItemAs(Package)\r
*/\r
*/\r
public class PlatformPackage extends Package {\r
\r
- private static final String PROP_VERSION = "Platform.Version"; //$NON-NLS-1$\r
+ private static final String PROP_VERSION = "Platform.Version"; //$NON-NLS-1$\r
+ private static final String PROP_MIN_TOOLS_REV = "Platform.MinToolsRev"; //$NON-NLS-1$\r
\r
+ /** The package version, for platform, add-on and doc packages. */\r
private final AndroidVersion mVersion;\r
+\r
+ /** The version, a string, for platform packages. */\r
private final String mVersionName;\r
\r
/**\r
+ * The minimal revision of the tools package required by this extra package, if > 0,\r
+ * or {@link #MIN_TOOLS_REV_NOT_SPECIFIED} if there is no such requirement.\r
+ */\r
+ private final int mMinToolsRevision;\r
+\r
+ /**\r
+ * The value of {@link #mMinToolsRevision} when the {@link SdkRepository#NODE_MIN_TOOLS_REV}\r
+ * was not specified in the XML source.\r
+ */\r
+ public static final int MIN_TOOLS_REV_NOT_SPECIFIED = 0;\r
+\r
+ /**\r
* Creates a new platform package from the attributes and elements of the given XML node.\r
* <p/>\r
* This constructor should throw an exception if the package cannot be created.\r
codeName = null;\r
}\r
mVersion = new AndroidVersion(apiLevel, codeName);\r
+\r
+ mMinToolsRevision = XmlParserUtils.getXmlInt(packageNode, SdkRepository.NODE_MIN_TOOLS_REV,\r
+ MIN_TOOLS_REV_NOT_SPECIFIED);\r
}\r
\r
/**\r
\r
mVersion = target.getVersion();\r
mVersionName = target.getVersionName();\r
+\r
+ mMinToolsRevision = Integer.parseInt(getProperty(props, PROP_MIN_TOOLS_REV,\r
+ Integer.toString(MIN_TOOLS_REV_NOT_SPECIFIED)));\r
}\r
\r
/**\r
super.saveProperties(props);\r
\r
mVersion.saveProperties(props);\r
- props.setProperty(PROP_VERSION, mVersionName);\r
+\r
+ if (mVersionName != null) {\r
+ props.setProperty(PROP_VERSION, mVersionName);\r
+ }\r
+\r
+ if (mMinToolsRevision != MIN_TOOLS_REV_NOT_SPECIFIED) {\r
+ props.setProperty(PROP_MIN_TOOLS_REV, Integer.toString(mMinToolsRevision));\r
+ }\r
}\r
\r
/** Returns the version, a string, for platform packages. */\r
return mVersion;\r
}\r
\r
+ /**\r
+ * The minimal revision of the tools package required by this extra package, if > 0,\r
+ * or {@link #MIN_TOOLS_REV_NOT_SPECIFIED} if there is no such requirement.\r
+ */\r
+ public int getMinToolsRevision() {\r
+ return mMinToolsRevision;\r
+ }\r
+\r
/** Returns a short description for an {@link IDescription}. */\r
@Override\r
public String getShortDescription() {\r
- if (mVersion.isPreview()) {\r
- return String.format("SDK Platform Android %1$s (Preview)",\r
- getVersionName());\r
- }\r
+ String s;\r
\r
- return String.format("SDK Platform Android %1$s, API %2$d",\r
+ if (mVersion.isPreview()) {\r
+ s = String.format("SDK Platform Android %1$s (Preview)", getVersionName());\r
+ } else {\r
+ s = String.format("SDK Platform Android %1$s, API %2$d",\r
getVersionName(),\r
mVersion.getApiLevel());\r
+ }\r
+\r
+ if (mMinToolsRevision != MIN_TOOLS_REV_NOT_SPECIFIED) {\r
+ s += String.format(" (tools rev: %1$d)", mMinToolsRevision);\r
+ }\r
+\r
+ return s;\r
}\r
\r
/** Returns a long description for an {@link IDescription}. */\r
@Override\r
public String getLongDescription() {\r
- return String.format("%1$s.\n%2$s",\r
- getShortDescription(),\r
- super.getLongDescription());\r
+ return getShortDescription() + ".";\r
}\r
\r
/**\r
import java.util.ArrayList;\r
import java.util.HashMap;\r
\r
+import javax.net.ssl.SSLKeyException;\r
import javax.xml.XMLConstants;\r
import javax.xml.parsers.DocumentBuilder;\r
import javax.xml.parsers.DocumentBuilderFactory;\r
*/\r
public class RepoSource implements IDescription {\r
\r
- private final String mUrl;\r
+ private String mUrl;\r
private final boolean mUserSource;\r
\r
private Package[] mPackages;\r
\r
/**\r
* Constructs a new source for the given repository URL.\r
+ * @param url The source URL. Cannot be null. If the URL ends with a /, the default\r
+ * repository.xml filename will be appended automatically.\r
+ * @param userSource True if this a user source (add-ons & packages only.)\r
*/\r
public RepoSource(String url, boolean userSource) {\r
\r
setDefaultDescription();\r
}\r
\r
+ /**\r
+ * Two repo source are equal if they have the same userSource flag and the same URL.\r
+ */\r
+ @Override\r
+ public boolean equals(Object obj) {\r
+ if (obj instanceof RepoSource) {\r
+ RepoSource rs = (RepoSource) obj;\r
+ return rs.isUserSource() == this.isUserSource() && rs.getUrl().equals(this.getUrl());\r
+ }\r
+ return false;\r
+ }\r
+\r
+ @Override\r
+ public int hashCode() {\r
+ return mUrl.hashCode() ^ Boolean.valueOf(mUserSource).hashCode();\r
+ }\r
+\r
/** Returns true if this is a user source. We only load addon and extra packages\r
* from a user source and ignore the rest. */\r
public boolean isUserSource() {\r
monitor.setDescription("Fetching %1$s", url);\r
monitor.incProgress(1);\r
\r
- ByteArrayInputStream xml = null;\r
-\r
- for (int tentative = 0; tentative < 2 && xml == null; tentative++) {\r
-\r
- // reset fetch error and fetch\r
- mFetchError = null;\r
- xml = fetchUrl(url, monitor);\r
+ mFetchError = null;\r
+ Exception[] exception = new Exception[] { null };\r
+ ByteArrayInputStream xml = fetchUrl(url, exception);\r
+ boolean validated = false;\r
+ if (xml != null) {\r
+ monitor.setDescription("Validate XML");\r
+ validated = validateXml(xml, url, monitor);\r
+ }\r
\r
- if (xml == null) {\r
- mDescription += String.format("\nFailed to fetch URL %1$s", url);\r
- mFetchError = "Failed to fetch URL";\r
- monitor.setResult("Failed to fetch URL %1$s", url);\r
- } else {\r
- // We got a document. It might not be XML or it might not be valid.\r
+ // If we failed the first time and the URL doesn't explicitly end with\r
+ // our filename, make another tentative after changing the URL.\r
+ if (!validated && !url.endsWith(SdkRepository.URL_DEFAULT_XML_FILE)) {\r
+ if (!url.endsWith("/")) { //$NON-NLS-1$\r
+ url += "/"; //$NON-NLS-1$\r
+ }\r
+ url += SdkRepository.URL_DEFAULT_XML_FILE;\r
\r
- monitor.setDescription("Validate XML");\r
+ xml = fetchUrl(url, exception);\r
+ if (xml != null) {\r
+ validated = validateXml(xml, url, monitor);\r
+ }\r
\r
- if (validateXml(xml, url, monitor)) {\r
- // We got a valid XML, keep it and use it.\r
+ if (validated) {\r
+ // If the second tentative succeeded, indicate it in the console\r
+ // with the URL that worked.\r
+ monitor.setResult("Repository found at %1$s", url);\r
\r
- if (tentative > 0) {\r
- // If the second tentative succeeded, indicate it in the console,\r
- // otherwise the user will only see the first failure\r
- // message and will think the whole thing failed. This also\r
- // indicates we modifed the URL.\r
- monitor.setResult("Repository found instead at %1$s", url);\r
- }\r
- break;\r
- } else {\r
- mDescription += String.format("\nFailed to validate XML at %1$s", url);\r
- mFetchError = "Failed to validate XML";\r
- monitor.setResult("Failed to validate XML at %1$s", url);\r
-\r
- // forget this XML, it wasn't any good.\r
- xml = null;\r
- }\r
+ // Keep the modified URL\r
+ mUrl = url;\r
}\r
+ }\r
\r
- // If we failed the first time and the URL doesn't explicitly end with\r
- // our filename, make another tentative. Otherwise abort.\r
- if (tentative == 0 && !url.endsWith(SdkRepository.URL_DEFAULT_XML_FILE)) {\r
- if (!url.endsWith("/")) { //$NON-NLS-1$\r
- url += "/"; //$NON-NLS-1$\r
+ if (!validated) {\r
+ mFetchError = "Failed to fetch URL";\r
+\r
+ String reason = "Unknown";\r
+ if (exception[0] != null) {\r
+ if (exception[0] instanceof FileNotFoundException) {\r
+ reason = "File not found";\r
+ } else if (exception[0] instanceof SSLKeyException) {\r
+ reason = "SSL error. You might want to force download through http in the settings.";\r
+ } else if (exception[0].getMessage() != null) {\r
+ reason = exception[0].getMessage();\r
}\r
- url += SdkRepository.URL_DEFAULT_XML_FILE;\r
- } else {\r
- break;\r
}\r
\r
+ monitor.setResult("Failed to fetch URL %1$s, reason: %2$s", url, reason);\r
}\r
\r
monitor.incProgress(1);\r
monitor.setDescription("Parse XML");\r
monitor.incProgress(1);\r
parsePackages(xml, monitor);\r
- if (mPackages.length == 0) {\r
+ if (mPackages == null || mPackages.length == 0) {\r
mDescription += "\nNo packages found.";\r
} else if (mPackages.length == 1) {\r
mDescription += "\nOne package found.";\r
* Java URL Reader: http://java.sun.com/docs/books/tutorial/networking/urls/readingURL.html\r
* Java set Proxy: http://java.sun.com/docs/books/tutorial/networking/urls/_setProxy.html\r
*/\r
- private ByteArrayInputStream fetchUrl(String urlString, ITaskMonitor monitor) {\r
+ private ByteArrayInputStream fetchUrl(String urlString, Exception[] outException) {\r
URL url;\r
try {\r
url = new URL(urlString);\r
}\r
}\r
\r
- } catch (FileNotFoundException e) {\r
- // The FNF message is just the URL. Make it a bit more useful.\r
- monitor.setResult("File not found: %1$s", e.getMessage());\r
-\r
} catch (IOException e) {\r
- monitor.setResult(e.getMessage());\r
+ outException[0] = e;\r
}\r
\r
return null;\r
}\r
\r
} catch (ParserConfigurationException e) {\r
- monitor.setResult("Failed to create XML document builder for %1$s");\r
+ monitor.setResult("Failed to create XML document builder");\r
\r
} catch (SAXException e) {\r
- monitor.setResult("Failed to parse XML document %1$s");\r
+ monitor.setResult("Failed to parse XML document");\r
\r
} catch (IOException e) {\r
monitor.setResult("Failed to read XML document");\r
for (int i = 0; i < count; i++) {\r
String url = props.getProperty(String.format("%s%02d", KEY_SRC, i)); //$NON-NLS-1$\r
if (url != null) {\r
- mSources.add(new RepoSource(url, true /*userSource*/));\r
+ RepoSource s = new RepoSource(url, true /*userSource*/);\r
+ if (!hasSource(s)) {\r
+ mSources.add(s);\r
+ }\r
}\r
}\r
}\r
}\r
\r
/**\r
+ * Returns true if there's already a similar source in the sources list.\r
+ * <p/>\r
+ * The search is O(N), which should be acceptable on the expectedly small source list.\r
+ */\r
+ public boolean hasSource(RepoSource source) {\r
+ for (RepoSource s : mSources) {\r
+ if (s.equals(source)) {\r
+ return true;\r
+ }\r
+ }\r
+ return false;\r
+ }\r
+\r
+ /**\r
* Saves all the user sources.\r
* @param log\r
*/\r
/** Returns a long description for an {@link IDescription}. */\r
@Override\r
public String getLongDescription() {\r
- return String.format("Android SDK Tools, revision %1$d.\n%2$s",\r
- getRevision(),\r
- super.getLongDescription());\r
+ return getShortDescription() + ".";\r
}\r
\r
/**\r
public static final String NODE_EXTRA = "extra"; //$NON-NLS-1$\r
\r
/** The license definition. */\r
- public static final String NODE_LICENSE = "license"; //$NON-NLS-1$\r
+ public static final String NODE_LICENSE = "license"; //$NON-NLS-1$\r
/** The optional uses-license for all packages (platform, add-on, tool, doc) or for a lib. */\r
- public static final String NODE_USES_LICENSE = "uses-license"; //$NON-NLS-1$\r
+ public static final String NODE_USES_LICENSE = "uses-license"; //$NON-NLS-1$\r
/** The revision, an int > 0, for all packages (platform, add-on, tool, doc). */\r
- public static final String NODE_REVISION = "revision"; //$NON-NLS-1$\r
+ public static final String NODE_REVISION = "revision"; //$NON-NLS-1$\r
/** The optional description for all packages (platform, add-on, tool, doc) or for a lib. */\r
- public static final String NODE_DESCRIPTION = "description"; //$NON-NLS-1$\r
+ public static final String NODE_DESCRIPTION = "description"; //$NON-NLS-1$\r
/** The optional description URL for all packages (platform, add-on, tool, doc). */\r
- public static final String NODE_DESC_URL = "desc-url"; //$NON-NLS-1$\r
+ public static final String NODE_DESC_URL = "desc-url"; //$NON-NLS-1$\r
+ /** The optional release note for all packages (platform, add-on, tool, doc). */\r
+ public static final String NODE_RELEASE_NOTE = "release-note"; //$NON-NLS-1$\r
+ /** The optional release note URL for all packages (platform, add-on, tool, doc). */\r
+ public static final String NODE_RELEASE_URL = "release-url"; //$NON-NLS-1$\r
+ /** The optional minimal tools revision required by platform & extra packages. */\r
+ public static final String NODE_MIN_TOOLS_REV = "min-tools-rev"; //$NON-NLS-1$\r
\r
/** The version, a string, for platform packages. */\r
public static final String NODE_VERSION = "version"; //$NON-NLS-1$\r
<xsd:element name="description" type="xsd:string" minOccurs="0" />
<!-- The optional description URL of this package -->
<xsd:element name="desc-url" type="xsd:token" minOccurs="0" />
+ <!-- The optional release note for this package. -->
+ <xsd:element name="release-note" type="xsd:string" minOccurs="0" />
+ <!-- The optional release note URL of this package -->
+ <xsd:element name="release-url" type="xsd:token" minOccurs="0" />
<!-- A list of file archives for this package. -->
<xsd:element name="archives" type="sdk:archivesType" />
+ <!-- The minimal revision of tools required by this package.
+ Optional. If present, must be an int > 0. -->
+ <xsd:element name="min-tools-rev" type="xsd:positiveInteger" minOccurs="0" />
</xsd:all>
</xsd:complexType>
</xsd:element>
<xsd:element name="description" type="xsd:string" minOccurs="0" />
<!-- The optional description URL of this package -->
<xsd:element name="desc-url" type="xsd:token" minOccurs="0" />
+ <!-- The optional release note for this package. -->
+ <xsd:element name="release-note" type="xsd:string" minOccurs="0" />
+ <!-- The optional release note URL of this package -->
+ <xsd:element name="release-url" type="xsd:token" minOccurs="0" />
<!-- A list of file archives for this package. -->
<xsd:element name="archives" type="sdk:archivesType" />
<xsd:element name="description" type="xsd:string" minOccurs="0" />
<!-- The optional description URL of this package -->
<xsd:element name="desc-url" type="xsd:token" minOccurs="0" />
+ <!-- The optional release note for this package. -->
+ <xsd:element name="release-note" type="xsd:string" minOccurs="0" />
+ <!-- The optional release note URL of this package -->
+ <xsd:element name="release-url" type="xsd:token" minOccurs="0" />
<!-- A list of file archives for this package. -->
<xsd:element name="archives" type="sdk:archivesType" />
</xsd:all>
<xsd:element name="description" type="xsd:string" minOccurs="0" />
<!-- The optional description URL of this package -->
<xsd:element name="desc-url" type="xsd:token" minOccurs="0" />
+ <!-- The optional release note for this package. -->
+ <xsd:element name="release-note" type="xsd:string" minOccurs="0" />
+ <!-- The optional release note URL of this package -->
+ <xsd:element name="release-url" type="xsd:token" minOccurs="0" />
<!-- A list of file archives for this package. -->
<xsd:element name="archives" type="sdk:archivesType" />
</xsd:all>
<xsd:element name="description" type="xsd:string" minOccurs="0" />
<!-- The optional description URL of this package -->
<xsd:element name="desc-url" type="xsd:token" minOccurs="0" />
+ <!-- The optional release note for this package. -->
+ <xsd:element name="release-note" type="xsd:string" minOccurs="0" />
+ <!-- The optional release note URL of this package -->
+ <xsd:element name="release-url" type="xsd:token" minOccurs="0" />
<!-- A list of file archives for this package. -->
<xsd:element name="archives" type="sdk:archivesType" />
+ <!-- The minimal revision of tools required by this package.
+ Optional. If present, must be an int > 0. -->
+ <xsd:element name="min-tools-rev" type="xsd:positiveInteger" minOccurs="0" />
</xsd:all>
</xsd:complexType>
</xsd:element>
<sdk:uses-license ref="license1" />\r
<sdk:description>Some optional description</sdk:description>\r
<sdk:desc-url>http://www.example.com/platform1.html</sdk:desc-url>\r
+ <sdk:release-note>This is an optional release note\r
+ for this package. It's a free multi-line text.\r
+ </sdk:release-note>\r
+ <sdk:release-url>http://some/url/for/the/release/note.html</sdk:release-url>\r
+ <sdk:min-tools-rev>2</sdk:min-tools-rev>\r
<!-- The archives node is mandatory and it cannot be empty. -->\r
<sdk:archives>\r
<sdk:archive os="any">\r
</sdk:archives>\r
<sdk:description>An Extra package for the USB driver, it will install in $SDK/usb_driver</sdk:description>\r
<sdk:desc-url>http://www.example.com/extra.html</sdk:desc-url>\r
+ <sdk:min-tools-rev>3</sdk:min-tools-rev>\r
</sdk:extra>\r
\r
</sdk:sdk-repository>\r
--- /dev/null
+/*\r
+ * Copyright (C) 2009 The Android Open Source Project\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+\r
+package com.android.sdkuilib.internal.repository;\r
+\r
+import com.android.sdklib.SdkConstants;\r
+import com.android.sdklib.internal.repository.ITaskMonitor;\r
+\r
+import java.io.BufferedReader;\r
+import java.io.File;\r
+import java.io.IOException;\r
+import java.io.InputStreamReader;\r
+import java.util.ArrayList;\r
+\r
+/**\r
+ * A lightweight wrapper to start & stop ADB.\r
+ */\r
+public class AdbWrapper {\r
+\r
+ /*\r
+ * Note: we could bring ddmlib in SdkManager for that purpose, however this allows us to\r
+ * specialize the start/stop methods to our needs (e.g. a task monitor, etc.)\r
+ */\r
+\r
+ private final String mAdbOsLocation;\r
+ private final ITaskMonitor mMonitor;\r
+\r
+ /**\r
+ * Creates a new lightweight ADB wrapper.\r
+ *\r
+ * @param osSdkPath The root OS path of the SDK. Cannot be null.\r
+ * @param monitor A logger object. Cannot be null.\r
+ */\r
+ public AdbWrapper(String osSdkPath, ITaskMonitor monitor) {\r
+ mMonitor = monitor;\r
+\r
+ if (!osSdkPath.endsWith(File.separator)) {\r
+ osSdkPath += File.separator;\r
+ }\r
+ mAdbOsLocation = osSdkPath + SdkConstants.OS_SDK_TOOLS_FOLDER + SdkConstants.FN_ADB;\r
+ }\r
+\r
+ private void display(String format, Object...args) {\r
+ mMonitor.setResult(format, args);\r
+ }\r
+\r
+ /**\r
+ * Starts the adb host side server.\r
+ * @return true if success\r
+ */\r
+ public synchronized boolean startAdb() {\r
+ if (mAdbOsLocation == null) {\r
+ display("Error: missing path to ADB."); //$NON-NLS-1$\r
+ return false;\r
+ }\r
+\r
+ Process proc;\r
+ int status = -1;\r
+\r
+ try {\r
+ String[] command = new String[2];\r
+ command[0] = mAdbOsLocation;\r
+ command[1] = "start-server"; //$NON-NLS-1$\r
+ proc = Runtime.getRuntime().exec(command);\r
+\r
+ ArrayList<String> errorOutput = new ArrayList<String>();\r
+ ArrayList<String> stdOutput = new ArrayList<String>();\r
+ status = grabProcessOutput(proc, errorOutput, stdOutput,\r
+ false /* waitForReaders */);\r
+\r
+ } catch (IOException ioe) {\r
+ display("Unable to run 'adb': %1$s.", ioe.getMessage()); //$NON-NLS-1$\r
+ // we'll return false;\r
+ } catch (InterruptedException ie) {\r
+ display("Unable to run 'adb': %1$s.", ie.getMessage()); //$NON-NLS-1$\r
+ // we'll return false;\r
+ }\r
+\r
+ if (status != 0) {\r
+ display("'adb start-server' failed."); //$NON-NLS-1$\r
+ return false;\r
+ }\r
+\r
+ display("'adb start-server' succeeded."); //$NON-NLS-1$\r
+\r
+ return true;\r
+ }\r
+\r
+ /**\r
+ * Stops the adb host side server.\r
+ * @return true if success\r
+ */\r
+ public synchronized boolean stopAdb() {\r
+ if (mAdbOsLocation == null) {\r
+ display("Error: missing path to ADB."); //$NON-NLS-1$\r
+ return false;\r
+ }\r
+\r
+ Process proc;\r
+ int status = -1;\r
+\r
+ try {\r
+ String[] command = new String[2];\r
+ command[0] = mAdbOsLocation;\r
+ command[1] = "kill-server"; //$NON-NLS-1$\r
+ proc = Runtime.getRuntime().exec(command);\r
+ status = proc.waitFor();\r
+ }\r
+ catch (IOException ioe) {\r
+ // we'll return false;\r
+ }\r
+ catch (InterruptedException ie) {\r
+ // we'll return false;\r
+ }\r
+\r
+ if (status != 0) {\r
+ display("'adb kill-server' failed -- run manually if necessary."); //$NON-NLS-1$\r
+ return false;\r
+ }\r
+\r
+ display("'adb kill-server' succeeded."); //$NON-NLS-1$\r
+ return true;\r
+ }\r
+\r
+ /**\r
+ * Get the stderr/stdout outputs of a process and return when the process is done.\r
+ * Both <b>must</b> be read or the process will block on windows.\r
+ * @param process The process to get the ouput from\r
+ * @param errorOutput The array to store the stderr output. cannot be null.\r
+ * @param stdOutput The array to store the stdout output. cannot be null.\r
+ * @param waitforReaders if true, this will wait for the reader threads.\r
+ * @return the process return code.\r
+ * @throws InterruptedException\r
+ */\r
+ private int grabProcessOutput(final Process process, final ArrayList<String> errorOutput,\r
+ final ArrayList<String> stdOutput, boolean waitforReaders)\r
+ throws InterruptedException {\r
+ assert errorOutput != null;\r
+ assert stdOutput != null;\r
+ // read the lines as they come. if null is returned, it's\r
+ // because the process finished\r
+ Thread t1 = new Thread("") { //$NON-NLS-1$\r
+ @Override\r
+ public void run() {\r
+ // create a buffer to read the stderr output\r
+ InputStreamReader is = new InputStreamReader(process.getErrorStream());\r
+ BufferedReader errReader = new BufferedReader(is);\r
+\r
+ try {\r
+ while (true) {\r
+ String line = errReader.readLine();\r
+ if (line != null) {\r
+ display("ADB Error: %1$s", line);\r
+ errorOutput.add(line);\r
+ } else {\r
+ break;\r
+ }\r
+ }\r
+ } catch (IOException e) {\r
+ // do nothing.\r
+ }\r
+ }\r
+ };\r
+\r
+ Thread t2 = new Thread("") { //$NON-NLS-1$\r
+ @Override\r
+ public void run() {\r
+ InputStreamReader is = new InputStreamReader(process.getInputStream());\r
+ BufferedReader outReader = new BufferedReader(is);\r
+\r
+ try {\r
+ while (true) {\r
+ String line = outReader.readLine();\r
+ if (line != null) {\r
+ display("ADB: %1$s", line);\r
+ stdOutput.add(line);\r
+ } else {\r
+ break;\r
+ }\r
+ }\r
+ } catch (IOException e) {\r
+ // do nothing.\r
+ }\r
+ }\r
+ };\r
+\r
+ t1.start();\r
+ t2.start();\r
+\r
+ // it looks like on windows process#waitFor() can return\r
+ // before the thread have filled the arrays, so we wait for both threads and the\r
+ // process itself.\r
+ if (waitforReaders) {\r
+ try {\r
+ t1.join();\r
+ } catch (InterruptedException e) {\r
+ }\r
+ try {\r
+ t2.join();\r
+ } catch (InterruptedException e) {\r
+ }\r
+ }\r
+\r
+ // get the return code from the process\r
+ return process.waitFor();\r
+ }\r
+\r
+}\r
*/\r
public interface ISettingsPage {\r
\r
- /** Java system setting picked up by {@link URL} for http proxy port. Type: String. */\r
+ /**\r
+ * Java system setting picked up by {@link URL} for http proxy port.\r
+ * Type: String.\r
+ */\r
public static final String KEY_HTTP_PROXY_PORT = "http.proxyPort"; //$NON-NLS-1$\r
- /** Java system setting picked up by {@link URL} for http proxy host. Type: String. */\r
+ /**\r
+ * Java system setting picked up by {@link URL} for http proxy host.\r
+ * Type: String.\r
+ */\r
public static final String KEY_HTTP_PROXY_HOST = "http.proxyHost"; //$NON-NLS-1$\r
- /** Setting to force using http:// instead of https:// connections. Type: Boolean. */\r
+ /**\r
+ * Setting to force using http:// instead of https:// connections.\r
+ * Type: Boolean.\r
+ * Default: False.\r
+ */\r
public static final String KEY_FORCE_HTTP = "sdkman.force.http"; //$NON-NLS-1$\r
- /** Setting to display only packages that are new or updates. Type: Boolean. */\r
+ /**\r
+ * Setting to display only packages that are new or updates.\r
+ * Type: Boolean.\r
+ * Default: True.\r
+ */\r
public static final String KEY_SHOW_UPDATE_ONLY = "sdkman.show.update.only"; //$NON-NLS-1$\r
+ /**\r
+ * Setting to ask for permission before restarting ADB.\r
+ * Type: Boolean.\r
+ * Default: True.\r
+ */\r
+ public static final String KEY_ASK_ADB_RESTART = "sdkman.ask.adb.restart"; //$NON-NLS-1$\r
\r
/** Loads settings from the given {@link Properties} container and update the page UI. */\r
public abstract void loadSettings(Properties in_settings);\r
private Group mDescriptionContainer;\r
private Button mAddSiteButton;\r
private Button mDeleteSiteButton;\r
- private Label mPlaceholder3;\r
private Button mRefreshButton;\r
private Button mInstallSelectedButton;\r
private Label mDescriptionLabel;\r
mColumnSource.setWidth(289);\r
mColumnSource.setText("Sources, Packages and Archives");\r
\r
- Composite composite = new Composite(parent, SWT.NONE);\r
- composite.setLayoutData(\r
- new GridData(SWT.FILL, SWT.BEGINNING, false, false, 5, 1));\r
- GridLayout gl;\r
- composite.setLayout(gl = new GridLayout(2, false));\r
- gl.marginHeight = gl.marginWidth = 0;\r
- // add an empty composite\r
- Composite spacer = new Composite(composite, SWT.NONE);\r
- GridData gd;\r
- spacer.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL));\r
- gd.heightHint = 0;\r
-\r
- mUpdateOnlyCheckBox = new Button(composite, SWT.CHECK);\r
- mUpdateOnlyCheckBox.setText("Display updates only");\r
- mUpdateOnlyCheckBox.setSelection(mUpdaterData.getSettingsController().getShowUpdateOnly());\r
- mUpdateOnlyCheckBox.addSelectionListener(new SelectionAdapter() {\r
- @Override\r
- public void widgetSelected(SelectionEvent arg0) {\r
- onShowUpdateOnly(); //$hide$\r
- }\r
- });\r
-\r
mDescriptionContainer = new Group(parent, SWT.NONE);\r
mDescriptionContainer.setLayout(new GridLayout(1, false));\r
mDescriptionContainer.setText("Description");\r
mDescriptionLabel.setText("Line1\nLine2\nLine3");\r
\r
mAddSiteButton = new Button(parent, SWT.NONE);\r
+ mAddSiteButton.setText("Add Site...");\r
+ mAddSiteButton.setToolTipText("Allows you to enter a new user external site. " +\r
+ "Such site can only contribute add-ons and extra packages.");\r
mAddSiteButton.addSelectionListener(new SelectionAdapter() {\r
@Override\r
public void widgetSelected(SelectionEvent e) {\r
onAddSiteSelected(); //$hide$\r
}\r
});\r
- mAddSiteButton.setText("Add Site...");\r
\r
mDeleteSiteButton = new Button(parent, SWT.NONE);\r
+ mDeleteSiteButton.setText("Delete Site...");\r
+ mDeleteSiteButton.setToolTipText("Allows you to remove an external site. " +\r
+ "Built-in sites cannot be removed.");\r
mDeleteSiteButton.addSelectionListener(new SelectionAdapter() {\r
@Override\r
public void widgetSelected(SelectionEvent e) {\r
onRemoveSiteSelected(); //$hide$\r
}\r
});\r
- mDeleteSiteButton.setText("Delete Site...");\r
\r
- mPlaceholder3 = new Label(parent, SWT.NONE);\r
- mPlaceholder3.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true, false, 1, 1));\r
+ mUpdateOnlyCheckBox = new Button(parent, SWT.CHECK);\r
+ mUpdateOnlyCheckBox.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true, false, 1, 1));\r
+ mUpdateOnlyCheckBox.setText("Display updates only");\r
+ mUpdateOnlyCheckBox.setToolTipText("When selected, only compatible update packages are shown in the list above.");\r
+ mUpdateOnlyCheckBox.setSelection(mUpdaterData.getSettingsController().getShowUpdateOnly());\r
+ mUpdateOnlyCheckBox.addSelectionListener(new SelectionAdapter() {\r
+ @Override\r
+ public void widgetSelected(SelectionEvent arg0) {\r
+ onShowUpdateOnly(); //$hide$\r
+ }\r
+ });\r
\r
mRefreshButton = new Button(parent, SWT.NONE);\r
+ mRefreshButton.setText("Refresh");\r
+ mRefreshButton.setToolTipText("Refreshes the list of packages from open sites.");\r
mRefreshButton.addSelectionListener(new SelectionAdapter() {\r
@Override\r
public void widgetSelected(SelectionEvent e) {\r
onRefreshSelected(); //$hide$\r
}\r
});\r
- mRefreshButton.setText("Refresh");\r
\r
mInstallSelectedButton = new Button(parent, SWT.NONE);\r
+ mInstallSelectedButton.setText("Install Selected");\r
+ mInstallSelectedButton.setToolTipText("Allows you to review all selected packages " +\r
+ "and install them.");\r
mInstallSelectedButton.addSelectionListener(new SelectionAdapter() {\r
@Override\r
public void widgetSelected(SelectionEvent e) {\r
onInstallSelectedArchives(); //$hide$\r
}\r
});\r
- mInstallSelectedButton.setText("Install Selected");\r
}\r
\r
@Override\r
}\r
\r
private void onShowUpdateOnly() {\r
- mUpdaterData.getSettingsController().setShowUpdateOnly(mUpdateOnlyCheckBox.getSelection());\r
+ SettingsController controller = mUpdaterData.getSettingsController();\r
+ controller.setShowUpdateOnly(mUpdateOnlyCheckBox.getSelection());\r
+ controller.saveSettings();\r
mTreeViewerSources.refresh();\r
}\r
\r
mDeleteSiteButton.setEnabled(hasSelectedUserSource);\r
mRefreshButton.setEnabled(true);\r
mInstallSelectedButton.setEnabled(hasCheckedArchive);\r
+\r
+ // set value on the show only update checkbox\r
+ mUpdateOnlyCheckBox.setSelection(mUpdaterData.getSettingsController().getShowUpdateOnly());\r
}\r
\r
// End of hiding from SWT Designer\r
\r
private final UpdaterData mUpdaterData;\r
\r
+ /**\r
+ * A dummy RepoSource entry returned for sources which had load errors.\r
+ * It displays a summary of the error as its short description or\r
+ * it displays the source's long description.\r
+ */\r
public static class RepoSourceError implements IDescription {\r
\r
private final RepoSource mSource;\r
}\r
}\r
\r
+ /**\r
+ * A dummy RepoSource entry returned for sources with no packages.\r
+ * We need that to force the SWT tree to display an open/close triangle\r
+ * even for empty sources.\r
+ */\r
+ public static class RepoSourceEmpty implements IDescription {\r
+\r
+ private final RepoSource mSource;\r
+ private final boolean mEmptyBecauseOfUpdateOnly;\r
+\r
+ public RepoSourceEmpty(RepoSource source, boolean emptyBecauseOfUpdateOnly) {\r
+ mSource = source;\r
+ mEmptyBecauseOfUpdateOnly = emptyBecauseOfUpdateOnly;\r
+ }\r
+\r
+ public String getLongDescription() {\r
+ return mSource.getLongDescription();\r
+ }\r
+\r
+ public String getShortDescription() {\r
+ if (mEmptyBecauseOfUpdateOnly) {\r
+ return "Some packages were found but are not compatible updates.";\r
+ } else {\r
+ return "No packages found";\r
+ }\r
+ }\r
+ }\r
\r
public RepoSourcesAdapter(UpdaterData updaterData) {\r
mUpdaterData = updaterData;\r
return mUpdaterData.getSources().getSources();\r
\r
} else if (parentElement instanceof RepoSource) {\r
- final RepoSource source = (RepoSource) parentElement;\r
- Package[] packages = source.getPackages();\r
+ return getRepoSourceChildren((RepoSource) parentElement);\r
+\r
+ } else if (parentElement instanceof Package) {\r
+ return getPackageChildren((Package) parentElement);\r
+ }\r
+\r
+ return new Object[0];\r
+ }\r
\r
- if (packages == null && source.getFetchError() == null) {\r
- final boolean forceHttp = mUpdaterData.getSettingsController().getForceHttp();\r
+ /**\r
+ * Returns the list of packages for this repo source, eventually filtered to display\r
+ * only update packages. If the list is empty, returns a specific empty node. If there's\r
+ * an error, returns a specific error node.\r
+ */\r
+ private Object[] getRepoSourceChildren(final RepoSource source) {\r
+ Package[] packages = source.getPackages();\r
\r
- mUpdaterData.getTaskFactory().start("Loading Source", new ITask() {\r
- public void run(ITaskMonitor monitor) {\r
- source.load(monitor, forceHttp);\r
- }\r
- });\r
+ if (packages == null && source.getFetchError() == null) {\r
+ final boolean forceHttp = mUpdaterData.getSettingsController().getForceHttp();\r
\r
- packages = source.getPackages();\r
- }\r
- if (packages != null) {\r
- // filter out only the packages that are new/upgrade.\r
- if (mUpdaterData.getSettingsController().getShowUpdateOnly()) {\r
- return filteredPackages(packages);\r
+ mUpdaterData.getTaskFactory().start("Loading Source", new ITask() {\r
+ public void run(ITaskMonitor monitor) {\r
+ source.load(monitor, forceHttp);\r
}\r
- return packages;\r
- } else if (source.getFetchError() != null) {\r
- // Return a dummy entry to display the fetch error\r
- return new Object[] { new RepoSourceError(source) };\r
- }\r
+ });\r
\r
- } else if (parentElement instanceof Package) {\r
- Archive[] archives = ((Package) parentElement).getArchives();\r
- if (mUpdaterData.getSettingsController().getShowUpdateOnly()) {\r
- for (Archive archive : archives) {\r
- // if we only want the compatible archives, then we just take the first\r
- // one. it's unlikely there are 2 compatible archives for the same\r
- // package\r
- if (archive.isCompatible()) {\r
- return new Object[] { archive };\r
- }\r
+ packages = source.getPackages();\r
+ }\r
+\r
+ boolean wasEmptyBeforeFilter = (packages == null || packages.length == 0);\r
+\r
+ // filter out only the packages that are new/upgrade.\r
+ if (packages != null && mUpdaterData.getSettingsController().getShowUpdateOnly()) {\r
+ packages = filteredPackages(packages);\r
+ }\r
+ if (packages != null && packages.length == 0) {\r
+ packages = null;\r
+ }\r
+\r
+ if (packages != null && source.getFetchError() != null) {\r
+ // Return a dummy entry to display the fetch error\r
+ return new Object[] { new RepoSourceError(source) };\r
+ }\r
+\r
+ // Either return a non-null package list or create a new empty node\r
+ if (packages != null) {\r
+ return packages;\r
+ } else {\r
+ return new Object[] { new RepoSourceEmpty(source, !wasEmptyBeforeFilter) } ;\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Returns the list of archives for the given package, eventually filtering it\r
+ * to only show the compatible archives.\r
+ */\r
+ private Object[] getPackageChildren(Package pkg) {\r
+ Archive[] archives = pkg.getArchives();\r
+ if (mUpdaterData.getSettingsController().getShowUpdateOnly()) {\r
+ for (Archive archive : archives) {\r
+ // if we only want the compatible archives, then we just take the first\r
+ // one. it's unlikely there are 2 compatible archives for the same\r
+ // package\r
+ if (archive.isCompatible()) {\r
+ return new Object[] { archive };\r
}\r
}\r
-\r
- return archives;\r
}\r
\r
- return new Object[0];\r
+ return archives;\r
}\r
\r
/**\r
* @param remotePackages the list of packages to filter.\r
* @return a non null (but maybe empty) list of new or update packages.\r
*/\r
- private Object[] filteredPackages(Package[] remotePackages) {\r
+ private Package[] filteredPackages(Package[] remotePackages) {\r
// get the installed packages\r
Package[] installedPackages = mUpdaterData.getInstalledPackage();\r
\r
if (info == UpdateInfo.UPDATE) {\r
filteredList.add(remotePkg);\r
newPkg = false;\r
- break; // there shouldn't be 2 revision of the same package\r
+ break; // there shouldn't be 2 revisions of the same package\r
} else if (info != UpdateInfo.INCOMPATIBLE) {\r
newPkg = false;\r
- break; // there shouldn't be 2 revision of the same package\r
+ break; // there shouldn't be 2 revisions of the same package\r
}\r
}\r
\r
}\r
}\r
\r
- return filteredList.toArray();\r
+ return filteredList.toArray(new Package[filteredList.size()]);\r
}\r
}\r
import java.util.Properties;\r
\r
/**\r
- *\r
+ * Controller class to get settings values. Settings are kept in-memory.\r
+ * Users of this class must first load the settings before changing them and save\r
+ * them when modified.\r
+ * <p/>\r
+ * Settings are enumerated by constants in {@link ISettingsPage}.\r
*/\r
public class SettingsController {\r
\r
\r
//--- Access to settings ------------\r
\r
+ /**\r
+ * Returns the value of the ISettingsPage#KEY_FORCE_HTTP setting.\r
+ * @see ISettingsPage#KEY_FORCE_HTTP\r
+ */\r
public boolean getForceHttp() {\r
return Boolean.parseBoolean(mProperties.getProperty(ISettingsPage.KEY_FORCE_HTTP));\r
}\r
\r
+ /**\r
+ * Returns the value of the ISettingsPage#KEY_ASK_ADB_RESTART setting.\r
+ * @see ISettingsPage#KEY_ASK_ADB_RESTART\r
+ */\r
+ public boolean getAskBeforeAdbRestart() {\r
+ String value = mProperties.getProperty(ISettingsPage.KEY_ASK_ADB_RESTART);\r
+ if (value == null) {\r
+ return true;\r
+ }\r
+ return Boolean.parseBoolean(value);\r
+ }\r
+\r
+ /**\r
+ * Returns the value of the ISettingsPage#KEY_SHOW_UPDATE_ONLY setting.\r
+ * @see ISettingsPage#KEY_SHOW_UPDATE_ONLY\r
+ */\r
public boolean getShowUpdateOnly() {\r
String value = mProperties.getProperty(ISettingsPage.KEY_SHOW_UPDATE_ONLY);\r
if (value == null) {\r
return Boolean.parseBoolean(value);\r
}\r
\r
+ /**\r
+ * Sets the value of the ISettingsPage#KEY_SHOW_UPDATE_ONLY setting.\r
+ * @param enabled True if only compatible update items should be shown.\r
+ * @see ISettingsPage#KEY_SHOW_UPDATE_ONLY\r
+ */\r
public void setShowUpdateOnly(boolean enabled) {\r
- mProperties.setProperty(ISettingsPage.KEY_SHOW_UPDATE_ONLY, Boolean.toString(enabled));\r
+ setSetting(ISettingsPage.KEY_SHOW_UPDATE_ONLY, enabled);\r
+ }\r
+\r
+ /**\r
+ * Internal helper to set a boolean setting.\r
+ */\r
+ private void setSetting(String key, boolean value) {\r
+ mProperties.setProperty(key, Boolean.toString(value));\r
}\r
\r
//--- Controller methods -------------\r
fis = new FileInputStream(f);\r
\r
mProperties.load(fis);\r
+\r
+ // Properly reformat some settings to enforce their default value when missing.\r
+ setShowUpdateOnly(getShowUpdateOnly());\r
+ setSetting(ISettingsPage.KEY_ASK_ADB_RESTART, getAskBeforeAdbRestart());\r
}\r
\r
} catch (AndroidLocationException e) {\r
import org.eclipse.jface.viewers.Viewer;\r
import org.eclipse.swt.SWT;\r
import org.eclipse.swt.custom.SashForm;\r
+import org.eclipse.swt.custom.StyleRange;\r
+import org.eclipse.swt.custom.StyledText;\r
import org.eclipse.swt.events.ControlAdapter;\r
import org.eclipse.swt.events.ControlEvent;\r
import org.eclipse.swt.events.SelectionAdapter;\r
import org.eclipse.swt.widgets.Shell;\r
import org.eclipse.swt.widgets.Table;\r
import org.eclipse.swt.widgets.TableColumn;\r
-import org.eclipse.swt.widgets.Text;\r
\r
import java.util.ArrayList;\r
import java.util.Collection;\r
private TableViewer mTableViewPackage;\r
private Table mTablePackage;\r
private TableColumn mTableColum;\r
- private Text mPackageText;\r
+ private StyledText mPackageText;\r
private Button mLicenseRadioAccept;\r
private Button mLicenseRadioReject;\r
private Button mLicenseRadioAcceptAll;\r
\r
/**\r
* Create the dialog.\r
+ * @param parentShell The shell to use, typically updaterData.getWindowShell()\r
* @param updaterData The updater data\r
* @param newToOldUpdates The map [new archive => old archive] of potential updates\r
*/\r
- public UpdateChooserDialog(UpdaterData updaterData, Map<Archive, Archive> newToOldUpdates) {\r
- super(updaterData.getWindowShell(), SWT.APPLICATION_MODAL);\r
+ public UpdateChooserDialog(Shell parentShell,\r
+ UpdaterData updaterData,\r
+ Map<Archive, Archive> newToOldUpdates) {\r
+ super(parentShell,\r
+ SWT.APPLICATION_MODAL);\r
mUpdaterData = updaterData;\r
\r
mNewToOldArchiveMap = new TreeMap<Archive, Archive>(new Comparator<Archive>() {\r
mPackageTextGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 4, 1));\r
mPackageTextGroup.setLayout(new GridLayout(1, false/*makeColumnsEqual*/));\r
\r
- mPackageText = new Text(mPackageTextGroup,\r
- SWT.MULTI | SWT.READ_ONLY | SWT.WRAP | SWT.V_SCROLL);\r
+ mPackageText = new StyledText(mPackageTextGroup,\r SWT.MULTI | SWT.READ_ONLY | SWT.WRAP | SWT.V_SCROLL);\r
+ mPackageText.setBackground(\r
+ getParent().getDisplay().getSystemColor(SWT.COLOR_WIDGET_BACKGROUND));\r
mPackageText.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));\r
\r
mLicenseRadioAccept = new Button(mPackageRootComposite, SWT.RADIO);\r
mLicenseRadioAccept.setText("Accept");\r
- mLicenseRadioAccept.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 1, 1));\r
mLicenseRadioAccept.addSelectionListener(new SelectionAdapter() {\r
@Override\r
public void widgetSelected(SelectionEvent e) {\r
\r
mLicenseRadioReject = new Button(mPackageRootComposite, SWT.RADIO);\r
mLicenseRadioReject.setText("Reject");\r
- mLicenseRadioReject.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 1, 1));\r
mLicenseRadioReject.addSelectionListener(new SelectionAdapter() {\r
@Override\r
public void widgetSelected(SelectionEvent e) {\r
\r
mLicenseRadioAcceptAll = new Button(mPackageRootComposite, SWT.RADIO);\r
mLicenseRadioAcceptAll.setText("Accept All");\r
- mLicenseRadioAcceptAll.setLayoutData(\r
- new GridData(SWT.LEFT, SWT.CENTER, false, false, 1, 1));\r
mLicenseRadioAcceptAll.addSelectionListener(new SelectionAdapter() {\r
@Override\r
public void widgetSelected(SelectionEvent e) {\r
onPackageSelected();\r
}\r
\r
-\r
/**\r
* Creates the icon of the window shell.\r
*/\r
return;\r
}\r
\r
- StringBuilder sb = new StringBuilder();\r
+ mPackageText.setText(""); //$NON-NLS-1$\r
+\r
+ addSectionTitle("Package Description\n");\r
+ addText(a.getParentPackage().getLongDescription(), "\n\n"); //$NON-NLS-1$\r
\r
Archive aold = mNewToOldArchiveMap.get(a);\r
if (aold != null) {\r
- sb.append("*** Existing Package Description:\n");\r
- sb.append(aold.getParentPackage().getLongDescription()).append("\n\n");\r
+ addText(String.format("This update will replace revision %1$s with revision %2$s.\n\n",\r
+ aold.getParentPackage().getRevision(),\r
+ a.getParentPackage().getRevision()));\r
}\r
\r
- sb.append("*** New Package Description:\n");\r
- sb.append(a.getParentPackage().getLongDescription()).append("\n\n");\r
\r
- sb.append("\n*** Archive Description:\n");\r
- sb.append(a.getLongDescription()).append("\n");\r
+ addSectionTitle("Archive Description\n");\r
+ addText(a.getLongDescription(), "\n\n"); //$NON-NLS-1$\r
\r
String license = a.getParentPackage().getLicense();\r
if (license != null) {\r
- sb.append("\n*** Package License:\n");\r
- sb.append(license).append("\n");\r
+ addSectionTitle("License\n");\r
+ addText(license.trim(), "\n"); //$NON-NLS-1$\r
+ }\r
+ }\r
+\r
+ private void addText(String...string) {\r
+ for (String s : string) {\r
+ mPackageText.append(s);\r
}\r
+ }\r
\r
- mPackageText.setText(sb.toString());\r
+ private void addSectionTitle(String string) {\r
+ String s = mPackageText.getText();\r
+ int start = (s == null ? 0 : s.length());\r
+ mPackageText.append(string);\r
+\r
+ StyleRange sr = new StyleRange();\r
+ sr.start = start;\r
+ sr.length = string.length();\r
+ sr.fontStyle = SWT.BOLD;\r
+ sr.underline = true;\r
+ mPackageText.setStyleRange(sr);\r
}\r
\r
private void updateLicenceRadios(Archive a) {\r
import com.android.sdkuilib.internal.repository.icons.ImageFactory;\r
import com.android.sdkuilib.repository.UpdaterWindow.ISdkListener;\r
\r
+import org.eclipse.jface.dialogs.MessageDialog;\r
+import org.eclipse.swt.widgets.Display;\r
import org.eclipse.swt.widgets.Shell;\r
\r
+import java.io.ByteArrayOutputStream;\r
+import java.io.PrintStream;\r
import java.util.ArrayList;\r
import java.util.Collection;\r
import java.util.HashMap;\r
break;\r
}\r
\r
- if (archive.getParentPackage() instanceof AddonPackage) {\r
- installedAddon = true;\r
- } else if (archive.getParentPackage() instanceof ToolPackage) {\r
- installedTools = true;\r
- }\r
-\r
if (archive.install(mOsSdkRoot, forceHttp, mSdkManager, monitor)) {\r
numInstalled++;\r
+\r
+ // Check if we successfully installed a tool or add-on package.\r
+ if (archive.getParentPackage() instanceof AddonPackage) {\r
+ installedAddon = true;\r
+ } else if (archive.getParentPackage() instanceof ToolPackage) {\r
+ installedTools = true;\r
+ }\r
}\r
\r
} catch (Throwable t) {\r
// Display anything unexpected in the monitor.\r
- monitor.setResult("Unexpected Error: %1$s", t.getMessage());\r
-\r
+ String msg = t.getMessage();\r
+ if (msg != null) {\r
+ monitor.setResult("Unexpected Error installing '%1$s': %2$s",\r
+ archive.getParentPackage().getShortDescription(), msg);\r
+ } else {\r
+ // no error info? get the stack call to display it\r
+ // At least that'll give us a better bug report.\r
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();\r
+ t.printStackTrace(new PrintStream(baos));\r
+\r
+ // and display it\r
+ monitor.setResult("Unexpected Error installing '%1$s'\n%2$s",\r
+ archive.getParentPackage().getShortDescription(),\r
+ baos.toString());\r
+ }\r
} finally {\r
\r
// Always move the progress bar to the desired position.\r
// Update the USB vendor ids for adb\r
try {\r
mSdkManager.updateAdb();\r
+ monitor.setResult("Updated ADB to support the USB devices declared in the SDK add-ons.");\r
} catch (Exception e) {\r
mSdkLog.error(e, "Update ADB failed");\r
+ monitor.setResult("failed to update adb to support the USB devices declared in the SDK add-ons.");\r
}\r
}\r
\r
// before updating the tools folder, as adb.exe is (surprisingly) not\r
// locked.\r
\r
- // TODO either bring in ddmlib and use its existing methods to stop adb\r
- // or use a shell exec to tools/adb.\r
+ askForAdbRestart(monitor);\r
+ }\r
+\r
+ if (installedTools) {\r
+ notifyToolsNeedsToBeRestarted();\r
}\r
\r
if (numInstalled == 0) {\r
}\r
\r
/**\r
+ * Attemps to restart ADB.\r
+ *\r
+ * If the "ask before restart" setting is set (the default), prompt the user whether\r
+ * now is a good time to restart ADB.\r
+ * @param monitor\r
+ */\r
+ private void askForAdbRestart(ITaskMonitor monitor) {\r
+ final boolean[] canRestart = new boolean[] { true };\r
+\r
+ if (getSettingsController().getAskBeforeAdbRestart()) {\r
+ // need to ask for permission first\r
+ Display display = mWindowShell.getDisplay();\r
+\r
+ display.syncExec(new Runnable() {\r
+ public void run() {\r
+ canRestart[0] = MessageDialog.openQuestion(mWindowShell,\r
+ "ADB Restart",\r
+ "A package that depends on ADB has been updated. It is recommended " +\r
+ "to restart ADB. Is it OK to do it now? If not, you can restart it " +\r
+ "manually later.");\r
+ }\r
+ });\r
+ }\r
+\r
+ if (canRestart[0]) {\r
+ AdbWrapper adb = new AdbWrapper(getOsSdkRoot(), monitor);\r
+ adb.stopAdb();\r
+ adb.startAdb();\r
+ }\r
+ }\r
+\r
+ private void notifyToolsNeedsToBeRestarted() {\r
+ Display display = mWindowShell.getDisplay();\r
+\r
+ display.syncExec(new Runnable() {\r
+ public void run() {\r
+ MessageDialog.openInformation(mWindowShell,\r
+ "Android Tools Updated",\r
+ "The Android SDK tool that you are currently using has been updated. " +\r
+ "It is recommended that you now close the Android SDK window and re-open it. " +\r
+ "If you started this window from Eclipse, please check if the Android " +\r
+ "plug-in needs to be updated.");\r
+ }\r
+ });\r
+ }\r
+\r
+\r
+ /**\r
* Tries to update all the *existing* local packages.\r
- * This first refreshes all sources, then compares the available remote packages when\r
+ * This first refreshes all sources, then compares the available remote packages with\r
* the current local ones and suggest updates to be done to the user. Finally all\r
* selected updates are installed.\r
*\r
// selected archives. If they do not match an update, list them anyway\r
// except they map themselves to null (no "old" archive)\r
for (Archive a : selectedArchives) {\r
- if (!updates.containsValue(a)) {\r
+ if (!updates.containsKey(a)) {\r
updates.put(a, null);\r
}\r
}\r
}\r
\r
- UpdateChooserDialog dialog = new UpdateChooserDialog(this, updates);\r
+ UpdateChooserDialog dialog = new UpdateChooserDialog(getWindowShell(), this, updates);\r
dialog.open();\r
\r
Collection<Archive> result = dialog.getResult();\r
// Map [remote archive => local archive] of suitable update candidates\r
Map<Archive, Archive> result = new HashMap<Archive, Archive>();\r
\r
- // First go thru all sources and make a local list of all available archives\r
+ // First go thru all sources and make a list of all available remote archives\r
// sorted by package class.\r
- HashMap<Class<? extends Package>, ArrayList<Archive>> availPkgs =\r
+ HashMap<Class<? extends Package>, ArrayList<Archive>> availablePkgs =\r
new HashMap<Class<? extends Package>, ArrayList<Archive>>();\r
\r
if (selectedArchives != null) {\r
if (a.isCompatible()) {\r
Class<? extends Package> clazz = a.getParentPackage().getClass();\r
\r
- ArrayList<Archive> list = availPkgs.get(clazz);\r
+ ArrayList<Archive> list = availablePkgs.get(clazz);\r
if (list == null) {\r
- availPkgs.put(clazz, list = new ArrayList<Archive>());\r
+ availablePkgs.put(clazz, list = new ArrayList<Archive>());\r
}\r
\r
list.add(a);\r
for (Package remotePkg : remotePkgs) {\r
Class<? extends Package> clazz = remotePkg.getClass();\r
\r
- ArrayList<Archive> list = availPkgs.get(clazz);\r
+ ArrayList<Archive> list = availablePkgs.get(clazz);\r
if (list == null) {\r
- availPkgs.put(clazz, list = new ArrayList<Archive>());\r
+ availablePkgs.put(clazz, list = new ArrayList<Archive>());\r
}\r
\r
for (Archive a : remotePkg.getArchives()) {\r
\r
for (Package localPkg : localPkgs) {\r
// get the available archive list for this package type\r
- ArrayList<Archive> list = availPkgs.get(localPkg.getClass());\r
+ ArrayList<Archive> list = availablePkgs.get(localPkg.getClass());\r
\r
// if this list is empty, we'll never find anything that matches\r
if (list == null || list.size() == 0) {\r
Archive[] localArchives = localPkg.getArchives();\r
if (localArchives != null && localArchives.length > 0) {\r
Archive localArchive = localArchives[0];\r
- // only consider archive compatible with the current platform\r
+ // only consider archives compatible with the current platform\r
if (localArchive != null && localArchive.isCompatible()) {\r
\r
// We checked all this archive stuff because that's what eventually gets\r
* Create contents of the window.\r
*/\r
protected void createContents() {\r
- mAndroidSdkUpdater = new Shell(mParentShell);\r
+ mAndroidSdkUpdater = new Shell(mParentShell, SWT.SHELL_TRIM);\r
mAndroidSdkUpdater.addDisposeListener(new DisposeListener() {\r
public void widgetDisposed(DisposeEvent e) {\r
onAndroidSdkUpdaterDispose(); //$hide$ (hide from SWT designer)\r
RepoSources sources = mUpdaterData.getSources();\r
sources.add(new RepoSource(SdkRepository.URL_GOOGLE_SDK_REPO_SITE, false /*userSource*/));\r
\r
- String str = System.getenv("TEMP_SDK_URL"); // TODO STOPSHIP temporary remove before shipping\r
+ // SDK_UPDATER_URLS is a semicolon-separated list of URLs that can be used to\r
+ // seed the SDK Updater list for full repositories.\r
+ String str = System.getenv("SDK_UPDATER_URLS");\r
if (str != null) {\r
String[] urls = str.split(";");\r
for (String url : urls) {\r
- sources.add(new RepoSource(url, false /*userSource*/));\r
+ if (url != null && url.length() > 0) {\r
+ RepoSource s = new RepoSource(url, false /*userSource*/);\r
+ if (!sources.hasSource(s)) {\r
+ sources.add(s);\r
+ }\r
+ }\r
}\r
}\r
\r
// Load user sources\r
sources.loadUserSources(mUpdaterData.getSdkLog());\r
\r
+ // SDK_UPDATER_USER_URLS is a semicolon-separated list of URLs that can be used to\r
+ // seed the SDK Updater list for user-only repositories. User sources can only provide\r
+ // add-ons and extra packages.\r
+ str = System.getenv("SDK_UPDATER_USER_URLS");\r
+ if (str != null) {\r
+ String[] urls = str.split(";");\r
+ for (String url : urls) {\r
+ if (url != null && url.length() > 0) {\r
+ RepoSource s = new RepoSource(url, true /*userSource*/);\r
+ if (!sources.hasSource(s)) {\r
+ sources.add(s);\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
mRemotePackagesPage.onSdkChange();\r
}\r
\r
*/\r
public Image getImageForObject(Object object) {\r
if (object instanceof RepoSource) {\r
- return getImageByName("source_icon16.png");\r
+ return getImageByName("source_icon16.png"); //$NON-NLS-1$\r
\r
} else if (object instanceof RepoSourcesAdapter.RepoSourceError) {\r
- return getImageByName("error_icon16.png");\r
+ return getImageByName("error_icon16.png"); //$NON-NLS-1$\r
+\r
+ } else if (object instanceof RepoSourcesAdapter.RepoSourceEmpty) {\r
+ return getImageByName("nopkg_icon16.png"); //$NON-NLS-1$\r
\r
} else if (object instanceof PlatformPackage) {\r
- return getImageByName("android_icon_16.png");\r
+ return getImageByName("android_icon_16.png"); //$NON-NLS-1$\r
\r
} else if (object instanceof AddonPackage) {\r
- return getImageByName("addon_icon16.png");\r
+ return getImageByName("addon_icon16.png"); //$NON-NLS-1$\r
\r
} else if (object instanceof ToolPackage) {\r
- return getImageByName("tool_icon16.png");\r
+ return getImageByName("tool_icon16.png"); //$NON-NLS-1$\r
\r
} else if (object instanceof DocPackage) {\r
- return getImageByName("doc_icon16.png");\r
+ return getImageByName("doc_icon16.png"); //$NON-NLS-1$\r
\r
} else if (object instanceof ExtraPackage) {\r
- return getImageByName("extra_icon16.png");\r
+ return getImageByName("extra_icon16.png"); //$NON-NLS-1$\r
\r
} else if (object instanceof Archive) {\r
if (((Archive) object).isCompatible()) {\r
- return getImageByName("archive_icon16.png");\r
+ return getImageByName("archive_icon16.png"); //$NON-NLS-1$\r
} else {\r
- return getImageByName("incompat_icon16.png");\r
+ return getImageByName("incompat_icon16.png"); //$NON-NLS-1$\r
}\r
}\r
return null;\r
private Button mNewButton;
private Button mRefreshButton;
private Button mManagerButton;
- private Button mUpdateButton;
+ private Button mRepairButton;
private Button mStartButton;
private SelectionListener mSelectionListener;
}
});
- mUpdateButton = new Button(buttons, SWT.PUSH | SWT.FLAT);
- mUpdateButton.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
- mUpdateButton.setText("Update...");
- mUpdateButton.setToolTipText("Updates the path of the selected AVD.");
- mUpdateButton.addSelectionListener(new SelectionAdapter() {
+ mRepairButton = new Button(buttons, SWT.PUSH | SWT.FLAT);
+ mRepairButton.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ mRepairButton.setText("Repair...");
+ mRepairButton.setToolTipText("Repairs the selected AVD.");
+ mRepairButton.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent arg0) {
- onUpdate();
+ onRepair();
}
});
mRefreshButton = new Button(buttons, SWT.PUSH | SWT.FLAT);
mRefreshButton.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
- mRefreshButton.setText("Resfresh");
- mRefreshButton.setToolTipText("Reloads the list of AVD.\nUse this if you create AVD from the command line.");
+ mRefreshButton.setText("Refresh");
+ mRefreshButton.setToolTipText("Reloads the list of AVD.\nUse this if you create AVDs from the command line.");
mRefreshButton.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent arg0) {
if (mDeleteButton != null) {
mDeleteButton.setEnabled(false);
}
- if (mUpdateButton != null) {
- mUpdateButton.setEnabled(false);
+ if (mRepairButton != null) {
+ mRepairButton.setEnabled(false);
}
} else {
AvdInfo selection = getTableSelection();
if (mDeleteButton != null) {
mDeleteButton.setEnabled(hasSelection);
}
- if (mUpdateButton != null) {
- mUpdateButton.setEnabled(hasSelection &&
+ if (mRepairButton != null) {
+ mRepairButton.setEnabled(hasSelection &&
selection.getStatus() == AvdStatus.ERROR_IMAGE_DIR);
}
}
}
}
- private void onUpdate() {
+ /**
+ * Repairs the selected AVD.
+ * <p/>
+ * For now this only supports fixing the wrong value in image.sysdir.*
+ */
+ private void onRepair() {
final AvdInfo avdInfo = getTableSelection();
// get the current Display
import java.net.URL;
import java.net.URLEncoder;
import java.util.Random;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
/** Utility class to send "ping" usage reports to the server. */
public class SdkStatsService {
"kfmclient openURL %URL%", // $NON-NLS-1$ Konqueror
"opera -newwindow %URL%", // $NON-NLS-1$ Opera
};
-
+
public final static String PING_OPT_IN = "pingOptIn"; //$NON-NLS-1$
public final static String PING_TIME = "pingTime"; //$NON-NLS-1$
public final static String PING_ID = "pingId"; //$NON-NLS-1$
private static PreferenceStore sPrefStore;
-
+
/**
* Send a "ping" to the Google toolbar server, if enough time has
* elapsed since the last ping, and if the user has not opted out.
if (!prefs.contains(PING_ID)) {
// First time: make up a new ID. TODO: Use something more random?
prefs.setValue(PING_ID, new Random().nextLong());
-
+
// Also give them a chance to opt out.
prefs.setValue(PING_OPT_IN, getUserPermission(display));
try {
catch (IOException ioe) {
}
}
-
+
// If the user has not opted in, do nothing and quietly return.
if (!prefs.getBoolean(PING_OPT_IN)) {
// user opted out.
return;
}
-
+
// If the last ping *for this app* was too recent, do nothing.
String timePref = PING_TIME + "." + app; // $NON-NLS-1$
long now = System.currentTimeMillis();
// too soon after a ping.
return;
}
-
+
// Record the time of the attempt, whether or not it succeeds.
prefs.setValue(timePref, now);
try {
}
catch (IOException ioe) {
}
-
+
// Send the ping itself in the background (don't block if the
// network is down or slow or confused).
final long id = prefs.getLong(PING_ID);
}.start();
}
}
-
+
/**
* Returns the DDMS {@link PreferenceStore}.
*/
if (homeDir != null) {
String rcFileName = homeDir + "ddms.cfg"; //$NON-NLS-1$
-
+
// also look for an old pref file in the previous location
String oldPrefPath = System.getProperty("user.home") //$NON-NLS-1$
+ File.separator + ".ddmsrc"; //$NON-NLS-1$
try {
PreferenceStore oldStore = new PreferenceStore(oldPrefPath);
oldStore.load();
-
+
oldStore.save(new FileOutputStream(rcFileName), "");
oldPrefFile.delete();
-
+
PreferenceStore newStore = new PreferenceStore(rcFileName);
newStore.load();
sPrefStore = newStore;
sPrefStore = new PreferenceStore();
}
}
-
+
return sPrefStore;
}
-
+
/**
* Unconditionally send a "ping" request to the Google toolbar server.
*
String os = System.getProperty("os.name"); // $NON-NLS-1$
if (os.startsWith("Mac OS")) { // $NON-NLS-1$
os = "mac"; // $NON-NLS-1$
+ String osVers = getVersion();
+ if (osVers != null) {
+ os = os + "-" + osVers; // $NON-NLS-1$
+ }
} else if (os.startsWith("Windows")) { // $NON-NLS-1$
os = "win"; // $NON-NLS-1$
+ String osVers = getVersion();
+ if (osVers != null) {
+ os = os + "-" + osVers; // $NON-NLS-1$
+ }
} else if (os.startsWith("Linux")) { // $NON-NLS-1$
os = "linux"; // $NON-NLS-1$
} else {
}
/**
+ * Returns the version of the os if it is defined as X.Y, or null otherwise.
+ * <p/>
+ * Example of returned versions can be found at http://lopica.sourceforge.net/os.html
+ * <p/>
+ * This method removes any exiting micro versions.
+ */
+ private static String getVersion() {
+ Pattern p = Pattern.compile("(\\d+)\\.(\\d+).*"); // $NON-NLS-1$
+ String osVers = System.getProperty("os.version"); // $NON-NLS-1$
+ Matcher m = p.matcher(osVers);
+ if (m.matches()) {
+ return m.group(1) + "." + m.group(2); // $NON-NLS-1$
+ }
+
+ return null;
+ }
+
+ /**
* Prompt the user for whether they want to opt out of reporting.
* @return whether the user allows reporting (they do not opt out).
*/