auto import from //depot/cupcake/@135843
This commit is contained in:
27
tools/sdkmanager/libs/sdklib/src/Android.mk
Normal file
27
tools/sdkmanager/libs/sdklib/src/Android.mk
Normal 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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
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;
|
||||
private final String mDescription;
|
||||
|
||||
OptionalLibrary(String jarName, String jarPath, String name, String description) {
|
||||
mJarName = jarName;
|
||||
mJarPath = jarPath;
|
||||
mName = name;
|
||||
mDescription = description;
|
||||
}
|
||||
|
||||
public String getJarName() {
|
||||
return mJarName;
|
||||
}
|
||||
|
||||
public String getJarPath() {
|
||||
return mJarPath;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return mName;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return mDescription;
|
||||
}
|
||||
}
|
||||
|
||||
private final String mLocation;
|
||||
private final PlatformTarget mBasePlatform;
|
||||
private final String mName;
|
||||
private final String mVendor;
|
||||
private final String mDescription;
|
||||
private String[] mSkins;
|
||||
private String mDefaultSkin;
|
||||
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 a 2 string array with the .jar filename, and the description.
|
||||
* @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.
|
||||
if (libMap != null) {
|
||||
mLibraries = new IOptionalLibrary[libMap.size()];
|
||||
int index = 0;
|
||||
for (Entry<String, String[]> entry : libMap.entrySet()) {
|
||||
String jarFile = entry.getValue()[0];
|
||||
String desc = entry.getValue()[1];
|
||||
mLibraries[index++] = new OptionalLibrary(jarFile,
|
||||
mLocation + SdkConstants.OS_ADDON_LIBS_FOLDER + jarFile,
|
||||
entry.getKey(), desc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public String getLocation() {
|
||||
return mLocation;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return mName;
|
||||
}
|
||||
|
||||
public String getVendor() {
|
||||
return mVendor;
|
||||
}
|
||||
|
||||
public String getFullName() {
|
||||
return String.format("%1$s (%2$s)", mName, 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 IAndroidTarget getParent() {
|
||||
return mBasePlatform;
|
||||
}
|
||||
|
||||
public String getPath(int pathId) {
|
||||
switch (pathId) {
|
||||
case IMAGES:
|
||||
return mLocation + SdkConstants.OS_IMAGES_FOLDER;
|
||||
case SKINS:
|
||||
return mLocation + SdkConstants.OS_SKINS_FOLDER;
|
||||
case DOCS:
|
||||
return mLocation + SdkConstants.FD_DOCS + File.separator;
|
||||
default :
|
||||
return mBasePlatform.getPath(pathId);
|
||||
}
|
||||
}
|
||||
|
||||
public String[] getSkins() {
|
||||
return mSkins;
|
||||
}
|
||||
|
||||
public String getDefaultSkin() {
|
||||
return mDefaultSkin;
|
||||
}
|
||||
|
||||
public IOptionalLibrary[] getOptionalLibraries() {
|
||||
return mLibraries;
|
||||
}
|
||||
|
||||
public boolean isCompatibleBaseFor(IAndroidTarget target) {
|
||||
// basic test
|
||||
if (target == this) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// if the receiver has no optional library, then anything with api version number >= to
|
||||
// the receiver is compatible.
|
||||
if (mLibraries.length == 0) {
|
||||
return target.getApiVersionNumber() >= getApiVersionNumber();
|
||||
}
|
||||
|
||||
// Otherwise, target is only compatible if the vendor and name are equals with the api
|
||||
// number greater or equal (ie target is a newer version of this add-on).
|
||||
if (target.isPlatform() == false) {
|
||||
return (mVendor.equals(target.getVendor()) && mName.equals(target.getName()) &&
|
||||
target.getApiVersionNumber() >= getApiVersionNumber());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
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, String defaultSkin) {
|
||||
mDefaultSkin = defaultSkin;
|
||||
|
||||
// we mix the add-on and base platform skins
|
||||
HashSet<String> skinSet = new HashSet<String>();
|
||||
skinSet.addAll(Arrays.asList(skins));
|
||||
skinSet.addAll(Arrays.asList(mBasePlatform.getSkins()));
|
||||
|
||||
mSkins = skinSet.toArray(new String[skinSet.size()]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
/*
|
||||
* 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> {
|
||||
|
||||
/** OS Path to the "android.jar" file. */
|
||||
public static int ANDROID_JAR = 1;
|
||||
/** OS Path to the "framework.aidl" file. */
|
||||
public static int ANDROID_AIDL = 2;
|
||||
/** OS Path to "images" folder which contains the emulator system images. */
|
||||
public static int IMAGES = 3;
|
||||
/** OS Path to the "samples" folder which contains sample projects. */
|
||||
public static int SAMPLES = 4;
|
||||
/** OS Path to the "skins" folder which contains the emulator skins. */
|
||||
public static int SKINS = 5;
|
||||
/** OS Path to the "templates" folder which contains the templates for new projects. */
|
||||
public static int TEMPLATES = 6;
|
||||
/** OS Path to the "data" folder which contains data & libraries for the SDK tools. */
|
||||
public static int DATA = 7;
|
||||
/** OS Path to the "attrs.xml" file. */
|
||||
public static int ATTRIBUTES = 8;
|
||||
/** OS Path to the "attrs_manifest.xml" file. */
|
||||
public static int MANIFEST_ATTRIBUTES = 9;
|
||||
/** OS Path to the "data/layoutlib.jar" library. */
|
||||
public static int LAYOUT_LIB = 10;
|
||||
/** OS Path to the "data/res" folder. */
|
||||
public static int RESOURCES = 11;
|
||||
/** OS Path to the "data/fonts" folder. */
|
||||
public static int FONTS = 12;
|
||||
/** OS Path to the "data/widgets.txt" file. */
|
||||
public static int WIDGETS = 13;
|
||||
/** OS Path to the "data/activity_actions.txt" file. */
|
||||
public static int ACTIONS_ACTIVITY = 14;
|
||||
/** OS Path to the "data/broadcast_actions.txt" file. */
|
||||
public static int ACTIONS_BROADCAST = 15;
|
||||
/** OS Path to the "data/service_actions.txt" file. */
|
||||
public static int ACTIONS_SERVICE = 16;
|
||||
/** OS Path to the "data/categories.txt" file. */
|
||||
public static int CATEGORIES = 17;
|
||||
/** OS Path to the "sources" folder. */
|
||||
public static int SOURCES = 18;
|
||||
/** OS Path to the target specific docs */
|
||||
public static int DOCS = 19;
|
||||
/** OS Path to the target's version of the aapt tool. */
|
||||
public static int AAPT = 20;
|
||||
/** OS Path to the target's version of the aidl tool. */
|
||||
public static int AIDL = 21;
|
||||
/** OS Path to the target's version of the dx too. */
|
||||
public static int DX = 22;
|
||||
/** OS Path to the target's version of the dx.jar file. */
|
||||
public static int DX_JAR = 23;
|
||||
|
||||
public interface IOptionalLibrary {
|
||||
String getName();
|
||||
String getJarName();
|
||||
String getJarPath();
|
||||
String getDescription();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the target location.
|
||||
*/
|
||||
String getLocation();
|
||||
|
||||
/**
|
||||
* Returns the name of the vendor of the target.
|
||||
*/
|
||||
String getVendor();
|
||||
|
||||
/**
|
||||
* Returns the name of the target.
|
||||
*/
|
||||
String getName();
|
||||
|
||||
/**
|
||||
* Returns the full name of the target, possibly including vendor name.
|
||||
*/
|
||||
String getFullName();
|
||||
|
||||
/**
|
||||
* 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 parent target. This is likely to only be non <code>null</code> if
|
||||
* {@link #isPlatform()} returns <code>false</code>
|
||||
*/
|
||||
IAndroidTarget getParent();
|
||||
|
||||
/**
|
||||
* Returns the path of a platform component.
|
||||
* @param pathId the id representing the path to return. Any of the constants defined in the
|
||||
* {@link IAndroidTarget} interface can be used.
|
||||
*/
|
||||
String getPath(int pathId);
|
||||
|
||||
/**
|
||||
* Returns the available skins for this target.
|
||||
*/
|
||||
String[] getSkins();
|
||||
|
||||
/**
|
||||
* Returns the default skin for this target.
|
||||
*/
|
||||
String getDefaultSkin();
|
||||
|
||||
/**
|
||||
* 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 whether the given target is compatible with the receiver.
|
||||
* <p/>A target is considered compatible if applications developed for the receiver can run on
|
||||
* the given target.
|
||||
*
|
||||
* @param target the IAndroidTarget to test.
|
||||
*/
|
||||
boolean isCompatibleBaseFor(IAndroidTarget target);
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* 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.util.Formatter;
|
||||
|
||||
/**
|
||||
* Interface used to display warnings/errors while parsing the SDK content.
|
||||
*/
|
||||
public interface ISdkLog {
|
||||
|
||||
/**
|
||||
* Prints a warning message on stdout.
|
||||
* <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.
|
||||
* @param args provides the arguments for warningFormat.
|
||||
*/
|
||||
void warning(String warningFormat, Object... args);
|
||||
|
||||
/**
|
||||
* Prints an error message on stderr.
|
||||
* <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.
|
||||
* @param errorFormat is an optional error format. If non-null, it will be printed
|
||||
* using a {@link Formatter} with the provided arguments.
|
||||
* @param args provides the arguments for errorFormat.
|
||||
*/
|
||||
void error(Throwable t, String errorFormat, Object... args);
|
||||
|
||||
/**
|
||||
* Prints a message as-is on stdout.
|
||||
* <p/>
|
||||
* Implementation should always display errors, independent of verbose mode.
|
||||
* No prefix is used, the message is printed as-is after formatting.
|
||||
*
|
||||
* @param msgFormat is an optional error format. If non-null, it will be printed
|
||||
* using a {@link Formatter} with the provided arguments.
|
||||
* @param args provides the arguments for msgFormat.
|
||||
*/
|
||||
void printf(String msgFormat, Object... args);
|
||||
}
|
||||
@@ -0,0 +1,214 @@
|
||||
/*
|
||||
* 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);
|
||||
mPaths.put(AAPT, mLocation + SdkConstants.OS_SDK_TOOLS_FOLDER + SdkConstants.FN_AAPT);
|
||||
mPaths.put(AIDL, mLocation + SdkConstants.OS_SDK_TOOLS_FOLDER + SdkConstants.FN_AIDL);
|
||||
mPaths.put(DX, mLocation + SdkConstants.OS_SDK_TOOLS_FOLDER + SdkConstants.FN_DX);
|
||||
mPaths.put(DX_JAR, mLocation + SdkConstants.OS_SDK_TOOLS_LIB_FOLDER +
|
||||
SdkConstants.FN_DX_JAR);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
public String getFullName() {
|
||||
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 IAndroidTarget getParent() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getPath(int pathId) {
|
||||
return mPaths.get(pathId);
|
||||
}
|
||||
|
||||
public String[] getSkins() {
|
||||
return mSkins;
|
||||
}
|
||||
|
||||
public String getDefaultSkin() {
|
||||
// at this time, this is the default skin for all the platform.
|
||||
return "HVGA";
|
||||
}
|
||||
|
||||
/*
|
||||
* 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 boolean isCompatibleBaseFor(IAndroidTarget target) {
|
||||
// basic test
|
||||
if (target == this) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// target is compatible wit the receiver as long as its api version number is greater or
|
||||
// equal.
|
||||
return target.getApiVersionNumber() >= mApiVersionNumber;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,282 @@
|
||||
/*
|
||||
* 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 {
|
||||
public final static int PLATFORM_UNKNOWN = 0;
|
||||
public final static int PLATFORM_LINUX = 1;
|
||||
public final static int PLATFORM_WINDOWS = 2;
|
||||
public final static int PLATFORM_DARWIN = 3;
|
||||
|
||||
/**
|
||||
* Returns current platform, one of {@link #PLATFORM_WINDOWS}, {@link #PLATFORM_DARWIN},
|
||||
* {@link #PLATFORM_LINUX} or {@link #PLATFORM_UNKNOWN}.
|
||||
*/
|
||||
public final static int CURRENT_PLATFORM = currentPlatform();
|
||||
|
||||
|
||||
/** An SDK Project's AndroidManifest.xml file */
|
||||
public static final String FN_ANDROID_MANIFEST_XML= "AndroidManifest.xml";
|
||||
/** An SDK Project's build.xml file */
|
||||
public final static String FN_BUILD_XML = "build.xml";
|
||||
|
||||
/** 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$
|
||||
|
||||
/** dex.jar file */
|
||||
public static final String FN_DX_JAR = "dx.jar"; //$NON-NLS-1$
|
||||
|
||||
/** dx executable */
|
||||
public final static String FN_DX = (CURRENT_PLATFORM == PLATFORM_WINDOWS) ?
|
||||
"dx.bat" : "dx"; //$NON-NLS-1$ //$NON-NLS-2$
|
||||
|
||||
/** aapt executable */
|
||||
public final static String FN_AAPT = (CURRENT_PLATFORM == PLATFORM_WINDOWS) ?
|
||||
"aapt.exe" : "aapt"; //$NON-NLS-1$ //$NON-NLS-2$
|
||||
|
||||
/** aidl executable */
|
||||
public final static String FN_AIDL = (CURRENT_PLATFORM == PLATFORM_WINDOWS) ?
|
||||
"aidl.exe" : "aidl"; //$NON-NLS-1$ //$NON-NLS-2$
|
||||
|
||||
/* Folder Names for Android Projects . */
|
||||
|
||||
/** Resources folder name, i.e. "res". */
|
||||
public final static String FD_RESOURCES = "res"; //$NON-NLS-1$
|
||||
/** Assets folder name, i.e. "assets" */
|
||||
public final static String FD_ASSETS = "assets"; //$NON-NLS-1$
|
||||
/** Default source folder name, i.e. "src" */
|
||||
public final static String FD_SOURCES = "src"; //$NON-NLS-1$
|
||||
/** Default generated source folder name, i.e. "gen" */
|
||||
public final static String FD_GEN_SOURCES = "gen"; //$NON-NLS-1$
|
||||
/** Default native library folder name inside the project, i.e. "libs"
|
||||
* While the folder inside the .apk is "lib", we call that one libs because
|
||||
* that's what we use in ant for both .jar and .so and we need to make the 2 development ways
|
||||
* compatible. */
|
||||
public final static String FD_NATIVE_LIBS = "libs"; //$NON-NLS-1$
|
||||
/** Native lib folder inside the APK: "lib" */
|
||||
public final static String FD_APK_NATIVE_LIBS = "lib"; //$NON-NLS-1$
|
||||
/** Default output folder name, i.e. "bin" */
|
||||
public final static String FD_OUTPUT = "bin"; //$NON-NLS-1$
|
||||
/** Default anim resource folder name, i.e. "anim" */
|
||||
public final static String FD_ANIM = "anim"; //$NON-NLS-1$
|
||||
/** Default color resource folder name, i.e. "color" */
|
||||
public final static String FD_COLOR = "color"; //$NON-NLS-1$
|
||||
/** Default drawable resource folder name, i.e. "drawable" */
|
||||
public final static String FD_DRAWABLE = "drawable"; //$NON-NLS-1$
|
||||
/** Default layout resource folder name, i.e. "layout" */
|
||||
public final static String FD_LAYOUT = "layout"; //$NON-NLS-1$
|
||||
/** Default menu resource folder name, i.e. "menu" */
|
||||
public final static String FD_MENU = "menu"; //$NON-NLS-1$
|
||||
/** Default values resource folder name, i.e. "values" */
|
||||
public final static String FD_VALUES = "values"; //$NON-NLS-1$
|
||||
/** Default xml resource folder name, i.e. "xml" */
|
||||
public final static String FD_XML = "xml"; //$NON-NLS-1$
|
||||
/** Default raw resource folder name, i.e. "raw" */
|
||||
public final static String FD_RAW = "raw"; //$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";
|
||||
/** 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";
|
||||
|
||||
/** Namespace for the resource XML, i.e. "http://schemas.android.com/apk/res/android" */
|
||||
public final static String NS_RESOURCES = "http://schemas.android.com/apk/res/android";
|
||||
|
||||
/* 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, or to a platform 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, or to a platform 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";
|
||||
|
||||
/** Returns the appropriate name for the 'android' command, which is 'android.bat' for
|
||||
* Windows and 'android' for all other platforms. */
|
||||
public static String androidCmdName() {
|
||||
String os = System.getProperty("os.name");
|
||||
String cmd = "android";
|
||||
if (os.startsWith("Windows")) {
|
||||
cmd += ".bat";
|
||||
}
|
||||
return cmd;
|
||||
}
|
||||
|
||||
/** Returns the appropriate name for the 'mksdcard' command, which is 'mksdcard.exe' for
|
||||
* Windows and 'mkdsdcard' for all other platforms. */
|
||||
public static String mkSdCardCmdName() {
|
||||
String os = System.getProperty("os.name");
|
||||
String cmd = "mksdcard";
|
||||
if (os.startsWith("Windows")) {
|
||||
cmd += ".exe";
|
||||
}
|
||||
return cmd;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns current platform
|
||||
*
|
||||
* @return one of {@link #PLATFORM_WINDOWS}, {@link #PLATFORM_DARWIN},
|
||||
* {@link #PLATFORM_LINUX} or {@link #PLATFORM_UNKNOWN}.
|
||||
*/
|
||||
private static int currentPlatform() {
|
||||
String os = System.getProperty("os.name"); //$NON-NLS-1$
|
||||
if (os.startsWith("Mac OS")) { //$NON-NLS-1$
|
||||
return PLATFORM_DARWIN;
|
||||
} else if (os.startsWith("Windows")) { //$NON-NLS-1$
|
||||
return PLATFORM_WINDOWS;
|
||||
} else if (os.startsWith("Linux")) { //$NON-NLS-1$
|
||||
return PLATFORM_LINUX;
|
||||
}
|
||||
|
||||
return PLATFORM_UNKNOWN;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,511 @@
|
||||
/*
|
||||
* 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 {
|
||||
|
||||
public final static String PROP_VERSION_SDK = "ro.build.version.sdk";
|
||||
public 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 String ADDON_DEFAULT_SKIN = "skin";
|
||||
|
||||
private final static Pattern PATTERN_PROP = Pattern.compile(
|
||||
"^([a-zA-Z0-9._-]+)\\s*=\\s*(.*)\\s*$");
|
||||
|
||||
private final static Pattern PATTERN_LIB_DATA = Pattern.compile(
|
||||
"^([a-zA-Z0-9._-]+\\.jar);(.*)$", Pattern.CASE_INSENSITIVE);
|
||||
|
||||
/** List of items in the platform to check when parsing it. These paths are relative to the
|
||||
* platform root folder. */
|
||||
private final static String[] sPlatformContentList = new String[] {
|
||||
SdkConstants.FN_FRAMEWORK_LIBRARY,
|
||||
SdkConstants.FN_FRAMEWORK_AIDL,
|
||||
SdkConstants.OS_SDK_TOOLS_FOLDER + SdkConstants.FN_AAPT,
|
||||
SdkConstants.OS_SDK_TOOLS_FOLDER + SdkConstants.FN_AIDL,
|
||||
SdkConstants.OS_SDK_TOOLS_FOLDER + SdkConstants.FN_DX,
|
||||
SdkConstants.OS_SDK_TOOLS_LIB_FOLDER + SdkConstants.FN_DX_JAR,
|
||||
};
|
||||
|
||||
/** 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, "Error parsing the sdk.");
|
||||
}
|
||||
}
|
||||
|
||||
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 {@link IAndroidTarget} hash string.
|
||||
* @return The matching {@link IAndroidTarget} or null.
|
||||
*/
|
||||
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) {
|
||||
// api number and name looks valid, perform a few more checks
|
||||
if (checkPlatformContent(platform, log) == false) {
|
||||
return 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(null,
|
||||
"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(null, "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) {
|
||||
// Add-ons have to be folders. Ignore files and no need to warn about them.
|
||||
if (addon.isDirectory()) {
|
||||
AddOnTarget target = loadAddon(addon, list, log);
|
||||
if (target != null) {
|
||||
list.add(target);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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> propertyMap = parsePropertyFile(addOnManifest, log);
|
||||
|
||||
if (propertyMap != null) {
|
||||
// look for some specific values in the map.
|
||||
// we require name, vendor, and api
|
||||
String name = propertyMap.get(ADDON_NAME);
|
||||
if (name == null) {
|
||||
displayAddonManifestError(log, addon.getName(), ADDON_NAME);
|
||||
return null;
|
||||
}
|
||||
|
||||
String vendor = propertyMap.get(ADDON_VENDOR);
|
||||
if (vendor == null) {
|
||||
displayAddonManifestError(log, addon.getName(), ADDON_VENDOR);
|
||||
return null;
|
||||
}
|
||||
|
||||
String api = propertyMap.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(null,
|
||||
"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(null,
|
||||
"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 = propertyMap.get(ADDON_DESCRIPTION);
|
||||
|
||||
// get the optional libraries
|
||||
String librariesValue = propertyMap.get(ADDON_LIBRARIES);
|
||||
Map<String, String[]> libMap = null;
|
||||
|
||||
if (librariesValue != null) {
|
||||
librariesValue = librariesValue.trim();
|
||||
if (librariesValue.length() > 0) {
|
||||
// split in the string into the libraries name
|
||||
String[] libraries = librariesValue.split(";");
|
||||
if (libraries.length > 0) {
|
||||
libMap = new HashMap<String, String[]>();
|
||||
for (String libName : libraries) {
|
||||
libName = libName.trim();
|
||||
|
||||
// get the library data from the properties
|
||||
String libData = propertyMap.get(libName);
|
||||
|
||||
if (libData != null) {
|
||||
// split the jar file from the description
|
||||
Matcher m = PATTERN_LIB_DATA.matcher(libData);
|
||||
if (m.matches()) {
|
||||
libMap.put(libName, new String[] {
|
||||
m.group(1), m.group(2) });
|
||||
} else if (log != null) {
|
||||
log.error(null,
|
||||
"Ignoring library '%1$s', property value has wrong format\n\t%2$s",
|
||||
libName, libData);
|
||||
}
|
||||
} else if (log != null) {
|
||||
log.error(null,
|
||||
"Ignoring library '%1$s', missing property value",
|
||||
libName, libData);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AddOnTarget target = new AddOnTarget(addon.getAbsolutePath(), name, vendor,
|
||||
description, libMap, baseTarget);
|
||||
|
||||
// need to parse the skins.
|
||||
String[] skins = parseSkinFolder(target.getPath(IAndroidTarget.SKINS));
|
||||
|
||||
// get the default skin, or take it from the base platform if needed.
|
||||
String defaultSkin = propertyMap.get(ADDON_DEFAULT_SKIN);
|
||||
|
||||
if (defaultSkin == null) {
|
||||
if (skins.length == 1) {
|
||||
defaultSkin = skins[1];
|
||||
} else {
|
||||
defaultSkin = baseTarget.getDefaultSkin();
|
||||
}
|
||||
}
|
||||
|
||||
target.setSkins(skins, defaultSkin);
|
||||
|
||||
return target;
|
||||
}
|
||||
} else if (log != null) {
|
||||
log.error(null, "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(null, "Ignoring add-on '%1$s': '%2$s' is missing from %3$s.",
|
||||
addonName, valueName, SdkConstants.FN_MANIFEST_INI);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the given platform has all the required files, and returns true if they are all
|
||||
* present.
|
||||
* <p/>This checks the presence of the following files: android.jar, framework.aidl, aapt(.exe),
|
||||
* aidl(.exe), dx(.bat), and dx.jar
|
||||
*/
|
||||
private boolean checkPlatformContent(File platform, ISdkLog log) {
|
||||
for (String relativePath : sPlatformContentList) {
|
||||
File f = new File(platform, relativePath);
|
||||
if (f.exists() == false) {
|
||||
log.error(null,
|
||||
"Ignoring platform '%1$s': %2$s is missing.",
|
||||
platform.getName(), relativePath);
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
FileInputStream fis = null;
|
||||
BufferedReader reader = null;
|
||||
try {
|
||||
fis = new FileInputStream(buildProp);
|
||||
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());
|
||||
}
|
||||
} finally {
|
||||
if (reader != null) {
|
||||
try {
|
||||
reader.close();
|
||||
} catch (IOException e) {
|
||||
// pass
|
||||
}
|
||||
}
|
||||
if (fis != null) {
|
||||
try {
|
||||
fis.close();
|
||||
} catch (IOException e) {
|
||||
// pass
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,800 @@
|
||||
/*
|
||||
* 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.avd;
|
||||
|
||||
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.BufferedReader;
|
||||
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.io.InputStreamReader;
|
||||
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;
|
||||
|
||||
/**
|
||||
* Android Virtual Device Manager to manage AVDs.
|
||||
*/
|
||||
public final class AvdManager {
|
||||
|
||||
public static final String AVD_FOLDER_EXTENSION = ".avd";
|
||||
|
||||
private final static String AVD_INFO_PATH = "path";
|
||||
private final static String AVD_INFO_TARGET = "target";
|
||||
|
||||
public final static String AVD_INI_SKIN_PATH = "skin.path";
|
||||
public final static String AVD_INI_SKIN_NAME = "skin.name";
|
||||
public final static String AVD_INI_SDCARD_PATH = "sdcard.path";
|
||||
public final static String AVD_INI_SDCARD_SIZE = "sdcard.size";
|
||||
public final static String AVD_INI_IMAGES_1 = "image.sysdir.1";
|
||||
public final static String AVD_INI_IMAGES_2 = "image.sysdir.2";
|
||||
|
||||
/**
|
||||
* Pattern to match pixel-sized skin "names", e.g. "320x480".
|
||||
*/
|
||||
public final static Pattern NUMERIC_SKIN_SIZE = Pattern.compile("[0-9]{2,}x[0-9]{2,}");
|
||||
|
||||
|
||||
private final static String USERDATA_IMG = "userdata.img";
|
||||
private final static String CONFIG_INI = "config.ini";
|
||||
private final static String SDCARD_IMG = "sdcard.img";
|
||||
|
||||
private final static String INI_EXTENSION = ".ini";
|
||||
private final static Pattern INI_NAME_PATTERN = Pattern.compile("(.+)\\" + INI_EXTENSION + "$",
|
||||
Pattern.CASE_INSENSITIVE);
|
||||
|
||||
private final static Pattern SDCARD_SIZE_PATTERN = Pattern.compile("\\d+[MK]?");
|
||||
|
||||
/** An immutable structure describing an Android Virtual Device. */
|
||||
public static final class AvdInfo {
|
||||
private final String mName;
|
||||
private final String mPath;
|
||||
private final IAndroidTarget mTarget;
|
||||
private final Map<String, String> mProperties;
|
||||
|
||||
/** Creates a new AVD info. Values are immutable.
|
||||
* @param properties */
|
||||
public AvdInfo(String name, String path, IAndroidTarget target,
|
||||
Map<String, String> properties) {
|
||||
mName = name;
|
||||
mPath = path;
|
||||
mTarget = target;
|
||||
mProperties = properties;
|
||||
}
|
||||
|
||||
/** Returns the name of the AVD. */
|
||||
public String getName() {
|
||||
return mName;
|
||||
}
|
||||
|
||||
/** Returns the path of the AVD data directory. */
|
||||
public String getPath() {
|
||||
return mPath;
|
||||
}
|
||||
|
||||
/** Returns the target of the AVD. */
|
||||
public IAndroidTarget getTarget() {
|
||||
return mTarget;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method that returns the .ini {@link File} for a given AVD name.
|
||||
* @throws AndroidLocationException if there's a problem getting android root directory.
|
||||
*/
|
||||
public static File getIniFile(String name) throws AndroidLocationException {
|
||||
String avdRoot;
|
||||
avdRoot = AndroidLocation.getFolder() + AndroidLocation.FOLDER_AVD;
|
||||
return new File(avdRoot, name + INI_EXTENSION);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the .ini {@link File} for this AVD.
|
||||
* @throws AndroidLocationException if there's a problem getting android root directory.
|
||||
*/
|
||||
public File getIniFile() throws AndroidLocationException {
|
||||
return getIniFile(mName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a map of properties for the AVD.
|
||||
*/
|
||||
public Map<String, String> getProperties() {
|
||||
return mProperties;
|
||||
}
|
||||
}
|
||||
|
||||
private final ArrayList<AvdInfo> mAvdList = new ArrayList<AvdInfo>();
|
||||
private ISdkLog mSdkLog;
|
||||
private final SdkManager mSdk;
|
||||
|
||||
public AvdManager(SdkManager sdk, ISdkLog sdkLog) throws AndroidLocationException {
|
||||
mSdk = sdk;
|
||||
mSdkLog = sdkLog;
|
||||
buildAvdList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the existing AVDs.
|
||||
* @return a newly allocated array containing all the AVDs.
|
||||
*/
|
||||
public AvdInfo[] getAvds() {
|
||||
return mAvdList.toArray(new AvdInfo[mAvdList.size()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link AvdInfo} matching the given <var>name</var>.
|
||||
* @return the matching AvdInfo or <code>null</code> if none were found.
|
||||
*/
|
||||
public AvdInfo getAvd(String name) {
|
||||
for (AvdInfo info : mAvdList) {
|
||||
if (info.getName().equals(name)) {
|
||||
return info;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new AVD. It is expected that there is no existing AVD with this name already.
|
||||
* @param avdFolder the data folder for the AVD. It will be created as needed.
|
||||
* @param name the name of the AVD
|
||||
* @param target the target of the AVD
|
||||
* @param skinName the name of the skin. Can be null. Must have been verified by caller.
|
||||
* @param sdcard the parameter value for the sdCard. Can be null. This is either a path to
|
||||
* an existing sdcard image or a sdcard size (\d+, \d+K, \dM).
|
||||
* @param hardwareConfig the hardware setup for the AVD
|
||||
* @param removePrevious If true remove any previous files.
|
||||
*/
|
||||
public AvdInfo createAvd(File avdFolder, String name, IAndroidTarget target,
|
||||
String skinName, String sdcard, Map<String,String> hardwareConfig,
|
||||
boolean removePrevious, ISdkLog log) {
|
||||
|
||||
File iniFile = null;
|
||||
boolean needCleanup = false;
|
||||
try {
|
||||
if (avdFolder.exists()) {
|
||||
if (removePrevious) {
|
||||
// AVD already exists and removePrevious is set, try to remove the
|
||||
// directory's content first (but not the directory itself).
|
||||
recursiveDelete(avdFolder);
|
||||
} else {
|
||||
// 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.",
|
||||
avdFolder.getAbsolutePath());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
// create the AVD folder.
|
||||
avdFolder.mkdir();
|
||||
}
|
||||
|
||||
// actually write the ini file
|
||||
iniFile = createAvdIniFile(name, avdFolder, target);
|
||||
|
||||
// writes the userdata.img in it.
|
||||
String imagePath = target.getPath(IAndroidTarget.IMAGES);
|
||||
File userdataSrc = new File(imagePath, USERDATA_IMG);
|
||||
|
||||
if (userdataSrc.exists() == false && target.isPlatform() == false) {
|
||||
imagePath = target.getParent().getPath(IAndroidTarget.IMAGES);
|
||||
userdataSrc = new File(imagePath, USERDATA_IMG);
|
||||
}
|
||||
|
||||
if (userdataSrc.exists() == false) {
|
||||
log.error(null, "Unable to find a '%1$s' file to copy into the AVD folder.",
|
||||
USERDATA_IMG);
|
||||
needCleanup = true;
|
||||
return null;
|
||||
}
|
||||
|
||||
FileInputStream fis = new FileInputStream(userdataSrc);
|
||||
|
||||
File userdataDest = new File(avdFolder, USERDATA_IMG);
|
||||
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.
|
||||
HashMap<String, String> values = new HashMap<String, String>();
|
||||
|
||||
// First the image folders of the target itself
|
||||
imagePath = getImageRelativePath(target, log);
|
||||
if (imagePath == null) {
|
||||
needCleanup = true;
|
||||
return null;
|
||||
}
|
||||
|
||||
values.put(AVD_INI_IMAGES_1, imagePath);
|
||||
|
||||
// If the target is an add-on we need to add the Platform image as a backup.
|
||||
IAndroidTarget parent = target.getParent();
|
||||
if (parent != null) {
|
||||
imagePath = getImageRelativePath(parent, log);
|
||||
if (imagePath == null) {
|
||||
needCleanup = true;
|
||||
return null;
|
||||
}
|
||||
|
||||
values.put(AVD_INI_IMAGES_2, imagePath);
|
||||
}
|
||||
|
||||
|
||||
// Now the skin.
|
||||
if (skinName == null) {
|
||||
skinName = target.getDefaultSkin();
|
||||
}
|
||||
|
||||
if (NUMERIC_SKIN_SIZE.matcher(skinName).matches()) {
|
||||
// Skin name is an actual screen resolution, no skin.path
|
||||
values.put(AVD_INI_SKIN_NAME, skinName);
|
||||
} else {
|
||||
// get the path of the skin (relative to the SDK)
|
||||
// assume skin name is valid
|
||||
String skinPath = getSkinRelativePath(skinName, target, log);
|
||||
if (skinPath == null) {
|
||||
needCleanup = true;
|
||||
return null;
|
||||
}
|
||||
|
||||
values.put(AVD_INI_SKIN_PATH, skinPath);
|
||||
values.put(AVD_INI_SKIN_NAME, skinName);
|
||||
}
|
||||
|
||||
if (sdcard != null) {
|
||||
File sdcardFile = new File(sdcard);
|
||||
if (sdcardFile.isFile()) {
|
||||
// sdcard value is an external sdcard, so we put its path into the config.ini
|
||||
values.put(AVD_INI_SDCARD_PATH, sdcard);
|
||||
} else {
|
||||
// Sdcard is possibly a size. In that case we create a file called 'sdcard.img'
|
||||
// in the AVD folder, and do not put any value in config.ini.
|
||||
|
||||
// First, check that it matches the pattern for sdcard size
|
||||
Matcher m = SDCARD_SIZE_PATTERN.matcher(sdcard);
|
||||
if (m.matches()) {
|
||||
// create the sdcard.
|
||||
sdcardFile = new File(avdFolder, SDCARD_IMG);
|
||||
String path = sdcardFile.getAbsolutePath();
|
||||
|
||||
// execute mksdcard with the proper parameters.
|
||||
File toolsFolder = new File(mSdk.getLocation(), SdkConstants.FD_TOOLS);
|
||||
File mkSdCard = new File(toolsFolder, SdkConstants.mkSdCardCmdName());
|
||||
|
||||
if (mkSdCard.isFile() == false) {
|
||||
log.error(null, "'%1$s' is missing from the SDK tools folder.",
|
||||
mkSdCard.getName());
|
||||
needCleanup = true;
|
||||
return null;
|
||||
}
|
||||
|
||||
if (createSdCard(mkSdCard.getAbsolutePath(), sdcard, path, log) == false) {
|
||||
needCleanup = true;
|
||||
return null; // mksdcard output has already been displayed, no need to
|
||||
// output anything else.
|
||||
}
|
||||
|
||||
// add a property containing the size of the sdcard for display purpose
|
||||
// only when the dev does 'android list avd'
|
||||
values.put(AVD_INI_SDCARD_SIZE, sdcard);
|
||||
} else {
|
||||
log.error(null,
|
||||
"'%1$s' is not recognized as a valid sdcard value.\n" +
|
||||
"Value should be:\n" +
|
||||
"1. path to an sdcard.\n" +
|
||||
"2. size of the sdcard to create: <size>[K|M]",
|
||||
sdcard);
|
||||
needCleanup = true;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hardwareConfig != null) {
|
||||
values.putAll(hardwareConfig);
|
||||
}
|
||||
|
||||
File configIniFile = new File(avdFolder, CONFIG_INI);
|
||||
createConfigIni(configIniFile, values);
|
||||
|
||||
if (log != null) {
|
||||
if (target.isPlatform()) {
|
||||
log.printf("Created AVD '%s' based on %s\n", name, target.getName());
|
||||
} else {
|
||||
log.printf("Created AVD '%s' based on %s (%s)\n", name, target.getName(),
|
||||
target.getVendor());
|
||||
}
|
||||
}
|
||||
|
||||
// create the AvdInfo object, and add it to the list
|
||||
AvdInfo avdInfo = new AvdInfo(name, avdFolder.getAbsolutePath(), target, values);
|
||||
|
||||
mAvdList.add(avdInfo);
|
||||
|
||||
return avdInfo;
|
||||
} catch (AndroidLocationException e) {
|
||||
if (log != null) {
|
||||
log.error(e, null);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
if (log != null) {
|
||||
log.error(e, null);
|
||||
}
|
||||
} finally {
|
||||
if (needCleanup) {
|
||||
if (iniFile != null && iniFile.exists()) {
|
||||
iniFile.delete();
|
||||
}
|
||||
|
||||
recursiveDelete(avdFolder);
|
||||
avdFolder.delete();
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the path to the target images folder as a relative path to the SDK.
|
||||
*/
|
||||
private String getImageRelativePath(IAndroidTarget target, ISdkLog log) {
|
||||
String imageFullPath = target.getPath(IAndroidTarget.IMAGES);
|
||||
|
||||
// make this path relative to the SDK location
|
||||
String sdkLocation = mSdk.getLocation();
|
||||
if (imageFullPath.startsWith(sdkLocation) == false) {
|
||||
// this really really should not happen.
|
||||
log.error(null, "Target location is not inside the SDK.");
|
||||
assert false;
|
||||
return null;
|
||||
}
|
||||
|
||||
imageFullPath = imageFullPath.substring(sdkLocation.length());
|
||||
if (imageFullPath.charAt(0) == File.separatorChar) {
|
||||
imageFullPath = imageFullPath.substring(1);
|
||||
}
|
||||
return imageFullPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the path to the skin, as a relative path to the SDK.
|
||||
*/
|
||||
private String getSkinRelativePath(String skinName, IAndroidTarget target, ISdkLog log) {
|
||||
// first look to see if the skin is in the target
|
||||
|
||||
String path = target.getPath(IAndroidTarget.SKINS);
|
||||
File skin = new File(path, skinName);
|
||||
|
||||
if (skin.exists() == false && target.isPlatform() == false) {
|
||||
target = target.getParent();
|
||||
|
||||
path = target.getPath(IAndroidTarget.SKINS);
|
||||
skin = new File(path, skinName);
|
||||
}
|
||||
|
||||
// skin really does not exist!
|
||||
if (skin.exists() == false) {
|
||||
log.error(null, "Skin '%1$s' does not exist.", skinName);
|
||||
return null;
|
||||
}
|
||||
|
||||
// get the skin path
|
||||
path = skin.getAbsolutePath();
|
||||
|
||||
// make this path relative to the SDK location
|
||||
String sdkLocation = mSdk.getLocation();
|
||||
if (path.startsWith(sdkLocation) == false) {
|
||||
// this really really should not happen.
|
||||
log.error(null, "Target location is not inside the SDK.");
|
||||
assert false;
|
||||
return null;
|
||||
}
|
||||
|
||||
path = path.substring(sdkLocation.length());
|
||||
if (path.charAt(0) == File.separatorChar) {
|
||||
path = path.substring(1);
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the ini file for an AVD.
|
||||
*
|
||||
* @param name of the AVD.
|
||||
* @param avdFolder path for the data folder of the AVD.
|
||||
* @param target of the AVD.
|
||||
* @throws AndroidLocationException if there's a problem getting android root directory.
|
||||
* @throws IOException if {@link File#getAbsolutePath()} fails.
|
||||
*/
|
||||
private File createAvdIniFile(String name, File avdFolder, IAndroidTarget target)
|
||||
throws AndroidLocationException, IOException {
|
||||
HashMap<String, String> values = new HashMap<String, String>();
|
||||
File iniFile = AvdInfo.getIniFile(name);
|
||||
values.put(AVD_INFO_PATH, avdFolder.getAbsolutePath());
|
||||
values.put(AVD_INFO_TARGET, target.hashString());
|
||||
createConfigIni(iniFile, values);
|
||||
|
||||
return iniFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the ini file for an AVD.
|
||||
*
|
||||
* @param info of the AVD.
|
||||
* @throws AndroidLocationException if there's a problem getting android root directory.
|
||||
* @throws IOException if {@link File#getAbsolutePath()} fails.
|
||||
*/
|
||||
private File createAvdIniFile(AvdInfo info) throws AndroidLocationException, IOException {
|
||||
return createAvdIniFile(info.getName(), new File(info.getPath()), info.getTarget());
|
||||
}
|
||||
|
||||
/**
|
||||
* Actually deletes the files of an existing AVD.
|
||||
* <p/>
|
||||
* This also remove it from the manager's list, The caller does not need to
|
||||
* call {@link #removeAvd(AvdInfo)} afterwards.
|
||||
*
|
||||
* @param avdInfo the information on the AVD to delete
|
||||
*/
|
||||
public void deleteAvd(AvdInfo avdInfo, ISdkLog log) {
|
||||
try {
|
||||
File f = avdInfo.getIniFile();
|
||||
if (f.exists()) {
|
||||
log.warning("Deleting file %s", f.getCanonicalPath());
|
||||
if (!f.delete()) {
|
||||
log.error(null, "Failed to delete %s", f.getCanonicalPath());
|
||||
}
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
removeAvd(avdInfo);
|
||||
} catch (AndroidLocationException e) {
|
||||
log.error(e, null);
|
||||
} catch (IOException e) {
|
||||
log.error(e, null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves and/or rename an existing AVD and its files.
|
||||
* This also change it in the manager's list.
|
||||
* <p/>
|
||||
* The caller should make sure the name or path given are valid, do not exist and are
|
||||
* actually different than current values.
|
||||
*
|
||||
* @param avdInfo the information on the AVD to move.
|
||||
* @param newName the new name of the AVD if non null.
|
||||
* @param paramFolderPath the new data folder if non null.
|
||||
* @return True if the move succeeded or there was nothing to do.
|
||||
* If false, this method will have had already output error in the log.
|
||||
*/
|
||||
public boolean moveAvd(AvdInfo avdInfo, String newName, String paramFolderPath, ISdkLog log) {
|
||||
|
||||
try {
|
||||
if (paramFolderPath != null) {
|
||||
File f = new File(avdInfo.getPath());
|
||||
log.warning("Moving '%s' to '%s'.", avdInfo.getPath(), paramFolderPath);
|
||||
if (!f.renameTo(new File(paramFolderPath))) {
|
||||
log.error(null, "Failed to move '%s' to '%s'.",
|
||||
avdInfo.getPath(), paramFolderPath);
|
||||
return false;
|
||||
}
|
||||
|
||||
// update avd info
|
||||
AvdInfo info = new AvdInfo(avdInfo.getName(), paramFolderPath, avdInfo.getTarget(),
|
||||
avdInfo.getProperties());
|
||||
mAvdList.remove(avdInfo);
|
||||
mAvdList.add(info);
|
||||
avdInfo = info;
|
||||
|
||||
// update the ini file
|
||||
createAvdIniFile(avdInfo);
|
||||
}
|
||||
|
||||
if (newName != null) {
|
||||
File oldIniFile = avdInfo.getIniFile();
|
||||
File newIniFile = AvdInfo.getIniFile(newName);
|
||||
|
||||
log.warning("Moving '%s' to '%s'.", oldIniFile.getPath(), newIniFile.getPath());
|
||||
if (!oldIniFile.renameTo(newIniFile)) {
|
||||
log.error(null, "Failed to move '%s' to '%s'.",
|
||||
oldIniFile.getPath(), newIniFile.getPath());
|
||||
return false;
|
||||
}
|
||||
|
||||
// update avd info
|
||||
AvdInfo info = new AvdInfo(newName, avdInfo.getPath(), avdInfo.getTarget(),
|
||||
avdInfo.getProperties());
|
||||
mAvdList.remove(avdInfo);
|
||||
mAvdList.add(info);
|
||||
}
|
||||
} catch (AndroidLocationException e) {
|
||||
log.error(e, null);
|
||||
} catch (IOException e) {
|
||||
log.error(e, null);
|
||||
}
|
||||
|
||||
// nothing to do or succeeded
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to recursively delete a folder's content (but not the folder itself).
|
||||
*
|
||||
* @throws SecurityException like {@link File#delete()} does if file/folder is not writable.
|
||||
*/
|
||||
public void recursiveDelete(File folder) {
|
||||
for (File f : folder.listFiles()) {
|
||||
if (f.isDirectory()) {
|
||||
recursiveDelete(folder);
|
||||
}
|
||||
f.delete();
|
||||
}
|
||||
}
|
||||
|
||||
private void buildAvdList() 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));
|
||||
} 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;
|
||||
}
|
||||
|
||||
File[] avds = 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
|
||||
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;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
for (File avd : avds) {
|
||||
AvdInfo info = parseAvdInfo(avd);
|
||||
if (info != null) {
|
||||
mAvdList.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) {
|
||||
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 = mSdk.getTargetFromHashString(targetHash);
|
||||
if (target == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// load the avd properties.
|
||||
File configIniFile = new File(avdPath, CONFIG_INI);
|
||||
Map<String, String> properties = SdkManager.parsePropertyFile(configIniFile, mSdkLog);
|
||||
|
||||
Matcher matcher = INI_NAME_PATTERN.matcher(path.getName());
|
||||
|
||||
AvdInfo info = new AvdInfo(
|
||||
matcher.matches() ? matcher.group(1) : path.getName(), // should not happen
|
||||
avdPath,
|
||||
target,
|
||||
properties);
|
||||
|
||||
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();
|
||||
|
||||
}
|
||||
|
||||
private boolean createSdCard(String toolLocation, String size, String location, ISdkLog log) {
|
||||
try {
|
||||
String[] command = new String[3];
|
||||
command[0] = toolLocation;
|
||||
command[1] = size;
|
||||
command[2] = location;
|
||||
Process process = Runtime.getRuntime().exec(command);
|
||||
|
||||
ArrayList<String> errorOutput = new ArrayList<String>();
|
||||
ArrayList<String> stdOutput = new ArrayList<String>();
|
||||
int status = grabProcessOutput(process, errorOutput, stdOutput,
|
||||
true /* waitForReaders */);
|
||||
|
||||
if (status != 0) {
|
||||
log.error(null, "Failed to create the SD card.");
|
||||
for (String error : errorOutput) {
|
||||
log.error(null, error);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (InterruptedException e) {
|
||||
log.error(null, "Failed to create the SD card.");
|
||||
} catch (IOException e) {
|
||||
log.error(null, "Failed to create the SD card.");
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the stderr/stdout outputs of a process and returns when the process is done.
|
||||
* Both <b>must</b> be read or the process will block on windows.
|
||||
* @param process The process to get the ouput from
|
||||
* @param errorOutput The array to store the stderr output. cannot be null.
|
||||
* @param stdOutput The array to store the stdout output. cannot be null.
|
||||
* @param waitforReaders if true, this will wait for the reader threads.
|
||||
* @return the process return code.
|
||||
* @throws InterruptedException
|
||||
*/
|
||||
private int grabProcessOutput(final Process process, final ArrayList<String> errorOutput,
|
||||
final ArrayList<String> stdOutput, boolean waitforReaders)
|
||||
throws InterruptedException {
|
||||
assert errorOutput != null;
|
||||
assert stdOutput != null;
|
||||
// read the lines as they come. if null is returned, it's
|
||||
// because the process finished
|
||||
Thread t1 = new Thread("") { //$NON-NLS-1$
|
||||
@Override
|
||||
public void run() {
|
||||
// create a buffer to read the stderr output
|
||||
InputStreamReader is = new InputStreamReader(process.getErrorStream());
|
||||
BufferedReader errReader = new BufferedReader(is);
|
||||
|
||||
try {
|
||||
while (true) {
|
||||
String line = errReader.readLine();
|
||||
if (line != null) {
|
||||
errorOutput.add(line);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
// do nothing.
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Thread t2 = new Thread("") { //$NON-NLS-1$
|
||||
@Override
|
||||
public void run() {
|
||||
InputStreamReader is = new InputStreamReader(process.getInputStream());
|
||||
BufferedReader outReader = new BufferedReader(is);
|
||||
|
||||
try {
|
||||
while (true) {
|
||||
String line = outReader.readLine();
|
||||
if (line != null) {
|
||||
stdOutput.add(line);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
// do nothing.
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
t1.start();
|
||||
t2.start();
|
||||
|
||||
// it looks like on windows process#waitFor() can return
|
||||
// before the thread have filled the arrays, so we wait for both threads and the
|
||||
// process itself.
|
||||
if (waitforReaders) {
|
||||
try {
|
||||
t1.join();
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
try {
|
||||
t2.join();
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
}
|
||||
|
||||
// get the return code from the process
|
||||
return process.waitFor();
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes an {@link AvdInfo} from the internal list.
|
||||
*
|
||||
* @param avdInfo The {@link AvdInfo} to remove.
|
||||
* @return true if this {@link AvdInfo} was present and has been removed.
|
||||
*/
|
||||
public boolean removeAvd(AvdInfo avdInfo) {
|
||||
return mAvdList.remove(avdInfo);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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.avd;
|
||||
|
||||
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 hardware definition file.
|
||||
* @param file 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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Copyright (C) 2009 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.sdklib.project;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
/**
|
||||
* Helper class to read and write Apk Configuration into a {@link ProjectProperties} file.
|
||||
*/
|
||||
public class ApkConfigurationHelper {
|
||||
/** Prefix for property names for config definition. This prevents having config named
|
||||
* after other valid properties such as "target". */
|
||||
final static String CONFIG_PREFIX = "apk-config-";
|
||||
|
||||
/**
|
||||
* Reads the Apk Configurations from a {@link ProjectProperties} file and returns them as a map.
|
||||
* <p/>If there are no defined configurations, the returned map will be empty.
|
||||
* @return a map of apk configurations. The map contains (name, filter) where name is
|
||||
* the name of the configuration (a-zA-Z0-9 only), and filter is the comma separated list of
|
||||
* resource configuration to include in the apk (see aapt -c)
|
||||
*/
|
||||
public static Map<String, String> getConfigs(ProjectProperties properties) {
|
||||
HashMap<String, String> configMap = new HashMap<String, String>();
|
||||
|
||||
// get the list of configs.
|
||||
String configList = properties.getProperty(ProjectProperties.PROPERTY_APK_CONFIGS);
|
||||
if (configList != null) {
|
||||
// this is a comma separated list
|
||||
String[] configs = configList.split(","); //$NON-NLS-1$
|
||||
|
||||
// read the value of each config and store it in a map
|
||||
for (String config : configs) {
|
||||
config = config.trim();
|
||||
String configValue = properties.getProperty(CONFIG_PREFIX + config);
|
||||
if (configValue != null) {
|
||||
configMap.put(config, configValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return configMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the Apk Configurations from a given map into a {@link ProjectProperties}.
|
||||
* @param properties the {@link ProjectProperties} in which to store the apk configurations.
|
||||
* @param configMap a map of apk configurations. The map contains (name, filter) where name is
|
||||
* the name of the configuration (a-zA-Z0-9 only), and filter is the comma separated list of
|
||||
* resource configuration to include in the apk (see aapt -c)
|
||||
* @return true if the {@link ProjectProperties} contained Apk Configuration that were not
|
||||
* present in the map.
|
||||
*/
|
||||
public static boolean setConfigs(ProjectProperties properties, Map<String, String> configMap) {
|
||||
// load the current configs, in order to remove the value properties for each of them
|
||||
// in case a config was removed.
|
||||
|
||||
// get the list of configs.
|
||||
String configList = properties.getProperty(ProjectProperties.PROPERTY_APK_CONFIGS);
|
||||
|
||||
boolean hasRemovedConfig = false;
|
||||
|
||||
if (configList != null) {
|
||||
// this is a comma separated list
|
||||
String[] configs = configList.split(","); //$NON-NLS-1$
|
||||
|
||||
for (String config : configs) {
|
||||
config = config.trim();
|
||||
if (configMap.containsKey(config) == false) {
|
||||
hasRemovedConfig = true;
|
||||
properties.removeProperty(CONFIG_PREFIX + config);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// now add the properties.
|
||||
Set<Entry<String, String>> entrySet = configMap.entrySet();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (Entry<String, String> entry : entrySet) {
|
||||
if (sb.length() > 0) {
|
||||
sb.append(",");
|
||||
}
|
||||
sb.append(entry.getKey());
|
||||
properties.setProperty(CONFIG_PREFIX + entry.getKey(), entry.getValue());
|
||||
}
|
||||
properties.setProperty(ProjectProperties.PROPERTY_APK_CONFIGS, sb.toString());
|
||||
|
||||
return hasRemovedConfig;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,742 @@
|
||||
/*
|
||||
* 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.project;
|
||||
|
||||
import com.android.sdklib.IAndroidTarget;
|
||||
import com.android.sdklib.ISdkLog;
|
||||
import com.android.sdklib.SdkConstants;
|
||||
import com.android.sdklib.project.ProjectProperties.PropertyType;
|
||||
|
||||
import org.w3c.dom.NodeList;
|
||||
import org.xml.sax.InputSource;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.xml.XMLConstants;
|
||||
import javax.xml.namespace.NamespaceContext;
|
||||
import javax.xml.xpath.XPath;
|
||||
import javax.xml.xpath.XPathConstants;
|
||||
import javax.xml.xpath.XPathExpressionException;
|
||||
import javax.xml.xpath.XPathFactory;
|
||||
|
||||
/**
|
||||
* Creates the basic files needed to get an Android project up and running. Also
|
||||
* allows creation of IntelliJ project files.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public class ProjectCreator {
|
||||
|
||||
/** Package path substitution string used in template files, i.e. "PACKAGE_PATH" */
|
||||
private final static String PH_JAVA_FOLDER = "PACKAGE_PATH";
|
||||
/** Package name substitution string used in template files, i.e. "PACKAGE" */
|
||||
private final static String PH_PACKAGE = "PACKAGE";
|
||||
/** Activity name substitution string used in template files, i.e. "ACTIVITY_NAME". */
|
||||
private final static String PH_ACTIVITY_NAME = "ACTIVITY_NAME";
|
||||
/** Project name substitution string used in template files, i.e. "PROJECT_NAME". */
|
||||
private final static String PH_PROJECT_NAME = "PROJECT_NAME";
|
||||
|
||||
private final static String FOLDER_TESTS = "tests";
|
||||
|
||||
public enum OutputLevel {
|
||||
/** Silent mode. Project creation will only display errors. */
|
||||
SILENT,
|
||||
/** Normal mode. Project creation will display what's being done, display
|
||||
* error but not warnings. */
|
||||
NORMAL,
|
||||
/** Verbose mode. Project creation will display what's being done, errors and warnings. */
|
||||
VERBOSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Exception thrown when a project creation fails, typically because a template
|
||||
* file cannot be written.
|
||||
*/
|
||||
private static class ProjectCreateException extends Exception {
|
||||
/** default UID. This will not be serialized anyway. */
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
ProjectCreateException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
ProjectCreateException(Throwable t, String format, Object... args) {
|
||||
super(format != null ? String.format(format, args) : format, t);
|
||||
}
|
||||
|
||||
ProjectCreateException(String format, Object... args) {
|
||||
super(String.format(format, args));
|
||||
}
|
||||
}
|
||||
|
||||
private final OutputLevel mLevel;
|
||||
|
||||
private final ISdkLog mLog;
|
||||
private final String mSdkFolder;
|
||||
|
||||
public ProjectCreator(String sdkFolder, OutputLevel level, ISdkLog log) {
|
||||
mSdkFolder = sdkFolder;
|
||||
mLevel = level;
|
||||
mLog = log;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new project.
|
||||
*
|
||||
* @param folderPath the folder of the project to create.
|
||||
* @param projectName the name of the project.
|
||||
* @param packageName the package of the project.
|
||||
* @param activityName the activity of the project as it will appear in the manifest.
|
||||
* @param target the project target.
|
||||
* @param isTestProject whether the project to create is a test project.
|
||||
*/
|
||||
public void createProject(String folderPath, String projectName,
|
||||
String packageName, String activityName, IAndroidTarget target,
|
||||
boolean isTestProject) {
|
||||
|
||||
// create project folder if it does not exist
|
||||
File projectFolder = new File(folderPath);
|
||||
if (!projectFolder.exists()) {
|
||||
|
||||
boolean created = false;
|
||||
Throwable t = null;
|
||||
try {
|
||||
created = projectFolder.mkdirs();
|
||||
} catch (Exception e) {
|
||||
t = e;
|
||||
}
|
||||
|
||||
if (created) {
|
||||
println("Created project directory: %1$s", projectFolder);
|
||||
} else {
|
||||
mLog.error(t, "Could not create directory: %1$s", projectFolder);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
Exception e = null;
|
||||
String error = null;
|
||||
try {
|
||||
String[] content = projectFolder.list();
|
||||
if (content == null) {
|
||||
error = "Project folder '%1$s' is not a directory.";
|
||||
} else if (content.length != 0) {
|
||||
error = "Project folder '%1$s' is not empty. Please consider using '%2$s update' instead.";
|
||||
}
|
||||
} catch (Exception e1) {
|
||||
e = e1;
|
||||
}
|
||||
|
||||
if (e != null || error != null) {
|
||||
mLog.error(e, error, projectFolder, SdkConstants.androidCmdName());
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
// first create the project properties.
|
||||
|
||||
// location of the SDK goes in localProperty
|
||||
ProjectProperties localProperties = ProjectProperties.create(folderPath,
|
||||
PropertyType.LOCAL);
|
||||
localProperties.setProperty(ProjectProperties.PROPERTY_SDK, mSdkFolder);
|
||||
localProperties.save();
|
||||
|
||||
// target goes in default properties
|
||||
ProjectProperties defaultProperties = ProjectProperties.create(folderPath,
|
||||
PropertyType.DEFAULT);
|
||||
defaultProperties.setAndroidTarget(target);
|
||||
defaultProperties.save();
|
||||
|
||||
// create an empty build.properties
|
||||
ProjectProperties buildProperties = ProjectProperties.create(folderPath,
|
||||
PropertyType.BUILD);
|
||||
buildProperties.save();
|
||||
|
||||
// create the map for place-holders of values to replace in the templates
|
||||
final HashMap<String, String> keywords = new HashMap<String, String>();
|
||||
|
||||
// create the required folders.
|
||||
// compute src folder path
|
||||
final String packagePath =
|
||||
stripString(packageName.replace(".", File.separator),
|
||||
File.separatorChar);
|
||||
|
||||
// put this path in the place-holder map for project files that needs to list
|
||||
// files manually.
|
||||
keywords.put(PH_JAVA_FOLDER, packagePath);
|
||||
|
||||
keywords.put(PH_PACKAGE, packageName);
|
||||
if (activityName != null) {
|
||||
keywords.put(PH_ACTIVITY_NAME, activityName);
|
||||
}
|
||||
|
||||
// Take the project name from the command line if there's one
|
||||
if (projectName != null) {
|
||||
keywords.put(PH_PROJECT_NAME, projectName);
|
||||
} else {
|
||||
if (activityName != null) {
|
||||
// Use the activity as project name
|
||||
keywords.put(PH_PROJECT_NAME, activityName);
|
||||
} else {
|
||||
// We need a project name. Just pick up the basename of the project
|
||||
// directory.
|
||||
projectName = projectFolder.getName();
|
||||
keywords.put(PH_PROJECT_NAME, projectName);
|
||||
}
|
||||
}
|
||||
|
||||
// create the source folder and the java package folders.
|
||||
String srcFolderPath = SdkConstants.FD_SOURCES + File.separator + packagePath;
|
||||
File sourceFolder = createDirs(projectFolder, srcFolderPath);
|
||||
String javaTemplate = "java_file.template";
|
||||
String activityFileName = activityName + ".java";
|
||||
if (isTestProject) {
|
||||
javaTemplate = "java_tests_file.template";
|
||||
activityFileName = activityName + "Test.java";
|
||||
}
|
||||
installTemplate(javaTemplate, new File(sourceFolder, activityFileName),
|
||||
keywords, target);
|
||||
|
||||
// create the generate source folder
|
||||
srcFolderPath = SdkConstants.FD_GEN_SOURCES + File.separator + packagePath;
|
||||
sourceFolder = createDirs(projectFolder, srcFolderPath);
|
||||
|
||||
// create other useful folders
|
||||
File resourceFodler = createDirs(projectFolder, SdkConstants.FD_RESOURCES);
|
||||
createDirs(projectFolder, SdkConstants.FD_OUTPUT);
|
||||
createDirs(projectFolder, SdkConstants.FD_NATIVE_LIBS);
|
||||
|
||||
if (isTestProject == false) {
|
||||
/* Make res files only for non test projects */
|
||||
File valueFolder = createDirs(resourceFodler, SdkConstants.FD_VALUES);
|
||||
installTemplate("strings.template", new File(valueFolder, "strings.xml"),
|
||||
keywords, target);
|
||||
|
||||
File layoutFolder = createDirs(resourceFodler, SdkConstants.FD_LAYOUT);
|
||||
installTemplate("layout.template", new File(layoutFolder, "main.xml"),
|
||||
keywords, target);
|
||||
}
|
||||
|
||||
/* Make AndroidManifest.xml and build.xml files */
|
||||
String manifestTemplate = "AndroidManifest.template";
|
||||
if (isTestProject) {
|
||||
manifestTemplate = "AndroidManifest.tests.template";
|
||||
}
|
||||
|
||||
installTemplate(manifestTemplate,
|
||||
new File(projectFolder, SdkConstants.FN_ANDROID_MANIFEST_XML),
|
||||
keywords, target);
|
||||
|
||||
installTemplate("build.template",
|
||||
new File(projectFolder, SdkConstants.FN_BUILD_XML),
|
||||
keywords);
|
||||
|
||||
// if this is not a test project, then we create one.
|
||||
if (isTestProject == false) {
|
||||
// create the test project folder.
|
||||
createDirs(projectFolder, FOLDER_TESTS);
|
||||
File testProjectFolder = new File(folderPath, FOLDER_TESTS);
|
||||
|
||||
createProject(testProjectFolder.getAbsolutePath(), projectName, packageName,
|
||||
activityName, target, true /*isTestProject*/);
|
||||
}
|
||||
} catch (ProjectCreateException e) {
|
||||
mLog.error(e, null);
|
||||
} catch (IOException e) {
|
||||
mLog.error(e, null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates an existing project.
|
||||
* <p/>
|
||||
* Workflow:
|
||||
* <ul>
|
||||
* <li> Check AndroidManifest.xml is present (required)
|
||||
* <li> Check there's a default.properties with a target *or* --target was specified
|
||||
* <li> Update default.prop if --target was specified
|
||||
* <li> Refresh/create "sdk" in local.properties
|
||||
* <li> Build.xml: create if not present or no <androidinit(\w|/>) in it
|
||||
* </ul>
|
||||
*
|
||||
* @param folderPath the folder of the project to update. This folder must exist.
|
||||
* @param target the project target. Can be null.
|
||||
* @param projectName The project name from --name. Can be null.
|
||||
*/
|
||||
public void updateProject(String folderPath, IAndroidTarget target, String projectName ) {
|
||||
// project folder must exist and be a directory, since this is an update
|
||||
File projectFolder = new File(folderPath);
|
||||
if (!projectFolder.isDirectory()) {
|
||||
mLog.error(null, "Project folder '%1$s' is not a valid directory, this is not an Android project you can update.",
|
||||
projectFolder);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check AndroidManifest.xml is present
|
||||
File androidManifest = new File(projectFolder, SdkConstants.FN_ANDROID_MANIFEST_XML);
|
||||
if (!androidManifest.isFile()) {
|
||||
mLog.error(null,
|
||||
"%1$s not found in '%2$s', this is not an Android project you can update.",
|
||||
SdkConstants.FN_ANDROID_MANIFEST_XML,
|
||||
folderPath);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check there's a default.properties with a target *or* --target was specified
|
||||
ProjectProperties props = ProjectProperties.load(folderPath, PropertyType.DEFAULT);
|
||||
if (props == null || props.getProperty(ProjectProperties.PROPERTY_TARGET) == null) {
|
||||
if (target == null) {
|
||||
mLog.error(null,
|
||||
"There is no %1$s file in '%2$s'. Please provide a --target to the '%3$s update' command.",
|
||||
PropertyType.DEFAULT.getFilename(),
|
||||
folderPath,
|
||||
SdkConstants.androidCmdName());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Update default.prop if --target was specified
|
||||
if (target != null) {
|
||||
// we already attempted to load the file earlier, if that failed, create it.
|
||||
if (props == null) {
|
||||
props = ProjectProperties.create(folderPath, PropertyType.DEFAULT);
|
||||
}
|
||||
|
||||
// set or replace the target
|
||||
props.setAndroidTarget(target);
|
||||
try {
|
||||
props.save();
|
||||
println("Updated %1$s", PropertyType.DEFAULT.getFilename());
|
||||
} catch (IOException e) {
|
||||
mLog.error(e, "Failed to write %1$s file in '%2$s'",
|
||||
PropertyType.DEFAULT.getFilename(),
|
||||
folderPath);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Refresh/create "sdk" in local.properties
|
||||
// because the file may already exists and contain other values (like apk config),
|
||||
// we first try to load it.
|
||||
props = ProjectProperties.load(folderPath, PropertyType.LOCAL);
|
||||
if (props == null) {
|
||||
props = ProjectProperties.create(folderPath, PropertyType.LOCAL);
|
||||
}
|
||||
|
||||
// set or replace the sdk location.
|
||||
props.setProperty(ProjectProperties.PROPERTY_SDK, mSdkFolder);
|
||||
try {
|
||||
props.save();
|
||||
println("Updated %1$s", PropertyType.LOCAL.getFilename());
|
||||
} catch (IOException e) {
|
||||
mLog.error(e, "Failed to write %1$s file in '%2$s'",
|
||||
PropertyType.LOCAL.getFilename(),
|
||||
folderPath);
|
||||
return;
|
||||
}
|
||||
|
||||
// Build.xml: create if not present or no <androidinit/> in it
|
||||
File buildXml = new File(projectFolder, SdkConstants.FN_BUILD_XML);
|
||||
boolean needsBuildXml = projectName != null || !buildXml.exists();
|
||||
if (!needsBuildXml) {
|
||||
// Note that "<androidinit" must be followed by either a whitespace, a "/" (for the
|
||||
// XML /> closing tag) or an end-of-line. This way we know the XML tag is really this
|
||||
// one and later we will be able to use an "androidinit2" tag or such as necessary.
|
||||
needsBuildXml = !checkFileContainsRegexp(buildXml, "<androidinit(?:\\s|/|$)");
|
||||
if (needsBuildXml) {
|
||||
println("File %1$s is too old and needs to be updated.", SdkConstants.FN_BUILD_XML);
|
||||
}
|
||||
}
|
||||
|
||||
if (needsBuildXml) {
|
||||
// create the map for place-holders of values to replace in the templates
|
||||
final HashMap<String, String> keywords = new HashMap<String, String>();
|
||||
|
||||
// Take the project name from the command line if there's one
|
||||
if (projectName != null) {
|
||||
keywords.put(PH_PROJECT_NAME, projectName);
|
||||
} else {
|
||||
extractPackageFromManifest(androidManifest, keywords);
|
||||
if (keywords.containsKey(PH_ACTIVITY_NAME)) {
|
||||
// Use the activity as project name
|
||||
keywords.put(PH_PROJECT_NAME, keywords.get(PH_ACTIVITY_NAME));
|
||||
} else {
|
||||
// We need a project name. Just pick up the basename of the project
|
||||
// directory.
|
||||
projectName = projectFolder.getName();
|
||||
keywords.put(PH_PROJECT_NAME, projectName);
|
||||
}
|
||||
}
|
||||
|
||||
if (mLevel == OutputLevel.VERBOSE) {
|
||||
println("Regenerating %1$s with project name %2$s",
|
||||
SdkConstants.FN_BUILD_XML,
|
||||
keywords.get(PH_PROJECT_NAME));
|
||||
}
|
||||
|
||||
try {
|
||||
installTemplate("build.template",
|
||||
new File(projectFolder, SdkConstants.FN_BUILD_XML),
|
||||
keywords);
|
||||
} catch (ProjectCreateException e) {
|
||||
mLog.error(e, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if any line of the input file contains the requested regexp.
|
||||
*/
|
||||
private boolean checkFileContainsRegexp(File file, String regexp) {
|
||||
Pattern p = Pattern.compile(regexp);
|
||||
|
||||
try {
|
||||
BufferedReader in = new BufferedReader(new FileReader(file));
|
||||
String line;
|
||||
|
||||
while ((line = in.readLine()) != null) {
|
||||
if (p.matcher(line).find()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
in.close();
|
||||
} catch (Exception e) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts a "full" package & activity name from an AndroidManifest.xml.
|
||||
* <p/>
|
||||
* The keywords dictionary is always filed the package name under the key {@link #PH_PACKAGE}.
|
||||
* If an activity name can be found, it is filed under the key {@link #PH_ACTIVITY_NAME}.
|
||||
* When no activity is found, this key is not created.
|
||||
*
|
||||
* @param manifestFile The AndroidManifest.xml file
|
||||
* @param outKeywords Place where to put the out parameters: package and activity names.
|
||||
* @return True if the package/activity was parsed and updated in the keyword dictionary.
|
||||
*/
|
||||
private boolean extractPackageFromManifest(File manifestFile,
|
||||
Map<String, String> outKeywords) {
|
||||
try {
|
||||
final String nsPrefix = "android";
|
||||
final String nsURI = SdkConstants.NS_RESOURCES;
|
||||
|
||||
XPath xpath = XPathFactory.newInstance().newXPath();
|
||||
|
||||
xpath.setNamespaceContext(new NamespaceContext() {
|
||||
public String getNamespaceURI(String prefix) {
|
||||
if (nsPrefix.equals(prefix)) {
|
||||
return nsURI;
|
||||
}
|
||||
return XMLConstants.NULL_NS_URI;
|
||||
}
|
||||
|
||||
public String getPrefix(String namespaceURI) {
|
||||
if (nsURI.equals(namespaceURI)) {
|
||||
return nsPrefix;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public Iterator getPrefixes(String namespaceURI) {
|
||||
if (nsURI.equals(namespaceURI)) {
|
||||
ArrayList<String> list = new ArrayList<String>();
|
||||
list.add(nsPrefix);
|
||||
return list.iterator();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
InputSource source = new InputSource(new FileReader(manifestFile));
|
||||
String packageName = xpath.evaluate("/manifest/@package", source);
|
||||
|
||||
source = new InputSource(new FileReader(manifestFile));
|
||||
|
||||
// Select the "android:name" attribute of all <activity> nodes but only if they
|
||||
// contain a sub-node <intent-filter><action> with an "android:name" attribute which
|
||||
// is 'android.intent.action.MAIN' and an <intent-filter><category> with an
|
||||
// "android:name" attribute which is 'android.intent.category.LAUNCHER'
|
||||
String expression = String.format("/manifest/application/activity" +
|
||||
"[intent-filter/action/@%1$s:name='android.intent.action.MAIN' and " +
|
||||
"intent-filter/category/@%1$s:name='android.intent.category.LAUNCHER']" +
|
||||
"/@%1$s:name", nsPrefix);
|
||||
|
||||
NodeList activityNames = (NodeList) xpath.evaluate(expression, source,
|
||||
XPathConstants.NODESET);
|
||||
|
||||
// If we get here, both XPath expressions were valid so we're most likely dealing
|
||||
// with an actual AndroidManifest.xml file. The nodes may not have the requested
|
||||
// attributes though, if which case we should warn.
|
||||
|
||||
if (packageName == null || packageName.length() == 0) {
|
||||
mLog.error(null,
|
||||
"Missing <manifest package=\"...\"> in '%1$s'",
|
||||
manifestFile.getName());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the first activity that matched earlier. If there is no activity,
|
||||
// activityName is set to an empty string and the generated "combined" name
|
||||
// will be in the form "package." (with a dot at the end).
|
||||
String activityName = "";
|
||||
if (activityNames.getLength() > 0) {
|
||||
activityName = activityNames.item(0).getNodeValue();
|
||||
}
|
||||
|
||||
if (mLevel == OutputLevel.VERBOSE && activityNames.getLength() > 1) {
|
||||
println("WARNING: There is more than one activity defined in '%1$s'.\n" +
|
||||
"Only the first one will be used. If this is not appropriate, you need\n" +
|
||||
"to specify one of these values manually instead:",
|
||||
manifestFile.getName());
|
||||
|
||||
for (int i = 0; i < activityNames.getLength(); i++) {
|
||||
String name = activityNames.item(i).getNodeValue();
|
||||
name = combinePackageActivityNames(packageName, name);
|
||||
println("- %1$s", name);
|
||||
}
|
||||
}
|
||||
|
||||
if (activityName.length() == 0) {
|
||||
mLog.warning("Missing <activity %1$s:name=\"...\"> in '%2$s'.\n" +
|
||||
"No activity will be generated.",
|
||||
nsPrefix, manifestFile.getName());
|
||||
} else {
|
||||
outKeywords.put(PH_ACTIVITY_NAME, activityName);
|
||||
}
|
||||
|
||||
outKeywords.put(PH_PACKAGE, packageName);
|
||||
return true;
|
||||
|
||||
} catch (IOException e) {
|
||||
mLog.error(e, "Failed to read %1$s", manifestFile.getName());
|
||||
} catch (XPathExpressionException e) {
|
||||
Throwable t = e.getCause();
|
||||
mLog.error(t == null ? e : t,
|
||||
"Failed to parse %1$s",
|
||||
manifestFile.getName());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private String combinePackageActivityNames(String packageName, String activityName) {
|
||||
// Activity Name can have 3 forms:
|
||||
// - ".Name" means this is a class name in the given package name.
|
||||
// The full FQCN is thus packageName + ".Name"
|
||||
// - "Name" is an older variant of the former. Full FQCN is packageName + "." + "Name"
|
||||
// - "com.blah.Name" is a full FQCN. Ignore packageName and use activityName as-is.
|
||||
// To be valid, the package name should have at least two components. This is checked
|
||||
// later during the creation of the build.xml file, so we just need to detect there's
|
||||
// a dot but not at pos==0.
|
||||
|
||||
int pos = activityName.indexOf('.');
|
||||
if (pos == 0) {
|
||||
return packageName + activityName;
|
||||
} else if (pos > 0) {
|
||||
return activityName;
|
||||
} else {
|
||||
return packageName + "." + activityName;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Installs a new file that is based on a template file provided by a given target.
|
||||
* Each match of each key from the place-holder map in the template will be replaced with its
|
||||
* corresponding value in the created file.
|
||||
*
|
||||
* @param templateName the name of to the template file
|
||||
* @param destFile the path to the destination file, relative to the project
|
||||
* @param placeholderMap a map of (place-holder, value) to create the file from the template.
|
||||
* @param target the Target of the project that will be providing the template.
|
||||
* @throws ProjectCreateException
|
||||
*/
|
||||
private void installTemplate(String templateName, File destFile,
|
||||
Map<String, String> placeholderMap, IAndroidTarget target)
|
||||
throws ProjectCreateException {
|
||||
// query the target for its template directory
|
||||
String templateFolder = target.getPath(IAndroidTarget.TEMPLATES);
|
||||
final String sourcePath = templateFolder + File.separator + templateName;
|
||||
|
||||
installFullPathTemplate(sourcePath, destFile, placeholderMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Installs a new file that is based on a template file provided by the tools folder.
|
||||
* Each match of each key from the place-holder map in the template will be replaced with its
|
||||
* corresponding value in the created file.
|
||||
*
|
||||
* @param templateName the name of to the template file
|
||||
* @param destFile the path to the destination file, relative to the project
|
||||
* @param placeholderMap a map of (place-holder, value) to create the file from the template.
|
||||
* @throws ProjectCreateException
|
||||
*/
|
||||
private void installTemplate(String templateName, File destFile,
|
||||
Map<String, String> placeholderMap)
|
||||
throws ProjectCreateException {
|
||||
// query the target for its template directory
|
||||
String templateFolder = mSdkFolder + File.separator + SdkConstants.OS_SDK_TOOLS_LIB_FOLDER;
|
||||
final String sourcePath = templateFolder + File.separator + templateName;
|
||||
|
||||
installFullPathTemplate(sourcePath, destFile, placeholderMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Installs a new file that is based on a template.
|
||||
* Each match of each key from the place-holder map in the template will be replaced with its
|
||||
* corresponding value in the created file.
|
||||
*
|
||||
* @param sourcePath the full path to the source template file
|
||||
* @param destFile the destination file
|
||||
* @param placeholderMap a map of (place-holder, value) to create the file from the template.
|
||||
* @throws ProjectCreateException
|
||||
*/
|
||||
private void installFullPathTemplate(String sourcePath, File destFile,
|
||||
Map<String, String> placeholderMap) throws ProjectCreateException {
|
||||
|
||||
boolean existed = destFile.exists();
|
||||
|
||||
try {
|
||||
BufferedWriter out = new BufferedWriter(new FileWriter(destFile));
|
||||
BufferedReader in = new BufferedReader(new FileReader(sourcePath));
|
||||
String line;
|
||||
|
||||
while ((line = in.readLine()) != null) {
|
||||
for (String key : placeholderMap.keySet()) {
|
||||
line = line.replace(key, placeholderMap.get(key));
|
||||
}
|
||||
|
||||
out.write(line);
|
||||
out.newLine();
|
||||
}
|
||||
|
||||
out.close();
|
||||
in.close();
|
||||
} catch (Exception e) {
|
||||
throw new ProjectCreateException(e, "Could not access %1$s: %2$s",
|
||||
destFile, e.getMessage());
|
||||
}
|
||||
|
||||
println("%1$s file %2$s",
|
||||
existed ? "Updated" : "Added",
|
||||
destFile);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints a message unless silence is enabled.
|
||||
* <p/>
|
||||
* This is just a convenience wrapper around {@link ISdkLog#printf(String, Object...)} from
|
||||
* {@link #mLog} after testing if ouput level is {@link OutputLevel#VERBOSE}.
|
||||
*
|
||||
* @param format Format for String.format
|
||||
* @param args Arguments for String.format
|
||||
*/
|
||||
private void println(String format, Object... args) {
|
||||
if (mLevel != OutputLevel.SILENT) {
|
||||
if (!format.endsWith("\n")) {
|
||||
format += "\n";
|
||||
}
|
||||
mLog.printf(format, args);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new folder, along with any parent folders that do not exists.
|
||||
*
|
||||
* @param parent the parent folder
|
||||
* @param name the name of the directory to create.
|
||||
* @throws ProjectCreateException
|
||||
*/
|
||||
private File createDirs(File parent, String name) throws ProjectCreateException {
|
||||
final File newFolder = new File(parent, name);
|
||||
boolean existedBefore = true;
|
||||
|
||||
if (!newFolder.exists()) {
|
||||
if (!newFolder.mkdirs()) {
|
||||
throw new ProjectCreateException("Could not create directory: %1$s", newFolder);
|
||||
}
|
||||
existedBefore = false;
|
||||
}
|
||||
|
||||
if (newFolder.isDirectory()) {
|
||||
if (!newFolder.canWrite()) {
|
||||
throw new ProjectCreateException("Path is not writable: %1$s", newFolder);
|
||||
}
|
||||
} else {
|
||||
throw new ProjectCreateException("Path is not a directory: %1$s", newFolder);
|
||||
}
|
||||
|
||||
if (!existedBefore) {
|
||||
try {
|
||||
println("Created directory %1$s", newFolder.getCanonicalPath());
|
||||
} catch (IOException e) {
|
||||
throw new ProjectCreateException(
|
||||
"Could not determine canonical path of created directory", e);
|
||||
}
|
||||
}
|
||||
|
||||
return newFolder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Strips the string of beginning and trailing characters (multiple
|
||||
* characters will be stripped, example stripString("..test...", '.')
|
||||
* results in "test";
|
||||
*
|
||||
* @param s the string to strip
|
||||
* @param strip the character to strip from beginning and end
|
||||
* @return the stripped string or the empty string if everything is stripped.
|
||||
*/
|
||||
private static String stripString(String s, char strip) {
|
||||
final int sLen = s.length();
|
||||
int newStart = 0, newEnd = sLen - 1;
|
||||
|
||||
while (newStart < sLen && s.charAt(newStart) == strip) {
|
||||
newStart++;
|
||||
}
|
||||
while (newEnd >= 0 && s.charAt(newEnd) == strip) {
|
||||
newEnd--;
|
||||
}
|
||||
|
||||
/*
|
||||
* newEnd contains a char we want, and substring takes end as being
|
||||
* exclusive
|
||||
*/
|
||||
newEnd++;
|
||||
|
||||
if (newStart >= sLen || newEnd < 0) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return s.substring(newStart, newEnd);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,263 @@
|
||||
/*
|
||||
* 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.IAndroidTarget;
|
||||
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_APK_CONFIGS = "apk-configurations";
|
||||
public final static String PROPERTY_SDK = "sdk-location";
|
||||
|
||||
public static enum PropertyType {
|
||||
BUILD("build.properties", BUILD_HEADER),
|
||||
DEFAULT("default.properties", DEFAULT_HEADER),
|
||||
LOCAL("local.properties", LOCAL_HEADER);
|
||||
|
||||
private final String mFilename;
|
||||
private final String mHeader;
|
||||
|
||||
PropertyType(String filename, String header) {
|
||||
mFilename = filename;
|
||||
mHeader = header;
|
||||
}
|
||||
|
||||
public String getFilename() {
|
||||
return mFilename;
|
||||
}
|
||||
}
|
||||
|
||||
private final static String LOCAL_HEADER =
|
||||
// 1-------10--------20--------30--------40--------50--------60--------70--------80
|
||||
"# This file is automatically generated by Android Tools.\n" +
|
||||
"# Do not modify this file -- YOUR CHANGES WILL BE ERASED!\n" +
|
||||
"# \n" +
|
||||
"# This file must *NOT* be checked in Version Control Systems,\n" +
|
||||
"# as it contains information specific to your local configuration.\n" +
|
||||
"\n";
|
||||
|
||||
private final static String DEFAULT_HEADER =
|
||||
// 1-------10--------20--------30--------40--------50--------60--------70--------80
|
||||
"# This file is automatically generated by Android Tools.\n" +
|
||||
"# Do not modify this file -- YOUR CHANGES WILL BE ERASED!\n" +
|
||||
"# \n" +
|
||||
"# This file must be checked in Version Control Systems.\n" +
|
||||
"# \n" +
|
||||
"# To customize properties used by the Ant build system use,\n" +
|
||||
"# \"build.properties\", and override values to adapt the script to your\n" +
|
||||
"# project structure.\n" +
|
||||
"\n";
|
||||
|
||||
private final static String BUILD_HEADER =
|
||||
// 1-------10--------20--------30--------40--------50--------60--------70--------80
|
||||
"# This file is used to override default values used by the Ant build system.\n" +
|
||||
"# \n" +
|
||||
"# This file must be checked in Version Control Systems, as it is\n" +
|
||||
"# integral to the build system of your project.\n" +
|
||||
"\n" +
|
||||
"# The name of your application package as defined in the manifest.\n" +
|
||||
"# Used by the 'uninstall' rule.\n"+
|
||||
"#application-package=com.example.myproject\n" +
|
||||
"\n" +
|
||||
"# The name of the source folder.\n" +
|
||||
"#source-folder=src\n" +
|
||||
"\n" +
|
||||
"# The name of the output folder.\n" +
|
||||
"#out-folder=bin\n" +
|
||||
"\n";
|
||||
|
||||
private final static Map<String, String> COMMENT_MAP = new HashMap<String, String>();
|
||||
static {
|
||||
// 1-------10--------20--------30--------40--------50--------60--------70--------80
|
||||
COMMENT_MAP.put(PROPERTY_TARGET,
|
||||
"# Project target.\n");
|
||||
COMMENT_MAP.put(PROPERTY_APK_CONFIGS,
|
||||
"# apk configurations. This property allows creation of APK files with limited\n" +
|
||||
"# resources. For example, if your application contains many locales and\n" +
|
||||
"# you wish to release multiple smaller apks instead of a large one, you can\n" +
|
||||
"# define configuration to create apks with limited language sets.\n" +
|
||||
"# Format is a comma separated list of configuration names. For each\n" +
|
||||
"# configuration, a property will declare the resource configurations to\n" +
|
||||
"# include. Example:\n" +
|
||||
"# " + PROPERTY_APK_CONFIGS +"=european,northamerica\n" +
|
||||
"# " + ApkConfigurationHelper.CONFIG_PREFIX + "european=en,fr,it,de,es\n" +
|
||||
"# " + ApkConfigurationHelper.CONFIG_PREFIX + "northamerica=en,es\n");
|
||||
COMMENT_MAP.put(PROPERTY_SDK,
|
||||
"# location of the SDK. This is only used by Ant\n" +
|
||||
"# For customization when using a Version Control System, please read the\n" +
|
||||
"# header note.\n");
|
||||
}
|
||||
|
||||
private final String mProjectFolderOsPath;
|
||||
private final Map<String, String> mProperties;
|
||||
private final PropertyType mType;
|
||||
|
||||
/**
|
||||
* Loads a project properties file and return a {@link ProjectProperties} object
|
||||
* containing the properties
|
||||
*
|
||||
* @param projectFolderOsPath the project folder.
|
||||
* @param type One the possible {@link PropertyType}s.
|
||||
*/
|
||||
public static ProjectProperties load(String projectFolderOsPath, PropertyType type) {
|
||||
File projectFolder = new File(projectFolderOsPath);
|
||||
if (projectFolder.isDirectory()) {
|
||||
File defaultFile = new File(projectFolder, type.mFilename);
|
||||
if (defaultFile.isFile()) {
|
||||
Map<String, String> map = SdkManager.parsePropertyFile(defaultFile, null /* log */);
|
||||
if (map != null) {
|
||||
return new ProjectProperties(projectFolderOsPath, map, type);
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges all properties from the given file into the current properties.
|
||||
* <p/>
|
||||
* This emulates the Ant behavior: existing properties are <em>not</em> overriden.
|
||||
* Only new undefined properties become defined.
|
||||
* <p/>
|
||||
* Typical usage:
|
||||
* <ul>
|
||||
* <li>Create a ProjectProperties with {@link PropertyType#BUILD}
|
||||
* <li>Merge in values using {@link PropertyType#DEFAULT}
|
||||
* <li>The result is that this contains all the properties from default plus those
|
||||
* overridden by the build.properties file.
|
||||
* </ul>
|
||||
*
|
||||
* @param type One the possible {@link PropertyType}s.
|
||||
* @return this object, for chaining.
|
||||
*/
|
||||
public ProjectProperties merge(PropertyType type) {
|
||||
File projectFolder = new File(mProjectFolderOsPath);
|
||||
if (projectFolder.isDirectory()) {
|
||||
File defaultFile = new File(projectFolder, type.mFilename);
|
||||
if (defaultFile.isFile()) {
|
||||
Map<String, String> map = SdkManager.parsePropertyFile(defaultFile, null /* log */);
|
||||
if (map != null) {
|
||||
for(Entry<String, String> entry : map.entrySet()) {
|
||||
String key = entry.getKey();
|
||||
String value = entry.getValue();
|
||||
if (!mProperties.containsKey(key) && value != null) {
|
||||
mProperties.put(key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new project properties object, with no properties.
|
||||
* <p/>The file is not created until {@link #save()} is called.
|
||||
* @param projectFolderOsPath the project folder.
|
||||
* @param type
|
||||
*/
|
||||
public static ProjectProperties create(String projectFolderOsPath, PropertyType type) {
|
||||
// create and return a ProjectProperties with an empty map.
|
||||
return new ProjectProperties(projectFolderOsPath, new HashMap<String, String>(), type);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the target property to the given {@link IAndroidTarget} object.
|
||||
* @param target the Android target.
|
||||
*/
|
||||
public void setAndroidTarget(IAndroidTarget target) {
|
||||
assert mType == PropertyType.DEFAULT;
|
||||
mProperties.put(PROPERTY_TARGET, target.hashString());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a property and returns its previous value (or null if the property did not exist).
|
||||
* @param name the name of the property to remove.
|
||||
*/
|
||||
public String removeProperty(String name) {
|
||||
return mProperties.remove(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the property file.
|
||||
* @throws IOException
|
||||
*/
|
||||
public void save() throws IOException {
|
||||
File toSave = new File(mProjectFolderOsPath, mType.mFilename);
|
||||
|
||||
FileWriter writer = new FileWriter(toSave);
|
||||
|
||||
// write the header
|
||||
writer.write(mType.mHeader);
|
||||
|
||||
// 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.
|
||||
* <p/>
|
||||
* Use {@link #load(String, PropertyType)} or {@link #create(String, PropertyType)}
|
||||
* to instantiate.
|
||||
*/
|
||||
private ProjectProperties(String projectFolderOsPath, Map<String, String> map,
|
||||
PropertyType type) {
|
||||
mProjectFolderOsPath = projectFolderOsPath;
|
||||
mProperties = map;
|
||||
mType = type;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user