Merge commit 'goog/readonly-p4-master'
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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}
|
||||
|
||||
|
||||
280
docs/howto_build_SDK.txt
Normal file
280
docs/howto_build_SDK.txt
Normal file
@@ -0,0 +1,280 @@
|
||||
Subject: How to build an Android SDK & ADT Eclipse plugin.
|
||||
Date: 2009/03/27
|
||||
|
||||
|
||||
Table of content:
|
||||
0- License
|
||||
1- Foreword
|
||||
2- Building an SDK for MacOS and Linux
|
||||
3- Building an SDK for Windows
|
||||
4- Building an ADT plugin for Eclipse
|
||||
5- Conclusion
|
||||
|
||||
|
||||
|
||||
----------
|
||||
0- License
|
||||
----------
|
||||
|
||||
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.
|
||||
|
||||
|
||||
|
||||
-----------
|
||||
1- Foreword
|
||||
-----------
|
||||
|
||||
This document explains how to build the Android SDK and the ADT Eclipse plugin.
|
||||
|
||||
It is designed for advanced users which are proficient with command-line
|
||||
operations and know how to setup the pre-required software.
|
||||
|
||||
Basically it's not trivial yet when done right it's not that complicated.
|
||||
|
||||
|
||||
|
||||
--------------------------------------
|
||||
2- Building an SDK for MacOS and Linux
|
||||
--------------------------------------
|
||||
|
||||
First, setup your development environment and get the Android source code from
|
||||
git as explained here:
|
||||
|
||||
http://source.android.com/download
|
||||
|
||||
For example for the cupcake branch:
|
||||
|
||||
$ mkdir ~/my-android-git
|
||||
$ cd ~/my-android-git
|
||||
$ repo init -u git://android.git.kernel.org/platform/manifest.git -b cupcake
|
||||
$ repo sync
|
||||
|
||||
Then once you have all the source, simply build the SDK using:
|
||||
|
||||
$ cd ~/my-android-git
|
||||
$ . build/envsetup.sh
|
||||
$ make sdk
|
||||
|
||||
This will take a while, maybe between 20 minutes and several hours depending on
|
||||
your machine. After a while you'll see this in the output:
|
||||
|
||||
Package SDK: out/host/darwin-x86/sdk/android-sdk_eng.<build-id>_mac-x86.zip
|
||||
|
||||
Some options:
|
||||
|
||||
- Depending on your machine you can tell 'make' to build more things in
|
||||
parallel, e.g. if you have a dual core, use "make -j4 sdk" to build faster.
|
||||
|
||||
- You can define "BUILD_NUMBER" to control the build identifier that gets
|
||||
incorporated in the resulting archive. The default is to use your username.
|
||||
One suggestion is to include the date, e.g.:
|
||||
|
||||
$ export BUILD_NUMBER=${USER}-`date +%Y%m%d-%H%M%S`
|
||||
|
||||
There are certain characters you should avoid in the build number, typically
|
||||
everything that might confuse 'make' or your shell. So for example avoid
|
||||
punctuation and characters like $ & : / \ < > , and .
|
||||
|
||||
|
||||
|
||||
------------------------------
|
||||
3- Building an SDK for Windows
|
||||
------------------------------
|
||||
|
||||
A- SDK pre-requisite
|
||||
--------------------
|
||||
|
||||
First you need to build an SDK for MacOS and Linux. The Windows build works by
|
||||
updating an existing MacOS or Linux SDK zip file and replacing the unix
|
||||
binaries by Windows binaries.
|
||||
|
||||
|
||||
|
||||
B- Cygwin pre-requisite & code checkout
|
||||
---------------------------------------
|
||||
|
||||
Second you need to install Cygwin and configure it:
|
||||
- Get the installer at http://sources.redhat.com/cygwin/
|
||||
- When installing Cygwin, set Default Text File Type to Unix/binary, not DOS/text.
|
||||
This is really important, otherwise you will get errors when trying to
|
||||
checkout code using git.
|
||||
- Packages that you must install or not:
|
||||
- Required packages: autoconf, bison, curl, flex, gcc, g++, git, gnupg, make,
|
||||
mingw-zlib, python, zip, unzip.
|
||||
- Suggested extra packages: diffutils, emacs, openssh, rsync, vim, wget.
|
||||
- Packages that must not be installed: readline.
|
||||
|
||||
Once you installed Cygwin properly, checkout the code from git as you did
|
||||
for MacOS or Linux. Make sure to get the same branch, and if possible keep
|
||||
it as close to the other one as possible:
|
||||
|
||||
$ mkdir ~/my-android-git
|
||||
$ cd ~/my-android-git
|
||||
$ repo init -u git://android.git.kernel.org/platform/manifest.git -b cupcake
|
||||
$ repo sync
|
||||
|
||||
|
||||
|
||||
C- Building the Windows SDK
|
||||
---------------------------
|
||||
|
||||
Now it's time to build that Windows SDK. You need:
|
||||
- The path to the MacOS or Linux SDK zip.
|
||||
- A directory where to place the final SDK. It will also hold some temporary
|
||||
files.
|
||||
- The build number will be extracted from the SDK zip filename, but this will
|
||||
only work if that build number has no underscores in it. It is suggested you
|
||||
just define SDK_NUMBER (and not BUILD_NUMBER!) on the command line before
|
||||
invoking the script.
|
||||
|
||||
Note that the "SDK number" is really a free identifier of your choice. It
|
||||
doesn't need to be strictly a number. As always it is suggested you avoid
|
||||
too much punctuation and special shell/make characters. Underscores cannot
|
||||
be used.
|
||||
|
||||
|
||||
To summarize, the steps on the command line would be something like this:
|
||||
|
||||
$ mkdir ~/mysdk
|
||||
$ export SDK_NUMBER=${USER}-`date +%Y%m%d-%H%M%S`
|
||||
$ cd ~/my-android-git
|
||||
$ development/build/tools/make_windows_sdk.sh /path/to/macos/or/linux/sdk.zip ~/mysdk
|
||||
|
||||
This will take a while to build some Windows-specific binaries, including the
|
||||
emulator, unzip the previous zip, rename & replace things and rezip the final
|
||||
Windows SDK zip file. A typical build time should be around 5-10 minutes.
|
||||
|
||||
|
||||
|
||||
-------------------------------------
|
||||
4- Building an ADT plugin for Eclipse
|
||||
-------------------------------------
|
||||
|
||||
Requirements:
|
||||
- You can currently only build an ADT plugin for Eclipse under Linux.
|
||||
- You must have a working version of Eclipse 3.4 "ganymede" RCP installed.
|
||||
- You need X11 to run Eclipse at least once.
|
||||
- You need a lot of patience. The trick is to do the initial setup correctly
|
||||
once, after it's a piece of cake.
|
||||
|
||||
|
||||
|
||||
A- Pre-requisites
|
||||
-----------------
|
||||
|
||||
Note for Ubuntu or Debian users: your apt repository probably only has Eclipse
|
||||
3.2 available and it's probably not suitable to build plugins in the first
|
||||
place. Forget that and install a working 3.4 manually as described below.
|
||||
|
||||
- Visit http://www.eclipse.org/downloads/ to grab the
|
||||
"Eclipse for RCP/Plug-in Developers (176 MB)" download for Linux.
|
||||
32-bit and 64-bit versions are available, depending on your Linux installation.
|
||||
|
||||
Note: we've always used a 32-bit one, so use the 64-bit one at your own risk.
|
||||
|
||||
Note: Eclipse comes in various editions. Do yourself a favor and just stick
|
||||
to the RCP for building this plugin. For example the J2EE contains too many
|
||||
useless features that will get in the way, and the "Java" version lacks some
|
||||
plugins you need to build other plugins. Please just use the RCP one.
|
||||
|
||||
- Unpack "eclipse-rcp-ganymede-SR2-linux-gtk.tar.gz" in the directory of
|
||||
your choice, e.g.:
|
||||
|
||||
$ mkdir ~/eclipse-3.4
|
||||
$ cd ~/eclipse-3.4
|
||||
$ tar xvzf eclipse-rcp-ganymede-SR2-linux-gtk.tar.gz
|
||||
|
||||
This will create an "eclipse" directory in the current directory.
|
||||
|
||||
- Set ECLIPSE_HOME to that "eclipse" directory:
|
||||
|
||||
$ export ECLIPSE_HOME=~/eclipse-3.4/eclipse
|
||||
|
||||
Note: it is important you set ECLIPSE_HOME before starting the build.
|
||||
Otherwise the build process will try to download and install its own Eclipse
|
||||
installation in /buildroot, which is probably limited to root.
|
||||
|
||||
- Now, before you can build anything, it is important that you start Eclipse
|
||||
*manually* once using the same user that you will use to build later. That's
|
||||
because your Eclipse installation is not finished: Eclipse must be run at
|
||||
least once to create some files in ~/.eclipse/. So run Eclipse now:
|
||||
|
||||
$ ~/eclipse-3.4/eclipse/eclipse &
|
||||
|
||||
Wait for it load, create a workspace when requested and then simply quit
|
||||
using the File > Quit menu. That's it. You won't need to run it manually
|
||||
again.
|
||||
|
||||
|
||||
|
||||
B- Building ADT
|
||||
---------------
|
||||
|
||||
Finally, you have Eclipse, it's installed and it created its own config files,
|
||||
so now you can build your ADT plugin. To do that you'll change directories to
|
||||
your git repository and invoke the build script by giving it a destination
|
||||
directory and an optional build number:
|
||||
|
||||
$ mkdir ~/mysdk
|
||||
$ cd ~/my-android-git # <-- this is where you did your "repo sync"
|
||||
$ development/tools/eclipse/scripts/build_server.sh ~/mysdk $USER
|
||||
|
||||
The first argument is the destination directory. It must be absolute. Do not
|
||||
give a relative destination directory such as "../mysdk". This will make the
|
||||
Eclipse build fail with a cryptic message:
|
||||
|
||||
BUILD SUCCESSFUL
|
||||
Total time: 1 minute 5 seconds
|
||||
**** Package in ../mysdk
|
||||
Error: Build failed to produce ../mysdk/android-eclipse
|
||||
Aborting
|
||||
|
||||
The second argument is the build "number". The example used "$USER" but it
|
||||
really is a free identifier of your choice. It cannot contain spaces nor
|
||||
periods (dashes are ok.) If the build number is missing, a build timestamp will
|
||||
be used instead in the filename.
|
||||
|
||||
The build should take something like 5-10 minutes.
|
||||
|
||||
|
||||
When the build succeeds, you'll see something like this at the end of the
|
||||
output:
|
||||
|
||||
ZIP of Update site available at ~/mysdk/android-eclipse-v200903272328.zip
|
||||
or
|
||||
ZIP of Update site available at ~/mysdk/android-eclipse-<buildnumber>.zip
|
||||
|
||||
When you load the plugin in Eclipse, its feature and plugin name will look like
|
||||
"com.android.ide.eclipse.adt_0.9.0.v200903272328-<buildnumber>.jar". The
|
||||
internal plugin ID is always composed of the package, the build timestamp and
|
||||
then your own build identifier (a.k.a. the "build number"), if provided. This
|
||||
means successive builds with the same build identifier are incremental and
|
||||
Eclipse will know how to update to more recent ones.
|
||||
|
||||
|
||||
|
||||
-------------
|
||||
5- Conclusion
|
||||
-------------
|
||||
|
||||
This completes the howto guide on building your own SDK and ADT plugin.
|
||||
Feedback is welcome on the public Android Open Source forums:
|
||||
http://source.android.com/discuss
|
||||
|
||||
If you are upgrading from a pre-cupcake to a cupcake or later SDK please read
|
||||
the accompanying document "howto_use_cupcake_sdk.txt".
|
||||
|
||||
-end-
|
||||
|
||||
371
docs/howto_use_cupcake_sdk.txt
Normal file
371
docs/howto_use_cupcake_sdk.txt
Normal file
@@ -0,0 +1,371 @@
|
||||
Subject: How to build use a Cupcake Android SDK & ADT Eclipse plugin.
|
||||
Date: 2009/03/27
|
||||
|
||||
|
||||
Table of content:
|
||||
0- License
|
||||
1- Foreword
|
||||
2- Installation steps
|
||||
3- For Eclipse users
|
||||
4- For Ant users
|
||||
5- Targets, AVDs, Emulator changes
|
||||
6- Conclusion
|
||||
|
||||
|
||||
|
||||
----------
|
||||
0- License
|
||||
----------
|
||||
|
||||
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.
|
||||
|
||||
|
||||
|
||||
-----------
|
||||
1- Foreword
|
||||
-----------
|
||||
|
||||
This explains how to use the "new" SDK provided starting with cupcake.
|
||||
The new SDK has as a different structure than the pre-cupcake ones.
|
||||
|
||||
This means:
|
||||
- The new SDK does not work with older Eclipse plugins (ADT 0.8)
|
||||
- The old SDKs (1.0 and 1.1) do NOT work with this Eclipse plugin (ADT 0.9)
|
||||
|
||||
|
||||
|
||||
----------------------
|
||||
2- Installation steps
|
||||
----------------------
|
||||
|
||||
First you will need to grab the zip of the SDK for your platform or build it
|
||||
yourself. Please refer to the accompanying document "howto_build_SDK.txt" if
|
||||
needed.
|
||||
|
||||
Unzip the SDK somewhere. We'll call that directory "SDK" in command-line
|
||||
examples.
|
||||
|
||||
Grab the new ADT Eclipse plugin zip file or build it yourself. Keep it
|
||||
somewhere (no need to unzip).
|
||||
|
||||
|
||||
|
||||
--------------------
|
||||
3- For Eclipse users
|
||||
--------------------
|
||||
|
||||
|
||||
Below we'll explain how you can upgrade your Eclipse install to the new plugin.
|
||||
If you already have a working Eclipse installation with a pre-0.9 ADT,
|
||||
another suggestion is to simply install a new copy of Eclipse and create a
|
||||
new empty workspace. This is just a precaution. The update process should
|
||||
be otherwise harmless.
|
||||
|
||||
|
||||
|
||||
A- Setting up Eclipse
|
||||
---------------------
|
||||
|
||||
- You must have Eclipse 3.3 or 3.4. Eclipse 3.2 is not longer supported.
|
||||
|
||||
There are many flavors, or "editions", of Eclipse. To develop, we'd recommend
|
||||
the "Java" edition. The "RCP" one is totally suitable too. The J2EE one is
|
||||
probably overkill.
|
||||
|
||||
|
||||
- If updating an existing Eclipse, use Help > Software Update and please
|
||||
uninstall the two features of the previous ADT: the "editors" feature and the
|
||||
ADT feature itself.
|
||||
|
||||
=> If you don't you will get a conflict on editors when installing
|
||||
the new one.
|
||||
|
||||
- Using Help > Software Update, add a new "archived site", point it to the new
|
||||
adt.zip (e.g. android-eclipse-<some-id>.zip), select the "Install" button at
|
||||
the top right and restart eclipse as needed.
|
||||
|
||||
- After it restarts, please use Window > Preferences > Android and select
|
||||
the new SDK folder that you unzipped in paragraph 2.
|
||||
|
||||
|
||||
|
||||
B- Updating older projects
|
||||
--------------------------
|
||||
|
||||
If you have pre-0.9 projects in your Eclipse workspace, or if you import them
|
||||
from your code repository, these projects will fail to build at first.
|
||||
|
||||
First right-click on the project and select "Properties":
|
||||
|
||||
- In the properties, open the Android panel and select the platform to use.
|
||||
The SDK comes with a 1.5 platform. Select it and close the properties panel.
|
||||
- Do a clean build.
|
||||
|
||||
|
||||
The new plugin creates a "gen" folder in your project where it puts the R.java
|
||||
and all automatically generated AIDL java files. If you get an error such as:
|
||||
|
||||
"The type R is already defined"
|
||||
|
||||
that means you must check to see if your old R.java or your old auto-generated
|
||||
AIDL Java files are still present in the "src" folder. If yes, remove them.
|
||||
|
||||
Note: this does not apply to your own hand-crafted parcelable AIDL java files.
|
||||
|
||||
Note: if you want to reuse the project with an older Eclipse ADT install,
|
||||
simply remove the "gen" folder from the build path of the project.
|
||||
|
||||
|
||||
C- New Wizards
|
||||
--------------
|
||||
|
||||
The "New Android Project" wizard has been expanded to use the multi-platform
|
||||
capabilities of the new SDK.
|
||||
|
||||
There is now a "New XML File" wizard that lets you create skeleton XML resource
|
||||
files for your Android projects. This makes it easier to create a new layout, a
|
||||
new strings file, etc.
|
||||
|
||||
Both wizard are available via File > New... as well as new icons in the main
|
||||
icon bar. If you do not see the new icons, you may need to use Window > Reset
|
||||
Perspective on your Java perspective.
|
||||
|
||||
|
||||
Please see step 5 "Emulator changes" below for important details on how to run
|
||||
the emulator.
|
||||
|
||||
|
||||
|
||||
----------------
|
||||
4- For Ant users
|
||||
----------------
|
||||
|
||||
|
||||
A- build.xml has changed
|
||||
------------------------
|
||||
|
||||
You must re-create your build.xml file.
|
||||
|
||||
First if you had customized your build.xml, make a copy of it:
|
||||
|
||||
$ cd my-project
|
||||
$ cp build.xml build.xml.old
|
||||
|
||||
|
||||
Then use the new "android" tool to create a new build.xml:
|
||||
|
||||
$ SDK/tools/android update project --path /path/to/my-project
|
||||
|
||||
or
|
||||
|
||||
$ cd my-project
|
||||
$ SDK/tools/android update project --path .
|
||||
|
||||
|
||||
A "gen" folder will be created the first time you build and your R.java and
|
||||
your AIDL Java files will be generated in this "gen" folder. You MUST remove
|
||||
the old R.java and old auto-generated AIDL java files manually. (Note: this
|
||||
does not apply to your own hand-crafted parcelabe AIDL java files.)
|
||||
|
||||
|
||||
B- Where is activitycreator?
|
||||
----------------------------
|
||||
|
||||
Note that the "activitycreator" tool has been replaced by the new "android"
|
||||
tool too. Example of how to create a new Ant project:
|
||||
|
||||
$ SDK/tools/android create project --path /path/to/my/project --name ProjectName
|
||||
--package com.mycompany.myapp --activity MyActivityClass
|
||||
--target 1 --mode activity
|
||||
|
||||
|
||||
Please see paragraph 5 below for important details on how to run the emulator
|
||||
and the meaning of that "--target 1" parameter.
|
||||
|
||||
|
||||
|
||||
----------------------------------
|
||||
5- Targets, AVDs, Emulator changes
|
||||
----------------------------------
|
||||
|
||||
This applies to BOTH Eclipse and Ant users.
|
||||
|
||||
One major change with the emulator is that now you must pre-create an "Android
|
||||
Virtual Device" (a.k.a "AVD") before you run the emulator.
|
||||
|
||||
|
||||
|
||||
A- What is an AVD and why do I need one?
|
||||
----------------------------------------
|
||||
|
||||
What is an "AVD"? If you forget, just run:
|
||||
|
||||
$ SDK/tools/emulator -help-virtual-device
|
||||
|
||||
An Android Virtual Device (AVD) models a single virtual device running the
|
||||
Android platform that has, at least, its own kernel, system image and data
|
||||
partition.
|
||||
|
||||
There is a lot more explanation given by the emulator. Please run the help
|
||||
command given above to read the rest.
|
||||
|
||||
The bottom line is that you can create many emulator configurations, or "AVDs",
|
||||
each with their own system image and most important each with their own user
|
||||
data and SD card data. Then you tell Eclipse or the emulator which one to use
|
||||
to debug or run your applications.
|
||||
|
||||
|
||||
Note for Eclipse users: eventually there will be a user interface to do all of
|
||||
these operations. For right now, please use the command line interface.
|
||||
|
||||
|
||||
B- Listing targets and AVDs
|
||||
---------------------------
|
||||
|
||||
There is a new tool called "android" in the SDK that lets you know which
|
||||
"target" and AVDs you can use.
|
||||
|
||||
A target is a specific version of Android that you can use. By default the SDK
|
||||
comes with an "Android 1.5" target, codenamed "cupcake". In the future there
|
||||
will be more versions of Android to use, e.g. "Android 2.0" or specific add-ons
|
||||
provided by hardware manufacturers. When you want to run an emulator, you need
|
||||
to specify a given flavor of Android: this is the "target".
|
||||
|
||||
|
||||
To learn about available targets in your SDK, use this command:
|
||||
|
||||
$ SDK/tools/android list targets
|
||||
|
||||
This will give you an output such as:
|
||||
|
||||
Available Android targets:
|
||||
[1] Android 1.5
|
||||
API level: 3
|
||||
Skins: HVGA (default), HVGA-L, HVGA-P, QVGA-L, QVGA-P
|
||||
|
||||
Note the "[1]". Later you will need to reference this as "--target 1" on the
|
||||
command line.
|
||||
|
||||
|
||||
Similarly you can list the available AVDs:
|
||||
|
||||
$ SDK/tools/android list avds
|
||||
|
||||
Which might output something as:
|
||||
|
||||
Available Android Virtual Devices:
|
||||
Name: my_avd
|
||||
Path: C:\Users\<username>\.android\avd\my_avd.avd
|
||||
Target: Android 1.5 (API level 3)
|
||||
Skin: 320x480
|
||||
Sdcard: 16M
|
||||
|
||||
|
||||
|
||||
C- Creating an AVD
|
||||
------------------
|
||||
|
||||
To create a configuration:
|
||||
|
||||
$ SDK/tools/android create avd --name my_avd_name --target 1
|
||||
|
||||
|
||||
where "target 1" is the index of a target listed by "android list targets".
|
||||
|
||||
The AVD name is purely an identifier used to refer to the AVD later.
|
||||
Since it is used as directory name, please avoid using shell or path specific
|
||||
characters.
|
||||
|
||||
To learn the various options available when creating an AVD, simply type:
|
||||
|
||||
$ SDK/tools/android create avd
|
||||
|
||||
The android tool will automatically print an explanation of required arguments.
|
||||
|
||||
|
||||
|
||||
D- Invoking an AVD from the command-line
|
||||
----------------------------------------
|
||||
|
||||
To use this AVD in the emulator from the command-line, type:
|
||||
|
||||
$ SDK/tools/emulator @my_avd_name
|
||||
|
||||
|
||||
For more options, please consult the emulator help:
|
||||
|
||||
$ SDK/tools/emulator -help-virtual-device
|
||||
|
||||
|
||||
|
||||
E- Invoking an AVD from Eclipse
|
||||
-------------------------------
|
||||
|
||||
By default Android projects in Eclipse have an "automatic target" mode.
|
||||
In this mode, when a project is deployed in debug or run, it checks:
|
||||
- If there's one running device or emulator, this is used for deployment.
|
||||
- If there's more than one running device or emulator, a "device chooser" is
|
||||
shown to let the user select which one to use.
|
||||
- If there are no running devices or emulators, ADT looks at available AVDs.
|
||||
If one matches the project configuration (e.g. same API level), it is
|
||||
automatically used.
|
||||
|
||||
Alternatively you can edit the "launch configuration" on your Android project
|
||||
in Eclipse by selecting the menu Run > Run Configurations. In the "target" tab
|
||||
of the configuration, you can choose:
|
||||
|
||||
- Manual or automatic targetting mode.
|
||||
|
||||
- Manual means to always present the device chooser.
|
||||
- Automatic is the behavior explained above.
|
||||
|
||||
- In automatic mode, which AVD is preferred. If none is selected, the first
|
||||
suitable is used.
|
||||
|
||||
|
||||
F- AVD concurrency
|
||||
------------------
|
||||
|
||||
You can no longer run several emulators at the same time on the same
|
||||
configuration.
|
||||
|
||||
Before this used to put the second or more emulators in a transient read-only
|
||||
mode that would not save user data.
|
||||
|
||||
Now you just need to create as many AVDs as you want to run emulators.
|
||||
|
||||
For example if you are working on a client/server application for Android, you
|
||||
could create a "client" AVD and a "server" AVD then run them both at once. The
|
||||
emulator window will show you the AVD name so that you know which one is which.
|
||||
|
||||
Example:
|
||||
|
||||
$ SDK/tools/android create avd --name client --target 1 --sdcard 16M --skin HVGA
|
||||
$ SDK/tools/android create avd --name server --target 1 --sdcard 32M --skin HVGA-P
|
||||
$ SDK/tools/emulator @server &
|
||||
$ SDK/tools/emulator @client &
|
||||
|
||||
|
||||
|
||||
-------------
|
||||
6- Conclusion
|
||||
-------------
|
||||
|
||||
This completes the howto guide on how to use the new Cupcake SDK.
|
||||
Feedback is welcome on the public Android Open Source forums:
|
||||
http://source.android.com/discuss
|
||||
|
||||
-end-
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
@@ -78,6 +84,11 @@ def SetVerbose(new_verbose=True):
|
||||
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
|
||||
|
||||
|
||||
@@ -41,23 +41,27 @@ class TestRunner(object):
|
||||
|
||||
# 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_defs.xml")
|
||||
|
||||
# vendor glob file path patterns to tests, relative to android
|
||||
# build root
|
||||
_VENDOR_TEST_PATH = os.path.join("vendor", "*", "tests", "testinfo",
|
||||
"tests.xml")
|
||||
"test_defs.xml")
|
||||
|
||||
_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")
|
||||
"test_defs.xml")
|
||||
|
||||
parser = optparse.OptionParser(usage=self._RUNTEST_USAGE)
|
||||
|
||||
@@ -149,7 +153,7 @@ class TestRunner(object):
|
||||
try:
|
||||
known_tests = test_defs.TestDefinitions()
|
||||
known_tests.Parse(core_test_path)
|
||||
# read all <android root>/vendor/*/tests/testinfo/tests.xml paths
|
||||
# read all <android root>/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 +182,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 +267,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()
|
||||
|
||||
|
||||
@@ -49,6 +49,7 @@ public class RemoteAndroidTestRunner {
|
||||
private static final String LOG_ARG_NAME = "log";
|
||||
private static final String DEBUG_ARG_NAME = "debug";
|
||||
private static final String COVERAGE_ARG_NAME = "coverage";
|
||||
private static final String PACKAGE_ARG_NAME = "package";
|
||||
|
||||
/**
|
||||
* Creates a remote Android test runner.
|
||||
@@ -145,6 +146,16 @@ public class RemoteAndroidTestRunner {
|
||||
setClassName(className + METHOD_SEPARATOR + testName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets to run all tests in specified package
|
||||
* Must be called before 'run'.
|
||||
*
|
||||
* @param packageName fully qualified package name (eg x.y.z)
|
||||
*/
|
||||
public void setTestPackageName(String packageName) {
|
||||
addInstrumentationArg(PACKAGE_ARG_NAME, packageName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a argument to include in instrumentation command.
|
||||
* <p/>
|
||||
|
||||
@@ -17,16 +17,17 @@
|
||||
package com.android.ddmlib.testrunner;
|
||||
|
||||
import com.android.ddmlib.Client;
|
||||
import com.android.ddmlib.Device.DeviceState;
|
||||
import com.android.ddmlib.FileListingService;
|
||||
import com.android.ddmlib.IDevice;
|
||||
import com.android.ddmlib.IShellOutputReceiver;
|
||||
import com.android.ddmlib.log.LogReceiver;
|
||||
import com.android.ddmlib.RawImage;
|
||||
import com.android.ddmlib.SyncService;
|
||||
import com.android.ddmlib.Device.DeviceState;
|
||||
import com.android.ddmlib.log.LogReceiver;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
/**
|
||||
@@ -80,6 +81,17 @@ public class RemoteAndroidTestRunnerTest extends TestCase {
|
||||
testName, TEST_PACKAGE, TEST_RUNNER), mMockDevice.getLastShellCommand());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the building of the instrumentation runner command with test package set.
|
||||
*/
|
||||
public void testRunWithPackage() {
|
||||
final String packageName = "foo.test";
|
||||
mRunner.setTestPackageName(packageName);
|
||||
mRunner.run(new EmptyListener());
|
||||
assertStringsEquals(String.format("am instrument -w -r -e package %s %s/%s", packageName,
|
||||
TEST_PACKAGE, TEST_RUNNER), mMockDevice.getLastShellCommand());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the building of the instrumentation runner command with extra argument added.
|
||||
*/
|
||||
|
||||
@@ -43,7 +43,8 @@ Require-Bundle: com.android.ide.eclipse.ddms,
|
||||
org.eclipse.jdt.junit,
|
||||
org.eclipse.jdt.junit.runtime,
|
||||
org.eclipse.ltk.core.refactoring,
|
||||
org.eclipse.ltk.ui.refactoring
|
||||
org.eclipse.ltk.ui.refactoring,
|
||||
org.eclipse.core.expressions
|
||||
Eclipse-LazyStart: true
|
||||
Export-Package: com.android.ide.eclipse.adt;x-friends:="com.android.ide.eclipse.tests",
|
||||
com.android.ide.eclipse.adt.build;x-friends:="com.android.ide.eclipse.tests",
|
||||
@@ -52,6 +53,7 @@ Export-Package: com.android.ide.eclipse.adt;x-friends:="com.android.ide.eclipse.
|
||||
com.android.ide.eclipse.adt.project.internal;x-friends:="com.android.ide.eclipse.tests",
|
||||
com.android.ide.eclipse.adt.sdk;x-friends:="com.android.ide.eclipse.tests",
|
||||
com.android.ide.eclipse.adt.wizards.newproject;x-friends:="com.android.ide.eclipse.tests",
|
||||
com.android.ide.eclipse.adt.ui;x-friends:="com.android.ide.eclipse.tests",
|
||||
com.android.ide.eclipse.common;x-friends:="com.android.ide.eclipse.tests",
|
||||
com.android.ide.eclipse.common.project;x-friends:="com.android.ide.eclipse.tests",
|
||||
com.android.ide.eclipse.common.resources;x-friends:="com.android.ide.eclipse.tests",
|
||||
@@ -77,7 +79,6 @@ Export-Package: com.android.ide.eclipse.adt;x-friends:="com.android.ide.eclipse.
|
||||
com.android.ide.eclipse.editors.ui;x-friends:="com.android.ide.eclipse.tests",
|
||||
com.android.ide.eclipse.editors.ui.tree;x-friends:="com.android.ide.eclipse.tests",
|
||||
com.android.ide.eclipse.editors.uimodel;x-friends:="com.android.ide.eclipse.tests",
|
||||
com.android.ide.eclipse.editors.wizards;x-friends:="com.android.ide.eclipse.tests",
|
||||
com.android.ide.eclipse.editors.xml;x-friends:="com.android.ide.eclipse.tests",
|
||||
com.android.ide.eclipse.editors.xml.descriptors;x-friends:="com.android.ide.eclipse.tests"
|
||||
|
||||
|
||||
@@ -551,7 +551,7 @@
|
||||
point="org.eclipse.debug.ui.launchShortcuts">
|
||||
<shortcut
|
||||
class="com.android.ide.eclipse.adt.launch.junit.AndroidJUnitLaunchShortcut"
|
||||
icon="icons/android.png"
|
||||
icon="icons/androidjunit.png"
|
||||
id="com.android.ide.eclipse.adt.junit.launchShortcut"
|
||||
label="Android JUnit Test"
|
||||
modes="run,debug">
|
||||
@@ -563,7 +563,7 @@
|
||||
<adapt type="org.eclipse.jdt.core.IJavaElement">
|
||||
<test property="org.eclipse.jdt.core.isInJavaProjectWithNature" value="com.android.ide.eclipse.adt.AndroidNature"/>
|
||||
<test property="org.eclipse.jdt.core.hasTypeOnClasspath" value="junit.framework.Test"/>
|
||||
<test property="org.eclipse.jdt.junit.canLaunchAsJUnit" forcePluginActivation="true"/>
|
||||
<test property="com.android.ide.eclipse.adt.canLaunchAsJUnit"/>
|
||||
</adapt>
|
||||
</iterate>
|
||||
</with>
|
||||
@@ -595,4 +595,14 @@
|
||||
id="com.android.ide.eclipse.adt.refactoring.extract.string">
|
||||
</contribution>
|
||||
</extension>
|
||||
<extension
|
||||
point="org.eclipse.core.expressions.propertyTesters">
|
||||
<propertyTester
|
||||
properties="isTest,canLaunchAsJUnit"
|
||||
namespace="com.android.ide.eclipse.adt"
|
||||
type="org.eclipse.core.runtime.IAdaptable"
|
||||
class="com.android.ide.eclipse.adt.launch.junit.AndroidJUnitPropertyTester"
|
||||
id="com.android.ide.eclipse.adt.AndroidJUnitPropertyTester">
|
||||
</propertyTester>
|
||||
</extension>
|
||||
</plugin>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,14 +56,18 @@ 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());
|
||||
mLaunchInfo.setLaunch(info.getLaunch());
|
||||
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());
|
||||
|
||||
@@ -82,20 +82,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 +108,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 +153,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 +165,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 +183,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 +193,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 +242,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 +261,4 @@ class AndroidJUnitLaunchAction implements IAndroidLaunchAction {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ 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.BaseProjectHelper;
|
||||
@@ -32,8 +33,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 +51,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 +60,7 @@ public class AndroidJUnitLaunchConfigDelegate extends LaunchConfigDelegate {
|
||||
AndroidLaunchConfiguration config, AndroidLaunchController controller,
|
||||
IFile applicationPackage, AndroidManifestParser manifestParser) {
|
||||
|
||||
String testPackage = manifestParser.getPackage();
|
||||
String appPackage = manifestParser.getPackage();
|
||||
String runner = getRunner(project, configuration, manifestParser);
|
||||
if (runner == null) {
|
||||
AdtPlugin.displayError("Android Launch",
|
||||
@@ -63,14 +68,62 @@ public class AndroidJUnitLaunchConfigDelegate extends LaunchConfigDelegate {
|
||||
androidLaunch.stopLaunch();
|
||||
return;
|
||||
}
|
||||
AndroidJUnitLaunchInfo junitLaunchInfo = new AndroidJUnitLaunchInfo(project, appPackage,
|
||||
runner);
|
||||
junitLaunchInfo.setTestClass(getTestClass(configuration));
|
||||
junitLaunchInfo.setTestPackage(getTestPackage(configuration));
|
||||
junitLaunchInfo.setTestMethod(getTestMethod(configuration));
|
||||
|
||||
IAndroidLaunchAction junitLaunch = new AndroidJUnitLaunchAction(testPackage, runner);
|
||||
IAndroidLaunchAction junitLaunch = new AndroidJUnitLaunchAction(junitLaunchInfo);
|
||||
|
||||
controller.launch(project, mode, applicationPackage, manifestParser.getPackage(),
|
||||
manifestParser.getDebuggable(), manifestParser.getApiLevelRequirement(),
|
||||
junitLaunch, config, androidLaunch, monitor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the test package stored in the launch configuration, or <code>null</code> if not
|
||||
* specified.
|
||||
*
|
||||
* @param configuration the {@link ILaunchConfiguration} to retrieve the test package info from
|
||||
* @return the test package or <code>null</code>.
|
||||
*/
|
||||
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. <code>null</code> 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. <code>null</code> if not specified.
|
||||
*/
|
||||
private String getTestMethod(ILaunchConfiguration configuration) {
|
||||
return getStringLaunchAttribute(JUnitLaunchConfigurationConstants.ATTR_TEST_METHOD_NAME,
|
||||
configuration);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a instrumentation runner for the launch.
|
||||
* <p/>
|
||||
@@ -114,11 +167,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. <code>null</code> 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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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);
|
||||
@@ -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) {
|
||||
@@ -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(),
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
* <p/>
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
@@ -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() {
|
||||
@@ -54,7 +57,79 @@ public class AndroidJUnitLaunchInfo {
|
||||
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. <code>null</code> 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.
|
||||
* <code>null</code> 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. <code>null</code> if not specified.
|
||||
*/
|
||||
public String getTestMethod() {
|
||||
return mTestMethod;
|
||||
}
|
||||
|
||||
public ILaunch getLaunch() {
|
||||
return mLaunch;
|
||||
}
|
||||
|
||||
public void setLaunch(ILaunch launch) {
|
||||
mLaunch = launch;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
if (mLaunchInfo.getTestClass() != null) {
|
||||
if (mLaunchInfo.getTestMethod() != null) {
|
||||
runner.setMethodName(mLaunchInfo.getTestClass(), mLaunchInfo.getTestMethod());
|
||||
} else {
|
||||
runner.setClassNames(testClassNames);
|
||||
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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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.adt.AdtPlugin;
|
||||
import com.android.ide.eclipse.common.resources.IResourceRepository;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -21,6 +21,8 @@ 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,8 +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;
|
||||
import com.android.ide.eclipse.editors.wizards.ConfigurationSelector.ConfigurationState;
|
||||
import com.android.sdklib.IAndroidTarget;
|
||||
import com.android.sdklib.SdkConstants;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<CharNode> 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<CharNode> 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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -459,6 +459,25 @@ class Main {
|
||||
mSdkLog.printf(" Sdcard: %s\n", sdcard);
|
||||
}
|
||||
}
|
||||
|
||||
// Are there some unused AVDs?
|
||||
List<AvdInfo> 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());
|
||||
}
|
||||
@@ -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,7 +602,8 @@ 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 {
|
||||
@@ -591,6 +611,19 @@ class Main {
|
||||
AvdManager avdManager = new AvdManager(mSdkManager, mSdkLog);
|
||||
AvdInfo info = avdManager.getAvd(avdName);
|
||||
|
||||
if (info == null) {
|
||||
// Look in unavailable AVDs
|
||||
List<AvdInfo> 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);
|
||||
return;
|
||||
|
||||
@@ -26,8 +26,10 @@ public interface ISdkLog {
|
||||
/**
|
||||
* Prints a warning message on stdout.
|
||||
* <p/>
|
||||
* 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.
|
||||
* <p/>
|
||||
* 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.
|
||||
* <p/>
|
||||
* 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.
|
||||
* <p/>
|
||||
* 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.
|
||||
|
||||
@@ -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<String, String> mProperties;
|
||||
private final String mError;
|
||||
|
||||
/** Creates a new AVD info. Values are immutable.
|
||||
* @param properties */
|
||||
/**
|
||||
* Creates a new valid AVD info. Values are immutable.
|
||||
* <p/>
|
||||
* 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<String, String> properties) {
|
||||
this(name, path, target, properties, null /*error*/);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new <em>invalid</em> AVD info. Values are immutable.
|
||||
* <p/>
|
||||
* 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<String, String> 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 {
|
||||
* <p/>
|
||||
* This also remove it from the manager's list, The caller does not need to
|
||||
* call {@link #removeAvd(AvdInfo)} afterwards.
|
||||
* <p/>
|
||||
* 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<AvdInfo> list) throws AndroidLocationException {
|
||||
/**
|
||||
* Returns a list of files that are potential AVD ini files.
|
||||
* <p/>
|
||||
* This lists the $HOME/.android/avd/<name>.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,164 @@ public final class AvdManager {
|
||||
}
|
||||
});
|
||||
|
||||
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<AvdInfo> list) throws AndroidLocationException {
|
||||
|
||||
File[] avds = buildAvdFilesList();
|
||||
|
||||
for (File avd : avds) {
|
||||
AvdInfo info = parseAvdInfo(avd);
|
||||
AvdInfo info = parseAvdInfo(avd, false /*acceptError*/);
|
||||
if (info != null) {
|
||||
list.add(info);
|
||||
if (avdListDebug) {
|
||||
mSdkLog.printf("[AVD LIST DEBUG] Added AVD '%s'\n", info.getPath());
|
||||
}
|
||||
} else if (avdListDebug) {
|
||||
mSdkLog.printf("[AVD LIST DEBUG] Failed to parse AVD '%s'\n", avd.getPath());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private AvdInfo parseAvdInfo(File path) {
|
||||
/**
|
||||
* Computes the internal list of <em>not</em> available AVDs.
|
||||
* <p/>
|
||||
* These are the AVDs that failed to load for some reason or another.
|
||||
* You can retrieve the load error using {@link AvdInfo#getError()}.
|
||||
* <p/>
|
||||
* 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<AvdInfo> getUnavailableAvds() throws AndroidLocationException {
|
||||
AvdInfo[] avds = getAvds();
|
||||
File[] allAvds = buildAvdFilesList();
|
||||
if (allAvds == null || allAvds.length == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
TreeSet<File> list = new TreeSet<File>(Arrays.asList(allAvds));
|
||||
|
||||
for (AvdInfo info : avds) {
|
||||
if (list.remove(info.getIniFile())) {
|
||||
if (list.size() == 0) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ArrayList<AvdInfo> errorAvds = new ArrayList<AvdInfo>(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<String, String> 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<String, String> 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<String, String> 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<String, String> values)
|
||||
throws IOException {
|
||||
FileWriter writer = new FileWriter(iniFile);
|
||||
|
||||
for (Entry<String, String> 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];
|
||||
@@ -745,22 +900,21 @@ public final class AvdManager {
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user