/* * Copyright (C) 2009 Jayway AB * * 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.jayway.maven.plugins.android; import org.apache.maven.plugin.MojoExecutionException; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.util.*; /** * Represents an Android SDK. * * @author hugo.josefson@jayway.com * @author Manfred Moser <manfred@simpligility.com> */ public class AndroidSdk { /** * property file in each platform folder with details about platform. */ private static final String SOURCE_PROPERTIES_FILENAME = "source.properties"; /** * property name for platform version in sdk source.properties file. */ private static final String PLATFORM_VERSION_PROPERTY = "Platform.Version"; /** * property name for api level version in sdk source.properties file. */ private static final String API_LEVEL_PROPERTY = "AndroidVersion.ApiLevel"; /** * folder name for the sdk sub folder that contains the different platform versions. */ private static final String PLATFORMS_FOLDER_NAME = "platforms"; private static final String PARAMETER_MESSAGE = "Please provide a proper Android SDK directory path as configuration parameter <sdk><path>...</path></sdk> in the plugin <configuration/>. As an alternative, you may add the parameter to commandline: -Dandroid.sdk.path=... or set environment variable " + AbstractAndroidMojo.ENV_ANDROID_HOME + "."; private static final class Platform { final String name; final String apiLevel; final String path; public Platform(String name, String apiLevel, String path) { super(); this.name = name; this.apiLevel = apiLevel; this.path = path; } } private final File sdkPath; private final Platform platform; private Set<Platform> availablePlatforms; public AndroidSdk(File sdkPath, String platformOrApiLevel) { this.sdkPath = sdkPath; findAvailablePlatforms(); if (platformOrApiLevel == null) { platform = null; // letting this through to preserve compatibility for now } else { platform = findPlatformByNameOrApiLevel(platformOrApiLevel); if (platform == null) throw new InvalidSdkException("Invalid SDK: Platform/API level " + platformOrApiLevel + " not available."); } } private Platform findPlatformByNameOrApiLevel(String platformOrApiLevel) { for (Platform p : availablePlatforms) { if (p.name.equals(platformOrApiLevel) || p.apiLevel.equals(platformOrApiLevel)) { return p; } } return null; } public enum Layout {LAYOUT_1_1, LAYOUT_1_5} ; public Layout getLayout() { assertPathIsDirectory(sdkPath); final File platforms = new File(sdkPath, PLATFORMS_FOLDER_NAME); if (platforms.exists() && platforms.isDirectory()) { return Layout.LAYOUT_1_5; } final File androidJar = new File(sdkPath, "android.jar"); if (androidJar.exists() && androidJar.isFile()) { return Layout.LAYOUT_1_1; } throw new InvalidSdkException("Android SDK could not be identified from path \"" + sdkPath + "\". " + PARAMETER_MESSAGE); } private void assertPathIsDirectory(final File path) { if (path == null) { throw new InvalidSdkException(PARAMETER_MESSAGE); } if (!path.isDirectory()) { throw new InvalidSdkException("Path \"" + path + "\" is not a directory. " + PARAMETER_MESSAGE); } } private static final Set<String> commonToolsIn11And15 = new HashSet<String>() { { add("adb"); add("android"); add("apkbuilder"); add("ddms"); add("dmtracedump"); add("draw9patch"); add("emulator"); add("hierarchyviewer"); add("hprof-conv"); add("mksdcard"); add("sqlite3"); add("traceview"); add("zipalign"); } }; /** * Returns the complete path for a tool, based on this SDK. * TODO: Implementation should try to find the tool in the different directories, instead of relying on a manually maintained list of where they are. (i.e. remove commonToolsIn11And15, and make lookup automatic based on which tools can actually be found where.) * * @param tool which tool, for example <code>adb</code>. * @return the complete path as a <code>String</code>, including the tool's filename. */ public String getPathForTool(String tool) { if (getLayout() == Layout.LAYOUT_1_1) { return sdkPath + "/tools/" + tool; } if (getLayout() == Layout.LAYOUT_1_5) { if (commonToolsIn11And15.contains(tool)) { return sdkPath + "/tools/" + tool; } else { return getPlatform() + "/tools/" + tool; } } throw new InvalidSdkException("Unsupported layout \"" + getLayout() + "\"! " + PARAMETER_MESSAGE); } /** * Get the emulator path. * * @return */ public String getEmulatorPath() { return getPathForTool("emulator"); } /** * Get the android debug tool path (adb). * * @return */ public String getAdbPath() { return getPathForTool("adb"); } /** * Get the android debug tool path (adb). * * @return */ public String getZipalignPath() { return getPathForTool("zipalign"); } /** * Returns the complete path for <code>framework.aidl</code>, based on this SDK. * * @return the complete path as a <code>String</code>, including the filename. */ public String getPathForFrameworkAidl() { if (getLayout() == Layout.LAYOUT_1_1) { return sdkPath + "/tools/lib/framework.aidl"; } if (getLayout() == Layout.LAYOUT_1_5) { return getPlatform() + "/framework.aidl"; } throw new InvalidSdkException("Unsupported layout \"" + getLayout() + "\"! " + PARAMETER_MESSAGE); } /** * Resolves the android.jar from this SDK. * * @return a <code>File</code> pointing to the android.jar file. * @throws org.apache.maven.plugin.MojoExecutionException * if the file can not be resolved. */ public File getAndroidJar() throws MojoExecutionException { if (getLayout() == Layout.LAYOUT_1_1) { return new File(sdkPath + "/android.jar"); } if (getLayout() == Layout.LAYOUT_1_5) { return new File(getPlatform() + "/android.jar"); } throw new MojoExecutionException("Invalid Layout \"" + getLayout() + "\"! " + PARAMETER_MESSAGE); } public File getPlatform() { assertPathIsDirectory(sdkPath); if (getLayout() == Layout.LAYOUT_1_1) { return sdkPath; } final File platformsDirectory = new File(sdkPath, PLATFORMS_FOLDER_NAME); assertPathIsDirectory(platformsDirectory); if (platform == null) { final File[] platformDirectories = platformsDirectory.listFiles(); Arrays.sort(platformDirectories); return platformDirectories[platformDirectories.length - 1]; } else { final File platformDirectory = new File(platform.path); assertPathIsDirectory(platformDirectory); return platformDirectory; } } /** * Initialize the maps matching platform and api levels form the source properties files. * * @throws InvalidSdkException */ private void findAvailablePlatforms() throws InvalidSdkException { availablePlatforms = new HashSet<Platform>(); ArrayList<File> platformDirectories = getPlatformDirectories(); for (File pDir : platformDirectories) { File propFile = new File(pDir, SOURCE_PROPERTIES_FILENAME); Properties properties = new Properties(); try { properties.load(new FileInputStream(propFile)); } catch (IOException e) { throw new InvalidSdkException("Error reading " + propFile.getAbsoluteFile()); } if (properties.containsKey(PLATFORM_VERSION_PROPERTY) && properties.containsKey(API_LEVEL_PROPERTY)) { String platform = properties.getProperty(PLATFORM_VERSION_PROPERTY); String apiLevel = properties.getProperty(API_LEVEL_PROPERTY); availablePlatforms.add(new Platform(platform, apiLevel, pDir.getAbsolutePath())); } } } /** * Gets the source properties files from all locally installed platforms. * * @return */ private ArrayList<File> getPlatformDirectories() { ArrayList<File> sourcePropertyFiles = new ArrayList<File>(); final File platformsDirectory = new File(sdkPath, PLATFORMS_FOLDER_NAME); assertPathIsDirectory(platformsDirectory); final File[] platformDirectories = platformsDirectory.listFiles(); for (File file : platformDirectories) { // only looking in android- folder so only works on reasonably new sdk revisions.. if (file.isDirectory() && file.getName().startsWith("android-")) sourcePropertyFiles.add(file); } return sourcePropertyFiles; } }