Code drop from //branches/cupcake/...@124589

This commit is contained in:
The Android Open Source Project
2008-12-17 18:04:04 -08:00
parent 5c11852110
commit e943f2fd8e
659 changed files with 47382 additions and 9976 deletions

View File

@@ -0,0 +1,18 @@
#
# Copyright (C) 2008 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
SDKMANAGER_LOCAL_DIR := $(call my-dir)
include $(SDKMANAGER_LOCAL_DIR)/app/Android.mk
include $(SDKMANAGER_LOCAL_DIR)/libs/Android.mk

View File

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry combineaccessrules="false" kind="src" path="/SdkLib"/>
<classpathentry combineaccessrules="false" kind="src" path="/AndroidPrefs"/>
<classpathentry combineaccessrules="false" kind="src" path="/SdkUiLib"/>
<classpathentry kind="output" path="bin"/>
</classpath>

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>SdkManager</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>

View File

@@ -0,0 +1,5 @@
# Copyright 2007 The Android Open Source Project
#
SDKMANAGERAPP_LOCAL_DIR := $(call my-dir)
include $(SDKMANAGERAPP_LOCAL_DIR)/etc/Android.mk
include $(SDKMANAGERAPP_LOCAL_DIR)/src/Android.mk

View File

@@ -0,0 +1,8 @@
# Copyright 2008 The Android Open Source Project
#
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_PREBUILT_EXECUTABLES := android
include $(BUILD_HOST_PREBUILT)

View File

@@ -0,0 +1,84 @@
#!/bin/sh
# Copyright 2005-2007, The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# Set up prog to be the path of this script, including following symlinks,
# and set up progdir to be the fully-qualified pathname of its directory.
prog="$0"
while [ -h "${prog}" ]; do
newProg=`/bin/ls -ld "${prog}"`
newProg=`expr "${newProg}" : ".* -> \(.*\)$"`
if expr "x${newProg}" : 'x/' >/dev/null; then
prog="${newProg}"
else
progdir=`dirname "${prog}"`
prog="${progdir}/${newProg}"
fi
done
oldwd=`pwd`
progdir=`dirname "${prog}"`
cd "${progdir}"
progdir=`pwd`
prog="${progdir}"/`basename "${prog}"`
cd "${oldwd}"
jarfile=sdkmanager.jar
frameworkdir="$progdir"
libdir="$progdir"
if [ ! -r "$frameworkdir/$jarfile" ]
then
frameworkdir=`dirname "$progdir"`/tools/lib
libdir=`dirname "$progdir"`/tools/lib
fi
if [ ! -r "$frameworkdir/$jarfile" ]
then
frameworkdir=`dirname "$progdir"`/framework
libdir=`dirname "$progdir"`/lib
fi
if [ ! -r "$frameworkdir/$jarfile" ]
then
echo `basename "$prog"`": can't find $jarfile"
exit 1
fi
# Check args.
if [ debug = "$1" ]; then
# add this in for debugging
java_debug=-agentlib:jdwp=transport=dt_socket,server=y,address=8050,suspend=y
shift 1
else
java_debug=
fi
# Mac OS X needs an additional arg, or you get an "illegal thread" complaint.
if [ `uname` = "Darwin" ]; then
os_opts="-XstartOnFirstThread"
#because Java 1.6 is 64 bits only and SWT doesn't support this, we force the usage of java 1.5
java_cmd="/System/Library/Frameworks/JavaVM.framework/Versions/1.5/Commands/java"
else
os_opts=
java_cmd="java"
fi
if [ "$OSTYPE" = "cygwin" ] ; then
jarpath=`cygpath -w "$frameworkdir/$jarfile"`
progdir=`cygpath -w "$progdir"`
else
jarpath="$frameworkdir/$jarfile"
fi
# need to use "java.ext.dirs" because "-jar" causes classpath to be ignored
# might need more memory, e.g. -Xmx128M
exec "$java_cmd" -Xmx256M $os_opts $java_debug -Djava.ext.dirs="$frameworkdir" -Djava.library.path="$libdir" -Dcom.android.sdkmanager.toolsdir="$progdir" -jar "$jarpath" "$@"

View File

@@ -0,0 +1,48 @@
@echo off
rem Copyright (C) 2007 The Android Open Source Project
rem
rem Licensed under the Apache License, Version 2.0 (the "License");
rem you may not use this file except in compliance with the License.
rem You may obtain a copy of the License at
rem
rem http://www.apache.org/licenses/LICENSE-2.0
rem
rem Unless required by applicable law or agreed to in writing, software
rem distributed under the License is distributed on an "AS IS" BASIS,
rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
rem See the License for the specific language governing permissions and
rem limitations under the License.
rem don't modify the caller's environment
setlocal
rem Set up prog to be the path of this script, including following symlinks,
rem and set up progdir to be the fully-qualified pathname of its directory.
set prog=%~f0
rem Change current directory to where ddms is, to avoid issues with directories
rem containing whitespaces.
cd %~dp0
set jarfile=sdkmanager.jar
set frameworkdir=
set libdir=
if exist %frameworkdir%%jarfile% goto JarFileOk
set frameworkdir=lib\
set libdir=lib\
if exist %frameworkdir%%jarfile% goto JarFileOk
set frameworkdir=..\framework\
set libdir=..\lib\
:JarFileOk
if debug NEQ "%1" goto NoDebug
set java_debug=-agentlib:jdwp=transport=dt_socket,server=y,address=8050,suspend=y
shift 1
:NoDebug
set jarpath=%frameworkdir%%jarfile%
call java %java_debug% -Djava.ext.dirs=%frameworkdir% -Djava.library.path=%libdir% -Dcom.android.sdkmanager.toolsdir= -jar %jarpath% %*

View File

@@ -0,0 +1 @@
Main-Class: com.android.sdkmanager.Main

View File

@@ -0,0 +1,16 @@
# Copyright 2007 The Android Open Source Project
#
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_JAR_MANIFEST := ../etc/manifest.txt
LOCAL_JAVA_LIBRARIES := \
androidprefs \
sdklib \
sdkuilib
LOCAL_MODULE := sdkmanager
include $(BUILD_HOST_JAVA_LIBRARY)

View File

@@ -0,0 +1,487 @@
/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.sdkmanager;
import com.android.prefs.AndroidLocation;
import com.android.prefs.AndroidLocation.AndroidLocationException;
import com.android.sdklib.IAndroidTarget;
import com.android.sdklib.ISdkLog;
import com.android.sdklib.SdkConstants;
import com.android.sdklib.SdkManager;
import com.android.sdklib.IAndroidTarget.IOptionalLibrary;
import com.android.sdklib.vm.HardwareProperties;
import com.android.sdklib.vm.VmManager;
import com.android.sdklib.vm.HardwareProperties.HardwareProperty;
import com.android.sdklib.vm.VmManager.VmInfo;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Main class for the 'android' application
*
*/
class Main {
private final static String TOOLSDIR = "com.android.sdkmanager.toolsdir";
private final static String ARG_LIST_TARGET = "target";
private final static String ARG_LIST_VM = "vm";
private final static String[] BOOLEAN_YES_REPLIES = new String[] { "yes", "y" };
private final static String[] BOOLEAN_NO_REPLIES = new String[] { "no", "n" };
private String mSdkFolder;
private SdkManager mSdkManager;
private VmManager mVmManager;
/* --list parameters */
private String mListObject;
/* --create parameters */
private boolean mCreateVm;
private int mCreateTargetId;
private IAndroidTarget mCreateTarget;
private String mCreateName;
public static void main(String[] args) {
new Main().run(args);
}
/**
* Runs the sdk manager app
* @param args
*/
private void run(String[] args) {
init();
parseArgs(args);
parseSdk();
doAction();
}
/**
* Init the application by making sure the SDK path is available and
* doing basic parsing of the SDK.
*/
private void init() {
/* We get passed a property for the tools dir */
String toolsDirProp = System.getProperty(TOOLSDIR);
if (toolsDirProp == null) {
// for debugging, it's easier to override using the process environment
toolsDirProp = System.getenv(TOOLSDIR);
}
if (toolsDirProp == null) {
printHelpAndExit("ERROR: The tools directory property is not set, please make sure you are executing android or android.bat");
}
// got back a level for the SDK folder
File tools = new File(toolsDirProp);
mSdkFolder = tools.getParent();
}
/**
* Parses command-line arguments, or prints help/usage and exits if error.
* @param args arguments passed to the program
*/
private void parseArgs(String[] args) {
final int numArgs = args.length;
try {
int argPos = 0;
for (; argPos < numArgs; argPos++) {
final String arg = args[argPos];
if (arg.equals("-l") || arg.equals("--list")) {
mListObject = args[++argPos];
} else if (arg.equals("-c") || arg.equals("--create")) {
mCreateVm = true;
parseCreateArgs(args, ++argPos);
}
}
} catch (ArrayIndexOutOfBoundsException e) {
/* Any OOB triggers help */
printHelpAndExit("ERROR: Not enough arguments.");
}
}
private void parseCreateArgs(String[] args, int argPos) {
final int numArgs = args.length;
try {
for (; argPos < numArgs; argPos++) {
final String arg = args[argPos];
if (arg.equals("-t") || arg.equals("--target")) {
String targetId = args[++argPos];
try {
// get the target id
mCreateTargetId = Integer.parseInt(targetId);
} catch (NumberFormatException e) {
printHelpAndExit("ERROR: Target Id is not a number");
}
} else if (arg.equals("-n") || arg.equals("--name")) {
mCreateName = args[++argPos];
} else {
printHelpAndExit("ERROR: '%s' unknown argument for --create mode",
args[argPos]);
}
}
} catch (ArrayIndexOutOfBoundsException e) {
/* Any OOB triggers help */
printHelpAndExit("ERROR: Not enough arguments for --create");
}
}
/**
* Does the basic SDK parsing required for all actions
*/
private void parseSdk() {
mSdkManager = SdkManager.createManager(mSdkFolder, new ISdkLog() {
public void error(String errorFormat, Object... args) {
System.err.printf("Error: " + errorFormat, args);
System.err.println("");
}
public void warning(String warningFormat, Object... args) {
if (false) {
// TODO: on display warnings in verbose mode.
System.out.printf("Warning: " + warningFormat, args);
System.out.println("");
}
}
});
if (mSdkManager == null) {
printHelpAndExit("ERROR: Unable to parse SDK content.");
}
}
/**
* Actually do an action...
*/
private void doAction() {
if (mListObject != null) {
// list action.
if (ARG_LIST_TARGET.equals(mListObject)) {
displayTargetList();
} else if (ARG_LIST_VM.equals(mListObject)) {
displayVmList();
} else {
printHelpAndExit("'%s' is not a valid --list option", mListObject);
}
} else if (mCreateVm) {
createVm();
} else {
printHelpAndExit(null);
}
}
/**
* Displays the list of available Targets (Platforms and Add-ons)
*/
private void displayTargetList() {
System.out.println("Available Android targets:");
int index = 1;
for (IAndroidTarget target : mSdkManager.getTargets()) {
if (target.isPlatform()) {
System.out.printf("[%d] %s\n", index, target.getName());
System.out.printf(" API level: %d\n", target.getApiVersionNumber());
} else {
System.out.printf("[%d] Add-on: %s\n", index, target.getName());
System.out.printf(" Vendor: %s\n", target.getVendor());
if (target.getDescription() != null) {
System.out.printf(" Description: %s\n", target.getDescription());
}
System.out.printf(" Based on Android %s (API level %d)\n",
target.getApiVersionName(), target.getApiVersionNumber());
// display the optional libraries.
IOptionalLibrary[] libraries = target.getOptionalLibraries();
if (libraries != null) {
for (IOptionalLibrary library : libraries) {
System.out.printf(" Library: %s (%s)\n", library.getName(),
library.getJarName());
}
}
}
// get the target skins
String[] skins = target.getSkins();
System.out.print(" Skins: ");
if (skins != null) {
boolean first = true;
for (String skin : skins) {
if (first == false) {
System.out.print(", ");
} else {
first = false;
}
System.out.print(skin);
}
System.out.println("");
} else {
System.out.println("no skins.");
}
index++;
}
}
/**
* Displays the list of available VMs.
*/
private void displayVmList() {
try {
mVmManager = new VmManager(mSdkManager, null /* sdklog */);
System.out.println("Available Android VMs:");
int index = 1;
for (VmInfo info : mVmManager.getVms()) {
System.out.printf("[%d] %s\n", index, info.getName());
System.out.printf(" Path: %s\n", info.getPath());
// get the target of the Vm
IAndroidTarget target = info.getTarget();
if (target.isPlatform()) {
System.out.printf(" Target: %s (API level %d)\n", target.getName(),
target.getApiVersionNumber());
} else {
System.out.printf(" Target: %s (%s)\n", target.getName(), target
.getVendor());
System.out.printf(" Based on Android %s (API level %d)\n", target
.getApiVersionName(), target.getApiVersionNumber());
}
index++;
}
} catch (AndroidLocationException e) {
printHelpAndExit(e.getMessage());
}
}
/**
* Creates a new VM. This is a text based creation with command line prompt.
*/
private void createVm() {
// find a matching target
if (mCreateTargetId >= 1 && mCreateTargetId <= mSdkManager.getTargets().length) {
mCreateTarget = mSdkManager.getTargets()[mCreateTargetId-1]; // target it is 1-based
} else {
printHelpAndExit(
"ERROR: Target Id is not a valid Id. Check android --list target for the list of targets.");
}
try {
// default to standard path now
String vmRoot = AndroidLocation.getFolder() + AndroidLocation.FOLDER_VMS;
Map<String, String> hardwareConfig = null;
if (mCreateTarget.isPlatform()) {
try {
hardwareConfig = promptForHardware(mCreateTarget);
} catch (IOException e) {
printHelpAndExit(e.getMessage());
}
}
VmManager.createVm(vmRoot, mCreateName, mCreateTarget, null /*skinName*/,
null /*sdcardPath*/, 0 /*sdcardSize*/, hardwareConfig,
null /* sdklog */);
} catch (AndroidLocationException e) {
printHelpAndExit(e.getMessage());
}
}
/**
* Prompts the user to setup a hardware config for a Platform-based VM.
* @throws IOException
*/
private Map<String, String> promptForHardware(IAndroidTarget createTarget) throws IOException {
byte[] readLineBuffer = new byte[256];
String result;
String defaultAnswer = "no";
System.out.print(String.format("%s is a basic Android platform.\n",
createTarget.getName()));
System.out.print(String.format("Do you which to create a custom hardware profile [%s]",
defaultAnswer));
result = readLine(readLineBuffer);
// handle default:
if (result.length() == 0) {
result = defaultAnswer;
}
if (getBooleanReply(result) == false) {
// no custom config.
return null;
}
System.out.println(""); // empty line
// get the list of possible hardware properties
File hardwareDefs = new File (mSdkFolder + File.separator +
SdkConstants.OS_SDK_TOOLS_LIB_FOLDER, SdkConstants.FN_HARDWARE_INI);
List<HardwareProperty> list = HardwareProperties.parseHardwareDefinitions(hardwareDefs,
null /*sdkLog*/);
HashMap<String, String> map = new HashMap<String, String>();
for (int i = 0 ; i < list.size() ;) {
HardwareProperty property = list.get(i);
String description = property.getDescription();
if (description != null) {
System.out.printf("%s: %s\n", property.getAbstract(), description);
} else {
System.out.println(property.getAbstract());
}
String defaultValue = property.getDefault();
if (defaultValue != null) {
System.out.printf("%s [%s]:", property.getName(), defaultValue);
} else {
System.out.printf("%s (%s):", property.getName(), property.getType());
}
result = readLine(readLineBuffer);
if (result.length() == 0) {
if (defaultValue != null) {
System.out.println(""); // empty line
i++; // go to the next property if we have a valid default value.
// if there's no default, we'll redo this property
}
continue;
}
switch (property.getType()) {
case BOOLEAN:
try {
if (getBooleanReply(result)) {
map.put(property.getName(), "yes");
i++; // valid reply, move to next property
} else {
map.put(property.getName(), "no");
i++; // valid reply, move to next property
}
} catch (IOException e) {
// display error, and do not increment i to redo this property
System.out.println("\n" + e.getMessage());
}
break;
case INTEGER:
try {
@SuppressWarnings("unused")
int value = Integer.parseInt(result);
map.put(property.getName(), result);
i++; // valid reply, move to next property
} catch (NumberFormatException e) {
// display error, and do not increment i to redo this property
System.out.println("\n" + e.getMessage());
}
break;
case DISKSIZE:
// TODO check validity
map.put(property.getName(), result);
i++; // valid reply, move to next property
break;
}
System.out.println(""); // empty line
}
return map;
}
/**
* Read the line from the input stream.
* @param buffer
* @return
* @throws IOException
*/
private String readLine(byte[] buffer) throws IOException {
int count = System.in.read(buffer);
// is the input longer than the buffer?
if (count == buffer.length && buffer[count-1] != 10) {
// create a new temp buffer
byte[] tempBuffer = new byte[256];
// and read the rest
String secondHalf = readLine(tempBuffer);
// return a concat of both
return new String(buffer, 0, count) + secondHalf;
}
return new String(buffer, 0, count - 1); // -1 to not include the carriage return
}
/**
* Returns the boolean value represented by the string.
* @throws IOException If the value is not a boolean string.
*/
private boolean getBooleanReply(String reply) throws IOException {
for (String valid : BOOLEAN_YES_REPLIES) {
if (valid.equalsIgnoreCase(reply)) {
return true;
}
}
for (String valid : BOOLEAN_NO_REPLIES) {
if (valid.equalsIgnoreCase(reply)) {
return false;
}
}
throw new IOException(String.format("%s is not a valid reply", reply));
}
/**
* Prints the help/usage and exits.
* @param errorFormat Optional error message to print prior to usage using String.format
* @param args Arguments for String.format
*/
private void printHelpAndExit(String errorFormat, Object... args) {
if (errorFormat != null) {
System.err.println(String.format(errorFormat, args));
}
/*
* usage should fit in 80 columns
* 12345678901234567890123456789012345678901234567890123456789012345678901234567890
*/
final String usage = "\n" +
"Usage:\n" +
" android --list [target|vm]\n" +
" android --create --target <target id> --name <name>\n" +
"\n" +
"Options:\n" +
" -l [target|vm], --list [target|vm]\n" +
" Outputs the available targets or Virtual Machines and their Ids.\n" +
"\n";
System.out.println(usage);
System.exit(1);
}
}

View File

@@ -0,0 +1,18 @@
#
# Copyright (C) 2008 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
SDKLIBS_LOCAL_DIR := $(call my-dir)
include $(SDKLIBS_LOCAL_DIR)/sdklib/Android.mk
include $(SDKLIBS_LOCAL_DIR)/sdkuilib/Android.mk

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry combineaccessrules="false" kind="src" path="/AndroidPrefs"/>
<classpathentry kind="output" path="bin"/>
</classpath>

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>SdkLib</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>

View File

@@ -0,0 +1,17 @@
#
# Copyright (C) 2008 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
SDKLIB_LOCAL_DIR := $(call my-dir)
include $(SDKLIB_LOCAL_DIR)/src/Android.mk

View File

@@ -0,0 +1,27 @@
#
# Copyright (C) 2008 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_JAVA_LIBRARIES := \
androidprefs
LOCAL_MODULE := sdklib
include $(BUILD_HOST_JAVA_LIBRARY)

View File

@@ -0,0 +1,198 @@
/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.sdklib;
import java.io.File;
import java.util.Map;
import java.util.Map.Entry;
/**
* Represents an add-on target in the SDK.
* An add-on extends a standard {@link PlatformTarget}.
*/
final class AddOnTarget implements IAndroidTarget {
/**
* String to compute hash for add-on targets.
* Format is vendor:name:apiVersion
* */
private final static String ADD_ON_FORMAT = "%s:%s:%d"; //$NON-NLS-1$
private final static class OptionalLibrary implements IOptionalLibrary {
private final String mJarName;
private final String mJarPath;
private final String mName;
OptionalLibrary(String jarName, String jarPath, String name) {
mJarName = jarName;
mJarPath = jarPath;
mName = name;
}
public String getJarName() {
return mJarName;
}
public String getJarPath() {
return mJarPath;
}
public String getName() {
return mName;
}
}
private final String mLocation;
private final PlatformTarget mBasePlatform;
private final String mName;
private final String mVendor;
private final String mDescription;
private String[] mSkins;
private IOptionalLibrary[] mLibraries;
/**
* Creates a new add-on
* @param location the OS path location of the add-on
* @param name the name of the add-on
* @param vendor the vendor name of the add-on
* @param description the add-on description
* @param libMap A map containing the optional libraries. The map key is the fully-qualified
* library name. The value is the .jar filename
* @param basePlatform the platform the add-on is extending.
*/
AddOnTarget(String location, String name, String vendor, String description,
Map<String, String> libMap, PlatformTarget basePlatform) {
if (location.endsWith(File.separator) == false) {
location = location + File.separator;
}
mLocation = location;
mName = name;
mVendor = vendor;
mDescription = description;
mBasePlatform = basePlatform;
// handle the optional libraries.
mLibraries = new IOptionalLibrary[libMap.size()];
int index = 0;
for (Entry<String, String> entry : libMap.entrySet()) {
mLibraries[index++] = new OptionalLibrary(entry.getValue(),
mLocation + SdkConstants.OS_ADDON_LIBS_FOLDER + entry.getValue(),
entry.getKey());
}
}
public String getName() {
return mName;
}
public String getVendor() {
return mVendor;
}
public String getDescription() {
return mDescription;
}
public String getApiVersionName() {
// this is always defined by the base platform
return mBasePlatform.getApiVersionName();
}
public int getApiVersionNumber() {
// this is always defined by the base platform
return mBasePlatform.getApiVersionNumber();
}
public boolean isPlatform() {
return false;
}
public String getPath(int pathId) {
switch (pathId) {
case IMAGES:
return mLocation + SdkConstants.OS_IMAGES_FOLDER;
case SKINS:
return mLocation + SdkConstants.OS_SKINS_FOLDER;
default :
return mBasePlatform.getPath(pathId);
}
}
public String[] getSkins() {
return mSkins;
}
public IOptionalLibrary[] getOptionalLibraries() {
return mLibraries;
}
public String hashString() {
return String.format(ADD_ON_FORMAT, mVendor, mName, mBasePlatform.getApiVersionNumber());
}
@Override
public int hashCode() {
return hashString().hashCode();
}
@Override
public boolean equals(Object obj) {
if (obj instanceof AddOnTarget) {
AddOnTarget addon = (AddOnTarget)obj;
return mVendor.equals(addon.mVendor) && mName.equals(addon.mName) &&
mBasePlatform.getApiVersionNumber() == addon.mBasePlatform.getApiVersionNumber();
}
return super.equals(obj);
}
/*
* Always return +1 if the object we compare to is a platform.
* Otherwise, do vendor then name then api version comparison.
* (non-Javadoc)
* @see java.lang.Comparable#compareTo(java.lang.Object)
*/
public int compareTo(IAndroidTarget target) {
if (target.isPlatform()) {
return +1;
}
// vendor
int value = mVendor.compareTo(target.getVendor());
// name
if (value == 0) {
value = mName.compareTo(target.getName());
}
// api version
if (value == 0) {
value = getApiVersionNumber() - target.getApiVersionNumber();
}
return value;
}
// ---- local methods.
public void setSkins(String[] skins) {
mSkins = skins;
}
}

View File

@@ -0,0 +1,104 @@
/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.sdklib;
/**
* A version of Android that application can target when building.
*/
public interface IAndroidTarget extends Comparable<IAndroidTarget> {
public static int ANDROID_JAR = 1;
public static int ANDROID_AIDL = 2;
public static int IMAGES = 3;
public static int SAMPLES = 4;
public static int SKINS = 5;
public static int TEMPLATES = 6;
public static int DATA = 7;
public static int ATTRIBUTES = 8;
public static int MANIFEST_ATTRIBUTES = 9;
public static int LAYOUT_LIB = 10;
public static int RESOURCES = 11;
public static int FONTS = 12;
public static int WIDGETS = 13;
public static int ACTIONS_ACTIVITY = 14;
public static int ACTIONS_BROADCAST = 15;
public static int ACTIONS_SERVICE = 16;
public static int CATEGORIES = 17;
public static int SOURCES = 18;
public interface IOptionalLibrary {
String getName();
String getJarName();
String getJarPath();
}
/**
* Returns the name of the vendor of the target.
*/
String getVendor();
/**
* Returns the name of the target.
*/
String getName();
/**
* Returns the description of the target.
*/
String getDescription();
/**
* Returns the api version as an integer.
*/
int getApiVersionNumber();
/**
* Returns the platform version as a readable string.
*/
String getApiVersionName();
/**
* Returns true if the target is a standard Android platform.
*/
boolean isPlatform();
/**
* Returns the path of a platform component.
* @param pathId the id representing the path to return. Any of the constants defined in the
* {@link ITargetDataProvider} interface can be used.
*/
String getPath(int pathId);
/**
* Returns the available skins for this target.
*/
String[] getSkins();
/**
* Returns the available optional libraries for this target.
* @return an array of optional libraries or <code>null</code> if there is none.
*/
IOptionalLibrary[] getOptionalLibraries();
/**
* Returns a string able to uniquely identify a target.
* Typically the target will encode information such as api level, whether it's a platform
* or add-on, and if it's an add-on vendor and add-on name.
*/
String hashString();
}

View File

@@ -0,0 +1,25 @@
/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.sdklib;
/**
* Interface used to display warnings/errors while parsing the SDK content.
*/
public interface ISdkLog {
void warning(String warningFormat, Object... args);
void error(String errorFormat, Object... args);
}

View File

@@ -0,0 +1,186 @@
/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.sdklib;
import java.io.File;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
/**
* Represents a platform target in the SDK.
*/
final class PlatformTarget implements IAndroidTarget {
/** String used to get a hash to the platform target */
private final static String PLATFORM_HASH = "android-%d";
private final static String PLATFORM_VENDOR = "Android";
private final static String PLATFORM_NAME = "Android %s";
private final String mLocation;
private final String mName;
private final int mApiVersionNumber;
private final String mApiVersionName;
private final Map<String, String> mProperties;
private final Map<Integer, String> mPaths = new HashMap<Integer, String>();
private String[] mSkins;
PlatformTarget(String location, Map<String, String> properties,
int apiNumber, String apiName) {
mName = String.format(PLATFORM_NAME, apiName);
if (location.endsWith(File.separator) == false) {
location = location + File.separator;
}
mLocation = location;
mProperties = Collections.unmodifiableMap(properties);
mApiVersionNumber = apiNumber;
mApiVersionName = apiName;
// pre-build the path to the platform components
mPaths.put(ANDROID_JAR, mLocation + SdkConstants.FN_FRAMEWORK_LIBRARY);
mPaths.put(SOURCES, mLocation + SdkConstants.FD_ANDROID_SOURCES);
mPaths.put(ANDROID_AIDL, mLocation + SdkConstants.FN_FRAMEWORK_AIDL);
mPaths.put(IMAGES, mLocation + SdkConstants.OS_IMAGES_FOLDER);
mPaths.put(SAMPLES, mLocation + SdkConstants.OS_PLATFORM_SAMPLES_FOLDER);
mPaths.put(SKINS, mLocation + SdkConstants.OS_SKINS_FOLDER);
mPaths.put(TEMPLATES, mLocation + SdkConstants.OS_PLATFORM_TEMPLATES_FOLDER);
mPaths.put(DATA, mLocation + SdkConstants.OS_PLATFORM_DATA_FOLDER);
mPaths.put(ATTRIBUTES, mLocation + SdkConstants.OS_PLATFORM_ATTRS_XML);
mPaths.put(MANIFEST_ATTRIBUTES, mLocation + SdkConstants.OS_PLATFORM_ATTRS_MANIFEST_XML);
mPaths.put(RESOURCES, mLocation + SdkConstants.OS_PLATFORM_RESOURCES_FOLDER);
mPaths.put(FONTS, mLocation + SdkConstants.OS_PLATFORM_FONTS_FOLDER);
mPaths.put(LAYOUT_LIB, mLocation + SdkConstants.OS_PLATFORM_DATA_FOLDER +
SdkConstants.FN_LAYOUTLIB_JAR);
mPaths.put(WIDGETS, mLocation + SdkConstants.OS_PLATFORM_DATA_FOLDER +
SdkConstants.FN_WIDGETS);
mPaths.put(ACTIONS_ACTIVITY, mLocation + SdkConstants.OS_PLATFORM_DATA_FOLDER +
SdkConstants.FN_INTENT_ACTIONS_ACTIVITY);
mPaths.put(ACTIONS_BROADCAST, mLocation + SdkConstants.OS_PLATFORM_DATA_FOLDER +
SdkConstants.FN_INTENT_ACTIONS_BROADCAST);
mPaths.put(ACTIONS_SERVICE, mLocation + SdkConstants.OS_PLATFORM_DATA_FOLDER +
SdkConstants.FN_INTENT_ACTIONS_SERVICE);
mPaths.put(CATEGORIES, mLocation + SdkConstants.OS_PLATFORM_DATA_FOLDER +
SdkConstants.FN_INTENT_CATEGORIES);
}
public String getLocation() {
return mLocation;
}
/*
* (non-Javadoc)
*
* For Platform, the vendor name is always "Android".
*
* @see com.android.sdklib.IAndroidTarget#getVendor()
*/
public String getVendor() {
return PLATFORM_VENDOR;
}
public String getName() {
return mName;
}
/*
* (non-Javadoc)
*
* Description for the Android platform is dynamically generated.
*
* @see com.android.sdklib.IAndroidTarget#getDescription()
*/
public String getDescription() {
return String.format("Standard Android platform %s", mApiVersionName);
}
public int getApiVersionNumber(){
return mApiVersionNumber;
}
public String getApiVersionName() {
return mApiVersionName;
}
public boolean isPlatform() {
return true;
}
public String getPath(int pathId) {
return mPaths.get(pathId);
}
public String[] getSkins() {
return mSkins;
}
/*
* Always returns null, as a standard platforms have no optional libraries.
*
* (non-Javadoc)
* @see com.android.sdklib.IAndroidTarget#getOptionalLibraries()
*/
public IOptionalLibrary[] getOptionalLibraries() {
return null;
}
public String hashString() {
return String.format(PLATFORM_HASH, mApiVersionNumber);
}
@Override
public int hashCode() {
return hashString().hashCode();
}
@Override
public boolean equals(Object obj) {
if (obj instanceof PlatformTarget) {
return mApiVersionNumber == ((PlatformTarget)obj).mApiVersionNumber;
}
return super.equals(obj);
}
/*
* Always return -1 if the object we compare to is an addon.
* Otherwise, compare api level.
* (non-Javadoc)
* @see java.lang.Comparable#compareTo(java.lang.Object)
*/
public int compareTo(IAndroidTarget target) {
if (target.isPlatform() == false) {
return -1;
}
return mApiVersionNumber - target.getApiVersionNumber();
}
// ---- platform only methods.
public String getProperty(String name) {
return mProperties.get(name);
}
public Map<String, String> getProperties() {
return mProperties; // mProperties is unmodifiable.
}
void setSkins(String[] skins) {
mSkins = skins;
}
}

View File

@@ -0,0 +1,174 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.sdklib;
import java.io.File;
/**
* Constant definition class.<br>
* <br>
* Most constants have a prefix defining the content.
* <ul>
* <li><code>OS_</code> OS path constant. These paths are different depending on the platform.</li>
* <li><code>FN_</code> File name constant.</li>
* <li><code>FD_</code> Folder name constant.</li>
* </ul>
*
*/
public final class SdkConstants {
/** Name of the framework library, i.e. "android.jar" */
public static final String FN_FRAMEWORK_LIBRARY = "android.jar";
/** Name of the layout attributes, i.e. "attrs.xml" */
public static final String FN_ATTRS_XML = "attrs.xml";
/** Name of the layout attributes, i.e. "attrs_manifest.xml" */
public static final String FN_ATTRS_MANIFEST_XML = "attrs_manifest.xml";
/** framework aidl import file */
public static final String FN_FRAMEWORK_AIDL = "framework.aidl";
/** layoutlib.jar file */
public static final String FN_LAYOUTLIB_JAR = "layoutlib.jar";
/** widget list file */
public static final String FN_WIDGETS = "widgets.txt";
/** Intent activity actions list file */
public static final String FN_INTENT_ACTIONS_ACTIVITY = "activity_actions.txt";
/** Intent broadcast actions list file */
public static final String FN_INTENT_ACTIONS_BROADCAST = "broadcast_actions.txt";
/** Intent service actions list file */
public static final String FN_INTENT_ACTIONS_SERVICE = "service_actions.txt";
/** Intent category list file */
public static final String FN_INTENT_CATEGORIES = "categories.txt";
/** platform build property file */
public final static String FN_BUILD_PROP = "build.prop";
/** plugin properties file */
public final static String FN_PLUGIN_PROP = "plugin.prop";
/** add-on manifest file */
public final static String FN_MANIFEST_INI = "manifest.ini";
/** hardware properties definition file */
public final static String FN_HARDWARE_INI = "hardware-properties.ini";
/** Skin layout file */
public final static String FN_SKIN_LAYOUT = "layout";//$NON-NLS-1$
/* Folder Names for the Android SDK */
/** Name of the SDK platforms folder. */
public final static String FD_PLATFORMS = "platforms";
/** Name of the SDK addons folder. */
public final static String FD_ADDONS = "add-ons";
/** Name of the SDK tools folder. */
public final static String FD_TOOLS = "tools";
/** Name of the SDK tools/lib folder. */
public final static String FD_LIB = "lib";
/** Name of the SDK docs folder. */
public final static String FD_DOCS = "docs";
/** Name of the SDK images folder. */
public final static String FD_IMAGES = "images";
/** Name of the SDK skins folder. */
public final static String FD_SKINS = "skins";
/** Name of the SDK samples folder. */
public final static String FD_SAMPLES = "samples";
/** Name of the SDK templates folder, i.e. "templates" */
public final static String FD_TEMPLATES = "templates";
/** Name of the SDK data folder, i.e. "data" */
public final static String FD_DATA = "data";
/** Name of the SDK resources folder, i.e. "res" */
public final static String FD_RES = "res";
/** Name of the SDK font folder, i.e. "fonts" */
public final static String FD_FONTS = "fonts";
/** Default values resource folder name, i.e. "values" */
public final static String FD_VALUES = "values";
/** Name of the android sources directory */
public static final String FD_ANDROID_SOURCES = "sources";
/** Name of the addon libs folder. */
public final static String FD_ADDON_LIBS = "libs";
/* Folder path relative to the SDK root */
/** Path of the documentation directory relative to the sdk folder.
* This is an OS path, ending with a separator. */
public final static String OS_SDK_DOCS_FOLDER = FD_DOCS + File.separator;
/** Path of the tools directory relative to the sdk folder.
* This is an OS path, ending with a separator. */
public final static String OS_SDK_TOOLS_FOLDER = FD_TOOLS + File.separator;
/** Path of the lib directory relative to the sdk folder.
* This is an OS path, ending with a separator. */
public final static String OS_SDK_TOOLS_LIB_FOLDER =
OS_SDK_TOOLS_FOLDER + FD_LIB + File.separator;
/* Folder paths relative to a platform or add-on folder */
/** Path of the images directory relative to a platform or addon folder.
* This is an OS path, ending with a separator. */
public final static String OS_IMAGES_FOLDER = FD_IMAGES + File.separator;
/** Path of the skin directory relative to a platform or addon folder.
* This is an OS path, ending with a separator. */
public final static String OS_SKINS_FOLDER = FD_SKINS + File.separator;
/* Folder paths relative to a Platform folder */
/** Path of the data directory relative to a platform folder.
* This is an OS path, ending with a separator. */
public final static String OS_PLATFORM_DATA_FOLDER = FD_DATA + File.separator;
/** Path of the samples directory relative to a platform folder.
* This is an OS path, ending with a separator. */
public final static String OS_PLATFORM_SAMPLES_FOLDER = FD_SAMPLES + File.separator;
/** Path of the resources directory relative to a platform folder.
* This is an OS path, ending with a separator. */
public final static String OS_PLATFORM_RESOURCES_FOLDER =
OS_PLATFORM_DATA_FOLDER + FD_RES + File.separator;
/** Path of the fonts directory relative to a platform folder.
* This is an OS path, ending with a separator. */
public final static String OS_PLATFORM_FONTS_FOLDER =
OS_PLATFORM_DATA_FOLDER + FD_FONTS + File.separator;
/** Path of the android source directory relative to a platform folder.
* This is an OS path, ending with a separator. */
public final static String OS_PLATFORM_SOURCES_FOLDER = FD_ANDROID_SOURCES + File.separator;
/** Path of the android templates directory relative to a platform folder.
* This is an OS path, ending with a separator. */
public final static String OS_PLATFORM_TEMPLATES_FOLDER = FD_TEMPLATES + File.separator;
/** Path of the attrs.xml file relative to a platform folder. */
public final static String OS_PLATFORM_ATTRS_XML =
OS_PLATFORM_RESOURCES_FOLDER + FD_VALUES + File.separator + FN_ATTRS_XML;
/** Path of the attrs_manifest.xml file relative to a platform folder. */
public final static String OS_PLATFORM_ATTRS_MANIFEST_XML =
OS_PLATFORM_RESOURCES_FOLDER + FD_VALUES + File.separator + FN_ATTRS_MANIFEST_XML;
/** Path of the layoutlib.jar file relative to a platform folder. */
public final static String OS_PLATFORM_LAYOUTLIB_JAR =
OS_PLATFORM_DATA_FOLDER + FN_LAYOUTLIB_JAR;
/* Folder paths relative to a addon folder */
/** Path of the images directory relative to a folder folder.
* This is an OS path, ending with a separator. */
public final static String OS_ADDON_LIBS_FOLDER = FD_ADDON_LIBS + File.separator;
/* Skin default */
public final static String SKIN_DEFAULT = "default";
}

View File

@@ -0,0 +1,420 @@
/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.sdklib;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* The SDK manager parses the SDK folder and gives access to the content.
* @see PlatformTarget
* @see AddOnTarget
*/
public final class SdkManager {
private final static String PROP_VERSION_SDK = "ro.build.version.sdk";
private final static String PROP_VERSION_RELEASE = "ro.build.version.release";
private final static String ADDON_NAME = "name";
private final static String ADDON_VENDOR = "vendor";
private final static String ADDON_API = "api";
private final static String ADDON_DESCRIPTION = "description";
private final static String ADDON_LIBRARIES = "libraries";
private final static Pattern PATTERN_PROP = Pattern.compile(
"^([a-zA-Z0-9._-]+)\\s*=\\s*(.*)\\s*$");
/** the location of the SDK */
private final String mSdkLocation;
private IAndroidTarget[] mTargets;
/**
* Creates an {@link SdkManager} for a given sdk location.
* @param sdkLocation the location of the SDK.
* @param log the ISdkLog object receiving warning/error from the parsing.
* @return the created {@link SdkManager} or null if the location is not valid.
*/
public static SdkManager createManager(String sdkLocation, ISdkLog log) {
try {
SdkManager manager = new SdkManager(sdkLocation);
ArrayList<IAndroidTarget> list = new ArrayList<IAndroidTarget>();
manager.loadPlatforms(list, log);
manager.loadAddOns(list, log);
// sort the targets/add-ons
Collections.sort(list);
manager.setTargets(list.toArray(new IAndroidTarget[list.size()]));
return manager;
} catch (IllegalArgumentException e) {
if (log != null) {
log.error(e.getMessage());
}
}
return null;
}
/**
* Returns the location of the SDK.
*/
public String getLocation() {
return mSdkLocation;
}
/**
* Returns the targets that are available in the SDK.
*/
public IAndroidTarget[] getTargets() {
return mTargets;
}
/**
* Returns a target from a hash that was generated by {@link IAndroidTarget#hashString()}.
* @param hash the hash
*/
public IAndroidTarget getTargetFromHashString(String hash) {
if (hash != null) {
for (IAndroidTarget target : mTargets) {
if (hash.equals(target.hashString())) {
return target;
}
}
}
return null;
}
private SdkManager(String sdkLocation) {
mSdkLocation = sdkLocation;
}
private void setTargets(IAndroidTarget[] targets) {
mTargets = targets;
}
/**
* Loads the Platforms from the SDK.
* @param list the list to fill with the platforms.
* @param log the ISdkLog object receiving warning/error from the parsing.
*/
private void loadPlatforms(ArrayList<IAndroidTarget> list, ISdkLog log) {
File platformFolder = new File(mSdkLocation, SdkConstants.FD_PLATFORMS);
if (platformFolder.isDirectory()) {
File[] platforms = platformFolder.listFiles();
for (File platform : platforms) {
if (platform.isDirectory()) {
PlatformTarget target = loadPlatform(platform, log);
if (target != null) {
list.add(target);
}
} else if (log != null) {
log.warning("Ignoring platform '%1$s', not a folder.", platform.getName());
}
}
return;
}
String message = null;
if (platformFolder.exists() == false) {
message = "%s is missing.";
} else {
message = "%s is not a folder.";
}
throw new IllegalArgumentException(String.format(message,
platformFolder.getAbsolutePath()));
}
/**
* Loads a specific Platform at a given location.
* @param platform the location of the platform.
* @param log the ISdkLog object receiving warning/error from the parsing.
*/
private PlatformTarget loadPlatform(File platform, ISdkLog log) {
File buildProp = new File(platform, SdkConstants.FN_BUILD_PROP);
if (buildProp.isFile()) {
Map<String, String> map = parsePropertyFile(buildProp, log);
if (map != null) {
// look for some specific values in the map.
try {
String apiNumber = map.get(PROP_VERSION_SDK);
String apiName = map.get(PROP_VERSION_RELEASE);
if (apiNumber != null && apiName != null) {
// create the target.
PlatformTarget target = new PlatformTarget(
platform.getAbsolutePath(),
map,
Integer.parseInt(apiNumber),
apiName);
// need to parse the skins.
String[] skins = parseSkinFolder(target.getPath(IAndroidTarget.SKINS));
target.setSkins(skins);
return target;
}
} catch (NumberFormatException e) {
// looks like apiNumber does not parse to a number.
// Ignore this platform.
if (log != null) {
log.error("Ignoring platform '%1$s': %2$s is not a valid number in %3$s.",
platform.getName(), PROP_VERSION_SDK, SdkConstants.FN_BUILD_PROP);
}
}
}
} else if (log != null) {
log.error("Ignoring platform '%1$s': %2$s is missing.", platform.getName(),
SdkConstants.FN_BUILD_PROP);
}
return null;
}
/**
* Loads the Add-on from the SDK.
* @param list the list to fill with the add-ons.
* @param log the ISdkLog object receiving warning/error from the parsing.
*/
private void loadAddOns(ArrayList<IAndroidTarget> list, ISdkLog log) {
File addonFolder = new File(mSdkLocation, SdkConstants.FD_ADDONS);
if (addonFolder.isDirectory()) {
File[] addons = addonFolder.listFiles();
for (File addon : addons) {
if (addon.isDirectory()) {
AddOnTarget target = loadAddon(addon, list, log);
if (target != null) {
list.add(target);
}
} else if (log != null) {
log.warning("Ignoring add-on '%1$s', not a folder.", addon.getName());
}
}
return;
}
String message = null;
if (addonFolder.exists() == false) {
message = "%s is missing.";
} else {
message = "%s is not a folder.";
}
throw new IllegalArgumentException(String.format(message,
addonFolder.getAbsolutePath()));
}
/**
* Loads a specific Add-on at a given location.
* @param addon the location of the addon.
* @param list
* @param log
*/
private AddOnTarget loadAddon(File addon, ArrayList<IAndroidTarget> list, ISdkLog log) {
File addOnManifest = new File(addon, SdkConstants.FN_MANIFEST_INI);
if (addOnManifest.isFile()) {
Map<String, String> map = parsePropertyFile(addOnManifest, log);
if (map != null) {
// look for some specific values in the map.
// we require name, vendor, and api
String name = map.get(ADDON_NAME);
if (name == null) {
displayAddonManifestError(log, addon.getName(), ADDON_NAME);
return null;
}
String vendor = map.get(ADDON_VENDOR);
if (vendor == null) {
displayAddonManifestError(log, addon.getName(), ADDON_VENDOR);
return null;
}
String api = map.get(ADDON_API);
PlatformTarget baseTarget = null;
if (api == null) {
displayAddonManifestError(log, addon.getName(), ADDON_API);
return null;
} else {
try {
int apiValue = Integer.parseInt(api);
for (IAndroidTarget target : list) {
if (target.isPlatform() &&
target.getApiVersionNumber() == apiValue) {
baseTarget = (PlatformTarget)target;
break;
}
}
if (baseTarget == null) {
if (log != null) {
log.error(
"Ignoring add-on '%1$s': Unable to find base platform with API level %2$d",
addon.getName(), apiValue);
}
return null;
}
} catch (NumberFormatException e) {
// looks like apiNumber does not parse to a number.
// Ignore this add-on.
if (log != null) {
log.error(
"Ignoring add-on '%1$s': %2$s is not a valid number in %3$s.",
addon.getName(), ADDON_API, SdkConstants.FN_BUILD_PROP);
}
return null;
}
}
// get the optional description
String description = map.get(ADDON_DESCRIPTION);
// get the optional libraries
String librariesValue = map.get(ADDON_LIBRARIES);
// split in the string into the values we care about
String[] libraries = librariesValue.split(";");
Map<String, String> libMap = null;
if (libraries.length > 0) {
libMap = new HashMap<String, String>();
for (String lib : libraries) {
String[] values = lib.split(":");
if (values.length == 2) {
libMap.put(values[0], values[1]);
} else {
// TODO: log error
}
}
}
AddOnTarget target = new AddOnTarget(addon.getAbsolutePath(), name, vendor,
description, libMap, baseTarget);
// need to parse the skins.
String[] skins = parseSkinFolder(target.getPath(IAndroidTarget.SKINS));
target.setSkins(skins);
return target;
}
} else if (log != null) {
log.error("Ignoring add-on '%1$s': %2$s is missing.", addon.getName(),
SdkConstants.FN_MANIFEST_INI);
}
return null;
}
private void displayAddonManifestError(ISdkLog log, String addonName, String valueName) {
if (log != null) {
log.error("Ignoring add-on '%1$s': '%2$s' is missing from %3$s.",
addonName, valueName, SdkConstants.FN_MANIFEST_INI);
}
}
/**
* Parses a property file and returns
* @param buildProp the property file to parse
* @param log the ISdkLog object receiving warning/error from the parsing.
* @return the map of (key,value) pairs, or null if the parsing failed.
*/
public static Map<String, String> parsePropertyFile(File buildProp, ISdkLog log) {
try {
FileInputStream fis = new FileInputStream(buildProp);
BufferedReader reader = new BufferedReader(new InputStreamReader(fis));
String line = null;
Map<String, String> map = new HashMap<String, String>();
while ((line = reader.readLine()) != null) {
if (line.length() > 0 && line.charAt(0) != '#') {
Matcher m = PATTERN_PROP.matcher(line);
if (m.matches()) {
map.put(m.group(1), m.group(2));
} else {
log.warning("Error parsing '%1$s': \"%2$s\" is not a valid syntax",
buildProp.getAbsolutePath(), line);
return null;
}
}
}
return map;
} catch (FileNotFoundException e) {
// this should not happen since we usually test the file existence before
// calling the method.
// Return null below.
} catch (IOException e) {
if (log != null) {
log.warning("Error parsing '%1$s': %2$s.", buildProp.getAbsolutePath(),
e.getMessage());
}
}
return null;
}
/**
* Parses the skin folder and builds the skin list.
* @param osPath The path of the skin root folder.
*/
private String[] parseSkinFolder(String osPath) {
File skinRootFolder = new File(osPath);
if (skinRootFolder.isDirectory()) {
ArrayList<String> skinList = new ArrayList<String>();
File[] files = skinRootFolder.listFiles();
for (File skinFolder : files) {
if (skinFolder.isDirectory()) {
// check for layout file
File layout = new File(skinFolder, SdkConstants.FN_SKIN_LAYOUT);
if (layout.isFile()) {
// for now we don't parse the content of the layout and
// simply add the directory to the list.
skinList.add(skinFolder.getName());
}
}
}
return skinList.toArray(new String[skinList.size()]);
}
return new String[0];
}
}

View File

@@ -0,0 +1,136 @@
/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.sdklib.project;
import com.android.sdklib.SdkManager;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
/**
* Class to load and save project properties for both ADT and Ant-based build.
*
*/
public final class ProjectProperties {
/** The property name for the project target */
public final static String PROPERTY_TARGET = "target";
public final static String PROPERTY_SDK = "sdk-folder";
private final static String PROPERTIES_FILE = "default.properties";
private final static String PROP_HEADER =
"# This file is automatically generated by Android Tools.\n" +
"# Do not modify this file -- YOUR CHANGES WILL BE ERASED!\n" +
"# For customized properties when using Ant, set new values\n" +
"# in a \"build.properties\" file.\n\n";
private final static Map<String, String> COMMENT_MAP = new HashMap<String, String>();
static {
COMMENT_MAP.put(PROPERTY_TARGET, "# Project target.\n");
COMMENT_MAP.put(PROPERTY_SDK, "# location of the SDK. Only used by Ant.\n");
}
private final String mProjectFolderOsPath;
private final Map<String, String> mProperties;
/**
* Loads a project properties file and return a {@link ProjectProperties} object
* containing the properties
* @param projectFolderOsPath the project folder.
*/
public static ProjectProperties load(String projectFolderOsPath) {
File projectFolder = new File(projectFolderOsPath);
if (projectFolder.isDirectory()) {
File defaultFile = new File(projectFolder, PROPERTIES_FILE);
if (defaultFile.isFile()) {
Map<String, String> map = SdkManager.parsePropertyFile(defaultFile, null /* log */);
if (map != null) {
return new ProjectProperties(projectFolderOsPath, map);
}
}
}
return null;
}
/**
* Creates a new project properties file, with no properties.
* <p/>The file is not created until {@link #save()} is called.
* @param projectFolderOsPath the project folder.
*/
public static ProjectProperties create(String projectFolderOsPath) {
// create and return a ProjectProperties with an empty map.
return new ProjectProperties(projectFolderOsPath, new HashMap<String, String>());
}
/**
* Sets a new properties. If a property with the same name already exists, it is replaced.
* @param name the name of the property.
* @param value the value of the property.
*/
public void setProperty(String name, String value) {
mProperties.put(name, value);
}
/**
* Returns the value of a property.
* @param name the name of the property.
* @return the property value or null if the property is not set.
*/
public String getProperty(String name) {
return mProperties.get(name);
}
/**
* Saves the property file.
* @throws IOException
*/
public void save() throws IOException {
File toSave = new File(mProjectFolderOsPath, PROPERTIES_FILE);
FileWriter writer = new FileWriter(toSave);
// write the header
writer.write(PROP_HEADER);
// write the properties.
for (Entry<String, String> entry : mProperties.entrySet()) {
String comment = COMMENT_MAP.get(entry.getKey());
if (comment != null) {
writer.write(comment);
}
writer.write(String.format("%s=%s\n", entry.getKey(), entry.getValue()));
}
// close the file to flush
writer.close();
}
/**
* Private constructor.
* Use {@link #load(String)} or {@link #create(String)} to instantiate.
* @param projectFolderOsPath
* @param map
*/
private ProjectProperties(String projectFolderOsPath, Map<String, String> map) {
mProjectFolderOsPath = projectFolderOsPath;
mProperties = map;
}
}

View File

@@ -0,0 +1,159 @@
/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.sdklib.vm;
import com.android.sdklib.ISdkLog;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class HardwareProperties {
private final static Pattern PATTERN_PROP = Pattern.compile(
"^([a-zA-Z0-9._-]+)\\s*=\\s*(.*)\\s*$");
private final static String HW_PROP_NAME = "name";
private final static String HW_PROP_TYPE = "type";
private final static String HW_PROP_DEFAULT = "default";
private final static String HW_PROP_ABSTRACT = "abstract";
private final static String HW_PROP_DESC = "description";
public enum ValueType {
INTEGER("integer"),
BOOLEAN("boolean"),
DISKSIZE("diskSize");
private String mValue;
ValueType(String value) {
mValue = value;
}
public static ValueType getEnum(String value) {
for (ValueType type : values()) {
if (type.mValue.equals(value)) {
return type;
}
}
return null;
}
}
public static final class HardwareProperty {
private String mName;
private ValueType mType;
/** the string representation of the default value. can be null. */
private String mDefault;
private String mAbstract;
private String mDescription;
public String getName() {
return mName;
}
public ValueType getType() {
return mType;
}
public String getDefault() {
return mDefault;
}
public String getAbstract() {
return mAbstract;
}
public String getDescription() {
return mDescription;
}
}
/**
* Parses the harware definition file.
* @param buildProp the property file to parse
* @param log the ISdkLog object receiving warning/error from the parsing.
* @return the map of (key,value) pairs, or null if the parsing failed.
*/
public static List<HardwareProperty> parseHardwareDefinitions(File file, ISdkLog log) {
try {
FileInputStream fis = new FileInputStream(file);
BufferedReader reader = new BufferedReader(new InputStreamReader(fis));
List<HardwareProperty> map = new ArrayList<HardwareProperty>();
String line = null;
HardwareProperty prop = null;
while ((line = reader.readLine()) != null) {
if (line.length() > 0 && line.charAt(0) != '#') {
Matcher m = PATTERN_PROP.matcher(line);
if (m.matches()) {
String valueName = m.group(1);
String value = m.group(2);
if (HW_PROP_NAME.equals(valueName)) {
prop = new HardwareProperty();
prop.mName = value;
map.add(prop);
}
if (prop == null) {
log.warning("Error parsing '%1$s': missing '%2$s'",
file.getAbsolutePath(), HW_PROP_NAME);
return null;
}
if (HW_PROP_TYPE.equals(valueName)) {
prop.mType = ValueType.getEnum(value);
} else if (HW_PROP_DEFAULT.equals(valueName)) {
prop.mDefault = value;
} else if (HW_PROP_ABSTRACT.equals(valueName)) {
prop.mAbstract = value;
} else if (HW_PROP_DESC.equals(valueName)) {
prop.mDescription = value;
}
} else {
log.warning("Error parsing '%1$s': \"%2$s\" is not a valid syntax",
file.getAbsolutePath(), line);
return null;
}
}
}
return map;
} catch (FileNotFoundException e) {
// this should not happen since we usually test the file existence before
// calling the method.
// Return null below.
} catch (IOException e) {
if (log != null) {
log.warning("Error parsing '%1$s': %2$s.", file.getAbsolutePath(),
e.getMessage());
}
}
return null;
}
}

View File

@@ -0,0 +1,258 @@
/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.sdklib.vm;
import com.android.prefs.AndroidLocation;
import com.android.prefs.AndroidLocation.AndroidLocationException;
import com.android.sdklib.IAndroidTarget;
import com.android.sdklib.ISdkLog;
import com.android.sdklib.SdkConstants;
import com.android.sdklib.SdkManager;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Virtual Machine manager to access the list of VMs or create new ones.
*/
public final class VmManager {
private final static String VM_INFO_PATH = "path";
private final static String VM_INFO_TARGET = "target";
private final static String IMAGE_USERDATA = "userdata.img";
private final static String CONFIG_INI = "config.ini";
private final static Pattern INI_NAME_PATTERN = Pattern.compile("(.+)\\.ini$",
Pattern.CASE_INSENSITIVE);
public static final class VmInfo {
String name;
String path;
IAndroidTarget target;
public String getName() {
return name;
}
public String getPath() {
return path;
}
public IAndroidTarget getTarget() {
return target;
}
}
private final ArrayList<VmInfo> mVmList = new ArrayList<VmInfo>();
private ISdkLog mSdkLog;
public VmManager(SdkManager sdk, ISdkLog sdkLog) throws AndroidLocationException {
mSdkLog = sdkLog;
buildVmList(sdk);
}
public VmInfo[] getVms() {
return mVmList.toArray(new VmInfo[mVmList.size()]);
}
/**
* Creates a new VM.
* @param parentFolder the folder to contain the VM. A new folder will be created in this
* folder with the name of the VM
* @param name the name of the VM
* @param target the target of the VM
* @param skinName the name of the skin. Can be null.
* @param sdcardPath the path to the sdCard. Can be null.
* @param sdcardSize the size of a local sdcard to create. Can be 0 for no local sdcard.
* @param hardwareConfig the hardware setup for the VM
*/
public static void createVm(String parentFolder, String name, IAndroidTarget target,
String skinName, String sdcardPath, int sdcardSize, Map<String,String> hardwareConfig,
ISdkLog log) {
// now write the ini file in the vmRoot folder.
// get the Android prefs location.
try {
File rootDirectory = new File(parentFolder);
if (rootDirectory.isDirectory() == false) {
if (log != null) {
log.error("%s does not exists.", parentFolder);
}
return;
}
File vmFolder = new File(parentFolder, name + ".avm");
if (vmFolder.exists()) {
if (log != null) {
log.error("%s already exists.", vmFolder.getAbsolutePath());
}
return;
}
// create the vm folder.
vmFolder.mkdir();
HashMap<String, String> values = new HashMap<String, String>();
// prepare the ini file.
String vmRoot = AndroidLocation.getFolder() + AndroidLocation.FOLDER_VMS;
File iniFile = new File(vmRoot, name + ".ini");
values.put(VM_INFO_PATH, vmFolder.getAbsolutePath());
values.put(VM_INFO_TARGET, target.hashString());
createConfigIni(iniFile, values);
// writes the userdata.img in it.
String imagePath = target.getPath(IAndroidTarget.IMAGES);
File userdataSrc = new File(imagePath, IMAGE_USERDATA);
FileInputStream fis = new FileInputStream(userdataSrc);
File userdataDest = new File(vmFolder, IMAGE_USERDATA);
FileOutputStream fos = new FileOutputStream(userdataDest);
byte[] buffer = new byte[4096];
int count;
while ((count = fis.read(buffer)) != -1) {
fos.write(buffer, 0, count);
}
fos.close();
fis.close();
// Config file
values.clear();
if (skinName != null) {
values.put("skin", skinName);
} else {
values.put("skin", SdkConstants.SKIN_DEFAULT);
}
if (sdcardPath != null) {
values.put("sdcard", sdcardPath);
} else if (sdcardSize != 0) {
// TODO: create sdcard image.
}
if (hardwareConfig != null) {
values.putAll(hardwareConfig);
}
File configIniFile = new File(vmFolder, CONFIG_INI);
createConfigIni(configIniFile, values);
if (target.isPlatform()) {
System.out.println(String.format(
"Created VM '%s' based on %s", name, target.getName()));
} else {
System.out.println(String.format(
"Created VM '%s' based on %s (%s)", name, target.getName(),
target.getVendor()));
}
} catch (AndroidLocationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private void buildVmList(SdkManager sdk) throws AndroidLocationException {
// get the Android prefs location.
String vmRoot = AndroidLocation.getFolder() + AndroidLocation.FOLDER_VMS;
// ensure folder validity.
File folder = new File(vmRoot);
if (folder.isFile()) {
throw new AndroidLocationException(String.format("%s is not a valid folder.", vmRoot));
} else if (folder.exists() == false) {
// folder is not there, we create it and return
folder.mkdirs();
return;
}
File[] vms = folder.listFiles(new FilenameFilter() {
public boolean accept(File parent, String name) {
if (INI_NAME_PATTERN.matcher(name).matches()) {
// check it's a file and not a folder
return new File(parent, name).isFile();
}
return false;
}
});
for (File vm : vms) {
VmInfo info = parseVmInfo(vm, sdk);
if (info != null) {
mVmList.add(info);
}
}
}
private VmInfo parseVmInfo(File path, SdkManager sdk) {
Map<String, String> map = SdkManager.parsePropertyFile(path, mSdkLog);
String vmPath = map.get(VM_INFO_PATH);
if (vmPath == null) {
return null;
}
String targetHash = map.get(VM_INFO_TARGET);
if (targetHash == null) {
return null;
}
IAndroidTarget target = sdk.getTargetFromHashString(targetHash);
if (target == null) {
return null;
}
VmInfo info = new VmInfo();
Matcher matcher = INI_NAME_PATTERN.matcher(path.getName());
if (matcher.matches()) {
info.name = matcher.group(1);
} else {
info.name = path.getName(); // really this should not happen.
}
info.path = vmPath;
info.target = target;
return info;
}
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.close();
}
}

View File

@@ -0,0 +1,4 @@
# Copyright 2008 The Android Open Source Project
#
SDKUILIB_LOCAL_DIR := $(call my-dir)
include $(SDKUILIB_LOCAL_DIR)/src/Android.mk

View File

@@ -0,0 +1,11 @@
Using the Eclipse projects for ddmuilib.
ddmuilib requires SWT to compile.
SWT is available in the depot under prebuild/<platform>/swt
Because the build path cannot contain relative path that are not inside the project directory,
the .classpath file references a user library called ANDROID_SWT.
In order to compile the project, make a user library called ANDROID_SWT containing the jar
available at prebuild/<platform>/swt.

View File

@@ -0,0 +1,21 @@
# Copyright 2008 The Android Open Source Project
#
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := $(call all-subdir-java-files)
# no resources yet.
# LOCAL_JAVA_RESOURCE_DIRS := resources
LOCAL_JAVA_LIBRARIES := \
sdklib \
swt \
org.eclipse.jface_3.2.0.I20060605-1400 \
org.eclipse.equinox.common_3.2.0.v20060603 \
org.eclipse.core.commands_3.2.0.I20060605-1400
LOCAL_MODULE := sdkuilib
include $(BUILD_HOST_JAVA_LIBRARY)

View File

@@ -0,0 +1,319 @@
/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Eclipse Public License, Version 1.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.eclipse.org/org/documents/epl-v10.php
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.sdkuilib;
import com.android.sdklib.IAndroidTarget;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ControlAdapter;
import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.TableItem;
import java.util.ArrayList;
/**
* The SDK target selector is a table that is added to the given parent composite.
*/
public class SdkTargetSelector {
private final IAndroidTarget[] mTargets;
private final boolean mAllowMultipleSelection;
private SelectionListener mSelectionListener;
private Table mTable;
private Label mDescription;
public SdkTargetSelector(Composite parent, IAndroidTarget[] targets,
boolean allowMultipleSelection) {
mTargets = targets;
// Layout has 1 column
Composite group = new Composite(parent, SWT.NONE);
group.setLayout(new GridLayout());
group.setLayoutData(new GridData(GridData.FILL_BOTH));
group.setFont(parent.getFont());
mAllowMultipleSelection = allowMultipleSelection;
mTable = new Table(group, SWT.CHECK | SWT.FULL_SELECTION | SWT.SINGLE | SWT.BORDER);
mTable.setHeaderVisible(true);
mTable.setLinesVisible(false);
GridData data = new GridData();
data.grabExcessVerticalSpace = true;
data.grabExcessHorizontalSpace = true;
data.horizontalAlignment = GridData.FILL;
data.verticalAlignment = GridData.FILL;
mTable.setLayoutData(data);
mDescription = new Label(group, SWT.WRAP);
mDescription.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
// create the table columns
final TableColumn column0 = new TableColumn(mTable, SWT.NONE);
column0.setText("SDK Target");
final TableColumn column1 = new TableColumn(mTable, SWT.NONE);
column1.setText("Vendor");
final TableColumn column2 = new TableColumn(mTable, SWT.NONE);
column2.setText("API Level");
adjustColumnsWidth(mTable, column0, column1, column2);
setupSelectionListener(mTable);
fillTable(mTable);
setupTooltip(mTable);
}
/**
* Sets a selection listener. Set it to null to remove it.
* The listener will be called <em>after</em> this table processed its selection
* events so that the caller can see the updated state.
* <p/>
* The event's item contains a {@link TableItem}.
* The {@link TableItem#getData()} contains an {@link IAndroidTarget}.
* <p/>
* It is recommended that the caller uses the {@link #getFirstSelected()} and
* {@link #getAllSelected()} methods instead.
*
* @param selectionListener The new listener or null to remove it.
*/
public void setSelectionListener(SelectionListener selectionListener) {
mSelectionListener = selectionListener;
}
/**
* Sets the current target selection.
* @param target the target to be selection
* @return true if the target could be selected, false otherwise.
*/
public boolean setSelection(IAndroidTarget target) {
boolean found = false;
for (TableItem i : mTable.getItems()) {
if ((IAndroidTarget) i.getData() == target) {
found = true;
i.setChecked(true);
} else {
i.setChecked(false);
}
}
return found;
}
/**
* Returns all selected items.
* This is useful when the table is in multiple-selection mode.
*
* @see #getFirstSelected()
* @return An array of selected items. The list can be empty but not null.
*/
public IAndroidTarget[] getAllSelected() {
ArrayList<IAndroidTarget> list = new ArrayList<IAndroidTarget>();
for (TableItem i : mTable.getItems()) {
if (i.getChecked()) {
list.add((IAndroidTarget) i.getData());
}
}
return list.toArray(new IAndroidTarget[list.size()]);
}
/**
* Returns the first selected item.
* This is useful when the table is in single-selection mode.
*
* @see #getAllSelected()
* @return The first selected item or null.
*/
public IAndroidTarget getFirstSelected() {
for (TableItem i : mTable.getItems()) {
if (i.getChecked()) {
return (IAndroidTarget) i.getData();
}
}
return null;
}
/**
* Adds a listener to adjust the columns width when the parent is resized.
* <p/>
* If we need something more fancy, we might want to use this:
* http://dev.eclipse.org/viewcvs/index.cgi/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/Snippet77.java?view=co
*/
private void adjustColumnsWidth(final Table table,
final TableColumn column0,
final TableColumn column1,
final TableColumn column2) {
// Add a listener to resize the column to the full width of the table
table.addControlListener(new ControlAdapter() {
@Override
public void controlResized(ControlEvent e) {
Rectangle r = table.getClientArea();
column0.setWidth(r.width * 3 / 10); // 30%
column1.setWidth(r.width * 5 / 10); // 50%
column2.setWidth(r.width * 2 / 10); // 20%
}
});
}
/**
* Creates a selection listener that will check or uncheck the whole line when
* double-clicked (aka "the default selection").
*/
private void setupSelectionListener(final Table table) {
// Add a selection listener that will check/uncheck items when they are double-clicked
table.addSelectionListener(new SelectionListener() {
/** Default selection means double-click on "most" platforms */
public void widgetDefaultSelected(SelectionEvent e) {
if (e.item instanceof TableItem) {
TableItem i = (TableItem) e.item;
i.setChecked(!i.getChecked());
enforceSingleSelection(i);
updateDescription(i);
}
if (mSelectionListener != null) {
mSelectionListener.widgetDefaultSelected(e);
}
}
public void widgetSelected(SelectionEvent e) {
if (e.item instanceof TableItem) {
TableItem i = (TableItem) e.item;
enforceSingleSelection(i);
updateDescription(i);
}
if (mSelectionListener != null) {
mSelectionListener.widgetSelected(e);
}
}
/**
* If we're not in multiple selection mode, uncheck all other
* items when this one is selected.
*/
private void enforceSingleSelection(TableItem item) {
if (!mAllowMultipleSelection && item.getChecked()) {
Table parentTable = item.getParent();
for (TableItem i2 : parentTable.getItems()) {
if (i2 != item && i2.getChecked()) {
i2.setChecked(false);
}
}
}
}
});
}
/**
* Fills the table with all SDK targets.
* The table columns are:
* <ul>
* <li>column 0: sdk name
* <li>column 1: sdk vendor
* <li>column 2: sdk api name
* </ul>
*/
private void fillTable(final Table table) {
if (mTargets != null && mTargets.length > 0) {
table.setEnabled(true);
for (IAndroidTarget target : mTargets) {
TableItem item = new TableItem(table, SWT.NONE);
item.setData(target);
item.setText(0, target.getName());
item.setText(1, target.getVendor());
item.setText(2, target.getApiVersionName());
}
} else {
table.setEnabled(false);
TableItem item = new TableItem(table, SWT.NONE);
item.setData(null);
item.setText(0, "--");
item.setText(1, "No target available");
item.setText(2, "--");
}
}
/**
* Sets up a tooltip that displays the current item description.
* <p/>
* Displaying a tooltip over the table looks kind of odd here. Instead we actually
* display the description in a label under the table.
*/
private void setupTooltip(final Table table) {
/*
* Reference:
* http://dev.eclipse.org/viewcvs/index.cgi/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/Snippet125.java?view=markup
*/
final Listener listener = new Listener() {
public void handleEvent(Event event) {
switch(event.type) {
case SWT.KeyDown:
case SWT.MouseExit:
case SWT.MouseDown:
return;
case SWT.MouseHover:
updateDescription(table.getItem(new Point(event.x, event.y)));
break;
case SWT.Selection:
if (event.item instanceof TableItem) {
updateDescription((TableItem) event.item);
}
break;
default:
return;
}
}
};
table.addListener(SWT.Dispose, listener);
table.addListener(SWT.KeyDown, listener);
table.addListener(SWT.MouseMove, listener);
table.addListener(SWT.MouseHover, listener);
}
/**
* Updates the description label with the description of the item's android target, if any.
*/
private void updateDescription(TableItem item) {
if (item != null) {
Object data = item.getData();
if (data instanceof IAndroidTarget) {
String newTooltip = ((IAndroidTarget) data).getDescription();
mDescription.setText(newTooltip == null ? "" : newTooltip); //$NON-NLS-1$
}
}
}
}