diff --git a/apps/Term/src/com/android/term/Term.java b/apps/Term/src/com/android/term/Term.java
index d74159b90..1f43843a3 100644
--- a/apps/Term/src/com/android/term/Term.java
+++ b/apps/Term/src/com/android/term/Term.java
@@ -548,16 +548,6 @@ public class Term extends Activity {
+ controlKey + " 6 ==> Control-^").
show();
}
-
- private void print(String msg) {
- char[] chars = msg.toCharArray();
- int len = chars.length;
- byte[] bytes = new byte[len];
- for (int i = 0; i < len; i++) {
- bytes[i] = (byte) chars[i];
- }
- mEmulatorView.append(bytes, 0, len);
- }
}
@@ -2707,8 +2697,13 @@ class EmulatorView extends View implements GestureDetector.OnGestureListener {
return null;
}
- public boolean hideStatusIcon() {
- return true;
+ public boolean performEditorAction(int actionCode) {
+ if(actionCode == EditorInfo.IME_ACTION_UNSPECIFIED) {
+ // The "return" key has been pressed on the IME.
+ sendText("\n");
+ return true;
+ }
+ return false;
}
public boolean performContextMenuAction(int id) {
@@ -2720,13 +2715,12 @@ class EmulatorView extends View implements GestureDetector.OnGestureListener {
}
public boolean sendKeyEvent(KeyEvent event) {
- switch(event.getKeyCode()) {
- case KeyEvent.KEYCODE_ENTER:
- sendChar('\r');
- break;
- case KeyEvent.KEYCODE_DEL:
- sendChar(127);
- break;
+ if (event.getAction() == KeyEvent.ACTION_DOWN) {
+ switch(event.getKeyCode()) {
+ case KeyEvent.KEYCODE_DEL:
+ sendChar(127);
+ break;
+ }
}
return true;
}
@@ -2739,10 +2733,6 @@ class EmulatorView extends View implements GestureDetector.OnGestureListener {
return true;
}
- public boolean showStatusIcon(String packageName, int resId) {
- return true;
- }
-
private void sendChar(int c) {
try {
mTermOut.write(c);
diff --git a/build/sdk.atree b/build/sdk.atree
index 0f62ea0bf..8c26b1e65 100644
--- a/build/sdk.atree
+++ b/build/sdk.atree
@@ -24,6 +24,7 @@ bin/aidl platforms/${PLATFORM_NAME}/tools/aidl
bin/adb tools/adb
bin/sqlite3 tools/sqlite3
bin/dmtracedump tools/dmtracedump
+bin/hprof-conv tools/hprof-conv
bin/mksdcard tools/mksdcard
# other tools
@@ -67,6 +68,7 @@ development/samples/ApiDemos platforms/${PLATFORM_NAME}/samples/ApiDemos
development/samples/SkeletonApp platforms/${PLATFORM_NAME}/samples/SkeletonApp
development/samples/Snake platforms/${PLATFORM_NAME}/samples/Snake
development/samples/SoftKeyboard platforms/${PLATFORM_NAME}/samples/SoftKeyboard
+development/samples/JetBoy platforms/${PLATFORM_NAME}/samples/JetBoy
# dx
bin/dx platforms/${PLATFORM_NAME}/tools/dx
diff --git a/build/tools/make_windows_sdk.sh b/build/tools/make_windows_sdk.sh
index b1e28ff52..77009de8b 100755
--- a/build/tools/make_windows_sdk.sh
+++ b/build/tools/make_windows_sdk.sh
@@ -74,7 +74,8 @@ function build() {
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 mksdcard sqlite3 || die "Build failed"
+ make prebuilt adb fastboot aidl aapt dexdump dmtracedump hprof-conv mksdcard sqlite3 \
+ || die "Build failed"
}
function package() {
@@ -114,7 +115,7 @@ function package() {
# Remove obsolete stuff from tools & platform
TOOLS="$DEST/tools"
LIB="$DEST/tools/lib"
- rm -v "$TOOLS"/{adb,emulator,traceview,draw9patch,hierarchyviewer,apkbuilder,ddms,dmtracedump,mksdcard,sqlite3,android}
+ rm -v "$TOOLS"/{adb,emulator,traceview,draw9patch,hierarchyviewer,apkbuilder,ddms,dmtracedump,hprof-conv,mksdcard,sqlite3,android}
rm -v --force "$LIB"/*.so "$LIB"/*.jnilib
rm -v "$PLATFORM_TOOLS"/{aapt,aidl,dx,dexdump}
diff --git a/emulator/keymaps/AVRCP.kl b/emulator/keymaps/AVRCP.kl
index 175824ef7..d0eba109d 100644
--- a/emulator/keymaps/AVRCP.kl
+++ b/emulator/keymaps/AVRCP.kl
@@ -1,7 +1,6 @@
-key 164 PLAYPAUSE WAKE
-key 128 STOP WAKE
-key 163 NEXTSONG WAKE
-key 165 PREVIOUSSONG WAKE
-key 168 REWIND WAKE
-key 159 FORWARD WAKE
-
+key 164 MEDIA_PLAY_PAUSE WAKE
+key 128 MEDIA_STOP WAKE
+key 163 MEDIA_NEXT WAKE
+key 165 MEDIA_PREVIOUS WAKE
+key 168 MEDIA_REWIND WAKE
+key 159 MEDIA_FAST_FORWARD WAKE
diff --git a/emulator/qemud/qemud.c b/emulator/qemud/qemud.c
index bd49e5253..8f7e616b5 100644
--- a/emulator/qemud/qemud.c
+++ b/emulator/qemud/qemud.c
@@ -42,15 +42,15 @@
* internal unique channel number > 0, then sends a connection
* initiation request to the emulator (i.e. through channel 0):
*
- * connect::
+ * connect::
*
- * where is the service name, and is a 4-hexchar
+ * where is the service name, and is a 2-hexchar
* number corresponding to the channel number.
*
* * in case of success, the emulator responds through channel 0
* with:
*
- * ok:connect:
+ * ok:connect:
*
* after this, all messages between the client and the emulator
* are passed in pass-through mode.
@@ -58,12 +58,12 @@
* * if the emulator refuses the service connection, it will
* send the following through channel 0:
*
- * ko:connect::reason-for-failure
+ * ko:connect::reason-for-failure
*
* * If the client closes the connection, qemud sends the following
* to the emulator:
*
- * disconnect:
+ * disconnect:
*
* The same message is the opposite direction if the emulator
* chooses to close the connection.
@@ -1429,8 +1429,8 @@ static void
multiplexer_handle_control( Multiplexer* mult, Packet* p )
{
/* connection registration success */
- if (p->len == 15 && !memcmp(p->data, "ok:connect:", 11)) {
- int channel = hex2int(p->data+11, 4);
+ if (p->len == 13 && !memcmp(p->data, "ok:connect:", 11)) {
+ int channel = hex2int(p->data+11, 2);
Client* client = multiplexer_find_client(mult, channel);
/* note that 'client' can be NULL if the corresponding
@@ -1443,8 +1443,8 @@ multiplexer_handle_control( Multiplexer* mult, Packet* p )
}
/* connection registration failure */
- if (p->len >= 15 && !memcmp(p->data, "ko:connect:",11)) {
- int channel = hex2int(p->data+11, 4);
+ if (p->len >= 13 && !memcmp(p->data, "ko:connect:",11)) {
+ int channel = hex2int(p->data+11, 2);
Client* client = multiplexer_find_client(mult, channel);
if (client != NULL)
@@ -1454,8 +1454,8 @@ multiplexer_handle_control( Multiplexer* mult, Packet* p )
}
/* emulator-induced client disconnection */
- if (p->len == 15 && !memcmp(p->data, "disconnect:",11)) {
- int channel = hex2int(p->data+11, 4);
+ if (p->len == 13 && !memcmp(p->data, "disconnect:",11)) {
+ int channel = hex2int(p->data+11, 2);
Client* client = multiplexer_find_client(mult, channel);
if (client != NULL)
@@ -1536,7 +1536,7 @@ multiplexer_open_channel( Multiplexer* mult, Packet* service )
goto TRY_AGAIN;
}
- len = snprintf((char*)p->data, sizeof p->data, "connect:%.*s:%04x", service->len, service->data, channel);
+ len = snprintf((char*)p->data, sizeof p->data, "connect:%.*s:%02x", service->len, service->data, channel);
if (len >= (int)sizeof(p->data)) {
D("%s: weird, service name too long (%d > %d)", __FUNCTION__, len, sizeof(p->data));
packet_free(&p);
@@ -1554,7 +1554,7 @@ static void
multiplexer_close_channel( Multiplexer* mult, int channel )
{
Packet* p = packet_alloc();
- int len = snprintf((char*)p->data, sizeof(p->data), "disconnect:%04x", channel);
+ int len = snprintf((char*)p->data, sizeof(p->data), "disconnect:%02x", channel);
if (len > (int)sizeof(p->data)) {
/* should not happen */
diff --git a/pdk/Pdk.mk b/pdk/Pdk.mk
index abe9491d2..fbe8b5c90 100644
--- a/pdk/Pdk.mk
+++ b/pdk/Pdk.mk
@@ -16,7 +16,7 @@
# Assemble the Platform Development Kit (PDK)
# (TODO) Figure out why $(ACP) builds with target ndk but not pdk_docs
-# (TODO) Build doxygen (depend on latest version)
+# (TODO) Build doxygen (depend on latest version) -> line 25 error
pdk:
@echo "Package: $@ has targets ndk, pdk_docs and pdk_all"
@@ -38,6 +38,8 @@ include $(LOCAL_PATH)/ndk/Ndk.mk
# Doxygenize the header files to create html docs in the generatedDocs dir.
# Copy the appengine files, the template files and the generated html
# to the docs dir and zip everything up to the distribution directory.
+# Run javadocs/droiddocs/clearsilver on the generatedDocs dir to get the right
+# styles added to the html.
# Workspace directory
@@ -149,13 +151,27 @@ $(pdk_docs_intermediates)/pdk.py: $(pdk_hosting_dir)/pdk.py
@echo "PDK: $@"
$(copy-file-to-target-with-cp)
+# Copy appengine server files for new system
+$(OUT_DOCS)/app.yaml: $(pdk_hosting_dir)/app.yaml
+ @echo "PDK: $@"
+ $(copy-file-to-target-with-cp)
+
+$(OUT_DOCS)/pdk.py: $(pdk_hosting_dir)/pdk.py
+ @echo "PDK: $@"
+ $(copy-file-to-target-with-cp)
+
+# All the files that we depend upon
+all_pdk_docs_files := $(pdk_doxygen_config_override_file) \
+ $(pdk_doxygen_config_file) $(pdk_docs_intermediates)/header.html \
+ $(pdk_docs_intermediates)/footer.html $(pdk_doxy_docsfiles_dir)/groups.dox \
+ $(pdk_doxy_docsfiles_dir)/main.dox all_copied_pdk_templates
# Run doxygen and copy all output and templates to the final destination
# We replace index.html with a template file so don't use the generated one
pdk_doxygen: all_copied_pdk_headers $(pdk_doxygen_config_override_file) \
$(pdk_doxygen_config_file) $(pdk_docs_intermediates)/header.html \
$(pdk_docs_intermediates)/footer.html $(pdk_doxy_docsfiles_dir)/groups.dox \
- $(pdk_doxy_docsfiles_dir)/main.dox
+ $(pdk_doxy_docsfiles_dir)/main.dox
@echo "Files for Doxygination: $^"
@mkdir -p $(pdk_generated_source_dir)
@rm -f $(pdk_generated_source_dir)/*
@@ -164,7 +180,40 @@ pdk_doxygen: all_copied_pdk_headers $(pdk_doxygen_config_override_file) \
@cd $(pdk_generated_source_dir) && chmod ug+rx *
@rm -f $(pdk_generated_source_dir)/index.html
@cp -fp $(pdk_generated_source_dir)/* $(pdk_docs_dest_dir)
-
+
+
+# ==== docs for the web (on the google app engine server) =======================
+# Run javadoc/droiddoc/clearsilver to get the formatting right
+
+# make droiddocs run after we make our doxygen docs
+$(pdk_docs_intermediates)/pdk-timestamp: pdk_doxygen
+ @touch $(pdk_docs_intermediates)/pdk-timestamp
+
+$(LOCAL_PATH)/pdk-timestamp: $(pdk_docs_intermediates)/pdk-timestamp
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := pdk-timestamp samples/samplejni/src/com/example/jniexample/JNIExample.java
+LOCAL_MODULE_CLASS := development/pdk/ndk/samples/samplejni/src/com/example/jniexample
+LOCAL_DROIDDOC_SOURCE_PATH := $(framework_docs_LOCAL_DROIDDOC_SOURCE_PATH)
+LOCAL_DROIDDOC_HTML_DIR := ../../../$(pdk_docs_dest_dir)
+
+LOCAL_MODULE := online-pdk
+
+LOCAL_DROIDDOC_OPTIONS := \
+ $(framework_docs_LOCAL_DROIDDOC_OPTIONS) \
+ $(web_docs_sample_code_flags) \
+ -toroot /online-pdk/ \
+ -hdf android.whichdoc online-pdk
+
+LOCAL_DROIDDOC_CUSTOM_TEMPLATE_DIR := build/tools/droiddoc/templates-pdk
+LOCAL_DROIDDOC_CUSTOM_ASSET_DIR := assets-pdk
+
+include $(BUILD_DROIDDOC)
+
+# The docs output dir is: out/target/common/docs/online-pdk
+DOCS_OUT_DIR := $(OUT_DOCS)/$(LOCAL_MODULE)
+
# Name the tar files
name := android_pdk_docs-$(REQUESTED_PRODUCT)
ifeq ($(TARGET_BUILD_TYPE),debug)
@@ -173,21 +222,25 @@ endif
name := $(name)-$(BUILD_NUMBER)
pdk_docs_tarfile := $(pdk_docs_intermediates)/$(name).tar
pdk_docs_tarfile_zipped := $(pdk_docs_tarfile).gz
+new_pdk_docs_tarfile := $(pdk_docs_intermediates)/new-$(name).tar
+new_pdk_docs_tarfile_zipped := $(new_pdk_docs_tarfile).gz
-.PHONY: pdk pdk_docs pdk_doxygen all_copied_pdk_headers all_copied_pdk_templates
+.PHONY: pdk pdk_docs pdk_doxygen all_copied_pdk_headers all_copied_pdk_templates pdk-timestamp
-pdk_docs: $(pdk_docs_tarfile_zipped)
+pdk_docs: $(pdk_docs_tarfile_zipped) $(new_pdk_docs_tarfile)
@echo "PDK: Docs tarred and zipped"
# Put the pdk_docs zip files in the distribution directory
$(call dist-for-goals,pdk_docs,$(pdk_docs_tarfile_zipped))
+$(call dist-for-goals,pdk_docs,$(new_pdk_docs_tarfile_zipped))
# zip up tar files
%.tar.gz: %.tar
- @echo "PDK: zipped $<"
+ @echo "PDK docs: zipped $<"
$(hide) gzip -cf $< > $@
# tar up all the files to make the pdk docs.
+# old version
$(pdk_docs_tarfile): pdk_doxygen all_copied_pdk_templates \
$(pdk_docs_intermediates)/pdk.py $(pdk_docs_intermediates)/app.yaml
@echo "PDK: $@"
@@ -195,6 +248,13 @@ $(pdk_docs_tarfile): pdk_doxygen all_copied_pdk_templates \
@rm -f $@
$(hide) tar rf $@ -C $(pdk_docs_intermediates) docs pdk.py app.yaml
+# new version
+$(new_pdk_docs_tarfile): $(DOCS_OUT_DIR)-timestamp
+ @echo "PDK docs: $@"
+ @mkdir -p $(dir $@)
+ @rm -f $@
+ $(hide) tar rf $@ -C $(OUT_DOCS) $(LOCAL_MODULE)
+
# Debugging reporting can go here, add it as a target to get output.
pdk_debug:
@echo "You are here: $@"
diff --git a/pdk/README b/pdk/README
index 86621552e..18ff5d120 100644
--- a/pdk/README
+++ b/pdk/README
@@ -4,78 +4,72 @@ Building the pdk (platform development kit)
(We currently support version 1.4.6)
sudo apt-get install doxygen
+
+Make sure that you are using the right version of java
+
+ sudo update-java-alternatives -s java-1.5.0-sun
+
+If that doesn't work, go through the instructions on
+
+ http://source.android.com/download again.
+
2) from the root
. build/envsetup.sh
-3) run choosecombo
- Build for the simulator or the device?
- 1. Device
- 2. Simulator
-
- Which would you like? [1] 1
-
-
- Build type choices are:
- 1. release
- 2. debug
-
- Which would you like? [1] 1
-
-
- Product choices are:
- 0. emulator
- 1. generic
- 2. sim
- 3. surf
- You can also type the name of a product if you know it.
- Which would you like? [generic] 1
-
-
- Variant choices are:
- 1. user
- 2. userdebug
- 3. eng
- Which would you like? [eng] 3
-
- ============================================
- TARGET_PRODUCT=generic
- TARGET_BUILD_VARIANT=eng
- TARGET_SIMULATOR=false
- TARGET_BUILD_TYPE=release
- TARGET_ARCH=arm
- HOST_ARCH=x86
- HOST_OS=linux
- HOST_BUILD_TYPE=release
- BUILD_ID=
- ============================================
4) mkdir dist
mkdir logs
- mkpdkcupcake.sh
+
+then build everything:
-(which contains:
+ time make -j4 pdk pdk_all dist DIST_DIR=dist 2>&1 | tee logs/`date +%y%m%d-%H%M%S`
-DT=`date +%y%m%d-%H%M%S`
-time make -j4 pdk pdk_all dist DIST_DIR=dist 2>&1 | tee logs/$DT
+so you can have a record of the build commands in the logs directory.
-so you can see the results of the build in the logs directory.)
5) the pdk tar file is put in the dist directory.
+6) the pdk-docs are in
+
+ out/target
+
The build target 'pdk' brings in the pdk/ndk make files into the build system.
- Then there are three targets:
- pdk_docs - which builds the pdk documentation
- ndk - which builds the native development kit (native compiler, linker, etc.)
- pdk_all - which builds the above two targets
+ Then there are three targets:
+ pdk_docs - which builds just the pdk documentation
+ ndk - which builds the native development kit (native compiler, linker, etc.)
+ pdk_all - which builds the above two targets
-for doxygen version changing you can pass in the variable:
-doxygen_version=''
+To chnage which version of doxygen runs you can pass in the variable:
+ doxygen_version=''
on the make line.
--------------------------------------------------------------------------------
-To host the pdk docs on appengine run:
-/home/build/static/projects/apphosting/devtools/appcfg.py update pdk/
-where the pdk directory contains: pdk.py, app.yaml, and the docs directory,
-all of which are tarred up by the Pdk.mk file when using the targer pdk_docs.
+# Testing
+You must install google appengine. See: http://code.google.com/appengine/downloads.html
+
+Here's the command to run the pdk-docs server locally:
+ python /dev_appserver.py --address 0.0.0.0 \
+ /android/out/target/common/docs/online-pdk
+
+To verify it is working you can access it with a browser loacally on port 8080:
+
+http://localhost:8080/index.html
+TODO: index.html needs correct links.
+TODO: app.yaml not working for redirecting, getting extra '.' in html names...
+
+ --------------------------------------------------------------------------------
+# Deployment
+To host the pdk docs on the interanl appengine run:
+/home/build/static/projects/apphosting/devtools/appcfg.py update /out/common/docs
+where the docs directory contains: pdk.py, app.yaml, and the online-pdk directory,
+all of which are tarred up by the Pdk.mk file when using the target pdk_docs.
+
+# Deployment
+To host the pdk docs on the external appengine run:
+/home/build/static/projects/apphosting/devtools/appcfg.py -s pdk-docs.appspot.com update /out/common/docs
+where the docs directory contains: pdk.py, app.yaml, and the online-pdk directory,
+all of which are tarred up by the Pdk.mk file when using the target pdk_docs.
+
+
diff --git a/pdk/docs/audio_sub_system.html b/pdk/docs/audio_sub_system.html
deleted file mode 100755
index 8639cf0b1..000000000
--- a/pdk/docs/audio_sub_system.html
+++ /dev/null
@@ -1,261 +0,0 @@
-
-
-
-
-Android - Porting Guide
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
AudioHardwareInterface serves as the glue between proprietary audio drivers and the Android AudioFlinger service, the core audio service that handles all audio-related requests from applications.
-
-
-Solid elements represent Android blocks and dashed elements represent partner-specific blocks.
-
-
-
-
Building an Audio Library
-
-
To implement an audio driver, create a shared library that implements the interface defined in AudioHardwareInterface.h. You must name your shared library libaudio.so so that it will get loaded from /system/lib at runtime. Place libaudio sources and Android.mk in partner/acme/chipset_or_board/libaudio/.
-
The following stub Android.mk file ensures that libaudio compiles and links to the appropriate libraries:
Note: This document relies on some Doxygen-generated content that appears in an iFrame below. To return to the Doxygen default content for this page, click here.
AudioHardwareInterface serves as the glue between proprietary audio drivers and the Android AudioFlinger service, the core audio service that handles all audio-related requests from applications.
+
+
+Solid elements represent Android blocks and dashed elements represent partner-specific blocks.
+
+
+
+
Building an Audio Library
+
+
To implement an audio driver, create a shared library that implements the interface defined in AudioHardwareInterface.h. You must name your shared library libaudio.so so that it will get loaded from /system/lib at runtime. Place libaudio sources and Android.mk in partner/acme/chipset_or_board/libaudio/.
+
The following stub Android.mk file ensures that libaudio compiles and links to the appropriate libraries:
Note: This document relies on some Doxygen-generated content that appears in an iFrame below. To return to the Doxygen default content for this page, click here.
Android's Bluetooth stack uses BlueZ version 3.36 for GAP, SDP, and RFCOMM profiles, and is a SIG-qualified Bluetooth 2.0 host stack.
-
-
Bluez is GPL licensed, so the Android framework interacts with userspace bluez code through D-BUS IPC to avoid proprietary code.
-
-
Headset and Handsfree (v1.5) profiles are implemented in the Android framework and are both tightly coupled with the Phone App. These profiles are also SIG qualified.
-
-
The diagram below offers a library-oriented view of the Bluetooth stack. Click Bluetooth Process Diagram for a process-oriented view.
-
-
-
-Solid elements represent Android blocks and dashed elements represent partner-specific blocks.
-
-
-
-
Porting
-
-
BlueZ is Bluetooth 2.0 compatible and should work with any 2.0 chipset. There are two integration points:
-
-
UART driver
-
Bluetooth Power On / Off
-
-
-
-
-
UART Driver
-
-
The BlueZ kernel sub-system attaches to your hardware-specific UART driver using the hciattach daemon.
-
For example, for MSM7201A, this is drivers/serial/msm_serial.c. You may also need to edit command line options to hciattach via init.rc.
-
-
-
Bluetooth Power On / Off
-
-
The method for powering on and off your bluetooth chip varies from Android V 1.0 to post 1.0.
-
-
-
1.0: Android framework writes a 0 or 1 to /sys/modules/board_[PLATFORM]/parameters/bluetooth_power_on.
-
-
Post 1.0: Android framework uses the linux rfkill API. See arch/arm/mach-msm/board-trout-rfkill.c for an example.
-
-
-
-
-
Tools
-
-
BlueZ provides a rich set of command line tools for debugging and interacting with the Bluetooth sub-system, including:
Android's Bluetooth stack uses BlueZ version 3.36 for GAP, SDP, and RFCOMM profiles, and is a SIG-qualified Bluetooth 2.0 host stack.
+
+
Bluez is GPL licensed, so the Android framework interacts with userspace bluez code through D-BUS IPC to avoid proprietary code.
+
+
Headset and Handsfree (v1.5) profiles are implemented in the Android framework and are both tightly coupled with the Phone App. These profiles are also SIG qualified.
+
+
The diagram below offers a library-oriented view of the Bluetooth stack. Click Bluetooth Process Diagram for a process-oriented view.
+
+
+
+Solid elements represent Android blocks and dashed elements represent partner-specific blocks.
+
+
+
+
Porting
+
+
BlueZ is Bluetooth 2.0 compatible and should work with any 2.0 chipset. There are two integration points:
+
+
UART driver
+
Bluetooth Power On / Off
+
+
+
+
+
UART Driver
+
+
The BlueZ kernel sub-system attaches to your hardware-specific UART driver using the hciattach daemon.
+
For example, for MSM7201A, this is drivers/serial/msm_serial.c. You may also need to edit command line options to hciattach via init.rc.
+
+
+
Bluetooth Power On / Off
+
+
The method for powering on and off your bluetooth chip varies from Android V 1.0 to post 1.0.
+
+
+
1.0: Android framework writes a 0 or 1 to /sys/modules/board_[PLATFORM]/parameters/bluetooth_power_on.
+
+
Post 1.0: Android framework uses the linux rfkill API. See arch/arm/mach-msm/board-trout-rfkill.c for an example.
+
+
+
+
+
Tools
+
+
BlueZ provides a rich set of command line tools for debugging and interacting with the Bluetooth sub-system, including:
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.
1. Confirm a Clean Installation of a Basic Linux Kernel
Before considering Android-specific modifications to the Linux kernel, verify that you can build, deploy, and boot a core Linux kernel on your target hardware.
+
2. Modify Your Kernel Configuration to Accommodate Android Drivers
Your kernel configuration file should include the following:
In the products directory, create an AndroidProducts.mk file that point to (and is responsible for finding) the individual product make files.
-
- #
- # This file should set PRODUCT_MAKEFILES to a list of product makefiles
- # to expose to the build system. LOCAL_DIR will already be set to
- # the directory containing this file.
- #
- # This file may not rely on the value of any variable other than
- # LOCAL_DIR; do not use any conditionals, and do not look up the
- # value of any variable that isn't set in this file or in a file that
- # it includes.
- #
-
- PRODUCT_MAKEFILES := \
- $(LOCAL_DIR)/first_product_name.mk \
-
Create a board-specific directory beneath your company directory that matches the PRODUCT_DEVICE variable <board_name> referenced in the product-specific make file above. This will include a make file that gets accessed by any product using this board.
-
Create a product_config.mk file in the directory created in the previous step (device/partner/<company_name>/<board_name>). If this directory does not include a product_config.mk file, the build will fail.
-
- # These definitions override the defaults in config/config.make for <board_name>
- #
- # TARGET_NO_BOOTLOADER := false
- # TARGET_HARDWARE_3D := false
- #
- TARGET_USE_GENERIC_AUDIO := true
-
If you wish to modify system properties, create a system.prop file in your <board_name> directory(device/partner/<company_name>/<board_name>).
-
- # system.prop for
- # This overrides settings in the products/generic/system.prop file
- #
- # rild.libpath=/system/lib/libreference-ril.so
- # rild.libargs=-d /dev/ttyS0
-
Add a pointer to <second_product_name>.mk within products/AndroidProducts.mk.
-
device/partner/<company_name>/<board_name> must include an Android.mk file with at least the following code:
-
- # make file for new hardware from
- #
- LOCAL_PATH := $(call my-dir)
- #
- # this is here to use the pre-built kernel
- ifeq ($(TARGET_PREBUILT_KERNEL),)
- TARGET_PREBUILT_KERNEL := $(LOCAL_PATH)/kernel
- endif
- #
- file := $(INSTALLED_KERNEL_TARGET)
- ALL_PREBUILT += $(file)
- $(file): $(TARGET_PREBUILT_KERNEL) | $(ACP)
- $(transform-prebuilt-to-target)
- #
- # no boot loader, so we don't need any of that stuff..
- #
- LOCAL_PATH := partner/<company_name>/<board_name>
- #
- include $(CLEAR_VARS)
- #
- # include more board specific stuff here? Such as Audio parameters.
- #
-
To create a second product for the same board, create a second product-specific make file called device/partner/company_name/products/<second_product_name>.mk that includes:
-
By now, you should have two new products, called <first_product_name> and <second_product_name> associated with <company_name>. To verify that a product is properly configured (<first_product_name>, for example), execute the following:
-
- cd device
- . ./envsetup.sh
- partner_setup <first_product_name>
- make PRODUCT-<first_product_name>-user
-
-
You should find new build binaries located in device/out/target/product/<board_name>.
-
-
-
New Product File Tree
-
-
The file tree below illustrates what your own system should look like after completing the steps above.
-
-
-
<company_name>
-
-
<board_name>
-
-
Android.mk
-
product_config.mk
-
system.prop
-
-
products
-
-
AndroidProducts.mk
-
<first_product_name>.mk
-
<second_product_name>.mk
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
v0.6 - 25 November 2008
-
-
-
diff --git a/pdk/docs/build_new_device.jd b/pdk/docs/build_new_device.jd
new file mode 100755
index 000000000..f0a816f54
--- /dev/null
+++ b/pdk/docs/build_new_device.jd
@@ -0,0 +1,130 @@
+page.title=Building Android for a new Mobile Device
+pdk.version=1.0
+@jd:body
+
+
+
+
+
In the products directory, create an AndroidProducts.mk file that point to (and is responsible for finding) the individual product make files.
+
+ #
+ # This file should set PRODUCT_MAKEFILES to a list of product makefiles
+ # to expose to the build system. LOCAL_DIR will already be set to
+ # the directory containing this file.
+ #
+ # This file may not rely on the value of any variable other than
+ # LOCAL_DIR; do not use any conditionals, and do not look up the
+ # value of any variable that isn't set in this file or in a file that
+ # it includes.
+ #
+
+ PRODUCT_MAKEFILES := \
+ $(LOCAL_DIR)/first_product_name.mk \
+
Create a board-specific directory beneath your company directory that matches the PRODUCT_DEVICE variable <board_name> referenced in the product-specific make file above. This will include a make file that gets accessed by any product using this board.
+
Create a product_config.mk file in the directory created in the previous step (device/partner/<company_name>/<board_name>). If this directory does not include a product_config.mk file, the build will fail.
+
+ # These definitions override the defaults in config/config.make for <board_name>
+ #
+ # TARGET_NO_BOOTLOADER := false
+ # TARGET_HARDWARE_3D := false
+ #
+ TARGET_USE_GENERIC_AUDIO := true
+
If you wish to modify system properties, create a system.prop file in your <board_name> directory(device/partner/<company_name>/<board_name>).
+
+ # system.prop for
+ # This overrides settings in the products/generic/system.prop file
+ #
+ # rild.libpath=/system/lib/libreference-ril.so
+ # rild.libargs=-d /dev/ttyS0
+
Add a pointer to <second_product_name>.mk within products/AndroidProducts.mk.
+
device/partner/<company_name>/<board_name> must include an Android.mk file with at least the following code:
+
+ # make file for new hardware from
+ #
+ LOCAL_PATH := $(call my-dir)
+ #
+ # this is here to use the pre-built kernel
+ ifeq ($(TARGET_PREBUILT_KERNEL),)
+ TARGET_PREBUILT_KERNEL := $(LOCAL_PATH)/kernel
+ endif
+ #
+ file := $(INSTALLED_KERNEL_TARGET)
+ ALL_PREBUILT += $(file)
+ $(file): $(TARGET_PREBUILT_KERNEL) | $(ACP)
+ $(transform-prebuilt-to-target)
+ #
+ # no boot loader, so we don't need any of that stuff..
+ #
+ LOCAL_PATH := partner/<company_name>/<board_name>
+ #
+ include $(CLEAR_VARS)
+ #
+ # include more board specific stuff here? Such as Audio parameters.
+ #
+
To create a second product for the same board, create a second product-specific make file called device/partner/company_name/products/<second_product_name>.mk that includes:
+
By now, you should have two new products, called <first_product_name> and <second_product_name> associated with <company_name>. To verify that a product is properly configured (<first_product_name>, for example), execute the following:
+
+ cd device
+ . ./envsetup.sh
+ partner_setup <first_product_name>
+ make PRODUCT-<first_product_name>-user
+
+
You should find new build binaries located in device/out/target/product/<board_name>.
+
+
+
New Product File Tree
+
+
The file tree below illustrates what your own system should look like after completing the steps above.
Android's camera subsystem connects the camera application to the application framework and user space libraries, which in turn communicate with the camera hardware layer that operates the physical camera.
-
The diagram below illustrates the structure of the camera subsystem.
-
-
-
-
Building a Camera Library
-
-
To implement a camera driver, create a shared library that implements the interface defined in CameraHardwareInterface.h. You must name your shared library libcamera.so so that it will get loaded from /system/lib at runtime. Place libcamera sources and Android.mk in partner/acme/chipset_or_board/libcamera/.
-
The following stub Android.mk file ensures that libcamera compiles and links to the appropriate libraries:
The following diagram illustrates the sequence of function calls and actions necessary for your camera to preview.
-
-
-
-
Taking a Picture
-
-
The following diagram illustrates the sequence of function calls and actions necessary for your camera to take a picture.
-
-
-
-
Interface
-
-
-
-
Note: This document relies on some Doxygen-generated content that appears in an iFrame below. To return to the Doxygen default content for this page, click here.
Android's camera subsystem connects the camera application to the application framework and user space libraries, which in turn communicate with the camera hardware layer that operates the physical camera.
+
The diagram below illustrates the structure of the camera subsystem.
+
+
+
+
Building a Camera Library
+
+
To implement a camera driver, create a shared library that implements the interface defined in CameraHardwareInterface.h. You must name your shared library libcamera.so so that it will get loaded from /system/lib at runtime. Place libcamera sources and Android.mk in partner/acme/chipset_or_board/libcamera/.
+
The following stub Android.mk file ensures that libcamera compiles and links to the appropriate libraries:
The following diagram illustrates the sequence of function calls and actions necessary for your camera to preview.
+
+
+
+
Taking a Picture
+
+
The following diagram illustrates the sequence of function calls and actions necessary for your camera to take a picture.
+
+
+
+
Interface
+
+
+
+
Note: This document relies on some Doxygen-generated content that appears in an iFrame below. To return to the Doxygen default content for this page, click here.
Android relies on Git, a version control system, to install the Android platform. You will need to install Git 1.5.2 or greater in order to access the source tree. Please visit http://git.or.cz/ for more information regarding Git.
-
Git permits you to control access to working directories, and we recommend that you use it to limit Android repository access to only a few people within your organization (please refer to your Google NDA for potential contractual restraints on sharing Android source access).
-
You may clone Google's repository to a local copy for sharing internally (see Git documentation for details).
-
-
-
Installing and Configuring Git
-
-
To install the Git package, execute:
-
-% sudo apt-get install git-core
-
-
-
-
Establishing Server Access
-
-
Once Git is cleanly installed, you need to establish a connection with Google's Git server, a connection that requires an RSA key in order to authenticate requests.
-
-
-
Generating RSA Keys
-
-
Each developer must have a unique RSA key in order to access Android source code. To generate an RSA key:
-
-
-
Type:
-
% ssh-keygen -t rsa -C email@domain.com
-You must use a valid email address to create your key.
-
When prompted, indicate the file to which you wish to write your key (id_rsa in this example).
-
When prompted, associate a passphrase with your key.
-
Upon success, you should have two files saved to the designated directory:
-
-
id_rsa: This file contains the private half of your RSA key. You shouldn't share this file with anyone.
-
id_rsa.pub: This is the public half or your RSA key and you should send it to your Google technical account manager.
-
-
-
-
Send your Google Account Manager your public key file in order to establish Git server access.
-
-
-
Verifying a Connection to the Git Server
-
-
Once you have generated an RSA key and shared the public file with Google, you can test your connection with the Git server with the following command:
-
-% ssh android-git.ext.google.com
-
-
-
You should receive one of the following results:
-
-
-
-
Result
-
Cause
-
Action
-
-
-
-fatal: What do you think I am? A shell?
-Connection to android-git closed.
-
-
Success
-
None. You successfully connected to the Git server. (You should not have shell access and it's expected to receive this error.)
-
-
-
ssh hangs and eventually times out.
-
Your setup is failing to locate and establish a basic connection.
-
Google needs to debug network settings.
-
-
-
Error: Permission denied <public key>
-
Either you are not using the matching username or the RSA private key does not match the public key.
Android source code is maintained in two repositories: device and kernel. The device repository includes the Android framework (things like the Activity Manager, Window Manager, Telephony Manager, View System, etc.). The kernel repository includes the core code necessary to run the operating system (things like the Display Driver, Camera Driver, Keypad Driver, Power Management, etc.). (Please see What is Android? for details.)
-
-
Save device and kernel code at the same directory level, for example:
-
-
/home/joe/android/device
-
/home/joe/android/kernel
-
-
Device Code
-
To download device code, you need your username and a unique <path> string supplied by Google to execute the following:
You likely already have Linux running on your platform and only need to integrate Android-specific changes. The following directions describe how to extract an Android patch.
-
-
Download a generic version of the Linux kernel that matches the Linux version downloaded with the Android Kernel code.
-
Run diff on the two kernel packages to get Android-specific changes.
Android relies on Git, a version control system, to install the Android platform. You will need to install Git 1.5.2 or greater in order to access the source tree. Please visit http://git.or.cz/ for more information regarding Git.
+
Git permits you to control access to working directories, and we recommend that you use it to limit Android repository access to only a few people within your organization (please refer to your Google NDA for potential contractual restraints on sharing Android source access).
+
You may clone Google's repository to a local copy for sharing internally (see Git documentation for details).
+
+
+
Installing and Configuring Git
+
+
To install the Git package, execute:
+
+% sudo apt-get install git-core
+
+
+
+
Establishing Server Access
+
+
Once Git is cleanly installed, you need to establish a connection with Google's Git server, a connection that requires an RSA key in order to authenticate requests.
+
+
+
Generating RSA Keys
+
+
Each developer must have a unique RSA key in order to access Android source code. To generate an RSA key:
+
+
+
Type:
+
% ssh-keygen -t rsa -C email@domain.com
+You must use a valid email address to create your key.
+
When prompted, indicate the file to which you wish to write your key (id_rsa in this example).
+
When prompted, associate a passphrase with your key.
+
Upon success, you should have two files saved to the designated directory:
+
+
id_rsa: This file contains the private half of your RSA key. You shouldn't share this file with anyone.
+
id_rsa.pub: This is the public half or your RSA key and you should send it to your Google technical account manager.
+
+
+
+
Send your Google Account Manager your public key file in order to establish Git server access.
+
+
+
Verifying a Connection to the Git Server
+
+
Once you have generated an RSA key and shared the public file with Google, you can test your connection with the Git server with the following command:
+
+% ssh android-git.ext.google.com
+
+
+
You should receive one of the following results:
+
+
+
+
Result
+
Cause
+
Action
+
+
+
+fatal: What do you think I am? A shell?
+Connection to android-git closed.
+
+
Success
+
None. You successfully connected to the Git server. (You should not have shell access and it's expected to receive this error.)
+
+
+
ssh hangs and eventually times out.
+
Your setup is failing to locate and establish a basic connection.
+
Google needs to debug network settings.
+
+
+
Error: Permission denied <public key>
+
Either you are not using the matching username or the RSA private key does not match the public key.
Android source code is maintained in two repositories: device and kernel. The device repository includes the Android framework (things like the Activity Manager, Window Manager, Telephony Manager, View System, etc.). The kernel repository includes the core code necessary to run the operating system (things like the Display Driver, Camera Driver, Keypad Driver, Power Management, etc.). (Please see What is Android? for details.)
+
+
Save device and kernel code at the same directory level, for example:
+
+
/home/joe/android/device
+
/home/joe/android/kernel
+
+
Device Code
+
To download device code, you need your username and a unique <path> string supplied by Google to execute the following:
You likely already have Linux running on your platform and only need to integrate Android-specific changes. The following directions describe how to extract an Android patch.
+
+
Download a generic version of the Linux kernel that matches the Linux version downloaded with the Android Kernel code.
+
Run diff on the two kernel packages to get Android-specific changes.
Android defines a user space C abstraction interface for GPS hardware. The interface header is defined in include/hardware/gps.h. In order to integate GPS with Android, you need to build a shared library that implements this interface.
-
-
-
Building a GPS Library
-
-
To implement a GPS driver, create a shared library that implements the interface defined in gps.h. You must name your shared library libgps.so so that it will get loaded from /system/lib at runtime. Place GPS sources and Android.mk in partner/acme/chipset_or_board/gps/ (where "acme" is your product name and "chipset_or_board" is your hardware target).
-
-
The following stub Android.mk file ensures that libgps compiles and links to the appropriate libraries:
-
-
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := libgps
-
-LOCAL_STATIC_LIBRARIES:= \
-# include any static library dependencies
-
-LOCAL_SHARED_LIBRARIES := \
-# include any shared library dependencies
-
-LOCAL_SRC_FILES += \
-# include your source files. eg. MyGpsLibrary.cpp
-
-LOCAL_CFLAGS += \
-# include any needed compile flags
-
-LOCAL_C_INCLUDES:= \
-# include any needed local header files
-
-include $(BUILD_SHARED_LIBRARY)
-
-
-
-
Interface
-
-
-
-
-
-
Note: This document relies on some Doxygen-generated content that appears in an iFrame below. To return to the Doxygen default content for this page, click here.
Android defines a user space C abstraction interface for GPS hardware. The interface header is defined in include/hardware/gps.h. In order to integate GPS with Android, you need to build a shared library that implements this interface.
+
+
+
Building a GPS Library
+
+
To implement a GPS driver, create a shared library that implements the interface defined in gps.h. You must name your shared library libgps.so so that it will get loaded from /system/lib at runtime. Place GPS sources and Android.mk in partner/acme/chipset_or_board/gps/ (where "acme" is your product name and "chipset_or_board" is your hardware target).
+
+
The following stub Android.mk file ensures that libgps compiles and links to the appropriate libraries:
+
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libgps
+
+LOCAL_STATIC_LIBRARIES:= \
+# include any static library dependencies
+
+LOCAL_SHARED_LIBRARIES := \
+# include any shared library dependencies
+
+LOCAL_SRC_FILES += \
+# include your source files. eg. MyGpsLibrary.cpp
+
+LOCAL_CFLAGS += \
+# include any needed compile flags
+
+LOCAL_C_INCLUDES:= \
+# include any needed local header files
+
+include $(BUILD_SHARED_LIBRARY)
+
The Open Handset Distribution (OHD) is a software distribution for mobile devices, often referred to as Android, developed by members of the Open Handset Alliance. Android includes an operating system, middleware, and key applications typically required for a mobile device.
-
-
This porting guide describes the steps necessary to port Android to a new mobile device. Android is designed as a highly-portable, hardware-independent platform based on Linux, and porting the platform to new devices requires little more than porting the Linux kernel and developing the Linux drivers necessary for your device.
-
-
The current version of this guide describes bringing Android up to "PDA-level" functionality; functionality sufficient to support non-multimedia apps that run on unconnected mobile devices through the standard user interface devices such as keypad and display. Future versions of this guide will cover complete telephony, multi-media and peripheral integration to create a complete mobile device.
-
-
-
Intended Audience
-
-
This porting guide is intended for engineers proficient with running (and writing drivers for) Linux on embedded devices.
-
The guide also assumes you have a target hardware that matches Device Requirements and that you
-can boot and run a recent (2.6.x) version of the Linux kernel
-with at least keypad and display drivers properly installed.
Install necessary packages and retrieve source code through a Git server. Build System offers a conceptual overview of Android's build system and instructions to affect a simple build.
Establish core components necessary to your device, such as keymaps / keyboard input and display drivers.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
v0.6 - 25 November 2008
-
-
-
diff --git a/pdk/docs/index.jd b/pdk/docs/index.jd
new file mode 100644
index 000000000..b7529d3a7
--- /dev/null
+++ b/pdk/docs/index.jd
@@ -0,0 +1,39 @@
+home=true
+page.title=Welcome to the Android Porting Guide
+pdk.version=1.0
+@jd:body
+
+
+
The Open Handset Distribution (OHD) is a software distribution for mobile devices, often referred to as Android, developed by members of the Open Handset Alliance. Android includes an operating system, middleware, and key applications typically required for a mobile device.
+
+
This porting guide describes the steps necessary to port Android to a new mobile device. Android is designed as a highly-portable, hardware-independent platform based on Linux, and porting the platform to new devices requires little more than porting the Linux kernel and developing the Linux drivers necessary for your device.
+
+
The current version of this guide describes bringing Android up to "PDA-level" functionality; functionality sufficient to support non-multimedia apps that run on unconnected mobile devices through the standard user interface devices such as keypad and display. Future versions of this guide will cover complete telephony, multi-media and peripheral integration to create a complete mobile device.
+
+
+
Intended Audience
+
+
This porting guide is intended for engineers proficient with running (and writing drivers for) Linux on embedded devices.
+
The guide also assumes you have a target hardware that matches Device Requirements and that you
+can boot and run a recent (2.6.x) version of the Linux kernel
+with at least keypad and display drivers properly installed.
Install necessary packages and retrieve source code through a Git server. Build System offers a conceptual overview of Android's build system and instructions to affect a simple build.
Android source code is maintained in two code bases: the Android Linux kernel (kernel directory) and Android platform and applications (device directory). This document provides a high-level introduction to the source code organization and an overview of the major components of each primary directory.
-
-
Android Source
-
-
-
Linux Kernel
-
-
The Android Linux kernel includes enhancements to the Linux 2.6 kernel that provide additional drivers to support the Android platform, including:
-
-
Binder: an OpenBinder-based driver to facilitate inter-process communication (IPC) in the Android platform.
-
Android Power Management: a light weight power management driver built on top of standard Linux power management but optimized for embedded systems.
-
Low Memory Killer: Based on hints from the userspace, the low memory killer can kill off processes to free up memory as necessary. It is designed to provide more flexibility than the Out Of Memory (OOM) killer in the standard kernel.
-
Logger: A light weight logging device used to capture system, radio, logdata, etc.
-
USB Gadget: Uses the USB function framework.
-
Android/PMEM: The PMEM (physical memory) driver is used to provide contiguous physical memory regions to userspace libraries that interact with the digital signal processor (DSP) and other hardware that cannot cope with scatter-gather.
-
Android Alarm: A driver which provides timers that can wake the device up from sleep and a monotonic timebase that runs while the device is asleep.
-
-
Look for Android-specific enhancements in the following directories:
-
-
/drivers/android
-
/drivers/misc
-
/include/linux
-
-
-
-
-
Android Platform and Applications
-
-
The following list outlines the directory structure found within the device branch of Android source code:
-
-
-
-
-
-
-
-
apps
-Core Android applications such as Phone, Camera, and Calendar.
-
-
-
-
boot
-Reference Android bootloader and other boot-related source code.
-
-
-
-
commands
-Common Android commands, the most important of which is the runtime command, which does much of the initialization of the system.
-
-
-
-
config
-System-wide makefiles and linker scripts.
-
data
-Fonts, keymaps, sounds, timezone information, etc.
-
-
-
-
docs
-Full set of Android documentation.
-
-
-
-
extlibs
-Non-Android libraries. This directory is intended to host unmodified external code. None of the libraries included within this directory rely on Android headers or libraries.
-
-
-
-
ide
-Tools for support of the IDE's used to write Android applications.
-
-
-
-
include
-Android system headers for inclusion.
-
-
-
-
java
-Android core APIs, as well as some external libraries.
-
-
-
-
libs
-Android-specific C++ based libraries.
-
-
-
-
partner
-Project-specific source code for various proprietary components.
-
-
-
-
prebuilt
-Prebuilt tools, like the toolchains and emulator binary.
-
-
-
-
product
-Device-specific configuration files. This directory will include a subdirectory for each new device.
-
-
-
-
samples
-Sample applications.
-
-
-
-
servers
-C++ based system servers.
-
-
-
-
system
-Core of the embedded Linux platform at the heart of Android. These essential bits are required for basic booting, operation, and debugging.
-
-
-
-
tests
-Platform and application test cases.
-
-
-
-
tools
-Tools for building and debugging Android (of particular interest for porting are "adb" and "emulator").
-
-
-
-
-
-
-
-
-
-
Adding Source Code
-
-
You can develop Android applications with the same standard tools you use to develop any Java application. The Android core libraries provide the functionality needed to build rich mobile applications and the Android development tools are designed to simplify running, debugging, and testing your applications.
-
-
Add project-specific source code to the Android source tree under the partner directory in a directory specific to the application or service you are building. For example, all Google-specific applications would be placed under device/partner/google/. A Google search application would be placed under device/partner/google/apps/Search.
-
Android source code is maintained in two code bases: the Android Linux kernel (kernel directory) and Android platform and applications (device directory). This document provides a high-level introduction to the source code organization and an overview of the major components of each primary directory.
+
+
Android Source
+
+
+
Linux Kernel
+
+
The Android Linux kernel includes enhancements to the Linux 2.6 kernel that provide additional drivers to support the Android platform, including:
+
+
Binder: an OpenBinder-based driver to facilitate inter-process communication (IPC) in the Android platform.
+
Android Power Management: a light weight power management driver built on top of standard Linux power management but optimized for embedded systems.
+
Low Memory Killer: Based on hints from the userspace, the low memory killer can kill off processes to free up memory as necessary. It is designed to provide more flexibility than the Out Of Memory (OOM) killer in the standard kernel.
+
Logger: A light weight logging device used to capture system, radio, logdata, etc.
+
USB Gadget: Uses the USB function framework.
+
Android/PMEM: The PMEM (physical memory) driver is used to provide contiguous physical memory regions to userspace libraries that interact with the digital signal processor (DSP) and other hardware that cannot cope with scatter-gather.
+
Android Alarm: A driver which provides timers that can wake the device up from sleep and a monotonic timebase that runs while the device is asleep.
+
+
Look for Android-specific enhancements in the following directories:
+
+
/drivers/android
+
/drivers/misc
+
/include/linux
+
+
+
+
+
Android Platform and Applications
+
+
The following list outlines the directory structure found within the device branch of Android source code:
+
+
+
+
+
+
+
+
apps
+Core Android applications such as Phone, Camera, and Calendar.
+
+
+
+
boot
+Reference Android bootloader and other boot-related source code.
+
+
+
+
commands
+Common Android commands, the most important of which is the runtime command, which does much of the initialization of the system.
+
+
+
+
config
+System-wide makefiles and linker scripts.
+
data
+Fonts, keymaps, sounds, timezone information, etc.
+
+
+
+
docs
+Full set of Android documentation.
+
+
+
+
extlibs
+Non-Android libraries. This directory is intended to host unmodified external code. None of the libraries included within this directory rely on Android headers or libraries.
+
+
+
+
ide
+Tools for support of the IDE's used to write Android applications.
+
+
+
+
include
+Android system headers for inclusion.
+
+
+
+
java
+Android core APIs, as well as some external libraries.
+
+
+
+
libs
+Android-specific C++ based libraries.
+
+
+
+
partner
+Project-specific source code for various proprietary components.
+
+
+
+
prebuilt
+Prebuilt tools, like the toolchains and emulator binary.
+
+
+
+
product
+Device-specific configuration files. This directory will include a subdirectory for each new device.
+
+
+
+
samples
+Sample applications.
+
+
+
+
servers
+C++ based system servers.
+
+
+
+
system
+Core of the embedded Linux platform at the heart of Android. These essential bits are required for basic booting, operation, and debugging.
+
+
+
+
tests
+Platform and application test cases.
+
+
+
+
tools
+Tools for building and debugging Android (of particular interest for porting are "adb" and "emulator").
+
+
+
+
+
+
+
+
+
+
Adding Source Code
+
+
You can develop Android applications with the same standard tools you use to develop any Java application. The Android core libraries provide the functionality needed to build rich mobile applications and the Android development tools are designed to simplify running, debugging, and testing your applications.
+
+
Add project-specific source code to the Android source tree under the partner directory in a directory specific to the application or service you are building. For example, all Google-specific applications would be placed under device/partner/google/. A Google search application would be placed under device/partner/google/apps/Search.
+
Android supports its own Power Management (on top of the standard Linux Power Management) designed with the premise that the CPU shouldn't consume power if no applications or services require power. For more information regarding standard Linux power management, please see Linux Power Management Support at http://kernel.org.
-
Android requires that applications and services request CPU resources with "wake locks" through the Android application framework and native Linux libraries. If there are no active wake locks, Android will shut down the CPU.
-
The image below illustrates the Android power management architecture.
-
-
-Solid elements represent Android blocks and dashed elements represent partner-specific blocks.
-
-
-
-
Wake Locks
-
-
Wake locks are used by applications and services to request CPU resources.
-
-
-
Types of Wake Locks
-
-
-
-
Wake Lock
-
Description
-
-
-
ACQUIRE_CAUSES_WAKEUP
-
Normally wake locks don't actually wake the device, they just cause it to remain on once it's already on. Think of the video player app as the normal behavior. Notifications that pop up and want the device to be on are the exception; use this flag to be like them.
-
-
-
FULL_WAKE_LOCK
-
Wake lock that ensures that the screen and keyboard are on at full brightness.
-
-
-
ON_AFTER_RELEASE
-
When this wake lock is released, poke the user activity timer so the screen stays on for a little longer.
-
-
-
PARTIAL_WAKE_LOCK
-
Wake lock that ensures that the CPU is running. The screen might not be on.
-
-
-
SCREEN_BRIGHT_WAKE_LOCK
-
Wake lock that ensures that the screen is on at full brightness; the keyboard backlight will be allowed to go off.
-
-
-
SCREEN_DIM_WAKE_LOCK
-
Wake lock that ensures that the screen is on, but the keyboard backlight will be allowed to go off, and the screen backlight will be allowed to go dim.
-
-
-
-
-
Exploring a Wake Lock Example
-
-
All power management calls follow the same basic format:
-
Acquire handle to the PowerManager service.
-
Create a wake lock and specify the power management flags for screen, timeout, etc.
-
Acquire wake lock.
-
Perform operation (play MP3, open HTML page, etc.).
The Android Framework exposes power management to services and applications through the PowerManager class.
-
User space native libraries (any hardware function in //device/lib/hardware/ meant to serve as supporting libraries for Android runtime) should never call into Android Power Management directly (see the image above). Bypassing the power management policy in the Android runtime will destabilize the system.
-
All calls into Power Management should go through the Android runtime PowerManager APIs.
You can register Kernel-level drivers with the Android Power Manager driver so that they're notified immediately before power down or after power up. For example, you might set a display driver to completely power down when a request comes in to power down from the user space (see the Android MSM MDDI display driver for a sample implementation).
-
To register drivers with the Android PM driver, implement call-back handlers and register them with the Android PM, as illustrated in the snippet below:
Android supports its own Power Management (on top of the standard Linux Power Management) designed with the premise that the CPU shouldn't consume power if no applications or services require power. For more information regarding standard Linux power management, please see Linux Power Management Support at http://kernel.org.
+
Android requires that applications and services request CPU resources with "wake locks" through the Android application framework and native Linux libraries. If there are no active wake locks, Android will shut down the CPU.
+
The image below illustrates the Android power management architecture.
+
+
+
Solid elements represent Android blocks and dashed elements represent partner-specific blocks.
+
+
+
+
Wake Locks
+
+
Wake locks are used by applications and services to request CPU resources.
+
+
+
Types of Wake Locks
+
+
+
+
Wake Lock
+
Description
+
+
+
ACQUIRE_CAUSES_WAKEUP
+
Normally wake locks don't actually wake the device, they just cause it to remain on once it's already on. Think of the video player app as the normal behavior. Notifications that pop up and want the device to be on are the exception; use this flag to be like them.
+
+
+
FULL_WAKE_LOCK
+
Wake lock that ensures that the screen and keyboard are on at full brightness.
+
+
+
ON_AFTER_RELEASE
+
When this wake lock is released, poke the user activity timer so the screen stays on for a little longer.
+
+
+
PARTIAL_WAKE_LOCK
+
Wake lock that ensures that the CPU is running. The screen might not be on.
+
+
+
SCREEN_BRIGHT_WAKE_LOCK
+
Wake lock that ensures that the screen is on at full brightness; the keyboard backlight will be allowed to go off.
+
+
+
SCREEN_DIM_WAKE_LOCK
+
Wake lock that ensures that the screen is on, but the keyboard backlight will be allowed to go off, and the screen backlight will be allowed to go dim.
+
+
+
+
+
Exploring a Wake Lock Example
+
+
All power management calls follow the same basic format:
+
Acquire handle to the PowerManager service.
+
Create a wake lock and specify the power management flags for screen, timeout, etc.
+
Acquire wake lock.
+
Perform operation (play MP3, open HTML page, etc.).
The Android Framework exposes power management to services and applications through the PowerManager class.
+
User space native libraries (any hardware function in //device/lib/hardware/ meant to serve as supporting libraries for Android runtime) should never call into Android Power Management directly (see the image above). Bypassing the power management policy in the Android runtime will destabilize the system.
+
All calls into Power Management should go through the Android runtime PowerManager APIs.
You can register Kernel-level drivers with the Android Power Manager driver so that they're notified immediately before power down or after power up. For example, you might set a display driver to completely power down when a request comes in to power down from the user space (see the Android MSM MDDI display driver for a sample implementation).
+
To register drivers with the Android PM driver, implement call-back handlers and register them with the Android PM, as illustrated in the snippet below:
This section provides instructions on how to configure your host system to build Android for mobile devices. While Android is designed as host-environment agnostic, it has been tested and is known to work on the following Linux operating system; Ubuntu 6.06 (Dapper), 7.10 (Gutsy), and 8.04. Cygwin is not recommended.
-
-
-
Installing Packages
-
-
-
-
Required Packages
-
-
Android requires the following system packages:
-
-
flex: This lexical analyzer generator is used to read a given input file for a description of a scanner to generate.
-
bison: This is a general-purpose parser generator.
-
gperf: This is a perfect hash function generator.
-
libesd0-dev: This enlightened sound daemon (dev files) is used to mix digitized audio streams for playback by a single device.
-
libwxgtk2.6-dev: This package provides GUI components and other facilities for many different platforms.
-
build-essential: This package contains a list of packages considered fundamental to building Debian packages.
This snippet includes an artificial line break to maintain a print-friendly document.
-
-
-
Ubuntu 7.10
-
-
The libwxgtk2.6-dev package will only work if the following code is included in your /etc/apt/source file.
-
-## N.B. software from this repository is ENTIRELY UNSUPPORTED by the Ubuntu
-## team, and may not be under a free license. Please satisfy yourself as to
-## your rights to use the software. Also, please note that software in
-## universe WILL NOT receive any review or updates from the Ubuntu security
-## team.
-# Line commented out by installer because it failed to verify:
-deb http://us.archive.ubuntu.com/ubuntu/ gutsy universe
-# Line commented out by installer because it failed to verify:
-deb-src http://us.archive.ubuntu.com/ubuntu/ gutsy universe
-# Line commented out by installer because it failed to verify:
-deb http://us.archive.ubuntu.com/ubuntu/ gutsy-updates universe
-# Line commented out by installer because it failed to verify:
-deb-src http://us.archive.ubuntu.com/ubuntu/ gutsy-updates universe
-
-
Install required packages with the following command:
-
Android source code includes a hard dependency on the Java Developer Kit (JDK) 5.0 Update 12 or greater. The specific file name of the Update 12 package is jdk-1_5_0_12-linux-i586.bin. To download this version of the Java JDK:
Select '5.0 Update 12' from the 'Java 2 Platform Standard Edition (J2SE)' -> 'JDK/JRE - 5.0' field and click 'Go.'
-
Click 'Download JDK.'
-
In the 'Linux Platform' section, click 'Linux self-extracting file' associated with the jdk-1_5_0_12-linux-i586.bin package.
-
Follow the installation instructions.
-
-
-
-
Once you have cleanly installed the JDK, modify your PATH environment variable to include <jdk-install-dir>/jdk1.5.0_12/bin at its beginning so that Dapper will use the correct installation.
-
Ubuntu 7.10
-
An alternative method to quickly install Java is to enable multiverse repo in /etc/apt/sources.list and then execute:
-
-% sudo apt-get install sun-java5-jdk
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
v0.5 - 25 September 2008
-
-
-
diff --git a/pdk/docs/source_setup_guide.jd b/pdk/docs/source_setup_guide.jd
new file mode 100755
index 000000000..5c0bed5e1
--- /dev/null
+++ b/pdk/docs/source_setup_guide.jd
@@ -0,0 +1,116 @@
+page.title=Host System Setup
+@jd:body
+
+
+
This section provides instructions on how to configure your host system to build Android for mobile devices. While Android is designed as host-environment agnostic, it has been tested and is known to work on the following Linux operating system; Ubuntu 6.06 (Dapper), 7.10 (Gutsy), and 8.04. Cygwin is not recommended.
+
+
+
Installing Packages
+
+
+
+
Required Packages
+
+
Android requires the following system packages:
+
+
flex: This lexical analyzer generator is used to read a given input file for a description of a scanner to generate.
+
bison: This is a general-purpose parser generator.
+
gperf: This is a perfect hash function generator.
+
libesd0-dev: This enlightened sound daemon (dev files) is used to mix digitized audio streams for playback by a single device.
+
libwxgtk2.6-dev: This package provides GUI components and other facilities for many different platforms.
+
build-essential: This package contains a list of packages considered fundamental to building Debian packages.
This snippet includes an artificial line break to maintain a print-friendly document.
+
+
+
Ubuntu 7.10
+
+
The libwxgtk2.6-dev package will only work if the following code is included in your /etc/apt/source file.
+
+## N.B. software from this repository is ENTIRELY UNSUPPORTED by the Ubuntu
+## team, and may not be under a free license. Please satisfy yourself as to
+## your rights to use the software. Also, please note that software in
+## universe WILL NOT receive any review or updates from the Ubuntu security
+## team.
+# Line commented out by installer because it failed to verify:
+deb http://us.archive.ubuntu.com/ubuntu/ gutsy universe
+# Line commented out by installer because it failed to verify:
+deb-src http://us.archive.ubuntu.com/ubuntu/ gutsy universe
+# Line commented out by installer because it failed to verify:
+deb http://us.archive.ubuntu.com/ubuntu/ gutsy-updates universe
+# Line commented out by installer because it failed to verify:
+deb-src http://us.archive.ubuntu.com/ubuntu/ gutsy-updates universe
+
+
Install required packages with the following command:
+
Android source code includes a hard dependency on the Java Developer Kit (JDK) 5.0 Update 12 or greater. The specific file name of the Update 12 package is jdk-1_5_0_12-linux-i586.bin. To download this version of the Java JDK:
Select '5.0 Update 12' from the 'Java 2 Platform Standard Edition (J2SE)' -> 'JDK/JRE - 5.0' field and click 'Go.'
+
Click 'Download JDK.'
+
In the 'Linux Platform' section, click 'Linux self-extracting file' associated with the jdk-1_5_0_12-linux-i586.bin package.
+
Follow the installation instructions.
+
+
+
+
Once you have cleanly installed the JDK, modify your PATH environment variable to include <jdk-install-dir>/jdk1.5.0_12/bin at its beginning so that Dapper will use the correct installation.
+
Ubuntu 7.10
+
An alternative method to quickly install Java is to enable multiverse repo in /etc/apt/sources.list and then execute:
While Android is designed to support a wide variety of hardware platforms and configurations, this section provides recommended minimum device requirements.
-
-
-
Feature
-
Minimum Requirement
-
Notes
-
-
-
Chipset
-
ARM-based
-
For the first release, Android is primarily targeted towards mobile handsets and portions of the platform, such as Dalvik VM graphics processing, currently assume an ARM architecture.
-
-
-
Memory
-
128 MB RAM; 256 MB Flash External
-
Android can boot and run in configurations with less memory, but it isn't recommended.
-
-
-
Storage
-
Mini or Micro SD
-
Not necessary for basic bring up, but recommended.
-
-
-
Primary Display
-
HVGA required
-
The current Android interface targets a touch-based HVGA resolution display with a touch-interface no smaller than 2.8 inches in size. However, smaller displays will suffice for initial porting.
-
-
-
Navigation Keys
-
5-way navigation with 5 application keys, power, camera and volume controls
-
-
-
-
Camera
-
2MP CMOS
-
Not required for basic bring up.
-
-
-
USB
-
Standard mini-B USB interface
-
Android uses the USB interface for flashing the device system images and debugging a running device.
-
-
-
Bluetooth
-
1.2 or 2.0
-
Not required for initial bring up.
-
-
-
-
If available, your Android device can also benefit from the following optional device characteristics:
While Android is designed to support a wide variety of hardware platforms and configurations, this section provides recommended minimum device requirements.
+
+
+
+
Feature
+
Minimum Requirement
+
Notes
+
+
+
Chipset
+
ARM-based
+
For the first release, Android is primarily targeted towards mobile handsets and portions of the platform, such as Dalvik VM graphics processing, currently assume an ARM architecture.
+
+
+
Memory
+
128 MB RAM; 256 MB Flash External
+
Android can boot and run in configurations with less memory, but it isn't recommended.
+
+
+
Storage
+
Mini or Micro SD
+
Not necessary for basic bring up, but recommended.
+
+
+
Primary Display
+
HVGA required
+
The current Android interface targets a touch-based HVGA resolution display with a touch-interface no smaller than 2.8 inches in size. However, smaller displays will suffice for initial porting.
+
+
+
Navigation Keys
+
5-way navigation with 5 application keys, power, camera and volume controls
+
+
+
+
Camera
+
2MP CMOS
+
Not required for basic bring up.
+
+
+
USB
+
Standard mini-B USB interface
+
Android uses the USB interface for flashing the device system images and debugging a running device.
+
+
+
Bluetooth
+
1.2 or 2.0
+
Not required for initial bring up.
+
+
+
+
If available, your Android device can also benefit from the following optional device characteristics:
Android uses wpa_supplicant as the platform interface to the Wi-Fi device. Your Wi-Fi driver must be compatible with the standard wpa_supplicant in addition to extensions added to the supplicant (specifically, the "DRIVER" commands described in wifi.h/wifi_command()).
-
-
-
Building a Wi-Fi Library
-
-
To create a Wi-Fi driver for Android:
-
-
create a shared library that implements the interface defined in include/hardware/wifi.h, which also defines the Wi-Fi supplicant.
Test your driver using the command line wpa_cli utilities.
-
-
-
You can find the default implementation in libs/hardware/wifi/wifi.c. If you need to make changes, create a new source file similar to wifi.c, for example, wifi_mywifi.c.
-
-
Update the default Android.mk file (libs/hardware/wifi/Android.mk) as shown below.
Note: This document relies on some Doxygen-generated content that appears in an iFrame below. To return to the Doxygen default content for this page, click here.
Android uses wpa_supplicant as the platform interface to the Wi-Fi device. Your Wi-Fi driver must be compatible with the standard wpa_supplicant in addition to extensions added to the supplicant (specifically, the "DRIVER" commands described in wifi.h/wifi_command()).
+
+
+
Building a Wi-Fi Library
+
+
To create a Wi-Fi driver for Android:
+
+
create a shared library that implements the interface defined in include/hardware/wifi.h, which also defines the Wi-Fi supplicant.
Test your driver using the command line wpa_cli utilities.
+
+
+
You can find the default implementation in libs/hardware/wifi/wifi.c. If you need to make changes, create a new source file similar to wifi.c, for example, wifi_mywifi.c.
+
+
Update the default Android.mk file (libs/hardware/wifi/Android.mk) as shown below.
Note: This document relies on some Doxygen-generated content that appears in an iFrame below. To return to the Doxygen default content for this page, click here.
+
+
+
+
+
+
diff --git a/pdk/hosting/app.yaml b/pdk/hosting/app.yaml
index 407cbb08b..6e08141bc 100644
--- a/pdk/hosting/app.yaml
+++ b/pdk/hosting/app.yaml
@@ -4,8 +4,8 @@ runtime: python
api_version: 1
handlers:
-- url: /docs
- static_dir: docs
+- url: /online-pdk
+ static_dir: online-pdk
- url: /
script: pdk.py
diff --git a/pdk/hosting/pdk.py b/pdk/hosting/pdk.py
index e88f826f8..421f19550 100644
--- a/pdk/hosting/pdk.py
+++ b/pdk/hosting/pdk.py
@@ -26,11 +26,9 @@ from google.appengine.ext.webapp.util import run_wsgi_app
class MainPage(webapp.RequestHandler):
def get(self):
- self.redirect('docs/index.html')
+ self.redirect('online-pdk/index.html')
-application = webapp.WSGIApplication(
- [('/', MainPage)],
- debug=True)
+application = webapp.WSGIApplication([('/', MainPage)], debug=True)
def main():
run_wsgi_app(application)
@@ -38,6 +36,18 @@ def main():
if __name__ == "__main__":
main()
+# Testing
+# You must install google appengine. See: http://code.google.com/appengine/downloads.html
+#
+# Here's the command to run the pdk-docs server locally:
+# python /dev_appserver.py --address 0.0.0.0 \
+# /android/out/target/common/docs
+
+# To verify it is working you can access it with a browser loacally on port 8080:
+
+# http://localhost:8080/index.html
+
+
# To upload this application:
# /home/build/static/projects/apphosting/devtools/appcfg.py update pdk/
# where the pdk directory contains: pdk.py, app.yaml, and the docs directory.
diff --git a/samples/ApiDemos/default.properties b/samples/ApiDemos/default.properties
new file mode 100644
index 000000000..4513a1e4f
--- /dev/null
+++ b/samples/ApiDemos/default.properties
@@ -0,0 +1,11 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system use,
+# "build.properties", and override values to adapt the script to your
+# project structure.
+
+# Project target.
+target=android-3
diff --git a/samples/ApiDemos/src/com/example/android/apis/appwidget/ExampleAppWidgetConfigure.java b/samples/ApiDemos/src/com/example/android/apis/appwidget/ExampleAppWidgetConfigure.java
index e0a4c76b0..d7ef9d632 100644
--- a/samples/ApiDemos/src/com/example/android/apis/appwidget/ExampleAppWidgetConfigure.java
+++ b/samples/ApiDemos/src/com/example/android/apis/appwidget/ExampleAppWidgetConfigure.java
@@ -89,7 +89,10 @@ public class ExampleAppWidgetConfigure extends Activity {
saveTitlePref(ExampleAppWidgetConfigure.this, mAppWidgetId,
mAppWidgetPrefix.getText().toString());
- setResult(RESULT_OK);
+ // Make sure we pass back the original appWidgetId
+ Intent resultValue = new Intent();
+ resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId);
+ setResult(RESULT_OK, resultValue);
finish();
}
};
diff --git a/samples/JetBoy/Android.mk b/samples/JetBoy/Android.mk
new file mode 100755
index 000000000..2e6b57133
--- /dev/null
+++ b/samples/JetBoy/Android.mk
@@ -0,0 +1,10 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := samples
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := JETBoy
+
+include $(BUILD_PACKAGE)
diff --git a/samples/JetBoy/AndroidManifest.xml b/samples/JetBoy/AndroidManifest.xml
new file mode 100755
index 000000000..bba069d4c
--- /dev/null
+++ b/samples/JetBoy/AndroidManifest.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/JetBoy/default.properties b/samples/JetBoy/default.properties
new file mode 100644
index 000000000..4513a1e4f
--- /dev/null
+++ b/samples/JetBoy/default.properties
@@ -0,0 +1,11 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system use,
+# "build.properties", and override values to adapt the script to your
+# project structure.
+
+# Project target.
+target=android-3
diff --git a/samples/JetBoy/res/drawable/asteroid01.png b/samples/JetBoy/res/drawable/asteroid01.png
new file mode 100755
index 000000000..1cede4f0b
Binary files /dev/null and b/samples/JetBoy/res/drawable/asteroid01.png differ
diff --git a/samples/JetBoy/res/drawable/asteroid02.png b/samples/JetBoy/res/drawable/asteroid02.png
new file mode 100755
index 000000000..7255da573
Binary files /dev/null and b/samples/JetBoy/res/drawable/asteroid02.png differ
diff --git a/samples/JetBoy/res/drawable/asteroid03.png b/samples/JetBoy/res/drawable/asteroid03.png
new file mode 100755
index 000000000..c343741ae
Binary files /dev/null and b/samples/JetBoy/res/drawable/asteroid03.png differ
diff --git a/samples/JetBoy/res/drawable/asteroid04.png b/samples/JetBoy/res/drawable/asteroid04.png
new file mode 100755
index 000000000..e89e6f13f
Binary files /dev/null and b/samples/JetBoy/res/drawable/asteroid04.png differ
diff --git a/samples/JetBoy/res/drawable/asteroid05.png b/samples/JetBoy/res/drawable/asteroid05.png
new file mode 100755
index 000000000..b0a78d772
Binary files /dev/null and b/samples/JetBoy/res/drawable/asteroid05.png differ
diff --git a/samples/JetBoy/res/drawable/asteroid06.png b/samples/JetBoy/res/drawable/asteroid06.png
new file mode 100755
index 000000000..0eed3412c
Binary files /dev/null and b/samples/JetBoy/res/drawable/asteroid06.png differ
diff --git a/samples/JetBoy/res/drawable/asteroid07.png b/samples/JetBoy/res/drawable/asteroid07.png
new file mode 100755
index 000000000..45b635bd1
Binary files /dev/null and b/samples/JetBoy/res/drawable/asteroid07.png differ
diff --git a/samples/JetBoy/res/drawable/asteroid08.png b/samples/JetBoy/res/drawable/asteroid08.png
new file mode 100755
index 000000000..720d8310a
Binary files /dev/null and b/samples/JetBoy/res/drawable/asteroid08.png differ
diff --git a/samples/JetBoy/res/drawable/asteroid09.png b/samples/JetBoy/res/drawable/asteroid09.png
new file mode 100755
index 000000000..bc353bfaf
Binary files /dev/null and b/samples/JetBoy/res/drawable/asteroid09.png differ
diff --git a/samples/JetBoy/res/drawable/asteroid10.png b/samples/JetBoy/res/drawable/asteroid10.png
new file mode 100755
index 000000000..f54acdb23
Binary files /dev/null and b/samples/JetBoy/res/drawable/asteroid10.png differ
diff --git a/samples/JetBoy/res/drawable/asteroid11.png b/samples/JetBoy/res/drawable/asteroid11.png
new file mode 100755
index 000000000..8690ccb0c
Binary files /dev/null and b/samples/JetBoy/res/drawable/asteroid11.png differ
diff --git a/samples/JetBoy/res/drawable/asteroid12.png b/samples/JetBoy/res/drawable/asteroid12.png
new file mode 100755
index 000000000..8e181d518
Binary files /dev/null and b/samples/JetBoy/res/drawable/asteroid12.png differ
diff --git a/samples/JetBoy/res/drawable/asteroid_explode1.png b/samples/JetBoy/res/drawable/asteroid_explode1.png
new file mode 100755
index 000000000..0c919c8e2
Binary files /dev/null and b/samples/JetBoy/res/drawable/asteroid_explode1.png differ
diff --git a/samples/JetBoy/res/drawable/asteroid_explode2.png b/samples/JetBoy/res/drawable/asteroid_explode2.png
new file mode 100755
index 000000000..89721f35b
Binary files /dev/null and b/samples/JetBoy/res/drawable/asteroid_explode2.png differ
diff --git a/samples/JetBoy/res/drawable/asteroid_explode3.png b/samples/JetBoy/res/drawable/asteroid_explode3.png
new file mode 100755
index 000000000..ddf837ca6
Binary files /dev/null and b/samples/JetBoy/res/drawable/asteroid_explode3.png differ
diff --git a/samples/JetBoy/res/drawable/asteroid_explode4.png b/samples/JetBoy/res/drawable/asteroid_explode4.png
new file mode 100755
index 000000000..7f0f9c7a4
Binary files /dev/null and b/samples/JetBoy/res/drawable/asteroid_explode4.png differ
diff --git a/samples/JetBoy/res/drawable/background_a.png b/samples/JetBoy/res/drawable/background_a.png
new file mode 100755
index 000000000..6cd2f4474
Binary files /dev/null and b/samples/JetBoy/res/drawable/background_a.png differ
diff --git a/samples/JetBoy/res/drawable/background_b.png b/samples/JetBoy/res/drawable/background_b.png
new file mode 100755
index 000000000..6ffbdb849
Binary files /dev/null and b/samples/JetBoy/res/drawable/background_b.png differ
diff --git a/samples/JetBoy/res/drawable/icon.png b/samples/JetBoy/res/drawable/icon.png
new file mode 100755
index 000000000..4bbd0944b
Binary files /dev/null and b/samples/JetBoy/res/drawable/icon.png differ
diff --git a/samples/JetBoy/res/drawable/int_timer.png b/samples/JetBoy/res/drawable/int_timer.png
new file mode 100755
index 000000000..e455e2679
Binary files /dev/null and b/samples/JetBoy/res/drawable/int_timer.png differ
diff --git a/samples/JetBoy/res/drawable/intbeam_1.png b/samples/JetBoy/res/drawable/intbeam_1.png
new file mode 100755
index 000000000..00903bbed
Binary files /dev/null and b/samples/JetBoy/res/drawable/intbeam_1.png differ
diff --git a/samples/JetBoy/res/drawable/intbeam_2.png b/samples/JetBoy/res/drawable/intbeam_2.png
new file mode 100755
index 000000000..57fcca8af
Binary files /dev/null and b/samples/JetBoy/res/drawable/intbeam_2.png differ
diff --git a/samples/JetBoy/res/drawable/intbeam_3.png b/samples/JetBoy/res/drawable/intbeam_3.png
new file mode 100755
index 000000000..13f10f5a3
Binary files /dev/null and b/samples/JetBoy/res/drawable/intbeam_3.png differ
diff --git a/samples/JetBoy/res/drawable/intbeam_4.png b/samples/JetBoy/res/drawable/intbeam_4.png
new file mode 100755
index 000000000..57fcca8af
Binary files /dev/null and b/samples/JetBoy/res/drawable/intbeam_4.png differ
diff --git a/samples/JetBoy/res/drawable/laser.png b/samples/JetBoy/res/drawable/laser.png
new file mode 100755
index 000000000..4a2dac528
Binary files /dev/null and b/samples/JetBoy/res/drawable/laser.png differ
diff --git a/samples/JetBoy/res/drawable/ship2_1.png b/samples/JetBoy/res/drawable/ship2_1.png
new file mode 100755
index 000000000..44ac43db6
Binary files /dev/null and b/samples/JetBoy/res/drawable/ship2_1.png differ
diff --git a/samples/JetBoy/res/drawable/ship2_2.png b/samples/JetBoy/res/drawable/ship2_2.png
new file mode 100755
index 000000000..b78ef50f8
Binary files /dev/null and b/samples/JetBoy/res/drawable/ship2_2.png differ
diff --git a/samples/JetBoy/res/drawable/ship2_3.png b/samples/JetBoy/res/drawable/ship2_3.png
new file mode 100755
index 000000000..6f1af8de1
Binary files /dev/null and b/samples/JetBoy/res/drawable/ship2_3.png differ
diff --git a/samples/JetBoy/res/drawable/ship2_4.png b/samples/JetBoy/res/drawable/ship2_4.png
new file mode 100755
index 000000000..7d1d2b847
Binary files /dev/null and b/samples/JetBoy/res/drawable/ship2_4.png differ
diff --git a/samples/JetBoy/res/drawable/ship2_hit_1.png b/samples/JetBoy/res/drawable/ship2_hit_1.png
new file mode 100755
index 000000000..227a71ab4
Binary files /dev/null and b/samples/JetBoy/res/drawable/ship2_hit_1.png differ
diff --git a/samples/JetBoy/res/drawable/ship2_hit_2.png b/samples/JetBoy/res/drawable/ship2_hit_2.png
new file mode 100755
index 000000000..61ffbc195
Binary files /dev/null and b/samples/JetBoy/res/drawable/ship2_hit_2.png differ
diff --git a/samples/JetBoy/res/drawable/ship2_hit_3.png b/samples/JetBoy/res/drawable/ship2_hit_3.png
new file mode 100755
index 000000000..68143e7b0
Binary files /dev/null and b/samples/JetBoy/res/drawable/ship2_hit_3.png differ
diff --git a/samples/JetBoy/res/drawable/ship2_hit_4.png b/samples/JetBoy/res/drawable/ship2_hit_4.png
new file mode 100755
index 000000000..669e4011b
Binary files /dev/null and b/samples/JetBoy/res/drawable/ship2_hit_4.png differ
diff --git a/samples/JetBoy/res/drawable/title_bg_hori.png b/samples/JetBoy/res/drawable/title_bg_hori.png
new file mode 100755
index 000000000..662a46f9e
Binary files /dev/null and b/samples/JetBoy/res/drawable/title_bg_hori.png differ
diff --git a/samples/JetBoy/res/drawable/title_hori.png b/samples/JetBoy/res/drawable/title_hori.png
new file mode 100755
index 000000000..f3f05593d
Binary files /dev/null and b/samples/JetBoy/res/drawable/title_hori.png differ
diff --git a/samples/JetBoy/res/layout/main.xml b/samples/JetBoy/res/layout/main.xml
new file mode 100755
index 000000000..ea2277f71
--- /dev/null
+++ b/samples/JetBoy/res/layout/main.xml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/JetBoy/res/raw/level1.jet b/samples/JetBoy/res/raw/level1.jet
new file mode 100755
index 000000000..dd9ee5764
Binary files /dev/null and b/samples/JetBoy/res/raw/level1.jet differ
diff --git a/samples/JetBoy/res/values/strings.xml b/samples/JetBoy/res/values/strings.xml
new file mode 100755
index 000000000..791d23fc1
--- /dev/null
+++ b/samples/JetBoy/res/values/strings.xml
@@ -0,0 +1,13 @@
+
+
+ JetBoy
+ START!
+ RETRY
+ RETURN
+ Help JET BOY get through the asteroid field! Blast the asteroids in time with the beat and listen to the music respond!\n\nUse your FIRE button to explode each asteroid as it passes through the laser guide.
+ You win, You are the master of the Universe!\n\nJETBOY by SONiVOX®\n\nProduced by Jennifer Hruska\nMusic by Eric Barlaan\nGraphics by Randy O’Connor\nProgramming by MageTech
+ Sorry, you lose.
+ 1:12
+
+
+
diff --git a/samples/JetBoy/res/values/styles.xml b/samples/JetBoy/res/values/styles.xml
new file mode 100755
index 000000000..9629fb754
--- /dev/null
+++ b/samples/JetBoy/res/values/styles.xml
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/JetBoy/src/com/example/android/jetboy/Asteroid.java b/samples/JetBoy/src/com/example/android/jetboy/Asteroid.java
new file mode 100755
index 000000000..c0510bc58
--- /dev/null
+++ b/samples/JetBoy/src/com/example/android/jetboy/Asteroid.java
@@ -0,0 +1,28 @@
+/*
+ * 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.
+ */
+
+// FIXME: review and cleanup
+
+package com.example.android.jetboy;
+
+public class Asteroid {
+ public int mAniIndex = 0;
+ public int mDrawY = 0;
+ public int mDrawX = 0;
+ public boolean mExploding = false;
+ public boolean mMissed = false;
+ public long mStartTime = 0;
+}
diff --git a/samples/JetBoy/src/com/example/android/jetboy/Explosion.java b/samples/JetBoy/src/com/example/android/jetboy/Explosion.java
new file mode 100755
index 000000000..f271e12bb
--- /dev/null
+++ b/samples/JetBoy/src/com/example/android/jetboy/Explosion.java
@@ -0,0 +1,25 @@
+/*
+ * 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.
+ */
+
+// FIXME: review and cleanup
+
+package com.example.android.jetboy;
+
+public class Explosion {
+ public int mAniIndex = 0;
+ public int mDrawY = 0;
+ public int mDrawX = 0;
+}
diff --git a/samples/JetBoy/src/com/example/android/jetboy/JetBoy.java b/samples/JetBoy/src/com/example/android/jetboy/JetBoy.java
new file mode 100755
index 000000000..88693afeb
--- /dev/null
+++ b/samples/JetBoy/src/com/example/android/jetboy/JetBoy.java
@@ -0,0 +1,149 @@
+/*
+ * 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.
+ */
+
+// FIXME: review and cleanup
+
+package com.example.android.jetboy;
+
+import com.example.android.jetboy.JetBoyView.JetBoyThread;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.View;
+import android.widget.Button;
+import android.widget.TextView;
+
+public class JetBoy extends Activity implements View.OnClickListener {
+
+ /** A handle to the thread that's actually running the animation. */
+ private JetBoyThread mJetBoyThread;
+
+ /** A handle to the View in which the game is running. */
+ private JetBoyView mJetBoyView;
+
+ // the play start button
+ private Button mButton;
+
+ // used to hit retry
+ private Button mButtonRetry;
+
+ // the window for instructions and such
+ private TextView mTextView;
+
+ // game window timer
+ private TextView mTimerView;
+
+ /**
+ * Required method from parent class
+ *
+ * @param savedInstanceState - The previous instance of this app
+ */
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.main);
+
+ // get handles to the JetView from XML and the JET thread.
+ mJetBoyView = (JetBoyView)findViewById(R.id.JetBoyView);
+ mJetBoyThread = mJetBoyView.getThread();
+
+ // look up the happy shiny button
+ mButton = (Button)findViewById(R.id.Button01);
+ mButton.setOnClickListener(this);
+
+ mButtonRetry = (Button)findViewById(R.id.Button02);
+ mButtonRetry.setOnClickListener(this);
+
+ // set up handles for instruction text and game timer text
+ mTextView = (TextView)findViewById(R.id.text);
+ mTimerView = (TextView)findViewById(R.id.timer);
+
+ mJetBoyView.setTimerView(mTimerView);
+
+ mJetBoyView.SetButtonView(mButtonRetry);
+
+ mJetBoyView.SetTextView(mTextView);
+ }
+
+ /**
+ * Handles component interaction
+ *
+ * @param v The object which has been clicked
+ */
+ public void onClick(View v) {
+ // this is the first screen
+ if (mJetBoyThread.getGameState() == JetBoyThread.STATE_START) {
+ mButton.setText("PLAY!");
+ mTextView.setVisibility(View.VISIBLE);
+
+ mTextView.setText(R.string.helpText);
+ mJetBoyThread.setGameState(JetBoyThread.STATE_PLAY);
+
+ }
+ // we have entered game play, now we about to start running
+ else if (mJetBoyThread.getGameState() == JetBoyThread.STATE_PLAY) {
+ mButton.setVisibility(View.INVISIBLE);
+ mTextView.setVisibility(View.INVISIBLE);
+ mTimerView.setVisibility(View.VISIBLE);
+ mJetBoyThread.setGameState(JetBoyThread.STATE_RUNNING);
+
+ }
+ // this is a retry button
+ else if (mButtonRetry.equals(v)) {
+
+ mTextView.setText(R.string.helpText);
+
+ mButton.setText("PLAY!");
+ mButtonRetry.setVisibility(View.INVISIBLE);
+ // mButtonRestart.setVisibility(View.INVISIBLE);
+
+ mTextView.setVisibility(View.VISIBLE);
+ mButton.setText("PLAY!");
+ mButton.setVisibility(View.VISIBLE);
+
+ mJetBoyThread.setGameState(JetBoyThread.STATE_PLAY);
+
+ } else {
+ Log.d("JB VIEW", "unknown click " + v.getId());
+
+ Log.d("JB VIEW", "state is " + mJetBoyThread.mState);
+
+ }
+ }
+
+ /**
+ * Standard override to get key-press events.
+ */
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent msg) {
+
+ if (keyCode == 4)
+ super.onKeyDown(keyCode, msg);
+
+ return mJetBoyThread.doKeyDown(keyCode, msg);
+ }
+
+ /**
+ * Standard override for key-up. We actually care about these, so we can
+ * turn off the engine or stop rotating.
+ */
+ @Override
+ public boolean onKeyUp(int keyCode, KeyEvent msg) {
+ return mJetBoyThread.doKeyUp(keyCode, msg);
+ }
+}
diff --git a/samples/JetBoy/src/com/example/android/jetboy/JetBoyView.java b/samples/JetBoy/src/com/example/android/jetboy/JetBoyView.java
new file mode 100755
index 000000000..c51f661ea
--- /dev/null
+++ b/samples/JetBoy/src/com/example/android/jetboy/JetBoyView.java
@@ -0,0 +1,1478 @@
+/*
+ * 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.
+ *
+ */
+
+// FIXME: review and cleanup
+
+package com.example.android.jetboy;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.media.JetPlayer;
+import android.media.JetPlayer.OnJetEventListener;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.View;
+import android.widget.Button;
+import android.widget.TextView;
+
+import java.util.Random;
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.Vector;
+import java.util.concurrent.ConcurrentLinkedQueue;
+
+public class JetBoyView extends SurfaceView implements SurfaceHolder.Callback {
+
+ // the number of asteroids that must be destroyed
+ public static final int mSuccessThreshold = 50;
+
+ // used to calculate level for mutes and trigger clip
+ public int mHitStreak = 0;
+
+ // total number asteroids you need to hit.
+ public int mHitTotal = 0;
+
+ // which music bed is currently playing?
+ public int mCurrentBed = 0;
+
+ // a lazy graphic fudge for the initial title splash
+ private Bitmap mTitleBG;
+
+ private Bitmap mTitleBG2;
+
+ /**
+ * Base class for any external event passed to the JetBoyThread. This can
+ * include user input, system events, network input, etc.
+ */
+ class GameEvent {
+ public GameEvent() {
+ eventTime = System.currentTimeMillis();
+ }
+
+ long eventTime;
+ }
+
+ /**
+ * A GameEvent subclass for key based user input. Values are those used by
+ * the standard onKey
+ */
+ class KeyGameEvent extends GameEvent {
+ /**
+ * Simple constructor to make populating this event easier.
+ */
+ public KeyGameEvent(int keyCode, boolean up, KeyEvent msg) {
+ this.keyCode = keyCode;
+ this.msg = msg;
+ this.up = up;
+ }
+
+ public int keyCode;
+ public KeyEvent msg;
+ public boolean up;
+ }
+
+ /**
+ * A GameEvent subclass for events from the JetPlayer.
+ */
+ class JetGameEvent extends GameEvent {
+ /**
+ * Simple constructor to make populating this event easier.
+ */
+ public JetGameEvent(JetPlayer player, short segment, byte track, byte channel,
+ byte controller, byte value) {
+ this.player = player;
+ this.segment = segment;
+ this.track = track;
+ this.channel = channel;
+ this.controller = controller;
+ this.value = value;
+ }
+
+ public JetPlayer player;
+ public short segment;
+ public byte track;
+ public byte channel;
+ public byte controller;
+ public byte value;
+ }
+
+ class JetBoyThread extends Thread implements OnJetEventListener {
+
+ /*
+ * State-tracking constants we don't actually use all of these in
+ * JetBoy, borrowed wholesale from lunar lander.
+ */
+ public static final int STATE_START = -1;
+ public static final int STATE_PLAY = 0;
+ public static final int STATE_LOSE = 1;
+ public static final int STATE_PAUSE = 2;
+ public static final int STATE_READY = 3;
+ public static final int STATE_RUNNING = 4;
+ public static final int STATE_WIN = 5;
+
+ // how many frames per beat? The basic animation can be changed for
+ // instance to 3/4 by changing this to 3.
+ // untested is the impact on other parts of game logic for non 4/4 time.
+ private static final int ANIMATION_FRAMES_PER_BEAT = 4;
+
+ public boolean mInitialized = false;
+
+ /** Queue for GameEvents */
+ protected ConcurrentLinkedQueue mEventQueue = new ConcurrentLinkedQueue();
+
+ /** Context for processKey to maintain state accross frames * */
+ protected Object mKeyContext = null;
+
+ // the timer display in seconds
+ public int mTimerLimit;
+
+ // used for internal timing logic.
+ public final int TIMER_LIMIT = 72;
+
+ // string value for timer display
+ private String mTimerValue = "1:12";
+
+ // start, play, running, lose are the states we use
+ public int mState;
+
+ // has laser been fired and for how long?
+ // user for fx logic on laser fire
+ boolean mLaserOn = false;
+
+ long mLaserFireTime = 0;
+
+ /** The drawable to use as the far background of the animation canvas */
+ private Bitmap mBackgroundImageFar;
+
+ /** The drawable to use as the close background of the animation canvas */
+ private Bitmap mBackgroundImageNear;
+
+ // event ID within JET file. 80,81, 82 are tested to use.
+ // in this game 80 is used for sending asteroid
+ // 82 is used as game time for 1/4 note beat.
+ private final String mSendEvent = "80";
+ private final String mTimerEvent = "82";
+
+ // used to track beat for synch of mute/unmute actions
+ private int mBeatCount = 1;
+
+ // our intrepid space boy
+ private Bitmap[] mShipFlying = new Bitmap[4];
+
+ // the twinkly bit
+ private Bitmap[] mBeam = new Bitmap[4];
+
+ // the things you are trying to hit
+ private Bitmap[] mAsteroids = new Bitmap[12];
+
+ // hit animation
+ private Bitmap[] mExplosions = new Bitmap[4];
+
+ private Bitmap mTimerShell;
+
+ private Bitmap mLaserShot;
+
+ // used to save the beat event system time.
+ // right now we use System.currentMillis
+ // should it use android stuff??
+ private long mLastBeatTime;
+
+ private long mPassedTime;
+
+ // how much do we move the asteroids per beat?
+ private int mPixelMoveX = 25;
+
+ // the asteroid send events are generated from the Jet File.
+ // but which land they start in is random.
+ private Random mRandom = new Random();
+
+ // the star of our show, a reference to the JetPlayer object.
+ private JetPlayer mJet = null;
+
+ private boolean mJetPlaying = false;
+
+ /** Message handler used by thread to interact with TextView */
+ private Handler mHandler;
+
+ /** Handle to the surface manager object we interact with */
+ private SurfaceHolder mSurfaceHolder;
+
+ /** Handle to the application context, used to e.g. fetch Drawables. */
+ private Context mContext;
+
+ /** Indicate whether the surface has been created & is ready to draw */
+ private boolean mRun = false;
+
+ // updates the screen clock. Also used for tempo timing.
+ private Timer mTimer = null;
+
+ private TimerTask mTimerTask = null;
+
+ // one second - used to update timer
+ private int mTaskIntervalInMillis = 1000;
+
+ /**
+ * Current height of the surface/canvas.
+ *
+ * @see #setSurfaceSize
+ */
+ private int mCanvasHeight = 1;
+
+ /**
+ * Current width of the surface/canvas.
+ *
+ * @see #setSurfaceSize
+ */
+ private int mCanvasWidth = 1;
+
+ // used to track the picture to draw for ship animation
+ private int mShipIndex = 0;
+
+ // stores all of the asteroid objects in order
+ private Vector mDangerWillRobinson;
+
+ private Vector mExplosion;
+
+ // right to left scroll tracker for near and far BG
+ private int mBGFarMoveX = 0;
+ private int mBGNearMoveX = 0;
+
+ // this is has "high" (close to top) that jet boy can fly
+ private int mJetBoyYMin = 40;
+ private int mJetBoyX = 0;
+ private int mJetBoyY = 0;
+
+ // this is the pixel position of the laser beam guide.
+ private int mAsteroidMoveLimitX = 110;
+
+ // how far up asteroid can be painted
+ private int mAsteroidMinY = 40;
+
+ // Jet does not pick up volume and pan changes?
+ private boolean mMuteArrayHack = false;
+
+ Resources mRes;
+
+ // eight music beds, added a 9th to fix some kind of bug in JET around
+ // pans and volume controls
+ // ask Jenn about this
+ private boolean muteMask[][] = new boolean[9][32];
+
+ /**
+ * This is the constructor for the main worker bee
+ *
+ * @param surfaceHolder
+ * @param context
+ * @param handler
+ */
+ public JetBoyThread(SurfaceHolder surfaceHolder, Context context, Handler handler) {
+
+ // get handles to some important objects
+ mSurfaceHolder = surfaceHolder;
+ mHandler = handler;
+ mContext = context;
+ mRes = context.getResources();
+
+ // this are the mute arrays associated with the music beds in the
+ // JET file
+
+ for (int ii = 0; ii < 8; ii++) {
+ for (int xx = 0; xx < 32; xx++) {
+ muteMask[ii][xx] = true;
+ }
+ }
+
+ muteMask[0][2] = false;
+ muteMask[0][3] = false;
+ muteMask[0][4] = false;
+ muteMask[0][5] = false;
+
+ muteMask[1][2] = false;
+ muteMask[1][3] = false;
+ muteMask[1][4] = false;
+ muteMask[1][5] = false;
+ muteMask[1][8] = false;
+ muteMask[1][9] = false;
+
+ muteMask[2][2] = false;
+ muteMask[2][3] = false;
+ muteMask[2][6] = false;
+ muteMask[2][7] = false;
+ muteMask[2][8] = false;
+ muteMask[2][9] = false;
+
+ muteMask[3][2] = false;
+ muteMask[3][3] = false;
+ muteMask[3][6] = false;
+ muteMask[3][11] = false;
+ muteMask[3][12] = false;
+
+ muteMask[4][2] = false;
+ muteMask[4][3] = false;
+ muteMask[4][10] = false;
+ muteMask[4][11] = false;
+ muteMask[4][12] = false;
+ muteMask[4][13] = false;
+
+ muteMask[5][2] = false;
+ muteMask[5][3] = false;
+ muteMask[5][10] = false;
+ muteMask[5][12] = false;
+ muteMask[5][15] = false;
+ muteMask[5][17] = false;
+
+ muteMask[6][2] = false;
+ muteMask[6][3] = false;
+ muteMask[6][14] = false;
+ muteMask[6][15] = false;
+ muteMask[6][16] = false;
+ muteMask[6][17] = false;
+
+ muteMask[7][2] = false;
+ muteMask[7][3] = false;
+ muteMask[7][6] = false;
+ muteMask[7][14] = false;
+ muteMask[7][15] = false;
+ muteMask[7][16] = false;
+ muteMask[7][17] = false;
+ muteMask[7][18] = false;
+
+ // set all tracks to play, do it for one beat and then switch to
+ // mute array zero
+ // hack for jet bug on pan and mutes
+ for (int xx = 0; xx < 32; xx++) {
+ muteMask[8][xx] = false;
+ }
+
+ // always set state to start, ensure we come in from front door if
+ // app gets tucked into background
+ mState = STATE_START;
+
+ setInitialGameState();
+
+ mTitleBG = BitmapFactory.decodeResource(mRes, R.drawable.title_hori);
+
+ // load background image as a Bitmap instead of a Drawable b/c
+ // we don't need to transform it and it's faster to draw this
+ // way...thanks lunar lander :)
+
+ // two background since we want them moving at different speeds
+ mBackgroundImageFar = BitmapFactory.decodeResource(mRes, R.drawable.background_a);
+
+ mLaserShot = BitmapFactory.decodeResource(mRes, R.drawable.laser);
+
+ mBackgroundImageNear = BitmapFactory.decodeResource(mRes, R.drawable.background_b);
+
+ mShipFlying[0] = BitmapFactory.decodeResource(mRes, R.drawable.ship2_1);
+ mShipFlying[1] = BitmapFactory.decodeResource(mRes, R.drawable.ship2_2);
+ mShipFlying[2] = BitmapFactory.decodeResource(mRes, R.drawable.ship2_3);
+ mShipFlying[3] = BitmapFactory.decodeResource(mRes, R.drawable.ship2_4);
+
+ mBeam[0] = BitmapFactory.decodeResource(mRes, R.drawable.intbeam_1);
+ mBeam[1] = BitmapFactory.decodeResource(mRes, R.drawable.intbeam_2);
+ mBeam[2] = BitmapFactory.decodeResource(mRes, R.drawable.intbeam_3);
+ mBeam[3] = BitmapFactory.decodeResource(mRes, R.drawable.intbeam_4);
+
+ mTimerShell = BitmapFactory.decodeResource(mRes, R.drawable.int_timer);
+
+ // I wanted them to rotate in a certain way
+ // so I loaded them backwards from the way created.
+ mAsteroids[11] = BitmapFactory.decodeResource(mRes, R.drawable.asteroid01);
+ mAsteroids[10] = BitmapFactory.decodeResource(mRes, R.drawable.asteroid02);
+ mAsteroids[9] = BitmapFactory.decodeResource(mRes, R.drawable.asteroid03);
+ mAsteroids[8] = BitmapFactory.decodeResource(mRes, R.drawable.asteroid04);
+ mAsteroids[7] = BitmapFactory.decodeResource(mRes, R.drawable.asteroid05);
+ mAsteroids[6] = BitmapFactory.decodeResource(mRes, R.drawable.asteroid06);
+ mAsteroids[5] = BitmapFactory.decodeResource(mRes, R.drawable.asteroid07);
+ mAsteroids[4] = BitmapFactory.decodeResource(mRes, R.drawable.asteroid08);
+ mAsteroids[3] = BitmapFactory.decodeResource(mRes, R.drawable.asteroid09);
+ mAsteroids[2] = BitmapFactory.decodeResource(mRes, R.drawable.asteroid10);
+ mAsteroids[1] = BitmapFactory.decodeResource(mRes, R.drawable.asteroid11);
+ mAsteroids[0] = BitmapFactory.decodeResource(mRes, R.drawable.asteroid12);
+
+ mExplosions[0] = BitmapFactory.decodeResource(mRes, R.drawable.asteroid_explode1);
+ mExplosions[1] = BitmapFactory.decodeResource(mRes, R.drawable.asteroid_explode2);
+ mExplosions[2] = BitmapFactory.decodeResource(mRes, R.drawable.asteroid_explode3);
+ mExplosions[3] = BitmapFactory.decodeResource(mRes, R.drawable.asteroid_explode4);
+
+ }
+
+ /**
+ * Does the grunt work of setting up initial jet requirements
+ */
+ private void initializeJetPlayer() {
+
+ // if (mJet!=null)
+ // {
+ // mJet.release();
+ // mJet = null;
+ // }
+
+ mJet = JetPlayer.getJetPlayer();
+
+ mJetPlaying = false;
+
+ // make sure we flush the queue
+ // otherwise left over events from previous gameplay
+ // can hang around.
+ mJet.clearQueue();
+
+ // mJet.setStatusUpdateListener(this);
+ mJet.setEventListener(this);
+
+ Log.d(TAG, "opening jet file");
+
+ // mJet.loadJetFile(m_PathToJetFile);
+
+ mJet.loadJetFile(mContext.getResources().openRawResourceFd(R.raw.level1));
+
+ Log.d(TAG, "opening jet file DONE");
+
+ mCurrentBed = 0;
+ byte sSegmentID = 0;
+
+ Log.d(TAG, " start queuing jet file");
+
+ // this is the main game play music
+ // it is located at segment 0
+ // it uses lib #0 this allows DLS to play
+ // -1 would mean no DLS
+ // higher numbers untested
+ mJet.queueJetSegment(0, 0, 0, 0, 0, sSegmentID);
+
+ // end game music, loop 4 times normal pitch
+ mJet.queueJetSegment(1, 0, 4, 0, 0, sSegmentID);
+
+ // end game music loop 4 times up an octave
+ mJet.queueJetSegment(1, 0, 4, 1, 0, sSegmentID);
+
+ mJet.setMuteArray(muteMask[8], true);
+
+ Log.d(TAG, " start queuing jet file DONE");
+
+ }
+
+ private void doDraw(Canvas canvas) {
+
+ if (mState == STATE_RUNNING) {
+ doDrawRunning(canvas);
+ } else if (mState == STATE_START) {
+ doDrawReady(canvas);
+ } else if (mState == STATE_PLAY || mState == STATE_LOSE) {
+
+ if (mTitleBG2 == null) {
+
+ mTitleBG2 = BitmapFactory.decodeResource(mRes, R.drawable.title_bg_hori);
+
+ }
+
+ doDrawPlay(canvas);
+
+ }// end state play block
+ }
+
+ /**
+ * Draws current state of the game Canvas.
+ */
+ private void doDrawRunning(Canvas canvas) {
+
+ // decrement the far background
+ mBGFarMoveX = mBGFarMoveX - 1;
+
+ // decrement the near background
+ mBGNearMoveX = mBGNearMoveX - 4;
+
+ // calculate the wrap factor for matching image draw
+ int newFarX = mBackgroundImageFar.getWidth() - (-mBGFarMoveX);
+
+ // if we have scrolled all the way, reset to start
+ if (newFarX <= 0) {
+ mBGFarMoveX = 0;
+ // only need one draw
+ canvas.drawBitmap(mBackgroundImageFar, mBGFarMoveX, 0, null);
+
+ } else {
+ // need to draw original and wrap
+ canvas.drawBitmap(mBackgroundImageFar, mBGFarMoveX, 0, null);
+ canvas.drawBitmap(mBackgroundImageFar, newFarX, 0, null);
+ }
+
+ // same story different image...
+ // TODO possible method call
+ int newNearX = mBackgroundImageNear.getWidth() - (-mBGNearMoveX);
+
+ if (newNearX <= 0) {
+ mBGNearMoveX = 0;
+ canvas.drawBitmap(mBackgroundImageNear, mBGNearMoveX, 0, null);
+
+ } else {
+ canvas.drawBitmap(mBackgroundImageNear, mBGNearMoveX, 0, null);
+ canvas.drawBitmap(mBackgroundImageNear, newNearX, 0, null);
+ }
+
+ doAsteroidAnimation(canvas);
+
+ canvas.drawBitmap(mBeam[mShipIndex], 51 + 20, 0, null);
+
+ mShipIndex++;
+
+ if (mShipIndex == 4)
+ mShipIndex = 0;
+
+ // draw the space ship.
+ // This will have code to match asteroid lane
+ canvas.drawBitmap(mShipFlying[mShipIndex], mJetBoyX, mJetBoyY, null);
+
+ if (mLaserOn) {
+ canvas.drawBitmap(mLaserShot, mJetBoyX + mShipFlying[0].getWidth(), mJetBoyY
+ + (mShipFlying[0].getHeight() / 2), null);
+ }
+
+ // tick tock
+ canvas.drawBitmap(mTimerShell, mCanvasWidth - mTimerShell.getWidth(), 0, null);
+
+ }
+
+ private void setInitialGameState() {
+ mTimerLimit = TIMER_LIMIT;
+
+ mJetBoyY = mJetBoyYMin;
+
+ mMuteArrayHack = false;
+
+ // set up jet stuff
+ initializeJetPlayer();
+
+ mTimer = new Timer();
+
+ mDangerWillRobinson = new Vector();
+
+ mExplosion = new Vector();
+
+ mInitialized = true;
+
+ mHitStreak = 0;
+ mHitTotal = 0;
+ // mTimerTotal="1:20";
+ }
+
+ private void doAsteroidAnimation(Canvas canvas) {
+ if ((mDangerWillRobinson == null | mDangerWillRobinson.size() == 0)
+ && (mExplosion != null && mExplosion.size() == 0))
+ return;
+
+ // Compute what percentage through a beat we are and adjust
+ // animation and postion
+ // based on that. This assumes 140bpm(428ms/beat), we really should
+ // compute this
+ // based on properties of the music file under ideal circumstances.
+ // This is just
+ // interbeat interpolation, no game state is updated
+ long frameDelta = System.currentTimeMillis() - mLastBeatTime;
+ // mPixelMoveX per beat
+ // This hid the feeling of the asteroids moving to the beat and
+ // caused some issues with
+ // explosions not aligned with asteroids last position, so fix that
+ // if we use this again.
+ // int asteroidDrawOffset = (int)(mPixelMoveX * frameDelta/428L);
+ // animation frames per beat
+ int animOffset = (int)(ANIMATION_FRAMES_PER_BEAT * frameDelta / 428);
+
+ for (int i = (mDangerWillRobinson.size() - 1); i >= 0; i--) {
+ Asteroid asteroid = mDangerWillRobinson.elementAt(i);
+
+ if (!asteroid.mMissed)
+ mJetBoyY = asteroid.mDrawY;
+
+ // Log.d(TAG, " drawing asteroid " + ii + " at " +
+ // asteroid.mDrawX );
+
+ canvas.drawBitmap(
+ mAsteroids[(asteroid.mAniIndex + animOffset) % mAsteroids.length],
+ asteroid.mDrawX, asteroid.mDrawY, null);
+ }
+
+ for (int i = (mExplosion.size() - 1); i >= 0; i--) {
+ Explosion ex = mExplosion.elementAt(i);
+
+ canvas.drawBitmap(mExplosions[(ex.mAniIndex + animOffset) % mExplosions.length],
+ ex.mDrawX, ex.mDrawY, null);
+ }
+ }
+
+ private void doDrawReady(Canvas canvas) {
+ canvas.drawBitmap(mTitleBG, 0, 0, null);
+ }
+
+ private void doDrawPlay(Canvas canvas) {
+ canvas.drawBitmap(mTitleBG2, 0, 0, null);
+ }
+
+ /**
+ * the heart of the worker bee
+ */
+
+ public void run() {
+ // while running do stuff in this loop...bzzz!
+ while (mRun) {
+ Canvas c = null;
+
+ if (mState == STATE_RUNNING) {
+ // Process any input and apply it to the game state
+ updateGameState();
+
+ if (!mJetPlaying) {
+
+ mInitialized = false;
+ Log.d(TAG, "------> STARTING JET PLAY");
+ mJet.play();
+
+ mJetPlaying = true;
+
+ }
+
+ mPassedTime = System.currentTimeMillis();
+
+ // kick off the timer task for counter update if not already
+ // initialized
+ if (mTimerTask == null) {
+ mTimerTask = new TimerTask() {
+ public void run() {
+ doCountDown();
+ }
+ };
+
+ mTimer.schedule(mTimerTask, mTaskIntervalInMillis);
+
+ }// end of TimerTask init block
+
+ }// end of STATE_RUNNING block
+ else if (mState == STATE_PLAY && !mInitialized)
+
+ {
+ setInitialGameState();
+ } else if (mState == STATE_LOSE) {
+
+ mInitialized = false;
+
+ }
+
+ try {
+ c = mSurfaceHolder.lockCanvas(null);
+ // synchronized (mSurfaceHolder) {
+ doDraw(c);
+ // }
+ } finally {
+ // do this in a finally so that if an exception is thrown
+ // during the above, we don't leave the Surface in an
+ // inconsistent state
+ if (c != null) {
+ mSurfaceHolder.unlockCanvasAndPost(c);
+ }
+ }// end finally block
+ }// end while mrun block
+ }
+
+ /**
+ * This method handles updating the model of the game state. No
+ * rendering is done here only processing of inputs and update of state.
+ * This includes positons of all game objects (asteroids, player,
+ * explosions), their state (animation frame, hit), creation of new
+ * objects, etc.
+ */
+ protected void updateGameState() {
+ // Process any game events and apply them
+ while (true) {
+ GameEvent event = mEventQueue.poll();
+ if (event == null)
+ break;
+
+ // Log.d(TAG,"*** EVENT = " + event);
+
+ // Process keys tracking the input context to pass in to later
+ // calls
+ if (event instanceof KeyGameEvent) {
+ // Process the key for affects other then asteroid hits
+ mKeyContext = processKeyEvent((KeyGameEvent)event, mKeyContext);
+
+ // Update laser state. Having this here allows the laser to
+ // be trigered right when the key is
+ // pressed. If we comment this out the laser will only be
+ // turned on when updateLaser is called
+ // when processing a timer event below.
+ updateLaser(mKeyContext);
+
+ }
+ // JET events trigger a state update
+ else if (event instanceof JetGameEvent) {
+ JetGameEvent jetEvent = (JetGameEvent)event;
+
+ // Only update state on a timer event
+ if (jetEvent.value == 82) {
+ // Note the time of the last beat
+ mLastBeatTime = System.currentTimeMillis();
+
+ // Update laser state, turning it on if a key has been
+ // pressed or off if it has been
+ // on for too long.
+ updateLaser(mKeyContext);
+
+ // Update explosions before we updated asteroids because
+ // updateAsteroids may add
+ // new explosions that we do not want updated until next
+ // frame
+ updateExplosions(mKeyContext);
+
+ // Update asteroid positions, hit status and animations
+ updateAsteroids(mKeyContext);
+ }
+
+ processJetEvent(jetEvent.player, jetEvent.segment, jetEvent.track,
+ jetEvent.channel, jetEvent.controller, jetEvent.value);
+ }
+ }
+ }
+
+ /**
+ * This method handles the state updates that can be caused by key press
+ * events. Key events may mean different things depending on what has
+ * come before, to support this concept this method takes an opaque
+ * context object as a parameter and returns an updated version. This
+ * context should be set to null for the first event then should be set
+ * to the last value returned for subsiquent events.
+ */
+ protected Object processKeyEvent(KeyGameEvent event, Object context) {
+ // Log.d(TAG, "key code is " + event.keyCode + " " + (event.up ?
+ // "up":"down"));
+
+ // If it is a key up on the fire key make sure we mute the
+ // associated sound
+ if (event.up) {
+ if (event.keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
+ return null;
+ }
+ }
+ // If it is a key down on the fire key start playing the sound and
+ // update the context
+ // to indicate that a key has been pressed and to ignore further
+ // presses
+ else {
+ if (event.keyCode == KeyEvent.KEYCODE_DPAD_CENTER && (context == null)) {
+ return event;
+ }
+ }
+
+ // Return the context unchanged
+ return context;
+ }
+
+ /**
+ * This method updates the laser status based on user input and shot
+ * duration
+ */
+ protected void updateLaser(Object inputContext) {
+ // Lookup the time of the fire event if there is one
+ long keyTime = inputContext == null ? 0 : ((GameEvent)inputContext).eventTime;
+
+ // Log.d(TAG,"keyTime delta = " +
+ // (System.currentTimeMillis()-keyTime) + ": obj = " +
+ // inputContext);
+
+ // If the laser has been on too long shut it down
+ if (mLaserOn && System.currentTimeMillis() - mLaserFireTime > 400) {
+
+ mLaserOn = false;
+
+ }
+
+ // trying to tune the laser hit timing
+ else if (System.currentTimeMillis() - mLaserFireTime > 300) {
+ // if (mJet!=null)
+ // {
+ mJet.setMuteFlag(23, true, false);
+ // }
+ }
+
+ // Now check to see if we should turn the laser on. We do this after
+ // the above shutdown
+ // logic so it can be turned back on in the same frame it was turned
+ // off in. If we want
+ // to add a cooldown period this may change.
+ if (!mLaserOn && System.currentTimeMillis() - keyTime <= 400) {
+
+ mLaserOn = true;
+ mLaserFireTime = keyTime;
+
+ // Log.d(TAG, "*** LASER ON ***");
+ mJet.setMuteFlag(23, false, false);
+ }
+ }
+
+ /**
+ * Update asteroid state including position and laser hit status.
+ */
+ protected void updateAsteroids(Object inputContext) {
+ if (mDangerWillRobinson == null | mDangerWillRobinson.size() == 0)
+ return;
+
+ for (int i = (mDangerWillRobinson.size() - 1); i >= 0; i--) {
+ Asteroid asteroid = mDangerWillRobinson.elementAt(i);
+
+ // If the asteroid is within laser ranged but not already missed
+ // check if the
+ // key was pressed close enough to the beat to make a hit
+
+ // there isnt any real logic here. just played with it until the
+ // game "felt right"
+ if (asteroid.mDrawX <= mAsteroidMoveLimitX + 20 && !asteroid.mMissed)
+
+ {
+ // If the laser was fired on the beat destroy the asteroid
+ if (mLaserOn) {
+ // Track hit streak for adjusting music
+ mHitStreak++;
+ mHitTotal++;
+
+ // replace the asteroid with an explosion
+ Explosion ex = new Explosion();
+ ex.mAniIndex = 0;
+ ex.mDrawX = asteroid.mDrawX;
+ ex.mDrawY = asteroid.mDrawY;
+ mExplosion.add(ex);
+
+ mJet.setMuteFlag(24, false, false);
+
+ mDangerWillRobinson.removeElementAt(i);
+
+ // This asteroid has been removed process the next one
+ continue;
+ } else {
+ // Sorry, timing was not good enough, marke the asteroid
+ // as missed so
+ // on next frame it cannot be hit even if it is still
+ // within range
+ asteroid.mMissed = true;
+
+ mHitStreak = mHitStreak - 1;
+
+ if (mHitStreak < 0)
+ mHitStreak = 0;
+
+ }
+ }
+
+ // Update the asteroids position, even missed ones keep moving
+ asteroid.mDrawX -= mPixelMoveX;
+
+ // Update asteroid animation frame
+ asteroid.mAniIndex = (asteroid.mAniIndex + ANIMATION_FRAMES_PER_BEAT)
+ % mAsteroids.length;
+
+ // if we have scrolled off the screen
+ if (asteroid.mDrawX < 0) {
+ mDangerWillRobinson.removeElementAt(i);
+ }
+ }
+ }
+
+ /**
+ * This method updates explosion animation and removes them once they
+ * have completed.
+ */
+ protected void updateExplosions(Object inputContext) {
+ if (mExplosion == null | mExplosion.size() == 0)
+ return;
+
+ for (int i = mExplosion.size() - 1; i >= 0; i--) {
+ Explosion ex = mExplosion.elementAt(i);
+
+ ex.mAniIndex += ANIMATION_FRAMES_PER_BEAT;
+
+ // When the animation completes remove the explosion
+ if (ex.mAniIndex > 3) {
+ mJet.setMuteFlag(24, true, false);
+ mJet.setMuteFlag(23, true, false);
+
+ mExplosion.removeElementAt(i);
+ }
+ }
+ }
+
+ /**
+ * This method handles the state updates that can be caused by JET
+ * events.
+ */
+ protected void processJetEvent(JetPlayer player, short segment, byte track, byte channel,
+ byte controller, byte value) {
+
+ Log.d(TAG, "onJetEvent(): seg=" + segment + " track=" + track + " chan=" + channel
+ + " cntrlr=" + controller + " val=" + value);
+
+ String eventID = "" + value;
+
+ // Check for an event that triggers a new asteroid
+ if (eventID.equalsIgnoreCase(mSendEvent)) {
+ // Log.d(TAG, "~~~~ setting create to true");
+
+ doAsteroidCreation();
+ }
+
+ mBeatCount++;
+
+ if (!mMuteArrayHack) {
+ mMuteArrayHack = true;
+
+ mJet.setMuteArray(muteMask[0], false);
+
+ }
+
+ if (mBeatCount > 4) {
+ mBeatCount = 1;
+
+ }
+
+ // Scale the music based on progress
+
+ // it was a game requirement to change the mute array on 1st beat of
+ // the next measure when needed
+ // and so we track beat count, after than we track hitStreak to
+ // determine music level
+ // if the level has go gone up, call trigger clip otherwise just
+ // execute rest of change music bed logic.
+ // could probably be a method call here.
+ if (mBeatCount == 1) {
+
+ // do it back wards so you fall into the correct one
+ if (mHitStreak > 28) {
+
+ // did the bed change?
+ if (mCurrentBed != 7) {
+ // did it go up?
+ if (mCurrentBed < 7) {
+ mJet.triggerClip(7);
+ }
+
+ mCurrentBed = 7;
+ mJet.setMuteArray(muteMask[7], false);
+
+ }
+ } else if (mHitStreak > 24) {
+ if (mCurrentBed != 6) {
+ if (mCurrentBed < 6) {
+ mJet.triggerClip(6);
+ }
+
+ mCurrentBed = 6;
+ mJet.setMuteArray(muteMask[6], false);
+ // mJet.triggerClip(6);
+ }
+ } else if (mHitStreak > 20) {
+ if (mCurrentBed != 5) {
+ if (mCurrentBed < 5) {
+ mJet.triggerClip(5);
+ }
+
+ mCurrentBed = 5;
+ mJet.setMuteArray(muteMask[5], false);
+ // mJet.triggerClip(5);
+ }
+ } else if (mHitStreak > 16) {
+ if (mCurrentBed != 4) {
+
+ if (mCurrentBed < 4) {
+ mJet.triggerClip(4);
+ }
+ mCurrentBed = 4;
+ mJet.setMuteArray(muteMask[4], false);
+ // mJet.triggerClip(4);
+ }
+ } else if (mHitStreak > 12) {
+ if (mCurrentBed != 3) {
+ if (mCurrentBed < 3) {
+ mJet.triggerClip(3);
+ }
+ mCurrentBed = 3;
+ mJet.setMuteArray(muteMask[3], false);
+ // mJet.triggerClip(3);
+ }
+ } else if (mHitStreak > 8) {
+ if (mCurrentBed != 2) {
+ if (mCurrentBed < 2) {
+ mJet.triggerClip(2);
+ }
+
+ mCurrentBed = 2;
+ mJet.setMuteArray(muteMask[2], false);
+ // mJet.triggerClip(2);
+ }
+ } else if (mHitStreak > 4) {
+ if (mCurrentBed != 1) {
+
+ if (mCurrentBed < 1) {
+ mJet.triggerClip(1);
+ }
+
+ mJet.setMuteArray(muteMask[1], false);
+ // mJet.triggerClip(1);
+
+ mCurrentBed = 1;
+ }
+ }
+ }
+
+ /*
+ * try { Log.d(TAG,"onJetEvent: segment =" + segment);
+ * Log.d(TAG,"onJetEvent(): track =" + track);
+ * Log.d(TAG,"onJetEvent(): channel =" + channel);
+ * Log.d(TAG,"onJetEvent(): controller =" + controller);
+ * Log.d(TAG,"onJetEvent(): value =" + value); } catch(Exception e1) {
+ * Log.e(TAG,"on Jet Event caught " + e1.toString()); }
+ */
+ }
+
+ private void doAsteroidCreation() {
+ // Log.d(TAG, "asteroid created");
+
+ Asteroid _as = new Asteroid();
+
+ int drawIndex = mRandom.nextInt(4);
+
+ // TODO Remove hard coded value
+ _as.mDrawY = mAsteroidMinY + (drawIndex * 63);
+
+ _as.mDrawX = (mCanvasWidth - mAsteroids[0].getWidth());
+
+ _as.mStartTime = System.currentTimeMillis();
+
+ mDangerWillRobinson.add(_as);
+ }
+
+ /**
+ * Used to signal the thread whether it should be running or not.
+ * Passing true allows the thread to run; passing false will shut it
+ * down if it's already running. Calling start() after this was most
+ * recently called with false will result in an immediate shutdown.
+ *
+ * @param b true to run, false to shut down
+ */
+ public void setRunning(boolean b) {
+ mRun = b;
+
+ if (mRun == false) {
+ if (mTimerTask != null)
+ mTimerTask.cancel();
+ }
+ }
+
+ /**
+ * returns the current int value of game state as defined by state
+ * tracking constants
+ *
+ * @return
+ */
+ public int getGameState() {
+ synchronized (mSurfaceHolder) {
+ return mState;
+ }
+ }
+
+ /**
+ * Sets the game mode. That is, whether we are running, paused, in the
+ * failure state, in the victory state, etc.
+ *
+ * @see #setState(int, CharSequence)
+ * @param mode one of the STATE_* constants
+ */
+ public void setGameState(int mode) {
+ synchronized (mSurfaceHolder) {
+ setGameState(mode, null);
+ }
+ }
+
+ /**
+ * Sets state based on input, optionally also passing in a text message.
+ *
+ * @param state
+ * @param message
+ */
+ // TODO - Modeled from lunar lander Determine if best way to do this.
+ public void setGameState(int state, CharSequence message) {
+
+ synchronized (mSurfaceHolder) {
+
+ // change state if needed
+ if (mState != state) {
+ mState = state;
+ }
+
+ if (mState == STATE_PLAY) {
+ Resources res = mContext.getResources();
+ mBackgroundImageFar = BitmapFactory
+ .decodeResource(res, R.drawable.background_a);
+
+ // don't forget to resize the background image
+ mBackgroundImageFar = Bitmap.createScaledBitmap(mBackgroundImageFar,
+ mCanvasWidth * 2, mCanvasHeight, true);
+
+ mBackgroundImageNear = BitmapFactory.decodeResource(res,
+ R.drawable.background_b);
+
+ // don't forget to resize the background image
+ mBackgroundImageNear = Bitmap.createScaledBitmap(mBackgroundImageNear,
+ mCanvasWidth * 2, mCanvasHeight, true);
+
+ } else if (mState == STATE_RUNNING) {
+ // When we enter the running state we should clear any old
+ // events in the queue
+ mEventQueue.clear();
+
+ // And reset the ket state so we dont think a button is pressed that isn't
+ mKeyContext = null;
+ }
+
+ }
+ }
+
+ /**
+ * Add key press input to the GameEvent queue
+ */
+
+ public boolean doKeyDown(int keyCode, KeyEvent msg) {
+ mEventQueue.add(new KeyGameEvent(keyCode, false, msg));
+
+ return true;
+ }
+
+ /**
+ * Add key press input to the GameEvent queue
+ */
+ public boolean doKeyUp(int keyCode, KeyEvent msg) {
+ mEventQueue.add(new KeyGameEvent(keyCode, true, msg));
+
+ return true;
+ }
+
+ /* Callback invoked when the surface dimensions change. */
+ public void setSurfaceSize(int width, int height) {
+ // synchronized to make sure these all change atomically
+ synchronized (mSurfaceHolder) {
+ mCanvasWidth = width;
+ mCanvasHeight = height;
+
+ // don't forget to resize the background image
+ mBackgroundImageFar = Bitmap.createScaledBitmap(mBackgroundImageFar, width * 2,
+ height, true);
+
+ // don't forget to resize the background image
+ mBackgroundImageNear = Bitmap.createScaledBitmap(mBackgroundImageNear, width * 2,
+ height, true);
+ }
+ }
+
+ /**
+ * Pauses the physics update & animation.
+ */
+ //TODO should probably add a pause to the menu button
+ public void pause() {
+ synchronized (mSurfaceHolder) {
+ if (mState == STATE_RUNNING)
+ setGameState(STATE_PAUSE);
+ if (mTimerTask != null) {
+ mTimerTask.cancel();
+ }
+
+ if (mJet != null) {
+ mJet.pause();
+ mJet.release();
+ }
+ }
+ }
+
+ /**
+ * Does the work of updating timer
+ *
+ */
+ private void doCountDown() {
+ //Log.d(TAG,"Time left is " + mTimerLimit);
+
+ mTimerLimit = mTimerLimit - 1;
+ try {
+ //subtract one minute and see what the result is.
+ int moreThanMinute = mTimerLimit - 60;
+
+ if (moreThanMinute >= 0) {
+
+ if (moreThanMinute > 9) {
+ mTimerValue = "1:" + moreThanMinute;
+
+ }
+ //need an extra '0' for formatting
+ else {
+ mTimerValue = "1:0" + moreThanMinute;
+ }
+ } else {
+ if (mTimerLimit > 9) {
+ mTimerValue = "0:" + mTimerLimit;
+ } else {
+ mTimerValue = "0:0" + mTimerLimit;
+ }
+ }
+ } catch (Exception e1) {
+ Log.e(TAG, "doCountDown threw " + e1.toString());
+ }
+
+ Message msg = mHandler.obtainMessage();
+
+ Bundle b = new Bundle();
+ b.putString("text", mTimerValue);
+
+ //time's up
+ if (mTimerLimit == 0) {
+ b.putString("STATE_LOSE", "" + STATE_LOSE);
+ mTimerTask = null;
+
+ mState = STATE_LOSE;
+
+ } else {
+
+ mTimerTask = new TimerTask() {
+ public void run() {
+ doCountDown();
+ }
+ };
+
+ mTimer.schedule(mTimerTask, mTaskIntervalInMillis);
+ }
+
+ //this is how we send data back up to the main JetBoyView thread.
+ //if you look in constructor of JetBoyView you will see code for
+ //Handling of messages. This is borrowed directly from lunar lander.
+ //Thanks again!
+ msg.setData(b);
+ mHandler.sendMessage(msg);
+
+ }
+
+ /**
+ * required JetPlayer method. Informs listener of when a queue segement
+ * event has been generated.
+ *
+ * @param nbSegments
+ */
+ public void onJetNumQueuedSegmentUpdate(int nbSegments) {
+ Log.i(TAG, "onJetNumQueuedSegmentUpdate(): nbSegments =" + nbSegments);
+ }
+
+ /**
+ * required JetPlayer method. A more specific handler.
+ *
+ * @param player
+ * @param nbSegments
+ */
+ public void onJetNumQueuedSegmentUpdate(JetPlayer player, int nbSegments) {
+ Log.i(TAG, "onJetNumQueuedUpdate(): nbSegs =" + nbSegments);
+
+ }
+
+ /**
+ * The method which receives notification from event listener.
+ * This is where we queue up events 80 and 82.
+ *
+ * Most of this data passed is unneeded for JetBoy logic but shown
+ * for code sample completeness.
+ *
+ * @param player
+ * @param segment
+ * @param track
+ * @param channel
+ * @param controller
+ * @param value
+ */
+ public void onJetEvent(JetPlayer player, short segment, byte track, byte channel,
+ byte controller, byte value) {
+
+ Log.d(TAG, "jet got event " + value);
+
+ //events fire outside the animation thread. This can cause timing issues.
+ //put in queue for processing by animation thread.
+ mEventQueue.add(new JetGameEvent(player, segment, track, channel, controller, value));
+ }
+
+ public void onJetPauseUpdate(JetPlayer player, int paused) {
+ Log.i(TAG, "onJetPauseUpdate(): paused =" + paused);
+
+ }
+
+ public void onJetUserIdUpdate(JetPlayer player, int userId, int repeatCount) {
+ Log.i(TAG, "onJetUserIdUpdate(): userId =" + userId + " repeatCount=" + repeatCount);
+
+ }
+
+ }//end thread class
+
+ public static final String TAG = "JetBoy";
+
+ /** The thread that actually draws the animation */
+ private JetBoyThread thread;
+
+ private TextView mTimerView;
+
+ private Button mButtonRetry;
+
+ // private Button mButtonRestart;
+ private TextView mTextView;
+
+ /**
+ * The constructor called from the main JetBoy activity
+ *
+ * @param context
+ * @param attrs
+ */
+ public JetBoyView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+
+ // register our interest in hearing about changes to our surface
+ SurfaceHolder holder = getHolder();
+ holder.addCallback(this);
+
+ // create thread only; it's started in surfaceCreated()
+ // except if used in the layout editor.
+ if (isInEditMode() == false) {
+ thread = new JetBoyThread(holder, context, new Handler() {
+
+ public void handleMessage(Message m) {
+
+ mTimerView.setText(m.getData().getString("text"));
+
+ //ok so maybe it isn't really a "lose"
+ //this bit was borrowed from lunar lander and then evolved.
+ //too close to deadline to mess with now.
+ if (m.getData().getString("STATE_LOSE") != null) {
+ //mButtonRestart.setVisibility(View.VISIBLE);
+ mButtonRetry.setVisibility(View.VISIBLE);
+
+ mTimerView.setVisibility(View.INVISIBLE);
+
+ mTextView.setVisibility(View.VISIBLE);
+
+ Log.d(TAG, "the total was " + mHitTotal);
+
+ if (mHitTotal >= mSuccessThreshold) {
+ mTextView.setText(R.string.winText);
+ } else {
+ mTextView.setText("Sorry, You Lose! You got " + mHitTotal
+ + ". You need 50 to win.");
+ }
+
+ mTimerView.setText("1:12");
+ mTextView.setHeight(20);
+
+ }
+ }//end handle msg
+ });
+ }
+
+ setFocusable(true); // make sure we get key events
+
+ Log.d(TAG, "@@@ done creating view!");
+ }
+
+ /**
+ * Pass in a reference to the timer view widget so we can update it from here.
+ *
+ * @param tv
+ */
+ public void setTimerView(TextView tv) {
+ mTimerView = tv;
+ }
+
+ /**
+ * Standard window-focus override. Notice focus lost so we can pause on
+ * focus lost. e.g. user switches to take a call.
+ */
+ @Override
+ public void onWindowFocusChanged(boolean hasWindowFocus) {
+
+ Log.d(TAG, "@@@FOCUS CHANGED!");
+
+ if (!hasWindowFocus) {
+ if (thread != null)
+ thread.pause();
+
+ }
+ }
+
+ /**
+ * Fetches the animation thread corresponding to this LunarView.
+ *
+ * @return the animation thread
+ */
+ public JetBoyThread getThread() {
+ return thread;
+ }
+
+ /* Callback invoked when the surface dimensions change. */
+ public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
+ thread.setSurfaceSize(width, height);
+ }
+
+ public void surfaceCreated(SurfaceHolder arg0) {
+ // start the thread here so that we don't busy-wait in run()
+ // waiting for the surface to be created
+ thread.setRunning(true);
+ thread.start();
+ }
+
+ public void surfaceDestroyed(SurfaceHolder arg0) {
+ boolean retry = true;
+ thread.setRunning(false);
+ while (retry) {
+ try {
+ thread.join();
+ retry = false;
+
+ } catch (InterruptedException e) {
+ }
+ }
+ }
+
+ /**
+ * A reference to the button to start game over.
+ *
+ * @param _buttonRetry
+ *
+ */
+ public void SetButtonView(Button _buttonRetry) {
+ mButtonRetry = _buttonRetry;
+ // mButtonRestart = _buttonRestart;
+ }
+
+ //we reuse the help screen from the end game screen.
+ public void SetTextView(TextView textView) {
+ mTextView = textView;
+
+ }
+}
diff --git a/testrunner/Android.mk b/testrunner/Android.mk
index 93c092841..b6bde553b 100644
--- a/testrunner/Android.mk
+++ b/testrunner/Android.mk
@@ -10,7 +10,7 @@ LOCAL_PATH := $(call my-dir)
########################
include $(CLEAR_VARS)
-LOCAL_MODULE := tests.xml
+LOCAL_MODULE := test_defs.xml
LOCAL_MODULE_TAGS := tests
LOCAL_MODULE_CLASS := ETC
LOCAL_MODULE_PATH := $(local_target_dir)
diff --git a/testrunner/adb_interface.py b/testrunner/adb_interface.py
index fb304df9d..ad1b2c94f 100755
--- a/testrunner/adb_interface.py
+++ b/testrunner/adb_interface.py
@@ -297,8 +297,7 @@ class AdbInterface:
WaitForResponseTimedOutError if wait_time elapses and pm still does not
respond.
"""
- logger.Log("Waiting for device package manager for %s seconds..."
- % wait_time)
+ logger.Log("Waiting for device package manager...")
self.SendCommand("wait-for-device")
# Now the device is there, but may not be running.
# Query the package manager with a basic command
@@ -315,7 +314,8 @@ class AdbInterface:
time.sleep(wait_period)
attempts += 1
if not pm_found:
- raise errors.WaitForResponseTimedOutError
+ raise errors.WaitForResponseTimedOutError(
+ "Package manager did not respond after %s seconds" % wait_time)
def Sync(self, retry_count=3):
"""Perform a adb sync.
@@ -331,13 +331,12 @@ class AdbInterface:
output = self.SendCommand("sync", retry_count=retry_count)
if "Read-only file system" in output:
logger.SilentLog(output)
- logger.Log("adb sync failed due to read only fs, retrying")
+ logger.Log("Remounting read-only filesystem")
self.SendCommand("remount")
output = self.SendCommand("sync", retry_count=retry_count)
if "No space left on device" in output:
logger.SilentLog(output)
- logger.Log("adb sync failed due to no space on device, trying shell" +
- " start/stop")
+ logger.Log("Restarting device runtime")
self.SendShellCommand("stop", retry_count=retry_count)
output = self.SendCommand("sync", retry_count=retry_count)
self.SendShellCommand("start", retry_count=retry_count)
@@ -345,3 +344,15 @@ class AdbInterface:
logger.SilentLog(output)
self.WaitForDevicePm()
return output
+
+ def IsDevicePresent(self):
+ """Check if targeted device is present.
+
+ Returns:
+ True if device is present, False otherwise.
+ """
+ output = self.SendShellCommand("ls", retry_count=0)
+ if output.startswith("error:"):
+ return False
+ else:
+ return True
diff --git a/testrunner/coverage.py b/testrunner/coverage.py
index 507c5c79d..39a2ceb21 100755
--- a/testrunner/coverage.py
+++ b/testrunner/coverage.py
@@ -70,6 +70,27 @@ class CoverageGenerator(object):
def EnableCoverageBuild(self):
"""Enable building an Android target with code coverage instrumentation."""
os.environ[self._EMMA_BUILD_FLAG] = "true"
+ #TODO: can emma.jar automagically be added to bootclasspath here?
+
+ def TestDeviceCoverageSupport(self):
+ """Check if device has support for generating code coverage metrics.
+
+ Currently this will check if the emma.jar file is on the device's boot
+ classpath.
+
+ Returns:
+ True if device can support code coverage. False otherwise.
+ """
+ output = self._adb.SendShellCommand("cat init.rc | grep BOOTCLASSPATH | "
+ "grep emma.jar")
+ if len(output) > 0:
+ return True
+ else:
+ logger.Log("Error: Targeted device does not have emma.jar on its "
+ "BOOTCLASSPATH.")
+ logger.Log("Modify the BOOTCLASSPATH entry in system/core/rootdir/init.rc"
+ " to add emma.jar")
+ return False
def ExtractReport(self, test_suite,
device_coverage_path=_DEVICE_COVERAGE_PATH,
diff --git a/testrunner/coverage_targets.py b/testrunner/coverage_targets.py
index 8847bca04..bc826de7a 100644
--- a/testrunner/coverage_targets.py
+++ b/testrunner/coverage_targets.py
@@ -120,6 +120,9 @@ class CoverageTarget:
def _ParsePaths(self, target_element):
src_elements = target_element.getElementsByTagName(self._SRC_TAG)
+ if len(src_elements) <= 0:
+ # no src tags specified. Assume build_path + src
+ self._paths.append(os.path.join(self.GetBuildPath(), "src"))
for src_element in src_elements:
rel_path = src_element.getAttribute(self._PATH_ATTR)
self._paths.append(os.path.join(self.GetBuildPath(), rel_path))
diff --git a/testrunner/logger.py b/testrunner/logger.py
index 762c89311..61463a198 100755
--- a/testrunner/logger.py
+++ b/testrunner/logger.py
@@ -25,6 +25,7 @@ import datetime
_LOG_FILE = None
_verbose = False
+_log_time = True
def Init(log_file_path):
"""Set the path to the log file"""
@@ -57,8 +58,13 @@ def _WriteLog(msg):
def _PrependTimeStamp(log_string):
"""Returns the log_string prepended with current timestamp """
- return "# %s: %s" % (datetime.datetime.now().strftime("%m/%d/%y %H:%M:%S"),
- log_string)
+ global _log_time
+ if _log_time:
+ return "# %s: %s" % (datetime.datetime.now().strftime("%m/%d/%y %H:%M:%S"),
+ log_string)
+ else:
+ # timestamp logging disabled
+ return log_string
def SilentLog(new_str):
"""Silently log new_str. Unless verbose mode is enabled, will log new_str
@@ -77,7 +83,12 @@ def SetVerbose(new_verbose=True):
""" Enable or disable verbose logging"""
global _verbose
_verbose = new_verbose
-
+
+def SetTimestampLogging(new_timestamp=True):
+ """ Enable or disable outputting a timestamp with each log entry"""
+ global _log_time
+ _log_time = new_timestamp
+
def main():
pass
diff --git a/testrunner/runtest.py b/testrunner/runtest.py
index bf5bb2232..243da77b5 100755
--- a/testrunner/runtest.py
+++ b/testrunner/runtest.py
@@ -39,25 +39,32 @@ import test_defs
class TestRunner(object):
"""Command line utility class for running pre-defined Android test(s)."""
+ _TEST_FILE_NAME = "test_defs.xml"
+
# file path to android core platform tests, relative to android build root
# TODO move these test data files to another directory
- _CORE_TEST_PATH = os.path.join("development", "testrunner", "tests.xml")
+ _CORE_TEST_PATH = os.path.join("development", "testrunner",
+ _TEST_FILE_NAME)
# vendor glob file path patterns to tests, relative to android
# build root
_VENDOR_TEST_PATH = os.path.join("vendor", "*", "tests", "testinfo",
- "tests.xml")
+ _TEST_FILE_NAME)
_RUNTEST_USAGE = (
"usage: runtest.py [options] short-test-name[s]\n\n"
"The runtest script works in two ways. You can query it "
"for a list of tests, or you can launch one or more tests.")
+ def __init__(self):
+ # disable logging of timestamp
+ logger.SetTimestampLogging(False)
+
def _ProcessOptions(self):
"""Processes command-line options."""
# TODO error messages on once-only or mutually-exclusive options.
user_test_default = os.path.join(os.environ.get("HOME"), ".android",
- "tests.xml")
+ self._TEST_FILE_NAME)
parser = optparse.OptionParser(usage=self._RUNTEST_USAGE)
@@ -149,7 +156,7 @@ class TestRunner(object):
try:
known_tests = test_defs.TestDefinitions()
known_tests.Parse(core_test_path)
- # read all /vendor/*/tests/testinfo/tests.xml paths
+ # read all /vendor/*/tests/testinfo/test_defs.xml paths
vendor_tests_pattern = os.path.join(self._root_path,
self._VENDOR_TEST_PATH)
test_file_paths = glob.glob(vendor_tests_pattern)
@@ -178,10 +185,14 @@ class TestRunner(object):
self._coverage_gen.EnableCoverageBuild()
self._AddBuildTarget(self._coverage_gen.GetEmmaBuildPath(), target_set)
target_build_string = " ".join(list(target_set))
- logger.Log("Building %s" % target_build_string)
+ logger.Log("mmm %s" % target_build_string)
cmd = 'ONE_SHOT_MAKEFILE="%s" make -C "%s" files' % (target_build_string,
self._root_path)
- if not self._options.preview:
+ if self._options.preview:
+ # in preview mode, just display to the user what command would have been
+ # run
+ logger.Log("adb sync")
+ else:
run_command.RunCommand(cmd, return_output=False)
logger.Log("Syncing to device...")
self._adb.Sync()
@@ -259,6 +270,10 @@ class TestRunner(object):
self._DumpTests()
return
+ if not self._adb.IsDevicePresent():
+ logger.Log("Error: specified device cannot be found")
+ return
+
if not self._options.skip_build:
self._DoBuild()
diff --git a/testrunner/tests.xml b/testrunner/test_defs.xml
similarity index 99%
rename from testrunner/tests.xml
rename to testrunner/test_defs.xml
index d186af4c7..87159fd96 100644
--- a/testrunner/tests.xml
+++ b/testrunner/test_defs.xml
@@ -191,8 +191,7 @@ These attributes map to the following commands:
build_path="packages/apps/Music"
package="com.android.music.tests"
runner=".MusicPlayerFunctionalTestRunner"
- coverage_target="Music"
- continuous="true" />
+ coverage_target="Music" />
+
+
+
+
@@ -563,7 +594,7 @@
-
+
@@ -595,4 +626,14 @@
id="com.android.ide.eclipse.adt.refactoring.extract.string">
+
+
+
+
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtPlugin.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtPlugin.java
index 42db64a61..4a7a002e2 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtPlugin.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtPlugin.java
@@ -29,8 +29,8 @@ import com.android.ide.eclipse.adt.sdk.AndroidTargetParser;
import com.android.ide.eclipse.adt.sdk.LoadStatus;
import com.android.ide.eclipse.adt.sdk.Sdk;
import com.android.ide.eclipse.adt.sdk.Sdk.ITargetChangeListener;
+import com.android.ide.eclipse.adt.ui.EclipseUiHelper;
import com.android.ide.eclipse.common.AndroidConstants;
-import com.android.ide.eclipse.common.EclipseUiHelper;
import com.android.ide.eclipse.common.SdkStatsHelper;
import com.android.ide.eclipse.common.StreamHelper;
import com.android.ide.eclipse.common.project.BaseProjectHelper;
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/ApkBuilder.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/ApkBuilder.java
index 1edcf79fd..47ea3e731 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/ApkBuilder.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/ApkBuilder.java
@@ -18,6 +18,7 @@ package com.android.ide.eclipse.adt.build;
import com.android.ide.eclipse.adt.AdtConstants;
import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.adt.project.ApkInstallManager;
import com.android.ide.eclipse.adt.project.ProjectHelper;
import com.android.ide.eclipse.adt.sdk.AndroidTargetData;
import com.android.ide.eclipse.adt.sdk.Sdk;
@@ -551,6 +552,9 @@ public class ApkBuilder extends BaseBuilder {
// and store it
saveProjectBooleanProperty(PROPERTY_BUILD_APK, mBuildFinalPackage);
+ // reset the installation manager to force new installs of this project
+ ApkInstallManager.getInstance().resetInstallationFor(project);
+
AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, getProject(),
"Build Success!");
}
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/PreCompilerBuilder.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/PreCompilerBuilder.java
index 6f9c2f16a..d4b525fcf 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/PreCompilerBuilder.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/PreCompilerBuilder.java
@@ -315,12 +315,9 @@ public class PreCompilerBuilder extends BaseBuilder {
String msg = String.format(
"Manifest min SDK version (%1$d) is lower than project target API level (%2$d)",
minSdkVersion, projectTarget.getApiVersionNumber());
- AdtPlugin.printErrorToConsole(project, msg);
+ AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project, msg);
BaseProjectHelper.addMarker(manifest, AdtConstants.MARKER_ADT, msg,
- IMarker.SEVERITY_ERROR);
-
- // This interrupts the build. The next builders will not run.
- stopBuild(msg);
+ IMarker.SEVERITY_WARNING);
}
if (javaPackage == null || javaPackage.length() == 0) {
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/AndroidLaunchController.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/AndroidLaunchController.java
index fafc4020b..7ee3def57 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/AndroidLaunchController.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/AndroidLaunchController.java
@@ -32,6 +32,7 @@ import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.adt.launch.AndroidLaunchConfiguration.TargetMode;
import com.android.ide.eclipse.adt.launch.DelayedLaunchInfo.InstallRetryMode;
import com.android.ide.eclipse.adt.launch.DeviceChooserDialog.DeviceChooserResponse;
+import com.android.ide.eclipse.adt.project.ApkInstallManager;
import com.android.ide.eclipse.adt.project.ProjectHelper;
import com.android.ide.eclipse.adt.sdk.Sdk;
import com.android.ide.eclipse.common.project.AndroidManifestParser;
@@ -313,6 +314,8 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
* defined by ILaunchManager - RUN_MODE or
* DEBUG_MODE.
* @param apk the resource to the apk to launch.
+ * @param packageName the Android package name of the app
+ * @param debugPackageName the Android package name to debug
* @param debuggable the debuggable value of the app, or null if not set.
* @param requiredApiVersionNumber the api version required by the app, or
* {@link AndroidManifestParser#INVALID_MIN_SDK} if none.
@@ -321,7 +324,7 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
* @param launch the launch object
*/
public void launch(final IProject project, String mode, IFile apk,
- String packageName, Boolean debuggable, int requiredApiVersionNumber,
+ String packageName, String debugPackageName, Boolean debuggable, int requiredApiVersionNumber,
final IAndroidLaunchAction launchAction, final AndroidLaunchConfiguration config,
final AndroidLaunch launch, IProgressMonitor monitor) {
@@ -330,7 +333,8 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
// create the launch info
final DelayedLaunchInfo launchInfo = new DelayedLaunchInfo(project, packageName,
- launchAction, apk, debuggable, requiredApiVersionNumber, launch, monitor);
+ debugPackageName, launchAction, apk, debuggable, requiredApiVersionNumber, launch,
+ monitor);
// set the debug mode
launchInfo.setDebugMode(mode.equals(ILaunchManager.DEBUG_MODE));
@@ -762,13 +766,46 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
/**
- * Syncs the application on the device/emulator.
+ * If needed, syncs the application and all its dependencies on the device/emulator.
*
* @param launchInfo The Launch information object.
* @param device the device on which to sync the application
* @return true if the install succeeded.
*/
private boolean syncApp(DelayedLaunchInfo launchInfo, IDevice device) {
+ boolean alreadyInstalled = ApkInstallManager.getInstance().isApplicationInstalled(
+ launchInfo.getProject(), device);
+
+ if (alreadyInstalled) {
+ AdtPlugin.printToConsole(launchInfo.getProject(),
+ "Application already deployed. No need to reinstall.");
+ } else {
+ if (doSyncApp(launchInfo, device) == false) {
+ return false;
+ }
+ }
+
+ // The app is now installed, now try the dependent projects
+ for (DelayedLaunchInfo dependentLaunchInfo : getDependenciesLaunchInfo(launchInfo)) {
+ String msg = String.format("Project dependency found, installing: %s",
+ dependentLaunchInfo.getProject().getName());
+ AdtPlugin.printToConsole(launchInfo.getProject(), msg);
+ if (syncApp(dependentLaunchInfo, device) == false) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Syncs the application on the device/emulator.
+ *
+ * @param launchInfo The Launch information object.
+ * @param device the device on which to sync the application
+ * @return true if the install succeeded.
+ */
+ private boolean doSyncApp(DelayedLaunchInfo launchInfo, IDevice device) {
SyncService sync = device.getSyncService();
if (sync != null) {
IPath path = launchInfo.getPackageFile().getLocation();
@@ -812,12 +849,10 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
return false;
}
- // The app is now installed, now try the dependent projects
- for (DelayedLaunchInfo dependentLaunchInfo : getDependenciesLaunchInfo(launchInfo)) {
- String msg = String.format("Project dependency found, syncing: %s",
- dependentLaunchInfo.getProject().getName());
- AdtPlugin.printToConsole(launchInfo.getProject(), msg);
- syncApp(dependentLaunchInfo, device);
+ // if the installation succeeded, we register it.
+ if (installResult) {
+ ApkInstallManager.getInstance().registerInstallation(
+ launchInfo.getProject(), device);
}
return installResult;
@@ -890,6 +925,7 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
DelayedLaunchInfo delayedLaunchInfo = new DelayedLaunchInfo(
androidProject.getProject(),
manifestParser.getPackage(),
+ manifestParser.getPackage(),
launchInfo.getLaunchAction(),
apk,
manifestParser.getDebuggable(),
@@ -1042,7 +1078,7 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
/**
* Performs the installation of an application whose package has been uploaded on the device.
- * Before doing it, if the application is already running on the device, it is killed.
+ *
* @param launchInfo the {@link DelayedLaunchInfo}.
* @param remotePath the path of the application package in the device tmp folder.
* @param device the device on which to install the application.
@@ -1052,12 +1088,6 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
*/
private String doInstall(DelayedLaunchInfo launchInfo, final String remotePath,
final IDevice device, boolean reinstall) throws IOException {
- // kill running application
- Client application = device.getClient(launchInfo.getPackageName());
- if (application != null) {
- application.kill();
- }
-
InstallReceiver receiver = new InstallReceiver();
try {
String cmd = String.format(
@@ -1492,14 +1522,14 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
for (int i = 0; i < mWaitingForDebuggerApplications.size(); ) {
final DelayedLaunchInfo launchInfo = mWaitingForDebuggerApplications.get(i);
if (client.getDevice() == launchInfo.getDevice() &&
- applicationName.equals(launchInfo.getPackageName())) {
+ applicationName.equals(launchInfo.getDebugPackageName())) {
// this is a match. We remove the launch info from the list
mWaitingForDebuggerApplications.remove(i);
// and connect the debugger.
String msg = String.format(
"Attempting to connect debugger to '%1$s' on port %2$d",
- launchInfo.getPackageName(), client.getDebuggerListenPort());
+ launchInfo.getDebugPackageName(), client.getDebuggerListenPort());
AdtPlugin.printToConsole(launchInfo.getProject(), msg);
new Thread("Debugger Connection") { //$NON-NLS-1$
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/DelayedLaunchInfo.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/DelayedLaunchInfo.java
index 7dae56d00..f3bd28a07 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/DelayedLaunchInfo.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/DelayedLaunchInfo.java
@@ -44,6 +44,9 @@ public final class DelayedLaunchInfo {
/** Package name */
private final String mPackageName;
+
+ /** Debug package name */
+ private final String mDebugPackageName;
/** IFile to the package (.apk) file */
private final IFile mPackageFile;
@@ -79,7 +82,8 @@ public final class DelayedLaunchInfo {
* Basic constructor with activity and package info.
*
* @param project the eclipse project that corresponds to Android app
- * @param packageName package name of Android app
+ * @param packageName package name of Android app
+ * @param debugPackageName the package name of the Andriod app to debug
* @param launchAction action to perform after app install
* @param pack IFile to the package (.apk) file
* @param debuggable debuggable attribute of the app's manifest file.
@@ -88,11 +92,12 @@ public final class DelayedLaunchInfo {
* @param launch the launch object
* @param monitor progress monitor for launch
*/
- public DelayedLaunchInfo(IProject project, String packageName,
+ public DelayedLaunchInfo(IProject project, String packageName, String debugPackageName,
IAndroidLaunchAction launchAction, IFile pack, Boolean debuggable,
int requiredApiVersionNumber, AndroidLaunch launch, IProgressMonitor monitor) {
mProject = project;
mPackageName = packageName;
+ mDebugPackageName = debugPackageName;
mPackageFile = pack;
mLaunchAction = launchAction;
mLaunch = launch;
@@ -129,6 +134,17 @@ public final class DelayedLaunchInfo {
return mPackageName;
}
+ /**
+ * Returns the Android app process name that the debugger should connect to. Typically this is
+ * the same value as {@link getPackageName}
+ */
+ public String getDebugPackageName() {
+ if (mDebugPackageName == null) {
+ return getPackageName();
+ }
+ return mDebugPackageName;
+ }
+
/**
* @return the application package file
*/
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/EmulatorConfigTab.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/EmulatorConfigTab.java
index 3789153e4..24380eb62 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/EmulatorConfigTab.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/EmulatorConfigTab.java
@@ -21,6 +21,7 @@ import com.android.ide.eclipse.adt.launch.AndroidLaunchConfiguration.TargetMode;
import com.android.ide.eclipse.adt.sdk.Sdk;
import com.android.ide.eclipse.common.project.BaseProjectHelper;
import com.android.ide.eclipse.ddms.DdmsPlugin;
+import com.android.prefs.AndroidLocation.AndroidLocationException;
import com.android.sdklib.IAndroidTarget;
import com.android.sdklib.avd.AvdManager;
import com.android.sdklib.avd.AvdManager.AvdInfo;
@@ -126,6 +127,14 @@ public class EmulatorConfigTab extends AbstractLaunchConfigurationTab {
*/
public void createControl(Composite parent) {
Font font = parent.getFont();
+
+ // reload the AVDs to make sure we are up to date
+ try {
+ Sdk.getCurrent().getAvdManager().reloadAvds();
+ } catch (AndroidLocationException e1) {
+ // this happens if the AVD Manager failed to find the folder in which the AVDs are
+ // stored. There isn't much we can do at this point.
+ }
Composite topComp = new Composite(parent, SWT.NONE);
setControl(topComp);
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/LaunchConfigDelegate.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/LaunchConfigDelegate.java
index d057ac709..4fa270e4e 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/LaunchConfigDelegate.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/LaunchConfigDelegate.java
@@ -23,6 +23,7 @@ import com.android.ide.eclipse.adt.project.ProjectHelper;
import com.android.ide.eclipse.common.AndroidConstants;
import com.android.ide.eclipse.common.project.AndroidManifestParser;
import com.android.ide.eclipse.common.project.BaseProjectHelper;
+import com.android.ide.eclipse.common.project.AndroidManifestParser.Activity;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
@@ -247,7 +248,7 @@ public class LaunchConfigDelegate extends LaunchConfigurationDelegate {
activityName = getActivityName(configuration);
// Get the full activity list and make sure the one we got matches.
- String[] activities = manifestParser.getActivities();
+ Activity[] activities = manifestParser.getActivities();
// first we check that there are, in fact, activities.
if (activities.length == 0) {
@@ -261,8 +262,11 @@ public class LaunchConfigDelegate extends LaunchConfigurationDelegate {
// if the activity we got is null, we look for the default one.
AdtPlugin.printErrorToConsole(project,
"No activity specified! Getting the launcher activity.");
- activityName = manifestParser.getLauncherActivity();
-
+ Activity launcherActivity = manifestParser.getLauncherActivity();
+ if (launcherActivity != null) {
+ activityName = launcherActivity.getName();
+ }
+
// if there's no default activity. We revert to a sync-only launch.
if (activityName == null) {
revertToNoActionLaunch(project, config);
@@ -271,8 +275,8 @@ public class LaunchConfigDelegate extends LaunchConfigurationDelegate {
// check the one we got from the config matches any from the list
boolean match = false;
- for (String a : activities) {
- if (a != null && a.equals(activityName)) {
+ for (Activity a : activities) {
+ if (a != null && a.getName().equals(activityName)) {
match = true;
break;
}
@@ -282,7 +286,10 @@ public class LaunchConfigDelegate extends LaunchConfigurationDelegate {
if (match == false) {
AdtPlugin.printErrorToConsole(project,
"The specified activity does not exist! Getting the launcher activity.");
- activityName = manifestParser.getLauncherActivity();
+ Activity launcherActivity = manifestParser.getLauncherActivity();
+ if (launcherActivity != null) {
+ activityName = launcherActivity.getName();
+ }
// if there's no default activity. We revert to a sync-only launch.
if (activityName == null) {
@@ -291,7 +298,10 @@ public class LaunchConfigDelegate extends LaunchConfigurationDelegate {
}
}
} else if (config.mLaunchAction == ACTION_DEFAULT) {
- activityName = manifestParser.getLauncherActivity();
+ Activity launcherActivity = manifestParser.getLauncherActivity();
+ if (launcherActivity != null) {
+ activityName = launcherActivity.getName();
+ }
// if there's no default activity. We revert to a sync-only launch.
if (activityName == null) {
@@ -306,9 +316,10 @@ public class LaunchConfigDelegate extends LaunchConfigurationDelegate {
// everything seems fine, we ask the launch controller to handle
// the rest
- controller.launch(project, mode, applicationPackage, manifestParser.getPackage(),
- manifestParser.getDebuggable(), manifestParser.getApiLevelRequirement(),
- launchAction, config, androidLaunch, monitor);
+ controller.launch(project, mode, applicationPackage,manifestParser.getPackage(),
+ manifestParser.getPackage(), manifestParser.getDebuggable(),
+ manifestParser.getApiLevelRequirement(), launchAction, config, androidLaunch,
+ monitor);
}
@Override
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/MainLaunchConfigTab.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/MainLaunchConfigTab.java
index 91bd21cb7..a32c2ee0e 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/MainLaunchConfigTab.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/MainLaunchConfigTab.java
@@ -20,6 +20,7 @@ import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.common.project.AndroidManifestParser;
import com.android.ide.eclipse.common.project.BaseProjectHelper;
import com.android.ide.eclipse.common.project.ProjectChooserHelper;
+import com.android.ide.eclipse.common.project.AndroidManifestParser.Activity;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
@@ -50,6 +51,8 @@ import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Text;
+import java.util.ArrayList;
+
/**
* Class for the main launch configuration tab.
*/
@@ -66,7 +69,7 @@ public class MainLaunchConfigTab extends AbstractLaunchConfigurationTab {
private Button mProjButton;
private Combo mActivityCombo;
- private String[] mActivities;
+ private final ArrayList mActivities = new ArrayList();
private WidgetListener mListener = new WidgetListener();
@@ -214,8 +217,9 @@ public class MainLaunchConfigTab extends AbstractLaunchConfigurationTab {
// add the activity
int selection = mActivityCombo.getSelectionIndex();
- if (mActivities != null && selection >=0 && selection < mActivities.length) {
- configuration.setAttribute(LaunchConfigDelegate.ATTR_ACTIVITY, mActivities[selection]);
+ if (mActivities != null && selection >=0 && selection < mActivities.size()) {
+ configuration.setAttribute(LaunchConfigDelegate.ATTR_ACTIVITY,
+ mActivities.get(selection).getName());
}
// link the project and the launch config.
@@ -349,11 +353,11 @@ public class MainLaunchConfigTab extends AbstractLaunchConfigurationTab {
mActivityCombo.setEnabled(true);
if (activityName == null || activityName.equals(EMPTY_STRING)) {
mActivityCombo.clearSelection();
- } else if (mActivities != null && mActivities.length > 0) {
+ } else if (mActivities != null && mActivities.size() > 0) {
// look for the name of the activity in the combo.
boolean found = false;
- for (int i = 0 ; i < mActivities.length ; i++) {
- if (activityName.equals(mActivities[i])) {
+ for (int i = 0 ; i < mActivities.size() ; i++) {
+ if (activityName.equals(mActivities.get(i).getName())) {
found = true;
mActivityCombo.select(i);
break;
@@ -404,17 +408,22 @@ public class MainLaunchConfigTab extends AbstractLaunchConfigurationTab {
BaseProjectHelper.getJavaProject(project), null /* errorListener */,
true /* gatherData */, false /* markErrors */);
if (manifestParser != null) {
- mActivities = manifestParser.getActivities();
-
+ Activity[] activities = manifestParser.getActivities();
+
+ mActivities.clear();
mActivityCombo.removeAll();
-
- if (mActivities.length > 0) {
+
+ for (Activity activity : activities) {
+ if (activity.getExported() && activity.hasAction()) {
+ mActivities.add(activity);
+ mActivityCombo.add(activity.getName());
+ }
+ }
+
+ if (mActivities.size() > 0) {
if (mLaunchAction == LaunchConfigDelegate.ACTION_ACTIVITY) {
mActivityCombo.setEnabled(true);
}
- for (String s : mActivities) {
- mActivityCombo.add(s);
- }
} else {
mActivityCombo.setEnabled(false);
}
@@ -435,7 +444,7 @@ public class MainLaunchConfigTab extends AbstractLaunchConfigurationTab {
// if we reach this point, either project is null, or we got an exception during
// the parsing. In either case, we empty the activity list.
mActivityCombo.removeAll();
- mActivities = null;
+ mActivities.clear();
}
/**
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchAction.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchAction.java
index 747fcfe5c..24ebe21c0 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchAction.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchAction.java
@@ -24,7 +24,6 @@ import com.android.ide.eclipse.adt.launch.junit.runtime.RemoteAdtTestRunner;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
-import org.eclipse.debug.core.DebugException;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.ILaunchManager;
@@ -39,18 +38,15 @@ import org.eclipse.jdt.launching.VMRunnerConfiguration;
*/
class AndroidJUnitLaunchAction implements IAndroidLaunchAction {
- private String mTestPackage;
- private String mRunner;
+ private final AndroidJUnitLaunchInfo mLaunchInfo;
/**
* Creates a AndroidJUnitLaunchAction.
*
- * @param testPackage the Android application package that contains the tests to run
- * @param runner the InstrumentationTestRunner that will execute the tests
+ * @param launchInfo the {@link AndroidJUnitLaunchInfo} for the JUnit run
*/
- public AndroidJUnitLaunchAction(String testPackage, String runner) {
- mTestPackage = testPackage;
- mRunner = runner;
+ public AndroidJUnitLaunchAction(AndroidJUnitLaunchInfo launchInfo) {
+ mLaunchInfo = launchInfo;
}
/**
@@ -60,17 +56,20 @@ class AndroidJUnitLaunchAction implements IAndroidLaunchAction {
* @see IAndroidLaunchAction#doLaunchAction(DelayedLaunchInfo, IDevice)
*/
public boolean doLaunchAction(DelayedLaunchInfo info, IDevice device) {
- String msg = String.format("Launching instrumentation %s on device %s", mRunner,
- device.getSerialNumber());
+ String msg = String.format("Launching instrumentation %s on device %s",
+ mLaunchInfo.getRunner(), device.getSerialNumber());
AdtPlugin.printToConsole(info.getProject(), msg);
try {
- JUnitLaunchDelegate junitDelegate = new JUnitLaunchDelegate(info, device);
+ mLaunchInfo.setDebugMode(info.isDebugMode());
+ mLaunchInfo.setDevice(info.getDevice());
+ JUnitLaunchDelegate junitDelegate = new JUnitLaunchDelegate(mLaunchInfo);
final String mode = info.isDebugMode() ? ILaunchManager.DEBUG_MODE :
ILaunchManager.RUN_MODE;
+
junitDelegate.launch(info.getLaunch().getLaunchConfiguration(), mode, info.getLaunch(),
info.getMonitor());
-
+
// TODO: need to add AMReceiver-type functionality somewhere
} catch (CoreException e) {
AdtPlugin.printErrorToConsole(info.getProject(), "Failed to launch test");
@@ -82,20 +81,18 @@ class AndroidJUnitLaunchAction implements IAndroidLaunchAction {
* {@inheritDoc}
*/
public String getLaunchDescription() {
- return String.format("%s JUnit launch", mRunner);
+ return String.format("%s JUnit launch", mLaunchInfo.getRunner());
}
/**
* Extends the JDT JUnit launch delegate to allow for JUnit UI reuse.
*/
- private class JUnitLaunchDelegate extends JUnitLaunchConfigurationDelegate {
+ private static class JUnitLaunchDelegate extends JUnitLaunchConfigurationDelegate {
- private IDevice mDevice;
- private DelayedLaunchInfo mLaunchInfo;
+ private AndroidJUnitLaunchInfo mLaunchInfo;
- public JUnitLaunchDelegate(DelayedLaunchInfo info, IDevice device) {
- mLaunchInfo = info;
- mDevice = device;
+ public JUnitLaunchDelegate(AndroidJUnitLaunchInfo launchInfo) {
+ mLaunchInfo = launchInfo;
}
/* (non-Javadoc)
@@ -110,34 +107,28 @@ class AndroidJUnitLaunchAction implements IAndroidLaunchAction {
/**
* {@inheritDoc}
- * @throws CoreException
* @see org.eclipse.jdt.junit.launcher.JUnitLaunchConfigurationDelegate#verifyMainTypeName(org.eclipse.debug.core.ILaunchConfiguration)
*/
@Override
- public String verifyMainTypeName(ILaunchConfiguration configuration) throws CoreException {
+ public String verifyMainTypeName(ILaunchConfiguration configuration) {
return "com.android.ide.eclipse.adt.junit.internal.runner.RemoteAndroidTestRunner"; //$NON-NLS-1$
}
/**
* Overrides parent to return a VM Runner implementation which launches a thread, rather
* than a separate VM process
- * @throws CoreException
*/
@Override
- public IVMRunner getVMRunner(ILaunchConfiguration configuration, String mode)
- throws CoreException {
- return new VMTestRunner(new AndroidJUnitLaunchInfo(mLaunchInfo.getProject(),
- mTestPackage, mRunner, mLaunchInfo.isDebugMode(), mDevice));
+ public IVMRunner getVMRunner(ILaunchConfiguration configuration, String mode) {
+ return new VMTestRunner(mLaunchInfo);
}
/**
* {@inheritDoc}
- * @throws CoreException
* @see org.eclipse.debug.core.model.LaunchConfigurationDelegate#getLaunch(org.eclipse.debug.core.ILaunchConfiguration, java.lang.String)
*/
@Override
- public ILaunch getLaunch(ILaunchConfiguration configuration, String mode)
- throws CoreException {
+ public ILaunch getLaunch(ILaunchConfiguration configuration, String mode) {
return mLaunchInfo.getLaunch();
}
}
@@ -161,7 +152,7 @@ class AndroidJUnitLaunchAction implements IAndroidLaunchAction {
IProgressMonitor monitor) throws CoreException {
TestRunnerProcess runnerProcess =
- new TestRunnerProcess(config, launch, mJUnitInfo);
+ new TestRunnerProcess(config, mJUnitInfo);
runnerProcess.start();
launch.addProcess(runnerProcess);
}
@@ -173,15 +164,12 @@ class AndroidJUnitLaunchAction implements IAndroidLaunchAction {
private static class TestRunnerProcess extends Thread implements IProcess {
private final VMRunnerConfiguration mRunConfig;
- private final ILaunch mLaunch;
private final AndroidJUnitLaunchInfo mJUnitInfo;
private RemoteAdtTestRunner mTestRunner = null;
private boolean mIsTerminated = false;
- TestRunnerProcess(VMRunnerConfiguration runConfig, ILaunch launch,
- AndroidJUnitLaunchInfo info) {
+ TestRunnerProcess(VMRunnerConfiguration runConfig, AndroidJUnitLaunchInfo info) {
mRunConfig = runConfig;
- mLaunch = launch;
mJUnitInfo = info;
}
@@ -194,10 +182,9 @@ class AndroidJUnitLaunchAction implements IAndroidLaunchAction {
/**
* {@inheritDoc}
- * @throws DebugException
* @see org.eclipse.debug.core.model.IProcess#getExitValue()
*/
- public int getExitValue() throws DebugException {
+ public int getExitValue() {
return 0;
}
@@ -205,14 +192,14 @@ class AndroidJUnitLaunchAction implements IAndroidLaunchAction {
* @see org.eclipse.debug.core.model.IProcess#getLabel()
*/
public String getLabel() {
- return mLaunch.getLaunchMode();
+ return mJUnitInfo.getLaunch().getLaunchMode();
}
/* (non-Javadoc)
* @see org.eclipse.debug.core.model.IProcess#getLaunch()
*/
public ILaunch getLaunch() {
- return mLaunch;
+ return mJUnitInfo.getLaunch();
}
/* (non-Javadoc)
@@ -254,10 +241,9 @@ class AndroidJUnitLaunchAction implements IAndroidLaunchAction {
/**
* {@inheritDoc}
- * @throws DebugException
* @see org.eclipse.debug.core.model.ITerminate#terminate()
*/
- public void terminate() throws DebugException {
+ public void terminate() {
if (mTestRunner != null) {
mTestRunner.terminate();
}
@@ -274,3 +260,4 @@ class AndroidJUnitLaunchAction implements IAndroidLaunchAction {
}
}
}
+
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchConfigDelegate.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchConfigDelegate.java
index fa8e4b01a..19067921b 100755
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchConfigDelegate.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchConfigDelegate.java
@@ -22,8 +22,10 @@ import com.android.ide.eclipse.adt.launch.AndroidLaunchConfiguration;
import com.android.ide.eclipse.adt.launch.AndroidLaunchController;
import com.android.ide.eclipse.adt.launch.IAndroidLaunchAction;
import com.android.ide.eclipse.adt.launch.LaunchConfigDelegate;
+import com.android.ide.eclipse.adt.launch.junit.runtime.AndroidJUnitLaunchInfo;
import com.android.ide.eclipse.common.AndroidConstants;
import com.android.ide.eclipse.common.project.AndroidManifestParser;
+import com.android.ide.eclipse.common.project.AndroidManifestParser.Instrumentation;
import com.android.ide.eclipse.common.project.BaseProjectHelper;
import org.eclipse.core.resources.IFile;
@@ -32,8 +34,11 @@ import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
+import org.eclipse.jdt.core.IJavaElement;
+import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.internal.junit.launcher.JUnitLaunchConfigurationConstants;
import org.eclipse.jdt.internal.junit.launcher.TestKindRegistry;
+import org.eclipse.jdt.launching.IJavaLaunchConfigurationConstants;
/**
* Run configuration that can execute JUnit tests on an Android platform.
@@ -47,6 +52,7 @@ public class AndroidJUnitLaunchConfigDelegate extends LaunchConfigDelegate {
/** Launch config attribute that stores instrumentation runner. */
static final String ATTR_INSTR_NAME = AdtPlugin.PLUGIN_ID + ".instrumentation"; //$NON-NLS-1$
+
private static final String EMPTY_STRING = ""; //$NON-NLS-1$
@Override
@@ -55,7 +61,6 @@ public class AndroidJUnitLaunchConfigDelegate extends LaunchConfigDelegate {
AndroidLaunchConfiguration config, AndroidLaunchController controller,
IFile applicationPackage, AndroidManifestParser manifestParser) {
- String testPackage = manifestParser.getPackage();
String runner = getRunner(project, configuration, manifestParser);
if (runner == null) {
AdtPlugin.displayError("Android Launch",
@@ -63,14 +68,89 @@ public class AndroidJUnitLaunchConfigDelegate extends LaunchConfigDelegate {
androidLaunch.stopLaunch();
return;
}
-
- IAndroidLaunchAction junitLaunch = new AndroidJUnitLaunchAction(testPackage, runner);
-
- controller.launch(project, mode, applicationPackage, manifestParser.getPackage(),
+ // get the target app's package
+ String targetAppPackage = getTargetPackage(manifestParser, runner);
+ if (targetAppPackage == null) {
+ AdtPlugin.displayError("Android Launch",
+ String.format("A target package for instrumention test runner %1$s could not be found!",
+ runner));
+ androidLaunch.stopLaunch();
+ return;
+ }
+ String testAppPackage = manifestParser.getPackage();
+ AndroidJUnitLaunchInfo junitLaunchInfo = new AndroidJUnitLaunchInfo(project,
+ testAppPackage, runner);
+ junitLaunchInfo.setTestClass(getTestClass(configuration));
+ junitLaunchInfo.setTestPackage(getTestPackage(configuration));
+ junitLaunchInfo.setTestMethod(getTestMethod(configuration));
+ junitLaunchInfo.setLaunch(androidLaunch);
+ IAndroidLaunchAction junitLaunch = new AndroidJUnitLaunchAction(junitLaunchInfo);
+
+ controller.launch(project, mode, applicationPackage, testAppPackage, targetAppPackage,
manifestParser.getDebuggable(), manifestParser.getApiLevelRequirement(),
junitLaunch, config, androidLaunch, monitor);
}
+
+ /**
+ * Get the target Android application's package for the given instrumentation runner, or
+ * null if it could not be found.
+ *
+ * @param manifestParser the {@link AndroidManifestParser} for the test project
+ * @param runner the instrumentation runner class name
+ * @return the target package or null
+ */
+ private String getTargetPackage(AndroidManifestParser manifestParser, String runner) {
+ for (Instrumentation instr : manifestParser.getInstrumentations()) {
+ if (instr.getName().equals(runner)) {
+ return instr.getTargetPackage();
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns the test package stored in the launch configuration, or null if not
+ * specified.
+ *
+ * @param configuration the {@link ILaunchConfiguration} to retrieve the test package info from
+ * @return the test package or null.
+ */
+ private String getTestPackage(ILaunchConfiguration configuration) {
+ // try to retrieve a package name from the JUnit container attribute
+ String containerHandle = getStringLaunchAttribute(
+ JUnitLaunchConfigurationConstants.ATTR_TEST_CONTAINER, configuration);
+ if (containerHandle != null && containerHandle.length() > 0) {
+ IJavaElement element = JavaCore.create(containerHandle);
+ // containerHandle could be a IProject, check if its a java package
+ if (element.getElementType() == IJavaElement.PACKAGE_FRAGMENT) {
+ return element.getElementName();
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns the test class stored in the launch configuration.
+ *
+ * @param configuration the {@link ILaunchConfiguration} to retrieve the test class info from
+ * @return the test class. null if not specified.
+ */
+ private String getTestClass(ILaunchConfiguration configuration) {
+ return getStringLaunchAttribute(IJavaLaunchConfigurationConstants.ATTR_MAIN_TYPE_NAME,
+ configuration);
+ }
+ /**
+ * Returns the test method stored in the launch configuration.
+ *
+ * @param configuration the {@link ILaunchConfiguration} to retrieve the test method info from
+ * @return the test method. null if not specified.
+ */
+ private String getTestMethod(ILaunchConfiguration configuration) {
+ return getStringLaunchAttribute(JUnitLaunchConfigurationConstants.ATTR_TEST_METHOD_NAME,
+ configuration);
+ }
+
/**
* Gets a instrumentation runner for the launch.
*
@@ -114,11 +194,29 @@ public class AndroidJUnitLaunchConfigDelegate extends LaunchConfigDelegate {
}
private String getRunnerFromConfig(ILaunchConfiguration configuration) throws CoreException {
- String runner = configuration.getAttribute(ATTR_INSTR_NAME, EMPTY_STRING);
- if (runner.length() < 1) {
- return null;
+ return getStringLaunchAttribute(ATTR_INSTR_NAME, configuration);
+ }
+
+ /**
+ * Helper method to retrieve a string attribute from the launch configuration
+ *
+ * @param attributeName name of the launch attribute
+ * @param configuration the {@link ILaunchConfiguration} to retrieve the attribute from
+ * @return the attribute's value. null if not found.
+ */
+ private String getStringLaunchAttribute(String attributeName,
+ ILaunchConfiguration configuration) {
+ try {
+ String attrValue = configuration.getAttribute(attributeName, EMPTY_STRING);
+ if (attrValue.length() < 1) {
+ return null;
+ }
+ return attrValue;
+ } catch (CoreException e) {
+ AdtPlugin.log(e, String.format("Error when retrieving launch info %1$s", //$NON-NLS-1$
+ attributeName));
}
- return runner;
+ return null;
}
/**
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchConfigurationTab.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchConfigurationTab.java
index eb5748269..34e64e327 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchConfigurationTab.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchConfigurationTab.java
@@ -18,6 +18,7 @@ package com.android.ide.eclipse.adt.launch.junit;
import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.adt.launch.MainLaunchConfigTab;
import com.android.ide.eclipse.common.AndroidConstants;
+import com.android.ide.eclipse.common.project.BaseProjectHelper;
import com.android.ide.eclipse.common.project.ProjectChooserHelper;
import org.eclipse.core.resources.IProject;
@@ -241,7 +242,7 @@ public class AndroidJUnitLaunchConfigurationTab extends AbstractLaunchConfigurat
private void createTestContainerSelectionGroup(Composite comp) {
mTestContainerRadioButton = new Button(comp, SWT.RADIO);
mTestContainerRadioButton.setText(
- JUnitMessages.JUnitLaunchConfigurationTab_label_containerTest);
+ "Run all tests in the selected project, or package");
GridData gd = new GridData();
gd.horizontalSpan = 3;
mTestContainerRadioButton.setLayoutData(gd);
@@ -249,12 +250,12 @@ public class AndroidJUnitLaunchConfigurationTab extends AbstractLaunchConfigurat
public void widgetSelected(SelectionEvent e) {
if (mTestContainerRadioButton.getSelection()) {
testModeChanged();
- }
+ }
}
public void widgetDefaultSelected(SelectionEvent e) {
}
});
-
+
mContainerText = new Text(comp, SWT.SINGLE | SWT.BORDER | SWT.READ_ONLY);
gd = new GridData(GridData.FILL_HORIZONTAL);
gd.horizontalIndent = 25;
@@ -265,7 +266,7 @@ public class AndroidJUnitLaunchConfigurationTab extends AbstractLaunchConfigurat
updateLaunchConfigurationDialog();
}
});
-
+
mContainerSearchButton = new Button(comp, SWT.PUSH);
mContainerSearchButton.setText(JUnitMessages.JUnitLaunchConfigurationTab_label_search);
mContainerSearchButton.addSelectionListener(new SelectionAdapter() {
@@ -821,7 +822,7 @@ public class AndroidJUnitLaunchConfigurationTab extends AbstractLaunchConfigurat
@SuppressWarnings("unchecked")
private IJavaElement chooseContainer(IJavaElement initElement) {
- Class[] acceptedClasses = new Class[] { IPackageFragmentRoot.class, IJavaProject.class,
+ Class[] acceptedClasses = new Class[] { IJavaProject.class,
IPackageFragment.class };
TypedElementSelectionValidator validator = new TypedElementSelectionValidator(
acceptedClasses, false) {
@@ -839,7 +840,7 @@ public class AndroidJUnitLaunchConfigurationTab extends AbstractLaunchConfigurat
if (element instanceof IPackageFragmentRoot &&
((IPackageFragmentRoot) element).isArchive()) {
return false;
- }
+ }
try {
if (element instanceof IPackageFragment &&
!((IPackageFragment) element).hasChildren()) {
@@ -852,7 +853,7 @@ public class AndroidJUnitLaunchConfigurationTab extends AbstractLaunchConfigurat
}
};
- StandardJavaElementContentProvider provider = new StandardJavaElementContentProvider();
+ AndroidJavaElementContentProvider provider = new AndroidJavaElementContentProvider();
ILabelProvider labelProvider = new JavaElementLabelProvider(
JavaElementLabelProvider.SHOW_DEFAULT);
ElementTreeSelectionDialog dialog = new ElementTreeSelectionDialog(getShell(),
@@ -954,7 +955,7 @@ public class AndroidJUnitLaunchConfigurationTab extends AbstractLaunchConfigurat
try {
mInstrValidator = new InstrumentationRunnerValidator(project);
mInstrumentations = (mInstrValidator == null ? null :
- mInstrValidator.getInstrumentations());
+ mInstrValidator.getInstrumentationNames());
if (mInstrumentations != null) {
mInstrumentationCombo.removeAll();
for (String instrumentation : mInstrumentations) {
@@ -974,4 +975,23 @@ public class AndroidJUnitLaunchConfigurationTab extends AbstractLaunchConfigurat
mInstrumentations = null;
mInstrumentationCombo.removeAll();
}
+
+ /**
+ * Overrides the {@link StandardJavaElementContentProvider} to only display Android projects
+ */
+ private static class AndroidJavaElementContentProvider
+ extends StandardJavaElementContentProvider {
+
+ /**
+ * Override parent to return only Android projects if at the root. Otherwise, use parent
+ * functionality.
+ */
+ @Override
+ public Object[] getChildren(Object element) {
+ if (element instanceof IJavaModel) {
+ return BaseProjectHelper.getAndroidProjects((IJavaModel) element);
+ }
+ return super.getChildren(element);
+ }
+ }
}
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitPropertyTester.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitPropertyTester.java
new file mode 100644
index 000000000..eadafee77
--- /dev/null
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitPropertyTester.java
@@ -0,0 +1,130 @@
+/*
+ * 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.launch.junit;
+
+import org.eclipse.core.expressions.PropertyTester;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.jdt.core.IClassFile;
+import org.eclipse.jdt.core.ICompilationUnit;
+import org.eclipse.jdt.core.IJavaElement;
+import org.eclipse.jdt.core.IMember;
+import org.eclipse.jdt.core.IPackageFragment;
+import org.eclipse.jdt.core.IType;
+import org.eclipse.jdt.core.JavaCore;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jdt.internal.junit.util.TestSearchEngine;
+
+/**
+ * A {@link PropertyTester} that checks if selected elements can be run as Android
+ * JUnit tests.
+ *
+ * Based on org.eclipse.jdt.internal.junit.JUnitPropertyTester. The only substantial difference in
+ * this implementation is source folders cannot be run as Android JUnit.
+ */
+@SuppressWarnings("restriction")
+public class AndroidJUnitPropertyTester extends PropertyTester {
+ private static final String PROPERTY_IS_TEST = "isTest"; //$NON-NLS-1$
+
+ private static final String PROPERTY_CAN_LAUNCH_AS_JUNIT_TEST = "canLaunchAsJUnit"; //$NON-NLS-1$
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jdt.internal.corext.refactoring.participants.properties.IPropertyEvaluator#test(java.lang.Object, java.lang.String, java.lang.String)
+ */
+ public boolean test(Object receiver, String property, Object[] args, Object expectedValue) {
+ if (!(receiver instanceof IAdaptable)) {
+ final String elementName = (receiver == null ? "null" : //$NON-NLS-1$
+ receiver.getClass().getName());
+ throw new IllegalArgumentException(
+ String.format("Element must be of type IAdaptable, is %s", //$NON-NLS-1$
+ elementName));
+ }
+
+ IJavaElement element;
+ if (receiver instanceof IJavaElement) {
+ element = (IJavaElement) receiver;
+ } else if (receiver instanceof IResource) {
+ element = JavaCore.create((IResource) receiver);
+ if (element == null) {
+ return false;
+ }
+ } else { // is IAdaptable
+ element= (IJavaElement) ((IAdaptable) receiver).getAdapter(IJavaElement.class);
+ if (element == null) {
+ IResource resource = (IResource) ((IAdaptable) receiver).getAdapter(
+ IResource.class);
+ element = JavaCore.create(resource);
+ if (element == null) {
+ return false;
+ }
+ }
+ }
+ if (PROPERTY_IS_TEST.equals(property)) {
+ return isJUnitTest(element);
+ } else if (PROPERTY_CAN_LAUNCH_AS_JUNIT_TEST.equals(property)) {
+ return canLaunchAsJUnitTest(element);
+ }
+ throw new IllegalArgumentException(
+ String.format("Unknown test property '%s'", property)); //$NON-NLS-1$
+ }
+
+ private boolean canLaunchAsJUnitTest(IJavaElement element) {
+ try {
+ switch (element.getElementType()) {
+ case IJavaElement.JAVA_PROJECT:
+ return true; // can run, let JDT detect if there are tests
+ case IJavaElement.PACKAGE_FRAGMENT_ROOT:
+ return false; // not supported by Android test runner
+ case IJavaElement.PACKAGE_FRAGMENT:
+ return ((IPackageFragment) element).hasChildren();
+ case IJavaElement.COMPILATION_UNIT:
+ case IJavaElement.CLASS_FILE:
+ case IJavaElement.TYPE:
+ case IJavaElement.METHOD:
+ return isJUnitTest(element);
+ default:
+ return false;
+ }
+ } catch (JavaModelException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Return whether the target resource is a JUnit test.
+ */
+ private boolean isJUnitTest(IJavaElement element) {
+ try {
+ IType testType = null;
+ if (element instanceof ICompilationUnit) {
+ testType = (((ICompilationUnit) element)).findPrimaryType();
+ } else if (element instanceof IClassFile) {
+ testType = (((IClassFile) element)).getType();
+ } else if (element instanceof IType) {
+ testType = (IType) element;
+ } else if (element instanceof IMember) {
+ testType = ((IMember) element).getDeclaringType();
+ }
+ if (testType != null && testType.exists()) {
+ return TestSearchEngine.isTestOrTestSuite(testType);
+ }
+ } catch (CoreException e) {
+ // ignore, return false
+ }
+ return false;
+ }
+}
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/InstrumentationRunnerValidator.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/InstrumentationRunnerValidator.java
index f22fc7cda..7ebbb0997 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/InstrumentationRunnerValidator.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/InstrumentationRunnerValidator.java
@@ -18,6 +18,7 @@ package com.android.ide.eclipse.adt.launch.junit;
import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.common.AndroidConstants;
import com.android.ide.eclipse.common.project.AndroidManifestParser;
+import com.android.ide.eclipse.common.project.AndroidManifestParser.Instrumentation;
import com.android.ide.eclipse.common.project.BaseProjectHelper;
import org.eclipse.core.resources.IProject;
@@ -29,7 +30,7 @@ import org.eclipse.jdt.core.IJavaProject;
*/
class InstrumentationRunnerValidator {
private final IJavaProject mJavaProject;
- private String[] mInstrumentations = null;
+ private String[] mInstrumentationNames = null;
private boolean mHasRunnerLibrary = false;
static final String INSTRUMENTATION_OK = null;
@@ -73,7 +74,11 @@ class InstrumentationRunnerValidator {
}
private void init(AndroidManifestParser manifestParser) {
- mInstrumentations = manifestParser.getInstrumentations();
+ Instrumentation[] instrumentations = manifestParser.getInstrumentations();
+ mInstrumentationNames = new String[instrumentations.length];
+ for (int i = 0; i < instrumentations.length; i++) {
+ mInstrumentationNames[i] = instrumentations[i].getName();
+ }
mHasRunnerLibrary = hasTestRunnerLibrary(manifestParser);
}
@@ -94,13 +99,13 @@ class InstrumentationRunnerValidator {
}
/**
- * Return the set of instrumentations for the Android project.
+ * Return the set of instrumentation names for the Android project.
*
* @return nullINSTRUMENTATION_OK if valid, otherwise returns error message
*/
String validateInstrumentationRunner(String instrumentation) {
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/runtime/AndroidJUnitLaunchInfo.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/runtime/AndroidJUnitLaunchInfo.java
index 89cad97ae..8ac80cab5 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/runtime/AndroidJUnitLaunchInfo.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/runtime/AndroidJUnitLaunchInfo.java
@@ -15,35 +15,38 @@
*/
package com.android.ide.eclipse.adt.launch.junit.runtime;
-import org.eclipse.core.resources.IProject;
-
import com.android.ddmlib.IDevice;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.debug.core.ILaunch;
+
/**
* Contains info about Android JUnit launch
*/
public class AndroidJUnitLaunchInfo {
private final IProject mProject;
- private final String mTestPackage;
+ private final String mAppPackage;
private final String mRunner;
- private final boolean mDebugMode;
- private final IDevice mDevice;
-
- public AndroidJUnitLaunchInfo(IProject project, String testPackage, String runner,
- boolean debugMode, IDevice device) {
+
+ private boolean mDebugMode = false;
+ private IDevice mDevice = null;
+ private String mTestPackage = null;
+ private String mTestClass = null;
+ private String mTestMethod = null;
+ private ILaunch mLaunch = null;
+
+ public AndroidJUnitLaunchInfo(IProject project, String appPackage, String runner) {
mProject = project;
- mTestPackage = testPackage;
+ mAppPackage = appPackage;
mRunner = runner;
- mDebugMode = debugMode;
- mDevice = device;
}
-
+
public IProject getProject() {
return mProject;
}
- public String getTestPackage() {
- return mTestPackage;
+ public String getAppPackage() {
+ return mAppPackage;
}
public String getRunner() {
@@ -53,8 +56,80 @@ public class AndroidJUnitLaunchInfo {
public boolean isDebugMode() {
return mDebugMode;
}
+
+ public void setDebugMode(boolean debugMode) {
+ mDebugMode = debugMode;
+ }
public IDevice getDevice() {
return mDevice;
}
+
+ public void setDevice(IDevice device) {
+ mDevice = device;
+ }
+
+ /**
+ * Specify to run all tests within given package.
+ *
+ * @param testPackage fully qualified java package
+ */
+ public void setTestPackage(String testPackage) {
+ mTestPackage = testPackage;
+ }
+
+ /**
+ * Return the package of tests to run.
+ *
+ * @return fully qualified java package. null if not specified.
+ */
+ public String getTestPackage() {
+ return mTestPackage;
+ }
+
+ /**
+ * Sets the test class to run.
+ *
+ * @param testClass fully qualfied test class to run
+ * Expected format: x.y.x.testclass
+ */
+ public void setTestClass(String testClass) {
+ mTestClass = testClass;
+ }
+
+ /**
+ * Returns the test class to run.
+ *
+ * @return fully qualfied test class to run.
+ * null if not specified.
+ */
+ public String getTestClass() {
+ return mTestClass;
+ }
+
+ /**
+ * Sets the test method to run. testClass must also be set.
+ *
+ * @param testMethod test method to run
+ */
+ public void setTestMethod(String testMethod) {
+ mTestMethod = testMethod;
+ }
+
+ /**
+ * Returns the test method to run.
+ *
+ * @return test method to run. null if not specified.
+ */
+ public String getTestMethod() {
+ return mTestMethod;
+ }
+
+ public ILaunch getLaunch() {
+ return mLaunch;
+ }
+
+ public void setLaunch(ILaunch launch) {
+ mLaunch = launch;
+ }
}
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/runtime/RemoteAdtTestRunner.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/runtime/RemoteAdtTestRunner.java
index 0a6a3daee..962d76133 100755
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/runtime/RemoteAdtTestRunner.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/runtime/RemoteAdtTestRunner.java
@@ -69,8 +69,9 @@ public class RemoteAdtTestRunner extends RemoteTestRunner {
* executing the tests, and send it back to JDT JUnit. The second is the actual test execution,
* whose results will be communicated back in real-time to JDT JUnit.
*
- * @param testClassNames array of fully qualified test class names to execute. Cannot be empty.
- * @param testName test to execute. If null, will be ignored.
+ * @param testClassNames ignored - the AndroidJUnitLaunchInfo will be used to determine which
+ * tests to run.
+ * @param testName ignored
* @param execution used to report test progress
*/
@Override
@@ -78,16 +79,21 @@ public class RemoteAdtTestRunner extends RemoteTestRunner {
// hold onto this execution reference so it can be used to report test progress
mExecution = execution;
- RemoteAndroidTestRunner runner = new RemoteAndroidTestRunner(mLaunchInfo.getTestPackage(),
+ RemoteAndroidTestRunner runner = new RemoteAndroidTestRunner(mLaunchInfo.getAppPackage(),
mLaunchInfo.getRunner(), mLaunchInfo.getDevice());
- if (testClassNames != null && testClassNames.length > 0) {
- if (testName != null) {
- runner.setMethodName(testClassNames[0], testName);
- } else {
- runner.setClassNames(testClassNames);
- }
+ if (mLaunchInfo.getTestClass() != null) {
+ if (mLaunchInfo.getTestMethod() != null) {
+ runner.setMethodName(mLaunchInfo.getTestClass(), mLaunchInfo.getTestMethod());
+ } else {
+ runner.setClassName(mLaunchInfo.getTestClass());
+ }
}
+
+ if (mLaunchInfo.getTestPackage() != null) {
+ runner.setTestPackageName(mLaunchInfo.getTestPackage());
+ }
+
// set log only to first collect test case info, so Eclipse has correct test case count/
// tree info
runner.setLogOnly(true);
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/ApkInstallManager.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/ApkInstallManager.java
new file mode 100644
index 000000000..172c555f4
--- /dev/null
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/ApkInstallManager.java
@@ -0,0 +1,207 @@
+/*
+ * 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.project;
+
+import com.android.ddmlib.AndroidDebugBridge;
+import com.android.ddmlib.Device;
+import com.android.ddmlib.IDevice;
+import com.android.ddmlib.AndroidDebugBridge.IDebugBridgeChangeListener;
+import com.android.ddmlib.AndroidDebugBridge.IDeviceChangeListener;
+import com.android.ide.eclipse.editors.resources.manager.ResourceMonitor;
+import com.android.ide.eclipse.editors.resources.manager.ResourceMonitor.IProjectListener;
+
+import org.eclipse.core.resources.IProject;
+
+import java.util.ArrayList;
+
+/**
+ * Registers which apk was installed on which device.
+ *
+ * The goal of this class is to remember the installation of APKs on devices, and provide
+ * information about whether a new APK should be installed on a device prior to running the
+ * application from a launch configuration.
+ *
+ * The manager uses {@link IProject} and {@link IDevice} to identify the target device and the
+ * (project generating the) APK. This ensures that disconnected and reconnected devices will
+ * always receive new APKs (since the APK could be uninstalled manually).
+ *
+ * Manually uninstalling an APK from a connected device will still be a problem, but this should
+ * be a limited use case.
+ *
+ * This is a singleton. To get the instance, use {@link #getInstance()}
+ */
+public class ApkInstallManager implements IDeviceChangeListener, IDebugBridgeChangeListener,
+ IProjectListener {
+
+ private final static ApkInstallManager sThis = new ApkInstallManager();
+
+ /**
+ * Internal struct to associate a project and a device.
+ */
+ private static class ApkInstall {
+ public ApkInstall(IProject project, IDevice device) {
+ this.project = project;
+ this.device = device;
+ }
+ IProject project;
+ IDevice device;
+ }
+
+ private final ArrayList mInstallList = new ArrayList();
+
+ public static ApkInstallManager getInstance() {
+ return sThis;
+ }
+
+ /**
+ * Registers an installation of project onto device
+ * @param project The project that was installed.
+ * @param device The device that received the installation.
+ */
+ public void registerInstallation(IProject project, IDevice device) {
+ synchronized (mInstallList) {
+ mInstallList.add(new ApkInstall(project, device));
+ }
+ }
+
+ /**
+ * Returns whether a project was installed on the device.
+ * @param project the project that may have been installed.
+ * @param device the device that may have received the installation.
+ * @return
+ */
+ public boolean isApplicationInstalled(IProject project, IDevice device) {
+ synchronized (mInstallList) {
+ for (ApkInstall install : mInstallList) {
+ if (project == install.project && device == install.device) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Resets registered installations for a specific {@link IProject}.
+ * This ensures that {@link #isApplicationInstalled(IProject, IDevice)} will always return
+ * null for this specified project, for any device.
+ * @param project the project for which to reset all installations.
+ */
+ public void resetInstallationFor(IProject project) {
+ synchronized (mInstallList) {
+ for (int i = 0 ; i < mInstallList.size() ;) {
+ ApkInstall install = mInstallList.get(i);
+ if (install.project == project) {
+ mInstallList.remove(i);
+ } else {
+ i++;
+ }
+ }
+ }
+ }
+
+ private ApkInstallManager() {
+ AndroidDebugBridge.addDeviceChangeListener(this);
+ AndroidDebugBridge.addDebugBridgeChangeListener(this);
+ ResourceMonitor.getMonitor().addProjectListener(this);
+ }
+
+ /*
+ * Responds to a bridge change by clearing the full installation list.
+ * (non-Javadoc)
+ * @see com.android.ddmlib.AndroidDebugBridge.IDebugBridgeChangeListener#bridgeChanged(com.android.ddmlib.AndroidDebugBridge)
+ */
+ public void bridgeChanged(AndroidDebugBridge bridge) {
+ // the bridge changed, there is no way to know which IDevice will be which.
+ // We reset everything
+ synchronized (mInstallList) {
+ mInstallList.clear();
+ }
+ }
+
+ /*
+ * Responds to a device being disconnected by removing all installations related to this device.
+ * (non-Javadoc)
+ * @see com.android.ddmlib.AndroidDebugBridge.IDeviceChangeListener#deviceDisconnected(com.android.ddmlib.Device)
+ */
+ public void deviceDisconnected(Device device) {
+ synchronized (mInstallList) {
+ for (int i = 0 ; i < mInstallList.size() ;) {
+ ApkInstall install = mInstallList.get(i);
+ if (install.device == device) {
+ mInstallList.remove(i);
+ } else {
+ i++;
+ }
+ }
+ }
+ }
+
+ /*
+ * Responds to a close project by resetting all its installation.
+ * (non-Javadoc)
+ * @see com.android.ide.eclipse.editors.resources.manager.ResourceMonitor.IProjectListener#projectClosed(org.eclipse.core.resources.IProject)
+ */
+ public void projectClosed(IProject project) {
+ resetInstallationFor(project);
+ }
+
+ /*
+ * Responds to a close project by resetting all its installation.
+ * (non-Javadoc)
+ * @see com.android.ide.eclipse.editors.resources.manager.ResourceMonitor.IProjectListener#projectDeleted(org.eclipse.core.resources.IProject)
+ */
+ public void projectDeleted(IProject project) {
+ resetInstallationFor(project);
+ }
+
+ /*
+ * Does nothing
+ * (non-Javadoc)
+ * @see com.android.ddmlib.AndroidDebugBridge.IDeviceChangeListener#deviceChanged(com.android.ddmlib.Device, int)
+ */
+ public void deviceChanged(Device device, int changeMask) {
+ // nothing to do.
+ }
+
+ /*
+ * Does nothing
+ * (non-Javadoc)
+ * @see com.android.ddmlib.AndroidDebugBridge.IDeviceChangeListener#deviceConnected(com.android.ddmlib.Device)
+ */
+ public void deviceConnected(Device device) {
+ // nothing to do.
+ }
+
+ /*
+ * Does nothing
+ * (non-Javadoc)
+ * @see com.android.ide.eclipse.editors.resources.manager.ResourceMonitor.IProjectListener#projectOpened(org.eclipse.core.resources.IProject)
+ */
+ public void projectOpened(IProject project) {
+ // nothing to do.
+ }
+
+ /*
+ * Does nothing
+ * (non-Javadoc)
+ * @see com.android.ide.eclipse.editors.resources.manager.ResourceMonitor.IProjectListener#projectOpenedWithWorkspace(org.eclipse.core.resources.IProject)
+ */
+ public void projectOpenedWithWorkspace(IProject project) {
+ // nothing to do.
+ }
+}
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/internal/AndroidClasspathContainerInitializer.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/internal/AndroidClasspathContainerInitializer.java
index e9df77f65..b1e79b762 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/internal/AndroidClasspathContainerInitializer.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/internal/AndroidClasspathContainerInitializer.java
@@ -71,10 +71,11 @@ public class AndroidClasspathContainerInitializer extends ClasspathContainerInit
private final static String CACHE_VERSION = "01"; //$NON-NLS-1$
private final static String CACHE_VERSION_SEP = CACHE_VERSION + PATH_SEPARATOR;
- private final static int PATH_ANDROID_JAR = 0;
- private final static int PATH_ANDROID_SRC = 1;
- private final static int PATH_ANDROID_DOCS = 2;
- private final static int PATH_ANDROID_OPT_DOCS = 3;
+ private final static int CACHE_INDEX_JAR = 0;
+ private final static int CACHE_INDEX_SRC = 1;
+ private final static int CACHE_INDEX_DOCS_URI = 2;
+ private final static int CACHE_INDEX_OPT_DOCS_URI = 3;
+ private final static int CACHE_INDEX_ADD_ON_START = CACHE_INDEX_OPT_DOCS_URI;
public AndroidClasspathContainerInitializer() {
// pass
@@ -172,7 +173,8 @@ public class AndroidClasspathContainerInitializer extends ClasspathContainerInit
// if we are loaded and the target is non null, we create a valid ClassPathContainer
if (sdkIsLoaded && target != null) {
- String targetName = target.getFullName();
+
+ String targetName = target.getClasspathName();
return new AndroidClasspathContainer(
createClasspathEntries(iProject, target, targetName),
@@ -385,26 +387,35 @@ public class AndroidClasspathContainerInitializer extends ClasspathContainerInit
// now we check the paths actually exist.
// There's an exception: If the source folder for android.jar does not exist, this is
// not a problem, so we skip it.
- // Also paths[PATH_ANDROID_DOCS] is a URI to the javadoc, so we test it a bit differently.
+ // Also paths[CACHE_INDEX_DOCS_URI] is a URI to the javadoc, so we test it a
+ // bit differently.
try {
- if (new File(paths[PATH_ANDROID_JAR]).exists() == false ||
- new File(new URI(paths[PATH_ANDROID_DOCS])).exists() == false) {
+ if (new File(paths[CACHE_INDEX_JAR]).exists() == false ||
+ new File(new URI(paths[CACHE_INDEX_DOCS_URI])).exists() == false) {
return null;
}
+
+ // check the path for the add-ons, if they exist.
+ if (paths.length > CACHE_INDEX_ADD_ON_START) {
+
+ // check the docs path separately from the rest of the paths as it's a URI.
+ if (new File(new URI(paths[CACHE_INDEX_OPT_DOCS_URI])).exists() == false) {
+ return null;
+ }
+
+ // now just check the remaining paths.
+ for (int i = CACHE_INDEX_ADD_ON_START + 1; i < paths.length; i++) {
+ String path = paths[i];
+ if (path.length() > 0) {
+ File f = new File(path);
+ if (f.exists() == false) {
+ return null;
+ }
+ }
+ }
+ }
} catch (URISyntaxException e) {
return null;
- } finally {
-
- }
-
- for (int i = 3 ; i < paths.length; i++) {
- String path = paths[i];
- if (path.length() > 0) {
- File f = new File(path);
- if (f.exists() == false) {
- return null;
- }
- }
}
IClasspathEntry[] entries = createClasspathEntriesFromPaths(paths);
@@ -423,13 +434,13 @@ public class AndroidClasspathContainerInitializer extends ClasspathContainerInit
// First, we create the IClasspathEntry for the framework.
// now add the android framework to the class path.
// create the path object.
- IPath android_lib = new Path(paths[PATH_ANDROID_JAR]);
- IPath android_src = new Path(paths[PATH_ANDROID_SRC]);
+ IPath android_lib = new Path(paths[CACHE_INDEX_JAR]);
+ IPath android_src = new Path(paths[CACHE_INDEX_SRC]);
// create the java doc link.
IClasspathAttribute cpAttribute = JavaCore.newClasspathAttribute(
IClasspathAttribute.JAVADOC_LOCATION_ATTRIBUTE_NAME,
- paths[PATH_ANDROID_DOCS]);
+ paths[CACHE_INDEX_DOCS_URI]);
// create the access rule to restrict access to classes in com.android.internal
IAccessRule accessRule = JavaCore.newAccessRule(
@@ -448,7 +459,7 @@ public class AndroidClasspathContainerInitializer extends ClasspathContainerInit
// now deal with optional libraries
if (paths.length >= 5) {
- String docPath = paths[PATH_ANDROID_OPT_DOCS];
+ String docPath = paths[CACHE_INDEX_OPT_DOCS_URI];
int i = 4;
while (i < paths.length) {
Path jarPath = new Path(paths[i++]);
@@ -533,21 +544,21 @@ public class AndroidClasspathContainerInitializer extends ClasspathContainerInit
}
// compare the main paths (android.jar, main sources, main javadoc)
- if (new File(targetPaths[PATH_ANDROID_JAR]).equals(
- new File(cachedPaths[PATH_ANDROID_JAR])) == false ||
- new File(targetPaths[PATH_ANDROID_SRC]).equals(
- new File(cachedPaths[PATH_ANDROID_SRC])) == false ||
- new File(targetPaths[PATH_ANDROID_DOCS]).equals(
- new File(cachedPaths[PATH_ANDROID_DOCS])) == false) {
+ if (new File(targetPaths[CACHE_INDEX_JAR]).equals(
+ new File(cachedPaths[CACHE_INDEX_JAR])) == false ||
+ new File(targetPaths[CACHE_INDEX_SRC]).equals(
+ new File(cachedPaths[CACHE_INDEX_SRC])) == false ||
+ new File(targetPaths[CACHE_INDEX_DOCS_URI]).equals(
+ new File(cachedPaths[CACHE_INDEX_DOCS_URI])) == false) {
// different paths, force resolve again.
i++;
continue;
}
- if (cachedPaths.length > PATH_ANDROID_OPT_DOCS) {
+ if (cachedPaths.length > CACHE_INDEX_OPT_DOCS_URI) {
// compare optional libraries javadoc
- if (new File(targetPaths[PATH_ANDROID_OPT_DOCS]).equals(
- new File(cachedPaths[PATH_ANDROID_OPT_DOCS])) == false) {
+ if (new File(targetPaths[CACHE_INDEX_OPT_DOCS_URI]).equals(
+ new File(cachedPaths[CACHE_INDEX_OPT_DOCS_URI])) == false) {
// different paths, force resolve again.
i++;
continue;
@@ -612,7 +623,7 @@ public class AndroidClasspathContainerInitializer extends ClasspathContainerInit
// all the optional libraries use the same javadoc, so we start with this
String targetDocPath = target.getPath(IAndroidTarget.DOCS);
if (targetDocPath != null) {
- paths.add(targetDocPath);
+ paths.add(ProjectHelper.getJavaDocPath(targetDocPath));
} else {
// we add an empty string, to always have the same count.
paths.add("");
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringInputPage.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringInputPage.java
index 5ffeeb05f..7303b0210 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringInputPage.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringInputPage.java
@@ -17,10 +17,10 @@
package com.android.ide.eclipse.adt.refactorings.extractstring;
+import com.android.ide.eclipse.adt.ui.ConfigurationSelector;
import com.android.ide.eclipse.common.AndroidConstants;
import com.android.ide.eclipse.editors.resources.configurations.FolderConfiguration;
import com.android.ide.eclipse.editors.resources.manager.ResourceFolderType;
-import com.android.ide.eclipse.editors.wizards.ConfigurationSelector;
import com.android.sdklib.SdkConstants;
import org.eclipse.core.resources.IFolder;
@@ -58,10 +58,8 @@ class ExtractStringInputPage extends UserInputWizardPage implements IWizardPage
/** The project where the user selection happened. */
private final IProject mProject;
- /** Field displaying the user-selected string to be replaced. */
- private Label mStringLabel;
/** Test field where the user enters the new ID to be generated or replaced with. */
- private Text mNewIdTextField;
+ private Text mStringIdField;
/** The configuration selector, to select the resource path of the XML file. */
private ConfigurationSelector mConfigSelector;
/** The combo to display the existing XML files or enter a new one. */
@@ -78,7 +76,9 @@ class ExtractStringInputPage extends UserInputWizardPage implements IWizardPage
private static final String RES_FOLDER_REL =
SdkConstants.FD_RESOURCES + AndroidConstants.WS_SEP;
- private static final String DEFAULT_RES_FILE_PATH = "/res/values/strings.xml";
+ private static final String DEFAULT_RES_FILE_PATH = "/res/values/strings.xml"; //$NON-NLS-1$
+
+ private XmlStringFileHelper mXmlHelper = new XmlStringFileHelper();
public ExtractStringInputPage(IProject project) {
super("ExtractStringInputPage"); //$NON-NLS-1$
@@ -92,14 +92,12 @@ class ExtractStringInputPage extends UserInputWizardPage implements IWizardPage
* {@link ExtractStringRefactoring}.
*/
public void createControl(Composite parent) {
-
Composite content = new Composite(parent, SWT.NONE);
-
GridLayout layout = new GridLayout();
layout.numColumns = 1;
content.setLayout(layout);
- createStringReplacementGroup(content);
+ createStringGroup(content);
createResFileGroup(content);
validatePage();
@@ -112,28 +110,43 @@ class ExtractStringInputPage extends UserInputWizardPage implements IWizardPage
*
* @param content A composite with a 1-column grid layout
*/
- private void createStringReplacementGroup(Composite content) {
+ public void createStringGroup(Composite content) {
final ExtractStringRefactoring ref = getOurRefactoring();
Group group = new Group(content, SWT.NONE);
group.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
- group.setText("String Replacement");
+ if (ref.getMode() == ExtractStringRefactoring.Mode.EDIT_SOURCE) {
+ group.setText("String Replacement");
+ } else {
+ group.setText("New String");
+ }
GridLayout layout = new GridLayout();
layout.numColumns = 2;
group.setLayout(layout);
- // line: String found in selection
+ // line: Textfield for string value (based on selection, if any)
Label label = new Label(group, SWT.NONE);
- label.setText("String:");
+ label.setText("String");
String selectedString = ref.getTokenString();
- mStringLabel = new Label(group, SWT.NONE);
- mStringLabel.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
- mStringLabel.setText(selectedString != null ? selectedString : "");
+ final Text stringValueField = new Text(group, SWT.SINGLE | SWT.LEFT | SWT.BORDER);
+ stringValueField.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ stringValueField.setText(selectedString != null ? selectedString : ""); //$NON-NLS-1$
+
+ ref.setNewStringValue(stringValueField.getText());
+
+ stringValueField.addModifyListener(new ModifyListener() {
+ public void modifyText(ModifyEvent e) {
+ if (validatePage()) {
+ ref.setNewStringValue(stringValueField.getText());
+ }
+ }
+ });
+
// TODO provide an option to replace all occurences of this string instead of
// just the one.
@@ -141,18 +154,24 @@ class ExtractStringInputPage extends UserInputWizardPage implements IWizardPage
// line : Textfield for new ID
label = new Label(group, SWT.NONE);
- label.setText("Replace by R.string.");
+ if (ref.getMode() == ExtractStringRefactoring.Mode.EDIT_SOURCE) {
+ label.setText("Replace by R.string.");
+ } else if (ref.getMode() == ExtractStringRefactoring.Mode.SELECT_NEW_ID) {
+ label.setText("New R.string.");
+ } else {
+ label.setText("ID R.string.");
+ }
- mNewIdTextField = new Text(group, SWT.SINGLE | SWT.LEFT | SWT.BORDER);
- mNewIdTextField.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
- mNewIdTextField.setText(guessId(selectedString));
+ mStringIdField = new Text(group, SWT.SINGLE | SWT.LEFT | SWT.BORDER);
+ mStringIdField.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ mStringIdField.setText(guessId(selectedString));
- ref.setReplacementStringId(mNewIdTextField.getText().trim());
+ ref.setNewStringId(mStringIdField.getText().trim());
- mNewIdTextField.addModifyListener(new ModifyListener() {
+ mStringIdField.addModifyListener(new ModifyListener() {
public void modifyText(ModifyEvent e) {
if (validatePage()) {
- ref.setReplacementStringId(mNewIdTextField.getText().trim());
+ ref.setNewStringId(mStringIdField.getText().trim());
}
}
});
@@ -211,6 +230,10 @@ class ExtractStringInputPage extends UserInputWizardPage implements IWizardPage
* Utility method to guess a suitable new XML ID based on the selected string.
*/
private String guessId(String text) {
+ if (text == null) {
+ return ""; //$NON-NLS-1$
+ }
+
// make lower case
text = text.toLowerCase();
@@ -242,9 +265,9 @@ class ExtractStringInputPage extends UserInputWizardPage implements IWizardPage
// Analyze fatal errors.
- String text = mNewIdTextField.getText().trim();
+ String text = mStringIdField.getText().trim();
if (text == null || text.length() < 1) {
- setErrorMessage("Please provide a resource ID to replace with.");
+ setErrorMessage("Please provide a resource ID.");
success = false;
} else {
for (int i = 0; i < text.length(); i++) {
@@ -283,15 +306,19 @@ class ExtractStringInputPage extends UserInputWizardPage implements IWizardPage
ref.setTargetFile(resFile);
sLastResFilePath.put(mProject.getFullPath().toPortableString(), resFile);
- if (ref.isResIdDuplicate(resFile, text)) {
- setMessage(
- String.format("There's already a string item called '%1$s' in %2$s.",
- text, resFile),
- WizardPage.WARNING);
+ if (mXmlHelper.isResIdDuplicate(mProject, resFile, text)) {
+ String msg = String.format("There's already a string item called '%1$s' in %2$s.",
+ text, resFile);
+ if (ref.getMode() == ExtractStringRefactoring.Mode.SELECT_NEW_ID) {
+ setErrorMessage(msg);
+ success = false;
+ } else {
+ setMessage(msg, WizardPage.WARNING);
+ }
} else if (mProject.findMember(resFile) == null) {
setMessage(
String.format("File %2$s does not exist and will be created.",
- text, resFile),
+ text, resFile),
WizardPage.INFORMATION);
} else {
setMessage(null);
@@ -336,7 +363,7 @@ class ExtractStringInputPage extends UserInputWizardPage implements IWizardPage
}
// get current leafname, if any
- String leafName = "";
+ String leafName = ""; //$NON-NLS-1$
String currPath = mResFileCombo.getText();
Matcher m = mPathRegex.matcher(currPath);
if (m.matches()) {
@@ -345,7 +372,7 @@ class ExtractStringInputPage extends UserInputWizardPage implements IWizardPage
currPath = m.group(1);
} else {
// There was a path but it was invalid. Ignore it.
- currPath = "";
+ currPath = ""; //$NON-NLS-1$
}
// recreate the res path from the current configuration
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringRefactoring.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringRefactoring.java
index 430ff1819..8a38e5268 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringRefactoring.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringRefactoring.java
@@ -18,7 +18,6 @@ package com.android.ide.eclipse.adt.refactorings.extractstring;
import com.android.ide.eclipse.common.AndroidConstants;
import com.android.ide.eclipse.common.project.AndroidManifestParser;
-import com.android.ide.eclipse.common.project.AndroidXPathFactory;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
@@ -64,8 +63,6 @@ import org.eclipse.text.edits.MultiTextEdit;
import org.eclipse.text.edits.ReplaceEdit;
import org.eclipse.text.edits.TextEdit;
import org.eclipse.text.edits.TextEditGroup;
-import org.w3c.dom.NodeList;
-import org.xml.sax.InputSource;
import java.io.BufferedReader;
import java.io.IOException;
@@ -73,14 +70,9 @@ import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;
-import java.util.HashSet;
import java.util.List;
import java.util.Map;
-import javax.xml.xpath.XPath;
-import javax.xml.xpath.XPathConstants;
-import javax.xml.xpath.XPathExpressionException;
-
/**
* This refactoring extracts a string from a file and replaces it by an Android resource ID
* such as R.string.foo.
@@ -105,8 +97,8 @@ import javax.xml.xpath.XPathExpressionException;
*
On success, the wizard is shown, which let the user input the new ID to use.
*
The wizard sets the user input values into this refactoring instance, e.g. the new string
* ID, the XML file to update, etc. The wizard does use the utility method
- * {@link #isResIdDuplicate(String, String)} to check whether the new ID is already defined
- * in the target XML file.
+ * {@link XmlStringFileHelper#isResIdDuplicate(IProject, String, String)} to check whether
+ * the new ID is already defined in the target XML file.
*
Once Preview or Finish is selected in the wizard, the
* {@link #checkFinalConditions(IProgressMonitor)} is called to double-check the user input
* and compute the actual changes.
@@ -127,68 +119,152 @@ import javax.xml.xpath.XPathExpressionException;
*
TODO: Have a pref in the wizard: [x] Change other Java Files
*
*/
-class ExtractStringRefactoring extends Refactoring {
+public class ExtractStringRefactoring extends Refactoring {
- /** The file model being manipulated. */
+ public enum Mode {
+ /**
+ * the Extract String refactoring is called on an existing source file.
+ * Its purpose is then to get the selected string of the source and propose to
+ * change it by an XML id. The XML id may be a new one or an existing one.
+ */
+ EDIT_SOURCE,
+ /**
+ * The Extract String refactoring is called without any source file.
+ * Its purpose is then to create a new XML string ID or select/modify an existing one.
+ */
+ SELECT_ID,
+ /**
+ * The Extract String refactoring is called without any source file.
+ * Its purpose is then to create a new XML string ID. The ID must not already exist.
+ */
+ SELECT_NEW_ID
+ }
+
+ /** The {@link Mode} of operation of the refactoring. */
+ private final Mode mMode;
+ /** The file model being manipulated.
+ * Value is null when not on {@link Mode#EDIT_SOURCE} mode. */
private final IFile mFile;
- /** The start of the selection in {@link #mFile}. */
+ /** The project that contains {@link #mFile} and that contains the target XML file to modify. */
+ private final IProject mProject;
+ /** The start of the selection in {@link #mFile}.
+ * Value is -1 when not on {@link Mode#EDIT_SOURCE} mode. */
private final int mSelectionStart;
- /** The end of the selection in {@link #mFile}. */
+ /** The end of the selection in {@link #mFile}.
+ * Value is -1 when not on {@link Mode#EDIT_SOURCE} mode. */
private final int mSelectionEnd;
/** The compilation unit, only defined if {@link #mFile} points to a usable Java source file. */
private ICompilationUnit mUnit;
- /** The actual string selected, after UTF characters have been escaped, good for display. */
+ /** The actual string selected, after UTF characters have been escaped, good for display.
+ * Value is null when not on {@link Mode#EDIT_SOURCE} mode. */
private String mTokenString;
/** The XML string ID selected by the user in the wizard. */
private String mXmlStringId;
+ /** The XML string value. Might be different than the initial selected string. */
+ private String mXmlStringValue;
/** The path of the XML file that will define {@link #mXmlStringId}, selected by the user
* in the wizard. */
private String mTargetXmlFileWsPath;
- /** A temporary cache of R.string IDs defined by a given xml file. The key is the
- * project path of the file, the data is a set of known string Ids for that file. */
- private HashMap> mResIdCache;
- /** An instance of XPath, created lazily on demand. */
- private XPath mXPath;
/** The list of changes computed by {@link #checkFinalConditions(IProgressMonitor)} and
* used by {@link #createChange(IProgressMonitor)}. */
private ArrayList mChanges;
+ private XmlStringFileHelper mXmlHelper = new XmlStringFileHelper();
+
+ private static final String KEY_MODE = "mode"; //$NON-NLS-1$
+ private static final String KEY_FILE = "file"; //$NON-NLS-1$
+ private static final String KEY_PROJECT = "proj"; //$NON-NLS-1$
+ private static final String KEY_SEL_START = "sel-start"; //$NON-NLS-1$
+ private static final String KEY_SEL_END = "sel-end"; //$NON-NLS-1$
+ private static final String KEY_TOK_ESC = "tok-esc"; //$NON-NLS-1$
+
public ExtractStringRefactoring(Map arguments)
throws NullPointerException {
+ mMode = Mode.valueOf(arguments.get(KEY_MODE));
- IPath path = Path.fromPortableString(arguments.get("file")); //$NON-NLS-1$
- mFile = (IFile) ResourcesPlugin.getWorkspace().getRoot().findMember(path);
- mSelectionStart = Integer.parseInt(arguments.get("sel-start")); //$NON-NLS-1$
- mSelectionEnd = Integer.parseInt(arguments.get("sel-end")); //$NON-NLS-1$
- mTokenString = arguments.get("tok-esc"); //$NON-NLS-1$
+ IPath path = Path.fromPortableString(arguments.get(KEY_PROJECT));
+ mProject = (IProject) ResourcesPlugin.getWorkspace().getRoot().findMember(path);
+
+ if (mMode == Mode.EDIT_SOURCE) {
+ path = Path.fromPortableString(arguments.get(KEY_FILE));
+ mFile = (IFile) ResourcesPlugin.getWorkspace().getRoot().findMember(path);
+
+ mSelectionStart = Integer.parseInt(arguments.get(KEY_SEL_START));
+ mSelectionEnd = Integer.parseInt(arguments.get(KEY_SEL_END));
+ mTokenString = arguments.get(KEY_TOK_ESC);
+ } else {
+ mFile = null;
+ mSelectionStart = mSelectionEnd = -1;
+ mTokenString = null;
+ }
}
private Map createArgumentMap() {
HashMap args = new HashMap();
- args.put("file", mFile.getFullPath().toPortableString()); //$NON-NLS-1$
- args.put("sel-start", Integer.toString(mSelectionStart)); //$NON-NLS-1$
- args.put("sel-end", Integer.toString(mSelectionEnd)); //$NON-NLS-1$
- args.put("tok-esc", mTokenString); //$NON-NLS-1$
+ args.put(KEY_MODE, mMode.name());
+ args.put(KEY_PROJECT, mProject.getFullPath().toPortableString());
+ if (mMode == Mode.EDIT_SOURCE) {
+ args.put(KEY_FILE, mFile.getFullPath().toPortableString());
+ args.put(KEY_SEL_START, Integer.toString(mSelectionStart));
+ args.put(KEY_SEL_END, Integer.toString(mSelectionEnd));
+ args.put(KEY_TOK_ESC, mTokenString);
+ }
return args;
}
+ /**
+ * Constructor to use when the Extract String refactoring is called on an
+ * *existing* source file. Its purpose is then to get the selected string of
+ * the source and propose to change it by an XML id. The XML id may be a new one
+ * or an existing one.
+ *
+ * @param file The source file to process. Cannot be null. File must exist in workspace.
+ * @param selection The selection in the source file. Cannot be null or empty.
+ */
public ExtractStringRefactoring(IFile file, ITextSelection selection) {
+ mMode = Mode.EDIT_SOURCE;
mFile = file;
+ mProject = file.getProject();
mSelectionStart = selection.getOffset();
mSelectionEnd = mSelectionStart + Math.max(0, selection.getLength() - 1);
}
+ /**
+ * Constructor to use when the Extract String refactoring is called without
+ * any source file. Its purpose is then to create a new XML string ID.
+ *
+ * @param project The project where the target XML file to modify is located. Cannot be null.
+ * @param enforceNew If true the XML ID must be a new one. If false, an existing ID can be
+ * used.
+ */
+ public ExtractStringRefactoring(IProject project, boolean enforceNew) {
+ mMode = enforceNew ? Mode.SELECT_NEW_ID : Mode.SELECT_ID;
+ mFile = null;
+ mProject = project;
+ mSelectionStart = mSelectionEnd = -1;
+ }
+
/**
* @see org.eclipse.ltk.core.refactoring.Refactoring#getName()
*/
@Override
public String getName() {
+ if (mMode == Mode.SELECT_ID) {
+ return "Create or USe Android String";
+ } else if (mMode == Mode.SELECT_NEW_ID) {
+ return "Create New Android String";
+ }
+
return "Extract Android String";
}
+ public Mode getMode() {
+ return mMode;
+ }
+
/**
* Gets the actual string selected, after UTF characters have been escaped,
* good for display.
@@ -197,6 +273,10 @@ class ExtractStringRefactoring extends Refactoring {
return mTokenString;
}
+ public String getXmlStringId() {
+ return mXmlStringId;
+ }
+
/**
* Step 1 of 3 of the refactoring:
* Checks that the current selection meets the initial condition before the ExtractString
@@ -225,6 +305,11 @@ class ExtractStringRefactoring extends Refactoring {
try {
monitor.beginTask("Checking preconditions...", 5);
+
+ if (mMode != Mode.EDIT_SOURCE) {
+ monitor.worked(5);
+ return status;
+ }
if (!checkSourceFile(mFile, status, monitor)) {
return status;
@@ -388,7 +473,7 @@ class ExtractStringRefactoring extends Refactoring {
status.addFatalError("Missing target xml file path");
}
monitor.worked(1);
-
+
// Either that resource must not exist or it must be a writeable file.
IResource targetXml = getTargetXmlResource(mTargetXmlFileWsPath);
if (targetXml != null) {
@@ -415,9 +500,9 @@ class ExtractStringRefactoring extends Refactoring {
// Prepare the change for the XML file.
- if (!isResIdDuplicate(mTargetXmlFileWsPath, mXmlStringId)) {
+ if (!mXmlHelper.isResIdDuplicate(mProject, mTargetXmlFileWsPath, mXmlStringId)) {
// We actually change it only if the ID doesn't exist yet
- Change change = createXmlChange((IFile) targetXml, mXmlStringId, mTokenString,
+ Change change = createXmlChange((IFile) targetXml, mXmlStringId, mXmlStringValue,
status, SubMonitor.convert(monitor, 1));
if (change != null) {
mChanges.add(change);
@@ -427,12 +512,14 @@ class ExtractStringRefactoring extends Refactoring {
if (status.hasError()) {
return status;
}
-
- // Prepare the change to the Java compilation unit
- List changes = computeJavaChanges(mUnit, mXmlStringId, mTokenString,
- status, SubMonitor.convert(monitor, 1));
- if (changes != null) {
- mChanges.addAll(changes);
+
+ if (mMode == Mode.EDIT_SOURCE) {
+ // Prepare the change to the Java compilation unit
+ List changes = computeJavaChanges(mUnit, mXmlStringId, mTokenString,
+ status, SubMonitor.convert(monitor, 1));
+ if (changes != null) {
+ mChanges.addAll(changes);
+ }
}
monitor.worked(1);
@@ -479,11 +566,14 @@ class ExtractStringRefactoring extends Refactoring {
content.append("\n"); //$NON-NLS-1$
edit = new InsertEdit(0, content.toString());
- editGroup = new TextEditGroup("Create ID in new XML file", edit);
+ editGroup = new TextEditGroup("Create in new XML file", edit);
} else {
// The file exist. Attempt to parse it as a valid XML document.
try {
int[] indices = new int[2];
+
+ // TODO case where we replace the value of an existing XML String ID
+
if (findXmlOpeningTagPos(targetXml.getContents(), "resources", indices)) { //$NON-NLS-1$
// Indices[1] indicates whether we found > or />. It can only be 1 or 2.
// Indices[0] is the position of the first character of either > or />.
@@ -507,7 +597,7 @@ class ExtractStringRefactoring extends Refactoring {
}
edit = new ReplaceEdit(offset, len, content.toString());
- editGroup = new TextEditGroup("Insert ID in XML file", edit);
+ editGroup = new TextEditGroup("Insert in XML file", edit);
}
} catch (CoreException e) {
// Failed to read file. Ignore. Will return null below.
@@ -653,8 +743,7 @@ class ExtractStringRefactoring extends Refactoring {
// the FQCN of the R class.
String packageName = null;
String error = null;
- IProject proj = unit.getJavaProject().getProject();
- IResource manifestFile = proj.findMember(AndroidConstants.FN_ANDROID_MANIFEST);
+ IResource manifestFile = mProject.findMember(AndroidConstants.FN_ANDROID_MANIFEST);
if (manifestFile == null || manifestFile.getType() != IResource.FILE) {
error = "File not found";
} else {
@@ -846,7 +935,7 @@ class ExtractStringRefactoring extends Refactoring {
mXmlStringId);
ExtractStringDescriptor desc = new ExtractStringDescriptor(
- mUnit.getJavaProject().getElementName(), //project
+ mProject.getName(), //project
comment, //description
comment, //comment
createArgumentMap());
@@ -865,93 +954,27 @@ class ExtractStringRefactoring extends Refactoring {
}
- /**
- * Utility method used by the wizard to check whether the given string ID is already
- * defined in the XML file which path is given.
- *
- * @param xmlFileWsPath The project path of the XML file, e.g. "/res/values/strings.xml".
- * The given file may or may not exist.
- * @param stringId The string ID to find.
- * @return True if such a string ID is already defined.
- */
- public boolean isResIdDuplicate(String xmlFileWsPath, String stringId) {
- // This is going to be called many times on the same file.
- // Build a cache of the existing IDs for a given file.
- if (mResIdCache == null) {
- mResIdCache = new HashMap>();
- }
- HashSet cache = mResIdCache.get(xmlFileWsPath);
- if (cache == null) {
- cache = getResIdsForFile(xmlFileWsPath);
- mResIdCache.put(xmlFileWsPath, cache);
- }
-
- return cache.contains(stringId);
- }
-
- /**
- * Extract all the defined string IDs from a given file using XPath.
- *
- * @param xmlFileWsPath The project path of the file to parse. It may not exist.
- * @return The set of all string IDs defined in the file. The returned set is always non
- * null. It is empty if the file does not exist.
- */
- private HashSet getResIdsForFile(String xmlFileWsPath) {
- HashSet ids = new HashSet();
-
- if (mXPath == null) {
- mXPath = AndroidXPathFactory.newXPath();
- }
-
- // Access the project that contains the resource that contains the compilation unit
- IResource resource = getTargetXmlResource(xmlFileWsPath);
-
- if (resource != null && resource.exists() && resource.getType() == IResource.FILE) {
- InputSource source;
- try {
- source = new InputSource(((IFile) resource).getContents());
-
- // We want all the IDs in an XML structure like this:
- //
- // something
- //
-
- String xpathExpr = "/resources/string/@name"; //$NON-NLS-1$
-
- Object result = mXPath.evaluate(xpathExpr, source, XPathConstants.NODESET);
- if (result instanceof NodeList) {
- NodeList list = (NodeList) result;
- for (int n = list.getLength() - 1; n >= 0; n--) {
- String id = list.item(n).getNodeValue();
- ids.add(id);
- }
- }
-
- } catch (CoreException e1) {
- // IFile.getContents failed. Ignore.
- } catch (XPathExpressionException e) {
- // mXPath.evaluate failed. Ignore.
- }
- }
-
- return ids;
- }
-
/**
* Given a file project path, returns its resource in the same project than the
* compilation unit. The resource may not exist.
*/
private IResource getTargetXmlResource(String xmlFileWsPath) {
- IProject proj = mFile.getProject();
- IResource resource = proj.getFile(xmlFileWsPath);
+ IResource resource = mProject.getFile(xmlFileWsPath);
return resource;
}
/**
* Sets the replacement string ID. Used by the wizard to set the user input.
*/
- public void setReplacementStringId(String replacementStringId) {
- mXmlStringId = replacementStringId;
+ public void setNewStringId(String newStringId) {
+ mXmlStringId = newStringId;
+ }
+
+ /**
+ * Sets the replacement string ID. Used by the wizard to set the user input.
+ */
+ public void setNewStringValue(String newStringValue) {
+ mXmlStringValue = newStringValue;
}
/**
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringWizard.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringWizard.java
index c5b0c7d11..cfcc54621 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringWizard.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringWizard.java
@@ -25,7 +25,7 @@ import org.eclipse.ltk.ui.refactoring.RefactoringWizard;
* @see ExtractStringInputPage
* @see ExtractStringRefactoring
*/
-class ExtractStringWizard extends RefactoringWizard {
+public class ExtractStringWizard extends RefactoringWizard {
private final IProject mProject;
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/XmlStringFileHelper.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/XmlStringFileHelper.java
new file mode 100644
index 000000000..6c8bbdbd3
--- /dev/null
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/XmlStringFileHelper.java
@@ -0,0 +1,122 @@
+/*
+ * 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.refactorings.extractstring;
+
+import com.android.ide.eclipse.common.project.AndroidXPathFactory;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.CoreException;
+import org.w3c.dom.NodeList;
+import org.xml.sax.InputSource;
+
+import java.util.HashMap;
+import java.util.HashSet;
+
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathExpressionException;
+
+/**
+ *
+ */
+class XmlStringFileHelper {
+
+ /** A temporary cache of R.string IDs defined by a given xml file. The key is the
+ * project path of the file, the data is a set of known string Ids for that file. */
+ private HashMap> mResIdCache;
+ /** An instance of XPath, created lazily on demand. */
+ private XPath mXPath;
+
+ public XmlStringFileHelper() {
+ }
+
+ /**
+ * Utility method used by the wizard to check whether the given string ID is already
+ * defined in the XML file which path is given.
+ *
+ * @param project The project contain the XML file.
+ * @param xmlFileWsPath The project path of the XML file, e.g. "/res/values/strings.xml".
+ * The given file may or may not exist.
+ * @param stringId The string ID to find.
+ * @return True if such a string ID is already defined.
+ */
+ public boolean isResIdDuplicate(IProject project, String xmlFileWsPath, String stringId) {
+ // This is going to be called many times on the same file.
+ // Build a cache of the existing IDs for a given file.
+ if (mResIdCache == null) {
+ mResIdCache = new HashMap>();
+ }
+ HashSet cache = mResIdCache.get(xmlFileWsPath);
+ if (cache == null) {
+ cache = getResIdsForFile(project, xmlFileWsPath);
+ mResIdCache.put(xmlFileWsPath, cache);
+ }
+
+ return cache.contains(stringId);
+ }
+
+ /**
+ * Extract all the defined string IDs from a given file using XPath.
+ * @param project The project contain the XML file.
+ * @param xmlFileWsPath The project path of the file to parse. It may not exist.
+ * @return The set of all string IDs defined in the file. The returned set is always non
+ * null. It is empty if the file does not exist.
+ */
+ private HashSet getResIdsForFile(IProject project, String xmlFileWsPath) {
+ HashSet ids = new HashSet();
+
+ if (mXPath == null) {
+ mXPath = AndroidXPathFactory.newXPath();
+ }
+
+ // Access the project that contains the resource that contains the compilation unit
+ IResource resource = project.getFile(xmlFileWsPath);
+
+ if (resource != null && resource.exists() && resource.getType() == IResource.FILE) {
+ InputSource source;
+ try {
+ source = new InputSource(((IFile) resource).getContents());
+
+ // We want all the IDs in an XML structure like this:
+ //
+ // something
+ //
+
+ String xpathExpr = "/resources/string/@name"; //$NON-NLS-1$
+
+ Object result = mXPath.evaluate(xpathExpr, source, XPathConstants.NODESET);
+ if (result instanceof NodeList) {
+ NodeList list = (NodeList) result;
+ for (int n = list.getLength() - 1; n >= 0; n--) {
+ String id = list.item(n).getNodeValue();
+ ids.add(id);
+ }
+ }
+
+ } catch (CoreException e1) {
+ // IFile.getContents failed. Ignore.
+ } catch (XPathExpressionException e) {
+ // mXPath.evaluate failed. Ignore.
+ }
+ }
+
+ return ids;
+ }
+
+}
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/Sdk.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/Sdk.java
index ba0b56803..40b3f76ec 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/Sdk.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/Sdk.java
@@ -100,7 +100,7 @@ public class Sdk implements IProjectListener {
ISdkLog log = new ISdkLog() {
public void error(Throwable throwable, String errorFormat, Object... arg) {
if (errorFormat != null) {
- logMessages.add(String.format(errorFormat, arg));
+ logMessages.add(String.format("Error: " + errorFormat, arg));
}
if (throwable != null) {
@@ -109,7 +109,7 @@ public class Sdk implements IProjectListener {
}
public void warning(String warningFormat, Object... arg) {
- logMessages.add(String.format(warningFormat, arg));
+ logMessages.add(String.format("Warning: " + warningFormat, arg));
}
public void printf(String msgFormat, Object... arg) {
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/ConfigurationSelector.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/ui/ConfigurationSelector.java
similarity index 99%
rename from tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/ConfigurationSelector.java
rename to tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/ui/ConfigurationSelector.java
index 4a05b1ee5..651d1e016 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/ConfigurationSelector.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/ui/ConfigurationSelector.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.ide.eclipse.editors.wizards;
+package com.android.ide.eclipse.adt.ui;
import com.android.ide.eclipse.editors.resources.configurations.CountryCodeQualifier;
import com.android.ide.eclipse.editors.resources.configurations.FolderConfiguration;
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/EclipseUiHelper.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/ui/EclipseUiHelper.java
similarity index 98%
rename from tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/EclipseUiHelper.java
rename to tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/ui/EclipseUiHelper.java
index 6dc856233..55878bf28 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/EclipseUiHelper.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/ui/EclipseUiHelper.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.ide.eclipse.common;
+package com.android.ide.eclipse.adt.ui;
import org.eclipse.ui.IViewPart;
import org.eclipse.ui.IWorkbenchPage;
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/ReferenceChooserDialog.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/ui/ReferenceChooserDialog.java
similarity index 67%
rename from tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/ReferenceChooserDialog.java
rename to tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/ui/ReferenceChooserDialog.java
index 6913ce07d..966c5c81f 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/ReferenceChooserDialog.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/ui/ReferenceChooserDialog.java
@@ -14,13 +14,16 @@
* limitations under the License.
*/
-package com.android.ide.eclipse.editors.wizards;
+package com.android.ide.eclipse.adt.ui;
import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.adt.refactorings.extractstring.ExtractStringRefactoring;
+import com.android.ide.eclipse.adt.refactorings.extractstring.ExtractStringWizard;
import com.android.ide.eclipse.common.resources.IResourceRepository;
import com.android.ide.eclipse.common.resources.ResourceItem;
import com.android.ide.eclipse.common.resources.ResourceType;
+import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.dialogs.DialogSettings;
@@ -30,14 +33,20 @@ import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.TreePath;
import org.eclipse.jface.viewers.TreeSelection;
import org.eclipse.jface.viewers.TreeViewer;
+import org.eclipse.ltk.ui.refactoring.RefactoringWizard;
+import org.eclipse.ltk.ui.refactoring.RefactoringWizardOpenOperation;
import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Tree;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.dialogs.FilteredTree;
import org.eclipse.ui.dialogs.PatternFilter;
import org.eclipse.ui.dialogs.SelectionStatusDialog;
@@ -58,21 +67,25 @@ public class ReferenceChooserDialog extends SelectionStatusDialog {
private IResourceRepository mResources;
private String mCurrentResource;
-
private FilteredTree mFilteredTree;
+ private Button mNewResButton;
+ private final IProject mProject;
+ private TreeViewer mTreeViewer;
/**
+ * @param project
* @param parent
*/
- public ReferenceChooserDialog(IResourceRepository resources, Shell parent) {
+ public ReferenceChooserDialog(IProject project, IResourceRepository resources, Shell parent) {
super(parent);
+ mProject = project;
+ mResources = resources;
int shellStyle = getShellStyle();
setShellStyle(shellStyle | SWT.MAX | SWT.RESIZE);
- setTitle("Reference Dialog");
+ setTitle("Reference Chooser");
setMessage(String.format("Choose a resource"));
- mResources = resources;
setDialogBoundsSettings(sDialogSettings, getDialogBoundsStrategy());
}
@@ -113,13 +126,26 @@ public class ReferenceChooserDialog extends SelectionStatusDialog {
// create the filtered tree
createFilteredTree(top);
-
+
// setup the initial selection
setupInitialSelection();
+ // create the "New Resource" button
+ createNewResButtons(top);
+
return top;
}
+ /**
+ * Creates the "New Resource" button.
+ * @param top the parent composite
+ */
+ private void createNewResButtons(Composite top) {
+ mNewResButton = new Button(top, SWT.NONE);
+ mNewResButton.addSelectionListener(new OnNewResButtonSelected());
+ updateNewResButton();
+ }
+
private void createFilteredTree(Composite parent) {
mFilteredTree = new FilteredTree(parent, SWT.BORDER | SWT.SINGLE | SWT.FULL_SELECTION,
new PatternFilter());
@@ -134,8 +160,8 @@ public class ReferenceChooserDialog extends SelectionStatusDialog {
mFilteredTree.setLayoutData(data);
mFilteredTree.setFont(parent.getFont());
- TreeViewer treeViewer = mFilteredTree.getViewer();
- Tree tree = treeViewer.getTree();
+ mTreeViewer = mFilteredTree.getViewer();
+ Tree tree = mTreeViewer.getTree();
tree.addSelectionListener(new SelectionListener() {
public void widgetDefaultSelected(SelectionEvent e) {
@@ -147,13 +173,14 @@ public class ReferenceChooserDialog extends SelectionStatusDialog {
}
});
- treeViewer.setLabelProvider(new ResourceLabelProvider());
- treeViewer.setContentProvider(new ResourceContentProvider(false /* fullLevels */));
- treeViewer.setInput(mResources);
+ mTreeViewer.setLabelProvider(new ResourceLabelProvider());
+ mTreeViewer.setContentProvider(new ResourceContentProvider(false /* fullLevels */));
+ mTreeViewer.setInput(mResources);
}
protected void handleSelection() {
validateCurrentSelection();
+ updateNewResButton();
}
protected void handleDoubleClick() {
@@ -205,6 +232,72 @@ public class ReferenceChooserDialog extends SelectionStatusDialog {
return status.isOK();
}
+
+ /**
+ * Updates the new res button when the list selection changes.
+ * The name of the button changes depending on the resource.
+ */
+ private void updateNewResButton() {
+ ResourceType type = getSelectedResourceType();
+
+ // We only support adding new strings right now
+ mNewResButton.setEnabled(type == ResourceType.STRING);
+
+ String title = String.format("New %1$s...",
+ type == null ? "Resource" : type.getDisplayName());
+ mNewResButton.setText(title);
+ mNewResButton.pack();
+ }
+
+ /**
+ * Callback invoked when the mNewResButton is selected by the user.
+ */
+ private class OnNewResButtonSelected extends SelectionAdapter {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ super.widgetSelected(e);
+
+ ResourceType type = getSelectedResourceType();
+
+ // We currently only support strings
+ if (type == ResourceType.STRING) {
+
+ ExtractStringRefactoring ref = new ExtractStringRefactoring(
+ mProject, true /*enforceNew*/);
+ RefactoringWizard wizard = new ExtractStringWizard(ref, mProject);
+ RefactoringWizardOpenOperation op = new RefactoringWizardOpenOperation(wizard);
+ try {
+ IWorkbench w = PlatformUI.getWorkbench();
+ if (op.run(w.getDisplay().getActiveShell(), wizard.getDefaultPageTitle()) ==
+ IDialogConstants.OK_ID) {
+ mTreeViewer.refresh();
+
+ // select it if possible
+ setupInitialSelection(type, ref.getXmlStringId());
+ }
+ } catch (InterruptedException ex) {
+ // Interrupted. Pass.
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns the {@link ResourceType} of the selected element, if any.
+ * Returns null if nothing suitable is selected.
+ */
+ private ResourceType getSelectedResourceType() {
+ ResourceType type = null;
+
+ TreePath selection = getSelection();
+ if (selection != null && selection.getSegmentCount() > 0) {
+ Object first = selection.getFirstSegment();
+ if (first instanceof ResourceType) {
+ type = (ResourceType) first;
+ }
+ }
+ return type;
+ }
/**
* Sets up the initial selection.
@@ -250,7 +343,9 @@ public class ReferenceChooserDialog extends SelectionStatusDialog {
if (resourceName.equals(resourceItem.getName())) {
// name of the resource match, we select it,
TreePath treePath = new TreePath(new Object[] { resourceType, resourceItem });
- mFilteredTree.getViewer().setSelection(new TreeSelection(treePath));
+ mFilteredTree.getViewer().setSelection(
+ new TreeSelection(treePath),
+ true /*reveal*/);
// and we're done.
return;
@@ -260,7 +355,9 @@ public class ReferenceChooserDialog extends SelectionStatusDialog {
// if we get here, the resource type is valid, but the resource is missing.
// we select and expand the resource type element.
TreePath treePath = new TreePath(new Object[] { resourceType });
- mFilteredTree.getViewer().setSelection(new TreeSelection(treePath));
+ mFilteredTree.getViewer().setSelection(
+ new TreeSelection(treePath),
+ true /*reveal*/);
mFilteredTree.getViewer().setExpandedState(resourceType, true /* expanded */);
}
}
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/ui/ResourceChooser.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/ui/ResourceChooser.java
new file mode 100644
index 000000000..d7eeb0455
--- /dev/null
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/ui/ResourceChooser.java
@@ -0,0 +1,293 @@
+/*
+ * Copyright (C) 2007 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.ui;
+
+import com.android.ide.eclipse.adt.refactorings.extractstring.ExtractStringRefactoring;
+import com.android.ide.eclipse.adt.refactorings.extractstring.ExtractStringWizard;
+import com.android.ide.eclipse.common.resources.IResourceRepository;
+import com.android.ide.eclipse.common.resources.ResourceItem;
+import com.android.ide.eclipse.common.resources.ResourceType;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.ltk.ui.refactoring.RefactoringWizard;
+import org.eclipse.ltk.ui.refactoring.RefactoringWizardOpenOperation;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.dialogs.AbstractElementListSelectionDialog;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * A dialog to let the user select a resource based on a resource type.
+ */
+public class ResourceChooser extends AbstractElementListSelectionDialog {
+
+ private Pattern mProjectResourcePattern;
+
+ private ResourceType mResourceType;
+
+ private IResourceRepository mProjectResources;
+
+ private final static boolean SHOW_SYSTEM_RESOURCE = false; // TODO re-enable at some point
+ private Pattern mSystemResourcePattern;
+ private IResourceRepository mSystemResources;
+ private Button mProjectButton;
+ private Button mSystemButton;
+
+ private String mCurrentResource;
+
+ private final IProject mProject;
+
+ /**
+ * Creates a Resource Chooser dialog.
+ * @param project Project being worked on
+ * @param type The type of the resource to choose
+ * @param projectResources The repository for the project
+ * @param systemResources The System resource repository
+ * @param parent the parent shell
+ */
+ public ResourceChooser(IProject project, ResourceType type,
+ IResourceRepository projectResources,
+ IResourceRepository systemResources,
+ Shell parent) {
+ super(parent, new ResourceLabelProvider());
+ mProject = project;
+
+ mResourceType = type;
+ mProjectResources = projectResources;
+
+ mProjectResourcePattern = Pattern.compile(
+ "@" + mResourceType.getName() + "/(.+)"); //$NON-NLS-1$ //$NON-NLS-2$
+
+ if (SHOW_SYSTEM_RESOURCE) {
+ mSystemResources = systemResources;
+ mSystemResourcePattern = Pattern.compile(
+ "@android:" + mResourceType.getName() + "/(.+)"); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ setTitle("Resource Chooser");
+ setMessage(String.format("Choose a %1$s resource",
+ mResourceType.getDisplayName().toLowerCase()));
+ }
+
+ public void setCurrentResource(String resource) {
+ mCurrentResource = resource;
+ }
+
+ public String getCurrentResource() {
+ return mCurrentResource;
+ }
+
+ @Override
+ protected void computeResult() {
+ Object[] elements = getSelectedElements();
+ if (elements.length == 1 && elements[0] instanceof ResourceItem) {
+ ResourceItem item = (ResourceItem)elements[0];
+
+ mCurrentResource = mResourceType.getXmlString(item,
+ SHOW_SYSTEM_RESOURCE && mSystemButton.getSelection());
+ }
+ }
+
+ @Override
+ protected Control createDialogArea(Composite parent) {
+ Composite top = (Composite)super.createDialogArea(parent);
+
+ createMessageArea(top);
+
+ createButtons(top);
+ createFilterText(top);
+ createFilteredList(top);
+
+ // create the "New Resource" button
+ createNewResButtons(top);
+
+ setupResourceList();
+ selectResourceString(mCurrentResource);
+
+ return top;
+ }
+
+ /**
+ * Creates the radio button to switch between project and system resources.
+ * @param top the parent composite
+ */
+ private void createButtons(Composite top) {
+ if (!SHOW_SYSTEM_RESOURCE) {
+ return;
+ }
+ mProjectButton = new Button(top, SWT.RADIO);
+ mProjectButton.setText("Project Resources");
+ mProjectButton.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ super.widgetSelected(e);
+ if (mProjectButton.getSelection()) {
+ setListElements(mProjectResources.getResources(mResourceType));
+ }
+ }
+ });
+ mSystemButton = new Button(top, SWT.RADIO);
+ mSystemButton.setText("System Resources");
+ mSystemButton.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ super.widgetSelected(e);
+ if (mProjectButton.getSelection()) {
+ setListElements(mSystemResources.getResources(mResourceType));
+ }
+ }
+ });
+ }
+
+ /**
+ * Creates the "New Resource" button.
+ * @param top the parent composite
+ */
+ private void createNewResButtons(Composite top) {
+
+ Button newResButton = new Button(top, SWT.NONE);
+
+ String title = String.format("New %1$s...", mResourceType.getDisplayName());
+ newResButton.setText(title);
+
+ // We only support adding new strings right now
+ newResButton.setEnabled(mResourceType == ResourceType.STRING);
+
+ newResButton.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ super.widgetSelected(e);
+
+ if (mResourceType == ResourceType.STRING) {
+ createNewString();
+ }
+ }
+ });
+ }
+
+ private void createNewString() {
+ ExtractStringRefactoring ref = new ExtractStringRefactoring(
+ mProject, true /*enforceNew*/);
+ RefactoringWizard wizard = new ExtractStringWizard(ref, mProject);
+ RefactoringWizardOpenOperation op = new RefactoringWizardOpenOperation(wizard);
+ try {
+ IWorkbench w = PlatformUI.getWorkbench();
+ if (op.run(w.getDisplay().getActiveShell(), wizard.getDefaultPageTitle()) ==
+ IDialogConstants.OK_ID) {
+
+ // Recompute the "current resource" to select the new id
+ setupResourceList();
+
+ // select it if possible
+ selectItemName(ref.getXmlStringId());
+ }
+ } catch (InterruptedException ex) {
+ // Interrupted. Pass.
+ }
+ }
+
+ /**
+ * @return The repository currently selected.
+ */
+ private IResourceRepository getCurrentRepository() {
+ IResourceRepository repo = mProjectResources;
+
+ if (SHOW_SYSTEM_RESOURCE && mSystemButton.getSelection()) {
+ repo = mSystemResources;
+ }
+ return repo;
+ }
+
+ /**
+ * Setups the current list.
+ */
+ private void setupResourceList() {
+ IResourceRepository repo = getCurrentRepository();
+ setListElements(repo.getResources(mResourceType));
+ }
+
+ /**
+ * Select an item by its name, if possible.
+ */
+ private void selectItemName(String itemName) {
+ if (itemName == null) {
+ return;
+ }
+
+ IResourceRepository repo = getCurrentRepository();
+
+ ResourceItem[] items = repo.getResources(mResourceType);
+
+ for (ResourceItem item : items) {
+ if (itemName.equals(item.getName())) {
+ setSelection(new Object[] { item });
+ break;
+ }
+ }
+ }
+
+ /**
+ * Select an item by its full resource string.
+ * This also selects between project and system repository based on the resource string.
+ */
+ private void selectResourceString(String resourceString) {
+ boolean isSystem = false;
+ String itemName = null;
+
+ // Is this a system resource?
+ // If not a system resource or if they are not available, this will be a project res.
+ if (SHOW_SYSTEM_RESOURCE) {
+ Matcher m = mSystemResourcePattern.matcher(resourceString);
+ if (m.matches()) {
+ itemName = m.group(1);
+ isSystem = true;
+ }
+ }
+
+ if (!isSystem && itemName == null) {
+ // Try to match project resource name
+ Matcher m = mProjectResourcePattern.matcher(resourceString);
+ if (m.matches()) {
+ itemName = m.group(1);
+ }
+ }
+
+ // Update the repository selection
+ if (SHOW_SYSTEM_RESOURCE) {
+ mProjectButton.setSelection(!isSystem);
+ mSystemButton.setSelection(isSystem);
+ }
+
+ // Update the list
+ setupResourceList();
+
+ // If we have a selection name, select it
+ if (itemName != null) {
+ selectItemName(itemName);
+ }
+ }
+}
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/ResourceContentProvider.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/ui/ResourceContentProvider.java
similarity index 98%
rename from tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/ResourceContentProvider.java
rename to tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/ui/ResourceContentProvider.java
index 7c6a539ca..3792fe312 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/ResourceContentProvider.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/ui/ResourceContentProvider.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.ide.eclipse.editors.wizards;
+package com.android.ide.eclipse.adt.ui;
import com.android.ide.eclipse.common.resources.IResourceRepository;
import com.android.ide.eclipse.common.resources.ResourceItem;
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/ResourceLabelProvider.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/ui/ResourceLabelProvider.java
similarity index 99%
rename from tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/ResourceLabelProvider.java
rename to tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/ui/ResourceLabelProvider.java
index 024d08433..f7c26346a 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/ResourceLabelProvider.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/ui/ResourceLabelProvider.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.ide.eclipse.editors.wizards;
+package com.android.ide.eclipse.adt.ui;
import com.android.ide.eclipse.common.resources.IIdResourceItem;
import com.android.ide.eclipse.common.resources.ResourceItem;
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/actions/NewXmlFileAction.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/actions/NewXmlFileAction.java
index 8c4a115b4..d1530d4ea 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/actions/NewXmlFileAction.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/actions/NewXmlFileAction.java
@@ -16,7 +16,7 @@
package com.android.ide.eclipse.adt.wizards.actions;
-import com.android.ide.eclipse.editors.wizards.NewXmlFileWizard;
+import com.android.ide.eclipse.adt.wizards.newxmlfile.NewXmlFileWizard;
import org.eclipse.jface.action.IAction;
import org.eclipse.ui.IWorkbenchWizard;
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/NewXmlFileWizardAction.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/actions/NewXmlFileWizardAction.java
similarity index 94%
rename from tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/NewXmlFileWizardAction.java
rename to tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/actions/NewXmlFileWizardAction.java
index c117b4e57..20cfc8275 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/NewXmlFileWizardAction.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/actions/NewXmlFileWizardAction.java
@@ -14,9 +14,9 @@
* limitations under the License.
*/
-package com.android.ide.eclipse.adt.project;
+package com.android.ide.eclipse.adt.wizards.actions;
-import com.android.ide.eclipse.editors.wizards.NewXmlFileWizard;
+import com.android.ide.eclipse.adt.wizards.newxmlfile.NewXmlFileWizard;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.viewers.ISelection;
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newproject/NewProjectCreationPage.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newproject/NewProjectCreationPage.java
index e26b31cc3..6e8ff47fe 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newproject/NewProjectCreationPage.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newproject/NewProjectCreationPage.java
@@ -27,6 +27,7 @@ import com.android.ide.eclipse.adt.sdk.Sdk;
import com.android.ide.eclipse.adt.sdk.Sdk.ITargetChangeListener;
import com.android.ide.eclipse.common.AndroidConstants;
import com.android.ide.eclipse.common.project.AndroidManifestParser;
+import com.android.ide.eclipse.common.project.AndroidManifestParser.Activity;
import com.android.sdklib.IAndroidTarget;
import com.android.sdklib.SdkConstants;
import com.android.sdklib.project.ProjectProperties;
@@ -859,6 +860,7 @@ public class NewProjectCreationPage extends WizardPage {
}
String packageName = null;
+ Activity activity = null;
String activityName = null;
int minSdkVersion = AndroidManifestParser.INVALID_MIN_SDK;
try {
@@ -866,11 +868,11 @@ public class NewProjectCreationPage extends WizardPage {
minSdkVersion = manifestData.getApiLevelRequirement();
// try to get the first launcher activity. If none, just take the first activity.
- activityName = manifestData.getLauncherActivity();
- if (activityName == null) {
- String[] activities = manifestData.getActivities();
+ activity = manifestData.getLauncherActivity();
+ if (activity == null) {
+ Activity[] activities = manifestData.getActivities();
if (activities != null && activities.length > 0) {
- activityName = activities[0];
+ activity = activities[0];
}
}
} catch (Exception e) {
@@ -881,7 +883,10 @@ public class NewProjectCreationPage extends WizardPage {
mPackageNameField.setText(packageName);
}
- activityName = AndroidManifestParser.extractActivityName(activityName, packageName);
+ if (activity != null) {
+ activityName = AndroidManifestParser.extractActivityName(activity.getName(),
+ packageName);
+ }
if (activityName != null && activityName.length() > 0) {
mInternalActivityNameUpdate = true;
@@ -1136,7 +1141,7 @@ public class NewProjectCreationPage extends WizardPage {
MSG_ERROR);
}
- String[] activities = manifestData.getActivities();
+ Activity[] activities = manifestData.getActivities();
if (activities == null || activities.length == 0) {
// This is acceptable now as long as no activity needs to be created
if (isCreateActivity()) {
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/NewXmlFileCreationPage.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newxmlfile/NewXmlFileCreationPage.java
similarity index 99%
rename from tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/NewXmlFileCreationPage.java
rename to tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newxmlfile/NewXmlFileCreationPage.java
index 83ab59be1..f85050444 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/NewXmlFileCreationPage.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newxmlfile/NewXmlFileCreationPage.java
@@ -15,12 +15,14 @@
*/
-package com.android.ide.eclipse.editors.wizards;
+package com.android.ide.eclipse.adt.wizards.newxmlfile;
import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.adt.sdk.AndroidTargetData;
import com.android.ide.eclipse.adt.sdk.Sdk;
import com.android.ide.eclipse.adt.sdk.Sdk.ITargetChangeListener;
+import com.android.ide.eclipse.adt.ui.ConfigurationSelector;
+import com.android.ide.eclipse.adt.ui.ConfigurationSelector.ConfigurationState;
import com.android.ide.eclipse.common.AndroidConstants;
import com.android.ide.eclipse.common.project.ProjectChooserHelper;
import com.android.ide.eclipse.editors.descriptors.DocumentDescriptor;
@@ -31,7 +33,6 @@ import com.android.ide.eclipse.editors.resources.configurations.FolderConfigurat
import com.android.ide.eclipse.editors.resources.configurations.ResourceQualifier;
import com.android.ide.eclipse.editors.resources.descriptors.ResourcesDescriptors;
import com.android.ide.eclipse.editors.resources.manager.ResourceFolderType;
-import com.android.ide.eclipse.editors.wizards.ConfigurationSelector.ConfigurationState;
import com.android.sdklib.IAndroidTarget;
import com.android.sdklib.SdkConstants;
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/NewXmlFileWizard.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newxmlfile/NewXmlFileWizard.java
similarity index 98%
rename from tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/NewXmlFileWizard.java
rename to tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newxmlfile/NewXmlFileWizard.java
index 125102b96..d7e43cfd7 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/NewXmlFileWizard.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newxmlfile/NewXmlFileWizard.java
@@ -16,11 +16,11 @@
-package com.android.ide.eclipse.editors.wizards;
+package com.android.ide.eclipse.adt.wizards.newxmlfile;
import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.adt.wizards.newxmlfile.NewXmlFileCreationPage.TypeInfo;
import com.android.ide.eclipse.editors.IconFactory;
-import com.android.ide.eclipse.editors.wizards.NewXmlFileCreationPage.TypeInfo;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/AndroidConstants.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/AndroidConstants.java
index 226357f7a..d0d8ae3af 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/AndroidConstants.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/AndroidConstants.java
@@ -90,7 +90,7 @@ public class AndroidConstants {
/** Name of the android sources directory */
public static final String FD_ANDROID_SOURCES = "sources"; //$NON-NLS-1$
-
+
/** Resource java class filename, i.e. "R.java" */
public final static String FN_RESOURCE_CLASS = "R.java"; //$NON-NLS-1$
/** Resource class file filename, i.e. "R.class" */
@@ -128,7 +128,8 @@ public class AndroidConstants {
public final static String WS_ASSETS = WS_SEP + SdkConstants.FD_ASSETS;
/** Leaf of the javaDoc folder. Does not start with a separator. */
- public final static String WS_JAVADOC_FOLDER_LEAF = SdkConstants.FD_DOCS + "/reference"; //$NON-NLS-1$
+ public final static String WS_JAVADOC_FOLDER_LEAF = SdkConstants.FD_DOCS + "/" +
+ SdkConstants.FD_DOCS_REFERENCE; //$NON-NLS-1$
/** Path of the samples directory relative to the sdk folder.
* This is an OS path, ending with a separator.
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/project/AndroidManifestParser.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/project/AndroidManifestParser.java
index 85ba96839..69982fc66 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/project/AndroidManifestParser.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/project/AndroidManifestParser.java
@@ -52,6 +52,8 @@ public class AndroidManifestParser {
private final static String ATTRIBUTE_PROCESS = "process"; //$NON-NLS-$
private final static String ATTRIBUTE_DEBUGGABLE = "debuggable"; //$NON-NLS-$
private final static String ATTRIBUTE_MIN_SDK_VERSION = "minSdkVersion"; //$NON-NLS-$
+ private final static String ATTRIBUTE_TARGET_PACKAGE = "targetPackage"; //$NON-NLS-1$
+ private final static String ATTRIBUTE_EXPORTED = "exported"; //$NON-NLS-1$
private final static String NODE_MANIFEST = "manifest"; //$NON-NLS-1$
private final static String NODE_APPLICATION = "application"; //$NON-NLS-1$
private final static String NODE_ACTIVITY = "activity"; //$NON-NLS-1$
@@ -76,6 +78,87 @@ public class AndroidManifestParser {
public final static int INVALID_MIN_SDK = -1;
+ /**
+ * Instrumentation info obtained from manifest
+ */
+ public static class Instrumentation {
+ private final String mName;
+ private final String mTargetPackage;
+
+ Instrumentation(String name, String targetPackage) {
+ mName = name;
+ mTargetPackage = targetPackage;
+ }
+
+ /**
+ * Returns the fully qualified instrumentation class name
+ */
+ public String getName() {
+ return mName;
+ }
+
+ /**
+ * Returns the Android app package that is the target of this instrumentation
+ */
+ public String getTargetPackage() {
+ return mTargetPackage;
+ }
+ }
+
+ /**
+ * Activity info obtained from the manifest.
+ */
+ public static class Activity {
+ private final String mName;
+ private final boolean mExported;
+ private boolean mHasAction = false;
+ private boolean mHasMainAction = false;
+ private boolean mHasLauncherCategory = false;
+
+ public Activity(String name, boolean exported) {
+ mName = name;
+ mExported = exported;
+ }
+
+ public String getName() {
+ return mName;
+ }
+
+ public boolean getExported() {
+ return mExported;
+ }
+
+ public boolean hasAction() {
+ return mHasAction;
+ }
+
+ public boolean isHomeActivity() {
+ return mHasMainAction && mHasLauncherCategory;
+ }
+
+ void setHasAction(boolean hasAction) {
+ mHasAction = hasAction;
+ }
+
+ /** If the activity doesn't yet have a filter set for the launcher, this resets both
+ * flags. This is to handle multiple intent-filters where one could have the valid
+ * action, and another one of the valid category.
+ */
+ void resetIntentFilter() {
+ if (isHomeActivity() == false) {
+ mHasMainAction = mHasLauncherCategory = false;
+ }
+ }
+
+ void setHasMainAction(boolean hasMainAction) {
+ mHasMainAction = hasMainAction;
+ }
+
+ void setHasLauncherCategory(boolean hasLauncherCategory) {
+ mHasLauncherCategory = hasLauncherCategory;
+ }
+ }
+
/**
* XML error & data handler used when parsing the AndroidManifest.xml file.
*
@@ -89,9 +172,9 @@ public class AndroidManifestParser {
/** Application package */
private String mPackage;
/** List of all activities */
- private final ArrayList mActivities = new ArrayList();
+ private final ArrayList mActivities = new ArrayList();
/** Launcher activity */
- private String mLauncherActivity = null;
+ private Activity mLauncherActivity = null;
/** list of process names declared by the manifest */
private Set mProcesses = null;
/** debuggable attribute value. If null, the attribute is not present. */
@@ -100,7 +183,8 @@ public class AndroidManifestParser {
* the attribute was not present. */
private int mApiLevelRequirement = INVALID_MIN_SDK;
/** List of all instrumentations declared by the manifest */
- private final ArrayList mInstrumentations = new ArrayList();
+ private final ArrayList mInstrumentations =
+ new ArrayList();
/** List of all libraries in use declared by the manifest */
private final ArrayList mLibraries = new ArrayList();
@@ -110,9 +194,7 @@ public class AndroidManifestParser {
private boolean mMarkErrors = false;
private int mCurrentLevel = 0;
private int mValidLevel = 0;
- private boolean mFoundMainAction = false;
- private boolean mFoundLauncherCategory = false;
- private String mCurrentActivity = null;
+ private Activity mCurrentActivity = null;
private Locator mLocator;
/**
@@ -144,8 +226,8 @@ public class AndroidManifestParser {
* Returns the list of activities found in the manifest.
* @return An array of fully qualified class names, or empty if no activity were found.
*/
- String[] getActivities() {
- return mActivities.toArray(new String[mActivities.size()]);
+ Activity[] getActivities() {
+ return mActivities.toArray(new Activity[mActivities.size()]);
}
/**
@@ -153,7 +235,7 @@ public class AndroidManifestParser {
* up in the HOME screen.
* @return the fully qualified name of a HOME activity or null if none were found.
*/
- String getLauncherActivity() {
+ Activity getLauncherActivity() {
return mLauncherActivity;
}
@@ -185,11 +267,11 @@ public class AndroidManifestParser {
/**
* Returns the list of instrumentations found in the manifest.
- * @return An array of instrumentation names, or empty if no instrumentations were
+ * @return An array of {@link Instrumentation}, or empty if no instrumentations were
* found.
*/
- String[] getInstrumentations() {
- return mInstrumentations.toArray(new String[mInstrumentations.size()]);
+ Instrumentation[] getInstrumentations() {
+ return mInstrumentations.toArray(new Instrumentation[mInstrumentations.size()]);
}
/**
@@ -285,27 +367,26 @@ public class AndroidManifestParser {
case LEVEL_INTENT_FILTER:
// only process this level if we are in an activity
if (mCurrentActivity != null && NODE_INTENT.equals(localName)) {
- // if we're at the intent level, lets reset some flag to
- // be used when parsing the children
- mFoundMainAction = false;
- mFoundLauncherCategory = false;
+ mCurrentActivity.resetIntentFilter();
mValidLevel++;
}
break;
case LEVEL_CATEGORY:
- if (mCurrentActivity != null && mLauncherActivity == null) {
+ if (mCurrentActivity != null) {
if (NODE_ACTION.equals(localName)) {
// get the name attribute
- if (ACTION_MAIN.equals(
- getAttributeValue(attributes, ATTRIBUTE_NAME,
- true /* hasNamespace */))) {
- mFoundMainAction = true;
+ String action = getAttributeValue(attributes, ATTRIBUTE_NAME,
+ true /* hasNamespace */);
+ if (action != null) {
+ mCurrentActivity.setHasAction(true);
+ mCurrentActivity.setHasMainAction(
+ ACTION_MAIN.equals(action));
}
} else if (NODE_CATEGORY.equals(localName)) {
- if (CATEGORY_LAUNCHER.equals(
- getAttributeValue(attributes, ATTRIBUTE_NAME,
- true /* hasNamespace */))) {
- mFoundLauncherCategory = true;
+ String category = getAttributeValue(attributes, ATTRIBUTE_NAME,
+ true /* hasNamespace */);
+ if (CATEGORY_LAUNCHER.equals(category)) {
+ mCurrentActivity.setHasLauncherCategory(true);
}
}
@@ -349,8 +430,7 @@ public class AndroidManifestParser {
case LEVEL_INTENT_FILTER:
// if we found both a main action and a launcher category, this is our
// launcher activity!
- if (mCurrentActivity != null &&
- mFoundMainAction && mFoundLauncherCategory) {
+ if (mCurrentActivity != null && mCurrentActivity.isHomeActivity()) {
mLauncherActivity = mCurrentActivity;
}
break;
@@ -403,17 +483,23 @@ public class AndroidManifestParser {
String activityName = getAttributeValue(attributes, ATTRIBUTE_NAME,
true /* hasNamespace */);
if (activityName != null) {
- mCurrentActivity = combinePackageAndClassName(mPackage, activityName);
+ activityName = combinePackageAndClassName(mPackage, activityName);
+
+ // get the exported flag.
+ String exportedStr = getAttributeValue(attributes, ATTRIBUTE_EXPORTED, true);
+ boolean exported = exportedStr == null ||
+ exportedStr.toLowerCase().equals("true"); // $NON-NLS-1$
+ mCurrentActivity = new Activity(activityName, exported);
mActivities.add(mCurrentActivity);
if (mMarkErrors) {
- checkClass(mCurrentActivity, AndroidConstants.CLASS_ACTIVITY,
+ checkClass(activityName, AndroidConstants.CLASS_ACTIVITY,
true /* testVisibility */);
}
} else {
// no activity found! Aapt will output an error,
// so we don't have to do anything
- mCurrentActivity = activityName;
+ mCurrentActivity = null;
}
String processName = getAttributeValue(attributes, ATTRIBUTE_PROCESS,
@@ -459,7 +545,9 @@ public class AndroidManifestParser {
true /* hasNamespace */);
if (instrumentationName != null) {
String instrClassName = combinePackageAndClassName(mPackage, instrumentationName);
- mInstrumentations.add(instrClassName);
+ String targetPackage = getAttributeValue(attributes, ATTRIBUTE_TARGET_PACKAGE,
+ true /* hasNamespace */);
+ mInstrumentations.add(new Instrumentation(instrClassName, targetPackage));
if (mMarkErrors) {
checkClass(instrClassName, AndroidConstants.CLASS_INSTRUMENTATION,
true /* testVisibility */);
@@ -539,12 +627,12 @@ public class AndroidManifestParser {
private static SAXParserFactory sParserFactory;
private final String mJavaPackage;
- private final String[] mActivities;
- private final String mLauncherActivity;
+ private final Activity[] mActivities;
+ private final Activity mLauncherActivity;
private final String[] mProcesses;
private final Boolean mDebuggable;
private final int mApiLevelRequirement;
- private final String[] mInstrumentations;
+ private final Instrumentation[] mInstrumentations;
private final String[] mLibraries;
static {
@@ -780,18 +868,18 @@ public class AndroidManifestParser {
/**
* Returns the list of activities found in the manifest.
- * @return An array of fully qualified class names, or empty if no activity were found.
+ * @return An array of {@link Activity}, or empty if no activity were found.
*/
- public String[] getActivities() {
+ public Activity[] getActivities() {
return mActivities;
}
/**
* Returns the name of one activity found in the manifest, that is configured to show
* up in the HOME screen.
- * @return the fully qualified name of a HOME activity or null if none were found.
+ * @return The {@link Activity} representing a HOME activity or null if none were found.
*/
- public String getLauncherActivity() {
+ public Activity getLauncherActivity() {
return mLauncherActivity;
}
@@ -819,9 +907,9 @@ public class AndroidManifestParser {
/**
* Returns the list of instrumentations found in the manifest.
- * @return An array of fully qualified class names, or empty if no instrumentations were found.
+ * @return An array of {@link Instrumentation}, or empty if no instrumentations were found.
*/
- public String[] getInstrumentations() {
+ public Instrumentation[] getInstrumentations() {
return mInstrumentations;
}
@@ -849,9 +937,9 @@ public class AndroidManifestParser {
* @param instrumentations the list of instrumentations parsed from the manifest.
* @param libraries the list of libraries in use parsed from the manifest.
*/
- private AndroidManifestParser(String javaPackage, String[] activities,
- String launcherActivity, String[] processes, Boolean debuggable,
- int apiLevelRequirement, String[] instrumentations, String[] libraries) {
+ private AndroidManifestParser(String javaPackage, Activity[] activities,
+ Activity launcherActivity, String[] processes, Boolean debuggable,
+ int apiLevelRequirement, Instrumentation[] instrumentations, String[] libraries) {
mJavaPackage = javaPackage;
mActivities = activities;
mLauncherActivity = launcherActivity;
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/GraphicalLayoutEditor.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/GraphicalLayoutEditor.java
index 12d49fe27..e22382034 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/GraphicalLayoutEditor.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/GraphicalLayoutEditor.java
@@ -22,6 +22,10 @@ import com.android.ide.eclipse.adt.sdk.LoadStatus;
import com.android.ide.eclipse.adt.sdk.Sdk;
import com.android.ide.eclipse.adt.sdk.AndroidTargetData.LayoutBridge;
import com.android.ide.eclipse.adt.sdk.Sdk.ITargetChangeListener;
+import com.android.ide.eclipse.adt.ui.ConfigurationSelector.DensityVerifier;
+import com.android.ide.eclipse.adt.ui.ConfigurationSelector.DimensionVerifier;
+import com.android.ide.eclipse.adt.ui.ConfigurationSelector.LanguageRegionVerifier;
+import com.android.ide.eclipse.adt.ui.ConfigurationSelector.MobileCodeVerifier;
import com.android.ide.eclipse.common.resources.ResourceType;
import com.android.ide.eclipse.editors.IconFactory;
import com.android.ide.eclipse.editors.layout.LayoutEditor.UiEditorActions;
@@ -55,10 +59,6 @@ import com.android.ide.eclipse.editors.ui.tree.CopyCutAction;
import com.android.ide.eclipse.editors.ui.tree.PasteAction;
import com.android.ide.eclipse.editors.uimodel.UiDocumentNode;
import com.android.ide.eclipse.editors.uimodel.UiElementNode;
-import com.android.ide.eclipse.editors.wizards.ConfigurationSelector.DensityVerifier;
-import com.android.ide.eclipse.editors.wizards.ConfigurationSelector.DimensionVerifier;
-import com.android.ide.eclipse.editors.wizards.ConfigurationSelector.LanguageRegionVerifier;
-import com.android.ide.eclipse.editors.wizards.ConfigurationSelector.MobileCodeVerifier;
import com.android.layoutlib.api.ILayoutLog;
import com.android.layoutlib.api.ILayoutResult;
import com.android.layoutlib.api.IProjectCallback;
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/LayoutCreatorDialog.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/LayoutCreatorDialog.java
index c4a8f5cc1..69402381a 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/LayoutCreatorDialog.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/LayoutCreatorDialog.java
@@ -16,12 +16,12 @@
package com.android.ide.eclipse.editors.layout;
+import com.android.ide.eclipse.adt.ui.ConfigurationSelector;
+import com.android.ide.eclipse.adt.ui.ConfigurationSelector.ConfigurationState;
import com.android.ide.eclipse.editors.IconFactory;
import com.android.ide.eclipse.editors.resources.configurations.FolderConfiguration;
import com.android.ide.eclipse.editors.resources.configurations.ResourceQualifier;
import com.android.ide.eclipse.editors.resources.manager.ResourceFolderType;
-import com.android.ide.eclipse.editors.wizards.ConfigurationSelector;
-import com.android.ide.eclipse.editors.wizards.ConfigurationSelector.ConfigurationState;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.TrayDialog;
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/LayoutEditor.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/LayoutEditor.java
index f3a5113a9..7bed7ce6a 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/LayoutEditor.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/LayoutEditor.java
@@ -18,8 +18,8 @@ package com.android.ide.eclipse.editors.layout;
import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.adt.sdk.AndroidTargetData;
+import com.android.ide.eclipse.adt.ui.EclipseUiHelper;
import com.android.ide.eclipse.common.AndroidConstants;
-import com.android.ide.eclipse.common.EclipseUiHelper;
import com.android.ide.eclipse.editors.AndroidEditor;
import com.android.ide.eclipse.editors.descriptors.DocumentDescriptor;
import com.android.ide.eclipse.editors.resources.manager.ResourceFolder;
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/UiContentOutlinePage.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/UiContentOutlinePage.java
index 536e9020b..4e0e35f06 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/UiContentOutlinePage.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/UiContentOutlinePage.java
@@ -17,7 +17,7 @@
package com.android.ide.eclipse.editors.layout;
-import com.android.ide.eclipse.common.EclipseUiHelper;
+import com.android.ide.eclipse.adt.ui.EclipseUiHelper;
import com.android.ide.eclipse.editors.IconFactory;
import com.android.ide.eclipse.editors.layout.parts.UiDocumentTreeEditPart;
import com.android.ide.eclipse.editors.layout.parts.UiElementTreeEditPart;
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/manifest/ManifestEditor.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/manifest/ManifestEditor.java
index d0f8d7b85..dc32383d9 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/manifest/ManifestEditor.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/manifest/ManifestEditor.java
@@ -159,9 +159,9 @@ public final class ManifestEditor extends AndroidEditor {
protected void xmlModelChanged(Document xml_doc) {
// create the ui root node on demand.
initUiRootNode(false /*force*/);
-
+
loadFromXml(xml_doc);
-
+
super.xmlModelChanged(xml_doc);
}
@@ -184,7 +184,7 @@ public final class ManifestEditor extends AndroidEditor {
}
}
}
-
+
private void onDescriptorsChanged(UiElementNode oldManifestNode) {
mUiManifestNode.reloadFromXmlNode(oldManifestNode.getXmlNode());
@@ -321,7 +321,6 @@ public final class ManifestEditor extends AndroidEditor {
if (mUiManifestNode != null && force == false) {
return;
}
-
AndroidManifestDescriptors manifestDescriptor = getManifestDescriptors();
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/explorer/ResourceExplorerView.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/explorer/ResourceExplorerView.java
index d1d88917f..b61eddc4d 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/explorer/ResourceExplorerView.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/explorer/ResourceExplorerView.java
@@ -17,14 +17,14 @@
package com.android.ide.eclipse.editors.resources.explorer;
import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.adt.ui.ResourceContentProvider;
+import com.android.ide.eclipse.adt.ui.ResourceLabelProvider;
import com.android.ide.eclipse.common.AndroidConstants;
import com.android.ide.eclipse.editors.resources.manager.ProjectResourceItem;
import com.android.ide.eclipse.editors.resources.manager.ProjectResources;
import com.android.ide.eclipse.editors.resources.manager.ResourceFile;
import com.android.ide.eclipse.editors.resources.manager.ResourceManager;
import com.android.ide.eclipse.editors.resources.manager.ResourceMonitor.IResourceEventListener;
-import com.android.ide.eclipse.editors.wizards.ResourceContentProvider;
-import com.android.ide.eclipse.editors.wizards.ResourceLabelProvider;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/ui/tree/NewItemSelectionDialog.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/ui/tree/NewItemSelectionDialog.java
index 07298810f..72fe06034 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/ui/tree/NewItemSelectionDialog.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/ui/tree/NewItemSelectionDialog.java
@@ -38,7 +38,7 @@ import java.util.Arrays;
/**
* A selection dialog to select the type of the new element node to
- * created, either in the application node or the selected sub node.
+ * create, either in the application node or the selected sub node.
*/
public class NewItemSelectionDialog extends AbstractElementListSelectionDialog {
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/ui/tree/UiModelTreeContentProvider.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/ui/tree/UiModelTreeContentProvider.java
index 9f34d9e72..30919926b 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/ui/tree/UiModelTreeContentProvider.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/ui/tree/UiModelTreeContentProvider.java
@@ -30,16 +30,15 @@ import java.util.ArrayList;
*/
class UiModelTreeContentProvider implements ITreeContentProvider {
- /** The root {@link UiElementNode} which contains all the elements that are to be
- * manipulated by this tree view. In general this is the manifest UI node. */
- private UiElementNode mUiRootNode;
/** The descriptor of the elements to be displayed as root in this tree view. All elements
* of the same type in the root will be displayed. */
private ElementDescriptor[] mDescriptorFilters;
+ /** Object which provides the uiRootNode */
+ private final UiRootNodeProvider mUiRootNodeProvider;
- public UiModelTreeContentProvider(UiElementNode uiRootNode,
+ public UiModelTreeContentProvider(UiRootNodeProvider rootNodeProvider,
ElementDescriptor[] descriptorFilters) {
- mUiRootNode = uiRootNode;
+ mUiRootNodeProvider = rootNodeProvider;
mDescriptorFilters = descriptorFilters;
}
@@ -87,8 +86,9 @@ class UiModelTreeContentProvider implements ITreeContentProvider {
*/
public Object[] getElements(Object inputElement) {
ArrayList roots = new ArrayList();
- if (mUiRootNode != null) {
- for (UiElementNode ui_node : mUiRootNode.getUiChildren()) {
+ UiElementNode uiRootNode = mUiRootNodeProvider.getRootNode();
+ if (uiRootNode != null) {
+ for (UiElementNode ui_node : uiRootNode.getUiChildren()) {
if (mDescriptorFilters == null || mDescriptorFilters.length == 0) {
roots.add(ui_node);
} else {
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/ui/tree/UiRootNodeProvider.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/ui/tree/UiRootNodeProvider.java
new file mode 100644
index 000000000..5121e3546
--- /dev/null
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/ui/tree/UiRootNodeProvider.java
@@ -0,0 +1,27 @@
+/*
+ * 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.editors.ui.tree;
+
+import com.android.ide.eclipse.editors.uimodel.UiElementNode;
+
+/**
+ * An object that can provide a uiRootNode.
+ */
+public interface UiRootNodeProvider {
+ /** Returns the UiDocumentNode for the current model. */
+ public abstract UiElementNode getRootNode();
+}
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/ui/tree/UiTreeBlock.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/ui/tree/UiTreeBlock.java
index fc384e8c0..8038a47de 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/ui/tree/UiTreeBlock.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/ui/tree/UiTreeBlock.java
@@ -83,7 +83,8 @@ import java.util.LinkedList;
* On the left is a details part that displays all the visible UI attributes for a given
* selected UI element node.
*/
-public final class UiTreeBlock extends MasterDetailsBlock implements ICommitXml {
+public final class UiTreeBlock extends MasterDetailsBlock
+ implements ICommitXml, UiRootNodeProvider {
/** Height hint for the tree view. Helps the grid layout resize properly on smaller screens. */
private static final int TREE_HEIGHT_HINT = 50;
@@ -181,6 +182,17 @@ public final class UiTreeBlock extends MasterDetailsBlock implements ICommitXml
return mMasterPart;
}
+ /**
+ * Returns the {@link UiElementNode} for the current model.
+ *
+ * This is used by the content provider attached to {@link #mTreeViewer} since
+ * the uiRootNode changes after each call to
+ * {@link #changeRootAndDescriptors(UiElementNode, ElementDescriptor[], boolean)}.
+ */
+ public UiElementNode getRootNode() {
+ return mUiRootNode;
+ }
+
@Override
protected void createMasterPart(final IManagedForm managedForm, Composite parent) {
FormToolkit toolkit = managedForm.getToolkit();
@@ -239,8 +251,7 @@ public final class UiTreeBlock extends MasterDetailsBlock implements ICommitXml
tree.setLayoutData(gd);
mTreeViewer = new TreeViewer(tree);
- mTreeViewer.setContentProvider(new UiModelTreeContentProvider(
- mUiRootNode, mDescriptorFilters));
+ mTreeViewer.setContentProvider(new UiModelTreeContentProvider(this, mDescriptorFilters));
mTreeViewer.setLabelProvider(new UiModelTreeLabelProvider());
mTreeViewer.setInput("unused"); //$NON-NLS-1$
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/uimodel/UiResourceAttributeNode.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/uimodel/UiResourceAttributeNode.java
index 32cac9f2b..284cff4ca 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/uimodel/UiResourceAttributeNode.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/uimodel/UiResourceAttributeNode.java
@@ -17,6 +17,8 @@
package com.android.ide.eclipse.editors.uimodel;
import com.android.ide.eclipse.adt.sdk.AndroidTargetData;
+import com.android.ide.eclipse.adt.ui.ReferenceChooserDialog;
+import com.android.ide.eclipse.adt.ui.ResourceChooser;
import com.android.ide.eclipse.common.resources.IResourceRepository;
import com.android.ide.eclipse.common.resources.ResourceItem;
import com.android.ide.eclipse.common.resources.ResourceType;
@@ -26,8 +28,6 @@ import com.android.ide.eclipse.editors.descriptors.DescriptorsUtils;
import com.android.ide.eclipse.editors.descriptors.TextAttributeDescriptor;
import com.android.ide.eclipse.editors.resources.manager.ResourceManager;
import com.android.ide.eclipse.editors.ui.SectionHelper;
-import com.android.ide.eclipse.editors.wizards.ReferenceChooserDialog;
-import com.android.ide.eclipse.editors.wizards.ResourceChooser;
import org.eclipse.core.resources.IProject;
import org.eclipse.jface.window.Window;
@@ -135,8 +135,11 @@ public class UiResourceAttributeNode extends UiTextAttributeNode {
IResourceRepository systemRepository = data.getSystemResources();
// open a resource chooser dialog for specified resource type.
- ResourceChooser dlg = new ResourceChooser(mType,
- projectRepository, systemRepository, shell);
+ ResourceChooser dlg = new ResourceChooser(project,
+ mType,
+ projectRepository,
+ systemRepository,
+ shell);
dlg.setCurrentResource(currentValue);
@@ -144,7 +147,9 @@ public class UiResourceAttributeNode extends UiTextAttributeNode {
return dlg.getCurrentResource();
}
} else {
- ReferenceChooserDialog dlg = new ReferenceChooserDialog(projectRepository,
+ ReferenceChooserDialog dlg = new ReferenceChooserDialog(
+ project,
+ projectRepository,
shell);
dlg.setCurrentResource(currentValue);
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/ResourceChooser.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/ResourceChooser.java
deleted file mode 100644
index 60a627b52..000000000
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/ResourceChooser.java
+++ /dev/null
@@ -1,193 +0,0 @@
-/*
- * Copyright (C) 2007 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.editors.wizards;
-
-import com.android.ide.eclipse.common.resources.IResourceRepository;
-import com.android.ide.eclipse.common.resources.ResourceItem;
-import com.android.ide.eclipse.common.resources.ResourceType;
-
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Control;
-import org.eclipse.swt.widgets.Shell;
-import org.eclipse.ui.dialogs.AbstractElementListSelectionDialog;
-
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-/**
- * A dialog to let the user select a resource based on a resource type.
- */
-public class ResourceChooser extends AbstractElementListSelectionDialog {
-
- private Pattern mProjectResourcePattern;
-
- private ResourceType mResourceType;
-
- private IResourceRepository mProjectResources;
-
- // TODO: enable when we can display the system resources.
- // private Pattern mSystemResourcePattern;
- // private IResourceRepository mSystemResources;
- // private Button mProjectButton;
- // private Button mSystemButton;
-
- private String mCurrentResource;
-
- /**
- * Creates a Resource Chooser dialog.
- * @param type The type of the resource to choose
- * @param project The repository for the project
- * @param system The System resource repository
- * @param parent the parent shell
- */
- public ResourceChooser(ResourceType type, IResourceRepository project,
- IResourceRepository system, Shell parent) {
- super(parent, new ResourceLabelProvider());
-
- mResourceType = type;
- mProjectResources = project;
- // TODO: enable when we can display the system resources.
- // mSystemResources = system;
-
- mProjectResourcePattern = Pattern.compile(
- "@" + mResourceType.getName() + "/(.+)"); //$NON-NLS-1$ //$NON-NLS-2$
- // TODO: enable when we can display the system resources.
- // mSystemResourcePattern = Pattern.compile(
- // "@android:" + mResourceType.getName() + "/(.+)"); //$NON-NLS-1$ //$NON-NLS-2$
-
- setTitle("Resource Chooser");
- setMessage(String.format("Choose a %1$s resource",
- mResourceType.getDisplayName().toLowerCase()));
- }
-
- public void setCurrentResource(String resource) {
- mCurrentResource = resource;
- }
-
- public String getCurrentResource() {
- return mCurrentResource;
- }
-
- @Override
- protected void computeResult() {
- Object[] elements = getSelectedElements();
- if (elements.length == 1 && elements[0] instanceof ResourceItem) {
- ResourceItem item = (ResourceItem)elements[0];
-
- mCurrentResource = mResourceType.getXmlString(item,
- // TODO: enable when we can display the system resources.
- false /*mSystemButton.getSelection()*/);
- }
- }
-
- @Override
- protected Control createDialogArea(Composite parent) {
- Composite top = (Composite)super.createDialogArea(parent);
-
- createMessageArea(top);
-
- // TODO: enable when we can display the system resources.
- // createButtons(top);
-
- createFilterText(top);
- createFilteredList(top);
-
- setupResourceListAndCurrent();
-
- return top;
- }
-
- /**
- * Creates the radio button to switch between project and system resources.
- * @param top the parent composite
- */
- /* TODO: enable when we can display the system resources.
- private void createButtons(Composite top) {
- mProjectButton = new Button(top, SWT.RADIO);
- mProjectButton.setText("Project Resources");
- mProjectButton.addSelectionListener(new SelectionAdapter() {
- @Override
- public void widgetSelected(SelectionEvent e) {
- super.widgetSelected(e);
- setListElements(mProjectResources.getResources(mResourceType));
- }
- });
- mSystemButton = new Button(top, SWT.RADIO);
- mSystemButton.setText("System Resources");
- mSystemButton.addSelectionListener(new SelectionAdapter() {
- @Override
- public void widgetSelected(SelectionEvent e) {
- super.widgetSelected(e);
- setListElements(mSystemResources.getResources(mResourceType));
- }
- });
- }
- */
-
- /**
- * Setups the current list based on the current resource.
- */
- private void setupResourceListAndCurrent() {
- if (setupInitialSelection(mProjectResourcePattern, mProjectResources) == false) {
- // if we couldn't understand the current value, we default to the project resources
- ResourceItem[] items = mProjectResources.getResources(mResourceType);
- setListElements(items);
- }
- /*
- * TODO: enable when we can display the system resources.
- if (setupInitialSelection(mProjectResourcePattern, mProjectResources) == false) {
- if (setupInitialSelection(mSystemResourcePattern, mSystemResources) == false) {
- // if we couldn't understand the current value, we default to the project resources
- IResourceItem[] items = mProjectResources.getResources(mResourceType);
- setListElements(items);
- mProjectButton.setSelection(true);
- } else {
- mSystemButton.setSelection(true);
- }
- } else {
- mProjectButton.setSelection(true);
- }*/
- }
-
- /**
- * Attempts to setup the list of element from a repository if the current resource
- * matches the provided pattern.
- * @param pattern the pattern to test the current value
- * @param repository the repository to use if the pattern matches.
- * @return true if success.
- */
- private boolean setupInitialSelection(Pattern pattern, IResourceRepository repository) {
- Matcher m = pattern.matcher(mCurrentResource);
- if (m.matches()) {
- // we have a project resource, let's setup the list
- ResourceItem[] items = repository.getResources(mResourceType);
- setListElements(items);
-
- // and let's look for the item we found
- String name = m.group(1);
-
- for (ResourceItem item : items) {
- if (name.equals(item.getName())) {
- setSelection(new Object[] { item });
- break;
- }
- }
- return true;
- }
- return false;
- }
-}
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/common/project/AndroidManifestParserTest.java b/tools/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/common/project/AndroidManifestParserTest.java
index 7e8b0af10..2f93e5179 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/common/project/AndroidManifestParserTest.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/common/project/AndroidManifestParserTest.java
@@ -33,7 +33,7 @@ public class AndroidManifestParserTest extends TestCase {
private static final String ACTIVITY_NAME = "com.android.testapp.MainActivity"; //$NON-NLS-1$
private static final String LIBRARY_NAME = "android.test.runner"; //$NON-NLS-1$
private static final String INSTRUMENTATION_NAME = "android.test.InstrumentationTestRunner"; //$NON-NLS-1$
-
+ private static final String INSTRUMENTATION_TARGET = "com.android.AndroidProject"; //$NON-NLS-1$
@Override
protected void setUp() throws Exception {
super.setUp();
@@ -51,7 +51,10 @@ public class AndroidManifestParserTest extends TestCase {
public void testGetInstrumentationInformation() {
assertEquals(1, mManifestInstrumentation.getInstrumentations().length);
- assertEquals(INSTRUMENTATION_NAME, mManifestTestApp.getInstrumentations()[0]);
+ assertEquals(INSTRUMENTATION_NAME,
+ mManifestInstrumentation.getInstrumentations()[0].getName());
+ assertEquals(INSTRUMENTATION_TARGET,
+ mManifestInstrumentation.getInstrumentations()[0].getTargetPackage());
}
public void testGetPackage() {
@@ -66,17 +69,12 @@ public class AndroidManifestParserTest extends TestCase {
public void testGetLauncherActivity() {
assertEquals(ACTIVITY_NAME, mManifestTestApp.getLauncherActivity());
}
-
+
public void testGetUsesLibraries() {
assertEquals(1, mManifestTestApp.getUsesLibraries().length);
assertEquals(LIBRARY_NAME, mManifestTestApp.getUsesLibraries()[0]);
}
-
- public void testGetInstrumentations() {
- assertEquals(1, mManifestTestApp.getInstrumentations().length);
- assertEquals(INSTRUMENTATION_NAME, mManifestTestApp.getInstrumentations()[0]);
- }
-
+
public void testGetPackageName() {
assertEquals(PACKAGE_NAME, mManifestTestApp.getPackage());
}
diff --git a/tools/eclipse/scripts/collect_sources_for_sdk.py b/tools/eclipse/scripts/collect_sources_for_sdk.py
new file mode 100755
index 000000000..773070ba9
--- /dev/null
+++ b/tools/eclipse/scripts/collect_sources_for_sdk.py
@@ -0,0 +1,170 @@
+#!/usr/bin/python
+#
+# 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.
+#
+"""
+Description:
+ This script collects all framework Java sources from the current android
+ source code and places them in a source folder suitable for the eclipse ADT
+ plugin.
+
+See usage() below.
+
+Copyright (C) 2009 The Android Open Source Project
+Licensed under the Apache License, Version 2.0 (the "License").
+"""
+
+import re
+import os
+import sys
+import getopt
+import shutil
+
+_RE_PKG = re.compile("^\s*package\s+([^\s;]+)\s*;.*")
+
+# Holds cmd-line arguments
+class Params(object):
+ def __init__(self):
+ self.DRY = False
+ self.DIR = "frameworks"
+ self.SRC = None
+ self.DST = None
+ self.CNT_USED = 0
+ self.CNT_NOPKG = 0
+
+
+# Prints a usage summary
+def usage(error=None):
+ print """
+ Description:
+ This script collects all framework Java sources from the current android
+ source code and places them in a source folder suitable for the eclipse ADT
+ plugin.
+
+ Usage:
+ %s [-n]
+
+ The source and destination directories must already exist.
+ Use -n for a dry-run.
+
+""" % sys.argv[0]
+
+ if error:
+ print >>sys.stderr, "Error:", error
+
+
+# Parse command line args, returns a Params instance or sys.exit(2) on error
+# after printing the error and the usage.
+def parseArgs(argv):
+ p = Params()
+ error = None
+
+ try:
+ opts, args = getopt.getopt(argv[1:],
+ "ns:",
+ [ "--dry", "--sourcedir=" ])
+ except getopt.GetoptError, e:
+ error = str(e)
+
+ if error is None:
+ for o, a in opts:
+ if o in [ "-n", "--dry" ]:
+ p.DRY = True
+ elif o in [ "-s", "--sourcedir" ]:
+ p.DIR = a
+
+ if len(args) != 2:
+ error = "Missing arguments: "
+ else:
+ p.SRC = args[0]
+ p.DST = args[1]
+
+ if not os.path.isdir(p.SRC):
+ error = "%s is not a directory" % p.SRC
+ elif not os.path.isdir(p.DST):
+ error = "%s is not a directory" % p.DST
+
+ if error:
+ usage(error)
+ sys.exit(2)
+
+ return p
+
+
+# Recursively parses the given directory and process java files found
+def parseSrcDir(p, srcdir):
+ for f in os.listdir(srcdir):
+ fp = os.path.join(srcdir, f)
+ if f.endswith(".java") and os.path.isfile(fp):
+ pkg = checkJavaFile(fp)
+ if pkg:
+ pkg = pkg.replace(".", os.path.sep) # e.g. android.view => android/view
+ copy(p, fp, f, pkg)
+ p.CNT_USED += 1 # one more copied
+ else:
+ p.CNT_NOPKG += 1 # this java file lacked a package declaration
+ elif os.path.isdir(fp):
+ parseSrcDir(p, fp)
+
+
+# Check a java file to find its package declaration, if any
+def checkJavaFile(path):
+ print "Process", path
+
+ try:
+ f = None
+ try:
+ f = file(path)
+ for l in f.readlines():
+ m = _RE_PKG.match(l)
+ if m:
+ return m.group(1)
+ finally:
+ if f: f.close()
+ except Exception:
+ pass
+
+ return None
+
+# Create destination directory based on package name then copy the
+# source file in there
+def copy(p, fp, f, pkg):
+ dstdir = os.path.join(p.DST, pkg)
+ _mkdir(p, dstdir)
+ _cp(p, fp, os.path.join(dstdir, f))
+
+def _mkdir(p, dir):
+ if not os.path.isdir(dir):
+ if p.DRY:
+ print "mkdir", dir
+ else:
+ os.makedirs(dir)
+
+def _cp(p, src, dst):
+ if p.DRY:
+ print "cp", src, dst
+ else:
+ shutil.copyfile(src, dst)
+
+
+def main():
+ p = parseArgs(sys.argv)
+ parseSrcDir(p, os.path.join(p.SRC, p.DIR))
+ print "%d java files copied" % p.CNT_USED
+ if p.CNT_NOPKG: print "%d java files ignored (no package)" % p.CNT_NOPKG
+ if p.DRY: print "This was in *DRY* mode. No copies done."
+
+if __name__ == "__main__":
+ main()
diff --git a/tools/eclipse/scripts/collect_sources_for_sdk.sh b/tools/eclipse/scripts/collect_sources_for_sdk.sh
deleted file mode 100644
index 4824da7f7..000000000
--- a/tools/eclipse/scripts/collect_sources_for_sdk.sh
+++ /dev/null
@@ -1,65 +0,0 @@
-#!/bin/bash
-
-function usage() {
- cat <
-
- The source and destination directories must already exist.
- Use -n for a dry-run.
-
-EOF
-}
-
-DRY=""
-if [ "-n" == "$1" ]; then
- DRY="echo"
- shift
-fi
-
-DIR="frameworks"
-if [ "-s" == "$1" ]; then
- shift
- DIR="$1"
- shift
-fi
-
-SRC="$1"
-DST="$2"
-
-if [ -z "$SRC" ] || [ -z "$DST" ] || [ ! -d "$SRC" ] || [ ! -d "$DST" ]; then
- usage
- exit 1
-fi
-
-function process() {
- echo "Examine" $1
-}
-
-N=0
-E=0
-for i in `find -L "${SRC}/${DIR}" -name "*.java"`; do
- if [ -f "$i" ]; then
- # look for ^package (android.view.blah);$
- PACKAGE=`sed -n '/^package [^ ;]\+; */{s/[^ ]* *\([^ ;]*\).*/\1/p;q}' "$i"`
- if [ -n "$PACKAGE" ]; then
- PACKAGE=${PACKAGE//./\/} # e.g. android.view => android/view
- JAVA=`basename "$i"` # e.g. View.java
- [ -z $DRY ] && [ ! -d "$DST/$PACKAGE" ] && mkdir -p -v "$DST/$PACKAGE"
- $DRY cp -v "$i" "$DST/$PACKAGE/$JAVA"
- N=$((N+1))
- else
- echo "Warning: $i does not have a Java package."
- E=$((E+1))
- fi
- fi
-done
-
-echo "$N java files copied"
-[ $E -gt 0 ] && echo "$E warnings"
-
diff --git a/tools/makedict/src/com/android/tools/dict/MakeBinaryDictionary.java b/tools/makedict/src/com/android/tools/dict/MakeBinaryDictionary.java
index cbe702872..8a8a677da 100755
--- a/tools/makedict/src/com/android/tools/dict/MakeBinaryDictionary.java
+++ b/tools/makedict/src/com/android/tools/dict/MakeBinaryDictionary.java
@@ -45,6 +45,10 @@ public class MakeBinaryDictionary {
public static final String TAG_WORD = "w";
public static final String ATTR_FREQ = "f";
+ private static final int FLAG_ADDRESS_MASK = 0x400000;
+ private static final int FLAG_TERMINAL_MASK = 0x800000;
+ private static final int ADDRESS_MASK = 0x3FFFFF;
+
public static final CharNode EMPTY_NODE = new CharNode();
List roots;
@@ -179,7 +183,7 @@ public class MakeBinaryDictionary {
parent.children.add(child);
}
child.data = data;
- child.freq += occur;
+ if (child.freq == 0) child.freq = occur;
if (word.length() > charAt + 1) {
addWordRec(child, word, charAt + 1, occur);
} else {
@@ -195,56 +199,76 @@ public class MakeBinaryDictionary {
static final int ADDR_WIDTH = 23; // Offset to children
static final int FREQ_WIDTH_BYTES = 1;
static final int COUNT_WIDTH_BYTES = 1;
- static final int NODE_SIZE_BYTES =
- (CHAR_WIDTH + FLAGS_WIDTH + ADDR_WIDTH) / 8 + FREQ_WIDTH_BYTES;
private void addCount(int count) {
dict[dictSize++] = (byte) (0xFF & count);
}
- /* TODO: Allow characters to be beyond the 0-255 range. This is required for some latin
- language not currently supported */
private void addNode(CharNode node) {
int charData = 0xFFFF & node.data;
if (charData > 254) {
- System.out.println("WARNING: Non-ASCII character encountered : " + node.data +
- ", value = " + charData);
- dict[dictSize++] = '@';
+ dict[dictSize++] = (byte) 255;
+ dict[dictSize++] = (byte) ((node.data >> 8) & 0xFF);
+ dict[dictSize++] = (byte) (node.data & 0xFF);
} else {
dict[dictSize++] = (byte) (0xFF & node.data);
}
- dictSize += 3; // Space for children address
- if ((0xFFFFFF & node.freq) > 255) {
- node.freq = (byte) 255;
+ if (node.children != null) {
+ dictSize += 3; // Space for children address
+ } else {
+ dictSize += 1; // Space for just the terminal/address flags
+ }
+ if ((0xFFFFFF & node.freq) > 255) {
+ node.freq = 255;
+ }
+ if (node.terminal) {
+ byte freq = (byte) (0xFF & node.freq);
+ dict[dictSize++] = freq;
}
- dict[dictSize++] = (byte) (0xFF & node.freq);
}
+ int nullChildrenCount = 0;
+ int notTerminalCount = 0;
+
private void updateNodeAddress(int nodeAddress, CharNode node,
int childrenAddress) {
- childrenAddress = 0x7FFFFF & childrenAddress;
+ if ((dict[nodeAddress] & 0xFF) == 0xFF) { // 3 byte character
+ nodeAddress += 2;
+ }
+ childrenAddress = ADDRESS_MASK & childrenAddress;
+ if (childrenAddress == 0) {
+ nullChildrenCount++;
+ } else {
+ childrenAddress |= FLAG_ADDRESS_MASK;
+ }
if (node.terminal) {
- childrenAddress |= 0x800000;
+ childrenAddress |= FLAG_TERMINAL_MASK;
+ } else {
+ notTerminalCount++;
}
dict[nodeAddress + 1] = (byte) (childrenAddress >> 16);
- dict[nodeAddress + 2] = (byte) ((childrenAddress & 0xFF00) >> 8);
- dict[nodeAddress + 3] = (byte) ((childrenAddress & 0xFF));
+ if ((childrenAddress & FLAG_ADDRESS_MASK) != 0) {
+ dict[nodeAddress + 2] = (byte) ((childrenAddress & 0xFF00) >> 8);
+ dict[nodeAddress + 3] = (byte) ((childrenAddress & 0xFF));
+ }
}
void writeWordsRec(List children) {
if (children == null || children.size() == 0) {
return;
}
- addCount(children.size());
- int childrenStart = dictSize;
- for (int j = 0; j < children.size(); j++) {
+ final int childCount = children.size();
+ addCount(childCount);
+ //int childrenStart = dictSize;
+ int[] childrenAddresses = new int[childCount];
+ for (int j = 0; j < childCount; j++) {
CharNode node = children.get(j);
+ childrenAddresses[j] = dictSize;
addNode(node);
}
- for (int j = 0; j < children.size(); j++) {
+ for (int j = 0; j < childCount; j++) {
CharNode node = children.get(j);
- // TODO: Fix this when child length becomes variable
- int nodeAddress = childrenStart + NODE_SIZE_BYTES * j;
+ int nodeAddress = childrenAddresses[j];
int cacheDictSize = dictSize;
writeWordsRec(node.children);
updateNodeAddress(nodeAddress, node, node.children != null
@@ -253,8 +277,8 @@ public class MakeBinaryDictionary {
}
void writeToDict(String dictFilename) {
- // 2MB max
- dict = new byte[2 * 1024 * 1024]; // 2MB upper limit. Actual is probably
+ // 4MB max, 22-bit offsets
+ dict = new byte[4 * 1024 * 1024]; // 4MB upper limit. Actual is probably
// < 1MB in most cases, as there is a limit in the
// resource size in apks.
dictSize = 0;
@@ -272,19 +296,29 @@ public class MakeBinaryDictionary {
void traverseDict(int pos, char[] word, int depth) {
int count = dict[pos++] & 0xFF;
for (int i = 0; i < count; i++) {
- char c = (char) (dict[pos] & 0xFF);
- word[depth] = c;
- if ((dict[pos + 1] & 0x80) > 0) {
- showWord(word, depth + 1, dict[pos + 4] & 0xFF);
+ char c = (char) (dict[pos++] & 0xFF);
+ if (c == 0xFF) {
+ c = (char) (((dict[pos] & 0xFF) << 8) | (dict[pos+1] & 0xFF));
+ pos += 2;
+ }
+ word[depth] = c;
+ boolean terminal = (dict[pos] & 0x80) > 0;
+ int address = 0;
+ if ((dict[pos] & (FLAG_ADDRESS_MASK >> 16)) > 0) {
+ address =
+ ((dict[pos + 0] & (FLAG_ADDRESS_MASK >> 16)) << 16)
+ | ((dict[pos + 1] & 0xFF) << 8)
+ | ((dict[pos + 2] & 0xFF));
+ pos += 2;
+ }
+ pos++;
+ if (terminal) {
+ showWord(word, depth + 1, dict[pos] & 0xFF);
+ pos++;
}
- int address =
- ((dict[pos + 1] & 0x7F) << 16)
- | ((dict[pos + 2] & 0xFF) << 8)
- | ((dict[pos + 3] & 0xFF));
if (address != 0) {
traverseDict(address, word, depth + 1);
}
- pos += NODE_SIZE_BYTES;
}
}
diff --git a/tools/sdkmanager/app/src/com/android/sdkmanager/Main.java b/tools/sdkmanager/app/src/com/android/sdkmanager/Main.java
index adf37ed0b..fe56ef664 100644
--- a/tools/sdkmanager/app/src/com/android/sdkmanager/Main.java
+++ b/tools/sdkmanager/app/src/com/android/sdkmanager/Main.java
@@ -279,8 +279,8 @@ class Main {
creator.createProject(projectDir,
projectName,
- activityName,
packageName,
+ activityName,
target,
false /* isTestProject*/);
}
@@ -459,11 +459,30 @@ class Main {
mSdkLog.printf(" Sdcard: %s\n", sdcard);
}
}
+
+ // Are there some unused AVDs?
+ List badAvds = avdManager.getUnavailableAvds();
+
+ if (badAvds == null || badAvds.size() == 0) {
+ return;
+ }
+
+ mSdkLog.printf("\nThe following Android Virtual Devices could not be loaded:\n");
+ boolean needSeparator = false;
+ for (AvdInfo info : badAvds) {
+ if (needSeparator) {
+ mSdkLog.printf("---------\n");
+ }
+ mSdkLog.printf(" Name: %s\n", info.getName() == null ? "--" : info.getName());
+ mSdkLog.printf(" Path: %s\n", info.getPath() == null ? "--" : info.getPath());
+ mSdkLog.printf(" Error: %s\n", info.getError() == null ? "--" : info.getError());
+ needSeparator = true;
+ }
} catch (AndroidLocationException e) {
errorAndExit(e.getMessage());
}
}
-
+
/**
* Creates a new AVD. This is a text based creation with command line prompt.
*/
@@ -573,7 +592,7 @@ class Main {
File dir = new File(oldAvdInfo.getPath());
avdManager.recursiveDelete(dir);
dir.delete();
- // Remove old avd info from manager
+ // Remove old AVD info from manager
avdManager.removeAvd(oldAvdInfo);
}
@@ -583,13 +602,27 @@ class Main {
}
/**
- * Delete an AVD.
+ * Delete an AVD. If the AVD name is not part of the available ones look for an
+ * invalid AVD (one not loaded due to some error) to remove it too.
*/
private void deleteAvd() {
try {
String avdName = mSdkCommandLine.getParamName();
AvdManager avdManager = new AvdManager(mSdkManager, mSdkLog);
AvdInfo info = avdManager.getAvd(avdName);
+
+ if (info == null) {
+ // Look in unavailable AVDs
+ List badAvds = avdManager.getUnavailableAvds();
+ if (badAvds != null) {
+ for (AvdInfo i : badAvds) {
+ if (i.getName().equals(avdName)) {
+ info = i;
+ break;
+ }
+ }
+ }
+ }
if (info == null) {
errorAndExit("There is no Android Virtual Device named '%s'.", avdName);
diff --git a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/AddOnTarget.java b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/AddOnTarget.java
index 0a5910734..8ed73f2e6 100644
--- a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/AddOnTarget.java
+++ b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/AddOnTarget.java
@@ -124,6 +124,10 @@ final class AddOnTarget implements IAndroidTarget {
return String.format("%1$s (%2$s)", mName, mVendor);
}
+ public String getClasspathName() {
+ return String.format("%1$s [%2$s]", mName, mBasePlatform.getName());
+ }
+
public String getDescription() {
return mDescription;
}
@@ -153,7 +157,8 @@ final class AddOnTarget implements IAndroidTarget {
case SKINS:
return mLocation + SdkConstants.OS_SKINS_FOLDER;
case DOCS:
- return mLocation + SdkConstants.FD_DOCS + File.separator;
+ return mLocation + SdkConstants.FD_DOCS + File.separator
+ + SdkConstants.FD_DOCS_REFERENCE;
default :
return mBasePlatform.getPath(pathId);
}
diff --git a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/IAndroidTarget.java b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/IAndroidTarget.java
index fa462bd6e..896a83cd8 100644
--- a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/IAndroidTarget.java
+++ b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/IAndroidTarget.java
@@ -96,6 +96,11 @@ public interface IAndroidTarget extends Comparable {
*/
String getFullName();
+ /**
+ * Returns the name to be displayed when representing all the libraries this target contains.
+ */
+ String getClasspathName();
+
/**
* Returns the description of the target.
*/
diff --git a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/ISdkLog.java b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/ISdkLog.java
index 489451746..163f7a950 100644
--- a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/ISdkLog.java
+++ b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/ISdkLog.java
@@ -26,8 +26,10 @@ public interface ISdkLog {
/**
* Prints a warning message on stdout.
*
+ * The message will be tagged with "Warning" on the output so the caller does not
+ * need to put such a prefix in the format string.
+ *
* Implementations should only display warnings in verbose mode.
- * The message should be prefixed with "Warning:".
*
* @param warningFormat is an optional error format. If non-null, it will be printed
* using a {@link Formatter} with the provided arguments.
@@ -38,8 +40,10 @@ public interface ISdkLog {
/**
* Prints an error message on stderr.
*
+ * The message will be tagged with "Error" on the output so the caller does not
+ * need to put such a prefix in the format string.
+ *
* Implementation should always display errors, independent of verbose mode.
- * The message should be prefixed with "Error:".
*
* @param t is an optional {@link Throwable} or {@link Exception}. If non-null, it's
* message will be printed out.
diff --git a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/PlatformTarget.java b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/PlatformTarget.java
index 5efd55376..d4e40b182 100644
--- a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/PlatformTarget.java
+++ b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/PlatformTarget.java
@@ -104,6 +104,10 @@ final class PlatformTarget implements IAndroidTarget {
public String getFullName() {
return mName;
}
+
+ public String getClasspathName() {
+ return mName;
+ }
/*
* (non-Javadoc)
diff --git a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkConstants.java b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkConstants.java
index 00594d12f..9eb6ade6b 100644
--- a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkConstants.java
+++ b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkConstants.java
@@ -143,6 +143,8 @@ public final class SdkConstants {
public final static String FD_LIB = "lib";
/** Name of the SDK docs folder. */
public final static String FD_DOCS = "docs";
+ /** Name of the doc folder containing API reference doc (javadoc) */
+ public static final String FD_DOCS_REFERENCE = "reference";
/** Name of the SDK images folder. */
public final static String FD_IMAGES = "images";
/** Name of the SDK skins folder. */
diff --git a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/avd/AvdManager.java b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/avd/AvdManager.java
index 93577e42b..48ef8213f 100644
--- a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/avd/AvdManager.java
+++ b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/avd/AvdManager.java
@@ -32,8 +32,11 @@ import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
+import java.util.TreeSet;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -118,15 +121,43 @@ public final class AvdManager {
private final String mPath;
private final IAndroidTarget mTarget;
private final Map mProperties;
-
- /** Creates a new AVD info. Values are immutable.
- * @param properties */
+ private final String mError;
+
+ /**
+ * Creates a new valid AVD info. Values are immutable.
+ *
+ * Such an AVD is available and can be used.
+ * The error string is set to null.
+ *
+ * @param name The name of the AVD (for display or reference)
+ * @param path The path to the config.ini file
+ * @param target The target. Cannot be null.
+ * @param properties The property map. Cannot be null.
+ */
public AvdInfo(String name, String path, IAndroidTarget target,
Map properties) {
+ this(name, path, target, properties, null /*error*/);
+ }
+
+ /**
+ * Creates a new invalid AVD info. Values are immutable.
+ *
+ * Such an AVD is not complete and cannot be used.
+ * The error string must be non-null.
+ *
+ * @param name The name of the AVD (for display or reference)
+ * @param path The path to the config.ini file
+ * @param target The target. Can be null.
+ * @param properties The property map. Can be null.
+ * @param error The error describing why this AVD is invalid. Cannot be null.
+ */
+ public AvdInfo(String name, String path, IAndroidTarget target,
+ Map properties, String error) {
mName = name;
mPath = path;
mTarget = target;
mProperties = properties;
+ mError = error;
}
/** Returns the name of the AVD. */
@@ -144,6 +175,11 @@ public final class AvdManager {
return mTarget;
}
+ /** Returns the error describing why an AVD failed to load. Always null for valid AVDs. */
+ public String getError() {
+ return mError;
+ }
+
/**
* Helper method that returns the .ini {@link File} for a given AVD name.
* @throws AndroidLocationException if there's a problem getting android root directory.
@@ -243,7 +279,7 @@ public final class AvdManager {
// AVD shouldn't already exist if removePrevious is false.
if (log != null) {
log.error(null,
- "Folder %s is in the way. Use --force if you want to overwrite.",
+ "Folder %1$s is in the way. Use --force if you want to overwrite.",
avdFolder.getAbsolutePath());
}
return null;
@@ -393,9 +429,9 @@ public final class AvdManager {
if (log != null) {
if (target.isPlatform()) {
- log.printf("Created AVD '%s' based on %s\n", name, target.getName());
+ log.printf("Created AVD '%1$s' based on %2$s\n", name, target.getName());
} else {
- log.printf("Created AVD '%s' based on %s (%s)\n", name, target.getName(),
+ log.printf("Created AVD '%1$s' based on %2$s (%3$s)\n", name, target.getName(),
target.getVendor());
}
}
@@ -527,29 +563,49 @@ public final class AvdManager {
*
* This also remove it from the manager's list, The caller does not need to
* call {@link #removeAvd(AvdInfo)} afterwards.
+ *
+ * This method is designed to somehow work with an unavailable AVD, that is an AVD that
+ * could not be loaded due to some error. That means this method still tries to remove
+ * the AVD ini file or its folder if it can be found. An error will be output if any of
+ * these operations fail.
*
* @param avdInfo the information on the AVD to delete
*/
public void deleteAvd(AvdInfo avdInfo, ISdkLog log) {
try {
+ boolean error = false;
+
File f = avdInfo.getIniFile();
- if (f.exists()) {
- log.warning("Deleting file %s", f.getCanonicalPath());
+ if (f != null && f.exists()) {
+ log.warning("Deleting file %1$s", f.getCanonicalPath());
if (!f.delete()) {
- log.error(null, "Failed to delete %s", f.getCanonicalPath());
+ log.error(null, "Failed to delete %1$s", f.getCanonicalPath());
+ error = true;
}
}
-
- f = new File(avdInfo.getPath());
- if (f.exists()) {
- log.warning("Deleting folder %s", f.getCanonicalPath());
- recursiveDelete(f);
- if (!f.delete()) {
- log.error(null, "Failed to delete %s", f.getCanonicalPath());
+
+ String path = avdInfo.getPath();
+ if (path != null) {
+ f = new File(path);
+ if (f.exists()) {
+ log.warning("Deleting folder %1$s", f.getCanonicalPath());
+ recursiveDelete(f);
+ if (!f.delete()) {
+ log.error(null, "Failed to delete %1$s", f.getCanonicalPath());
+ error = true;
+ }
}
}
removeAvd(avdInfo);
+
+ if (error) {
+ log.printf("AVD '%1$s' deleted with errors. See warnings above.",
+ avdInfo.getName());
+ } else {
+ log.printf("AVD '%1$s' deleted.", avdInfo.getName());
+ }
+
} catch (AndroidLocationException e) {
log.error(e, null);
} catch (IOException e) {
@@ -575,14 +631,14 @@ public final class AvdManager {
try {
if (paramFolderPath != null) {
File f = new File(avdInfo.getPath());
- log.warning("Moving '%s' to '%s'.", avdInfo.getPath(), paramFolderPath);
+ log.warning("Moving '%1$s' to '%2$s'.", avdInfo.getPath(), paramFolderPath);
if (!f.renameTo(new File(paramFolderPath))) {
- log.error(null, "Failed to move '%s' to '%s'.",
+ log.error(null, "Failed to move '%1$s' to '%2$s'.",
avdInfo.getPath(), paramFolderPath);
return false;
}
- // update avd info
+ // update AVD info
AvdInfo info = new AvdInfo(avdInfo.getName(), paramFolderPath, avdInfo.getTarget(),
avdInfo.getProperties());
mAvdList.remove(avdInfo);
@@ -597,19 +653,22 @@ public final class AvdManager {
File oldIniFile = avdInfo.getIniFile();
File newIniFile = AvdInfo.getIniFile(newName);
- log.warning("Moving '%s' to '%s'.", oldIniFile.getPath(), newIniFile.getPath());
+ log.warning("Moving '%1$s' to '%2$s'.", oldIniFile.getPath(), newIniFile.getPath());
if (!oldIniFile.renameTo(newIniFile)) {
- log.error(null, "Failed to move '%s' to '%s'.",
+ log.error(null, "Failed to move '%1$s' to '%2$s'.",
oldIniFile.getPath(), newIniFile.getPath());
return false;
}
- // update avd info
+ // update AVD info
AvdInfo info = new AvdInfo(newName, avdInfo.getPath(), avdInfo.getTarget(),
avdInfo.getProperties());
mAvdList.remove(avdInfo);
mAvdList.add(info);
}
+
+ log.printf("AVD '%1$s' moved.", avdInfo.getName());
+
} catch (AndroidLocationException e) {
log.error(e, null);
} catch (IOException e) {
@@ -634,29 +693,28 @@ public final class AvdManager {
}
}
- private void buildAvdList(ArrayList list) throws AndroidLocationException {
+ /**
+ * Returns a list of files that are potential AVD ini files.
+ *
+ * This lists the $HOME/.android/avd/.ini files.
+ * Such files are properties file than then indicate where the AVD folder is located.
+ *
+ * @return A new {@link File} array or null. The array might be empty.
+ * @throws AndroidLocationException if there's a problem getting android root directory.
+ */
+ private File[] buildAvdFilesList() throws AndroidLocationException {
// get the Android prefs location.
String avdRoot = AndroidLocation.getFolder() + AndroidLocation.FOLDER_AVD;
- final boolean avdListDebug = System.getenv("AVD_LIST_DEBUG") != null;
- if (avdListDebug) {
- mSdkLog.printf("[AVD LIST DEBUG] AVD root: '%s'\n", avdRoot);
- }
-
// ensure folder validity.
File folder = new File(avdRoot);
if (folder.isFile()) {
- if (avdListDebug) {
- mSdkLog.printf("[AVD LIST DEBUG] AVD root is a file.\n");
- }
- throw new AndroidLocationException(String.format("%s is not a valid folder.", avdRoot));
+ throw new AndroidLocationException(
+ String.format("%1$s is not a valid folder.", avdRoot));
} else if (folder.exists() == false) {
- if (avdListDebug) {
- mSdkLog.printf("[AVD LIST DEBUG] AVD root folder doesn't exist, creating.\n");
- }
// folder is not there, we create it and return
folder.mkdirs();
- return;
+ return null;
}
File[] avds = folder.listFiles(new FilenameFilter() {
@@ -664,10 +722,6 @@ public final class AvdManager {
if (INI_NAME_PATTERN.matcher(name).matches()) {
// check it's a file and not a folder
boolean isFile = new File(parent, name).isFile();
- if (avdListDebug) {
- mSdkLog.printf("[AVD LIST DEBUG] Item '%s': %s\n",
- name, isFile ? "accepted file" : "rejected");
- }
return isFile;
}
@@ -675,63 +729,165 @@ public final class AvdManager {
}
});
- for (File avd : avds) {
- AvdInfo info = parseAvdInfo(avd);
- if (info != null) {
- list.add(info);
- if (avdListDebug) {
- mSdkLog.printf("[AVD LIST DEBUG] Added AVD '%s'\n", info.getPath());
+ return avds;
+ }
+
+ /**
+ * Computes the internal list of available AVDs.
+ * This only contains AVDs that reference the target currently available.
+ *
+ * @param list An array list that will contain the list of AVDs.
+ * @throws AndroidLocationException if there's a problem getting android root directory.
+ */
+ private void buildAvdList(ArrayList list) throws AndroidLocationException {
+
+ File[] avds = buildAvdFilesList();
+ if (avds != null) {
+ for (File avd : avds) {
+ AvdInfo info = parseAvdInfo(avd, false /*acceptError*/);
+ if (info != null) {
+ list.add(info);
}
- } else if (avdListDebug) {
- mSdkLog.printf("[AVD LIST DEBUG] Failed to parse AVD '%s'\n", avd.getPath());
}
}
}
-
- private AvdInfo parseAvdInfo(File path) {
- Map map = SdkManager.parsePropertyFile(path, mSdkLog);
+
+ /**
+ * Computes the internal list of not available AVDs.
+ *
+ * These are the AVDs that failed to load for some reason or another.
+ * You can retrieve the load error using {@link AvdInfo#getError()}.
+ *
+ * These {@link AvdInfo} must not be used for usual operations (e.g. instanciating
+ * an emulator) or trying to use them for anything else but {@link #deleteAvd(AvdInfo, ISdkLog)}
+ * will have unpredictable results -- that is most likely the operation will fail.
+ *
+ * @return A list of unavailable AVDs, all with errors. The list can be null or empty if there
+ * are no AVDs to return.
+ * @throws AndroidLocationException if there's a problem getting android root directory.
+ */
+ public List getUnavailableAvds() throws AndroidLocationException {
+ AvdInfo[] avds = getAvds();
+ File[] allAvds = buildAvdFilesList();
+ if (allAvds == null || allAvds.length == 0) {
+ return null;
+ }
+
+ TreeSet list = new TreeSet(Arrays.asList(allAvds));
+
+ for (AvdInfo info : avds) {
+ if (list.remove(info.getIniFile())) {
+ if (list.size() == 0) {
+ return null;
+ }
+ }
+ }
+ ArrayList errorAvds = new ArrayList(list.size());
+ for (File file : list) {
+ errorAvds.add(parseAvdInfo(file, true /*acceptError*/));
+ }
+
+ return errorAvds;
+ }
+
+ /**
+ * Parses an AVD config.ini to create an {@link AvdInfo}.
+ *
+ * @param path The path to the AVD config.ini
+ * @param acceptError When false, an AVD that fails to load will be discarded and null will be
+ * returned. When true, such an AVD will be returned with an error description.
+ * @return A new {@link AvdInfo} or null if the file is not valid or null if the AVD is not
+ * valid and acceptError is false.
+ */
+ private AvdInfo parseAvdInfo(File path, boolean acceptError) {
+ String error = null;
+ Map map = SdkManager.parsePropertyFile(path, mSdkLog);
+
String avdPath = map.get(AVD_INFO_PATH);
- if (avdPath == null) {
- return null;
- }
-
String targetHash = map.get(AVD_INFO_TARGET);
- if (targetHash == null) {
- return null;
+
+ IAndroidTarget target = null;
+ File configIniFile = null;
+ Map properties = null;
+
+ if (targetHash != null) {
+ target = mSdk.getTargetFromHashString(targetHash);
}
- IAndroidTarget target = mSdk.getTargetFromHashString(targetHash);
- if (target == null) {
- return null;
+ // load the AVD properties.
+ if (avdPath != null) {
+ configIniFile = new File(avdPath, CONFIG_INI);
}
- // load the avd properties.
- File configIniFile = new File(avdPath, CONFIG_INI);
- Map properties = SdkManager.parsePropertyFile(configIniFile, mSdkLog);
+ if (configIniFile != null) {
+ properties = SdkManager.parsePropertyFile(configIniFile, mSdkLog);
+ }
+ // get name
+ String name = path.getName();
Matcher matcher = INI_NAME_PATTERN.matcher(path.getName());
-
+ if (matcher.matches()) {
+ name = matcher.group(1);
+ }
+
+ if (!acceptError) {
+ if (avdPath == null ||
+ targetHash == null ||
+ target == null ||
+ configIniFile == null ||
+ properties == null) {
+ return null;
+ }
+ } else {
+ if (avdPath == null || configIniFile == null) {
+ error = String.format("Missing AVD 'path' property in %1$s", name);
+ } else if (targetHash == null) {
+ error = String.format("Missing 'target' property in %1$s", name);
+ } else if (target == null) {
+ error = String.format("Unknown target '%2$s' in %1$s", name, targetHash);
+ } else if (properties == null) {
+ error = String.format("Failed to parse properties from %1$s", avdPath);
+ }
+ }
+
AvdInfo info = new AvdInfo(
- matcher.matches() ? matcher.group(1) : path.getName(), // should not happen
+ name,
avdPath,
target,
- properties);
+ properties,
+ error);
return info;
}
+ /**
+ * Writes a new AVD config.ini file from a set of properties.
+ *
+ * @param iniFile The file to generate.
+ * @param values THe properties to place in the ini file.
+ * @throws IOException if {@link FileWriter} fails to open, write or close the file.
+ */
private static void createConfigIni(File iniFile, Map values)
throws IOException {
FileWriter writer = new FileWriter(iniFile);
for (Entry entry : values.entrySet()) {
- writer.write(String.format("%s=%s\n", entry.getKey(), entry.getValue()));
+ writer.write(String.format("%1$s=%2$s\n", entry.getKey(), entry.getValue()));
}
writer.close();
}
+ /**
+ * Invokes the tool to create a new SD card image file.
+ *
+ * @param toolLocation The path to the mksdcard tool.
+ * @param size The size of the new SD Card, compatible with {@link #SDCARD_SIZE_PATTERN}.
+ * @param location The path of the new sdcard image file to generate.
+ * @param log The logger object, to report errors.
+ * @return True if the sdcard could be created.
+ */
private boolean createSdCard(String toolLocation, String size, String location, ISdkLog log) {
try {
String[] command = new String[3];
@@ -744,26 +900,25 @@ public final class AvdManager {
ArrayList stdOutput = new ArrayList();
int status = grabProcessOutput(process, errorOutput, stdOutput,
true /* waitForReaders */);
-
- if (status != 0) {
- log.error(null, "Failed to create the SD card.");
+
+ if (status == 0) {
+ return true;
+ } else {
for (String error : errorOutput) {
log.error(null, error);
}
-
- return false;
}
- return true;
} catch (InterruptedException e) {
- log.error(null, "Failed to create the SD card.");
+ // pass, print error below
} catch (IOException e) {
- log.error(null, "Failed to create the SD card.");
+ // pass, print error below
}
+ log.error(null, "Failed to create the SD card.");
return false;
}
-
+
/**
* Gets the stderr/stdout outputs of a process and returns when the process is done.
* Both must be read or the process will block on windows.
diff --git a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/AvdSelector.java b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/AvdSelector.java
index 67c70a676..d62c231c1 100644
--- a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/AvdSelector.java
+++ b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/AvdSelector.java
@@ -89,9 +89,9 @@ public final class AvdSelector {
final TableColumn column1 = new TableColumn(mTable, SWT.NONE);
column1.setText("Target Name");
final TableColumn column2 = new TableColumn(mTable, SWT.NONE);
- column2.setText("API Level");
+ column2.setText("SDK");
final TableColumn column3 = new TableColumn(mTable, SWT.NONE);
- column3.setText("SDK");
+ column3.setText("API Level");
adjustColumnsWidth(mTable, column0, column1, column2, column3);
setupSelectionListener(mTable);
@@ -235,8 +235,8 @@ public final class AvdSelector {
Rectangle r = table.getClientArea();
column0.setWidth(r.width * 30 / 100); // 30%
column1.setWidth(r.width * 45 / 100); // 45%
- column2.setWidth(r.width * 15 / 100); // 15%
- column3.setWidth(r.width * 10 / 100); // 10%
+ column2.setWidth(r.width * 10 / 100); // 10%
+ column3.setWidth(r.width * 15 / 100); // 15%
}
});
}