summaryrefslogtreecommitdiff
path: root/src/plugins/common/src/com/motorola/studio/android/common/utilities/AndroidUtils.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/common/src/com/motorola/studio/android/common/utilities/AndroidUtils.java')
-rw-r--r--src/plugins/common/src/com/motorola/studio/android/common/utilities/AndroidUtils.java612
1 files changed, 612 insertions, 0 deletions
diff --git a/src/plugins/common/src/com/motorola/studio/android/common/utilities/AndroidUtils.java b/src/plugins/common/src/com/motorola/studio/android/common/utilities/AndroidUtils.java
new file mode 100644
index 0000000..1a522d2
--- /dev/null
+++ b/src/plugins/common/src/com/motorola/studio/android/common/utilities/AndroidUtils.java
@@ -0,0 +1,612 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.common.utilities;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.StringTokenizer;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.preferences.IEclipsePreferences;
+import org.eclipse.core.runtime.preferences.InstanceScope;
+import org.osgi.framework.Bundle;
+
+import com.motorola.studio.android.common.CommonPlugin;
+import com.motorola.studio.android.common.exception.AndroidException;
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.common.utilities.i18n.UtilitiesNLS;
+
+public class AndroidUtils
+{
+ private static final String CLASS_COM_ANDROID_IDE_ECLIPSE_ADT_INTERNAL_SDK_ANDROID_TARGET_DATA =
+ "com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData"; //$NON-NLS-1$
+
+ private static final String CLASS_COM_ANDROID_SDKLIB_I_ANDROID_TARGET =
+ "com.android.sdklib.IAndroidTarget"; //$NON-NLS-1$
+
+ private static final String CLASS_COM_ANDROID_SDKLIB_ANDROID_VERSION =
+ "com.android.sdklib.AndroidVersion"; //$NON-NLS-1$
+
+ private static final String CLASS_COM_ANDROID_IDE_ECLIPSE_ADT_INTERNAL_SDK_SDK =
+ "com.android.ide.eclipse.adt.internal.sdk.Sdk"; //$NON-NLS-1$
+
+ //Constants
+ private static final String ANDROID_VERSION_API_LEVEL = "AndroidVersion.ApiLevel"; //$NON-NLS-1$
+
+ private static final String SOURCE_PROPERTIES = "source.properties"; //$NON-NLS-1$
+
+ private static final String ANDROID_JAR = "android.jar"; //$NON-NLS-1$
+
+ private static final String PLATFORMS = "platforms"; //$NON-NLS-1$
+
+ private static final String ANDROID = "android-"; //$NON-NLS-1$
+
+ private static final String TARGET = "target"; //$NON-NLS-1$
+
+ private static final String DEFAULT_PROPERTIES = "default.properties"; //$NON-NLS-1$
+
+ private static final String PROJECT_PROPERTIES = "project.properties"; //$NON-NLS-1$
+
+ /*
+ * Contains the exceptions to the general rule (that prepends android.permission. to the permissionName)
+ */
+ private static final Map<String, String> permissionNameToPrefixToAppend =
+ new HashMap<String, String>();
+
+ static
+ {
+ permissionNameToPrefixToAppend.put("SET_ALARM", "com.android.alarm.permission"); //$NON-NLS-1$ //$NON-NLS-2$
+ permissionNameToPrefixToAppend.put("READ_HISTORY_BOOKMARKS", //$NON-NLS-1$
+ "com.android.browser.permission"); //$NON-NLS-1$
+ permissionNameToPrefixToAppend.put("WRITE_HISTORY_BOOKMARKS", //$NON-NLS-1$
+ "com.android.browser.permission"); //$NON-NLS-1$
+ permissionNameToPrefixToAppend.put("ADD_VOICEMAIL", "com.android.voicemail.permission"); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ /**
+ * Gets Android Sdk set in the preference page
+ * @return
+ */
+ public static String getSDKPathByPreference()
+ {
+ IEclipsePreferences pref = InstanceScope.INSTANCE.getNode("com.android.ide.eclipse.adt"); //$NON-NLS-1$
+ return pref.get("com.android.ide.eclipse.adt.sdk", ""); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ /**
+ * Gets android jar from the project
+ * @param projectDir
+ * @return path to android.jar
+ * @throws AndroidException
+ */
+ public static File getAndroidJar(File projectDir) throws AndroidException
+ {
+ File androidTargetFolder = getAndroidTargetPathForProject(projectDir);
+ return new File(androidTargetFolder, "android.jar"); //$NON-NLS-1$
+ }
+
+ /**
+ * Returns all available intent filter permissions from a given project
+ *
+ * @return String array containing the available permissions
+ */
+ public static String[] getIntentFilterPermissions(IProject project)
+ {
+ String[] attributeValues = new String[0];
+
+ if ((project != null) && project.isOpen())
+ {
+ try
+ {
+ //reflection for: Sdk sdk = Sdk.getCurrent();
+ Class<?> clsSdk = Class.forName(CLASS_COM_ANDROID_IDE_ECLIPSE_ADT_INTERNAL_SDK_SDK);
+ Method mtdGetCurrent = clsSdk.getMethod("getCurrent", (Class[]) null); //$NON-NLS-1$
+ Object sdk = mtdGetCurrent.invoke(null, (Object[]) null);
+
+ //reflection for: IAndroidTarget target = sdk.getTarget(project);
+ Method mtdGetTarget = clsSdk.getMethod("getTarget", IProject.class); //$NON-NLS-1$
+ Object target = mtdGetTarget.invoke(sdk, project);
+
+ //reflection for: AndroidTargetData targetData = sdk.getTargetData(target);
+ Class<?> interfaceIAndroidTarget =
+ Class.forName(CLASS_COM_ANDROID_SDKLIB_I_ANDROID_TARGET);
+ Method mtdGetTargetData =
+ clsSdk.getMethod("getTargetData", interfaceIAndroidTarget); //$NON-NLS-1$
+ Object targetData = mtdGetTargetData.invoke(sdk, target);
+
+ if (targetData != null)
+ {
+ //reflection for: attributeValues = targetData.getAttributeValues("uses-permission", "android:name"); //$NON-NLS-1$ //$NON-NLS-2$
+ Class<?> clsAndroidTargetData =
+ Class.forName(CLASS_COM_ANDROID_IDE_ECLIPSE_ADT_INTERNAL_SDK_ANDROID_TARGET_DATA);
+ Method mtdGetAttributeValues =
+ clsAndroidTargetData.getMethod("getAttributeValues", String.class, //$NON-NLS-1$
+ String.class);
+ attributeValues =
+ (String[]) mtdGetAttributeValues.invoke(targetData, "uses-permission", //$NON-NLS-1$
+ "android:name"); //$NON-NLS-1$
+ }
+ }
+ catch (Exception e)
+ {
+ StudioLogger.warn("It was not possible to reach ADT methods (reflection break)",
+ e.getMessage());
+ try
+ {
+ attributeValues = getIntentFilterPermissions().toArray(attributeValues);
+ }
+ catch (IOException e1)
+ {
+ StudioLogger.error(
+ UtilitiesNLS.AndroidUtils_NotPossibleToReachPermissionsFile_Error,
+ e1.getMessage());
+ }
+ EclipseUtils.showWarningDialog(
+ UtilitiesNLS.AndroidUtils_ERROR_GETINTENTPERMISSIONSBYREFLECTION_TITLE,
+ UtilitiesNLS.AndroidUtils_ERROR_GETINTENTPERMISSIONSBYREFLECTION_MESSAGE);
+ }
+ }
+
+ return attributeValues;
+ }
+
+ /**
+ * Retrieves the path to platform/$target$, where
+ * $target$ is defined inside default.properties/project.properties file
+ *
+ * @param projectDir
+ * @return file with path to target folder
+ * @throws AndroidException
+ * problem to read default.properties/project.properties
+ */
+ public static File getAndroidTargetPathForProject(File projectDir) throws AndroidException
+ {
+ File androidTarget = null;
+ Properties properties = new Properties();
+ // changed from default.properties to project.properties after R14
+ File defaultPropertiesFile = new File(projectDir, PROJECT_PROPERTIES);
+ if (!defaultPropertiesFile.exists())
+ {
+ // WARNING: do not remove statement below assigning
+ // default.properties file to keep compatibility with projects
+ // created with ADTs before R14
+ defaultPropertiesFile = new File(projectDir, DEFAULT_PROPERTIES);
+ }
+ if (defaultPropertiesFile.exists())
+ {
+ FileInputStream fileInputStream = null;
+ try
+ {
+ fileInputStream = new FileInputStream(defaultPropertiesFile);
+ properties.load(fileInputStream);
+ }
+ catch (IOException e)
+ {
+ throw new AndroidException(
+ UtilitiesNLS.AndroidUtils_ErrorReadingDefaultPropertiesFile, e);
+ }
+ finally
+ {
+ if (fileInputStream != null)
+ {
+ try
+ {
+ fileInputStream.close();
+ }
+ catch (Exception e)
+ {
+ //Do nothing.
+ }
+ }
+ }
+ if (properties.containsKey(TARGET))
+ {
+ String targetValue = properties.getProperty(TARGET);
+ if (targetValue != null)
+ {
+ if (!targetValue.startsWith(ANDROID))
+ {
+ try
+ {
+ // add-on => <name>:<model>:<version>
+ int colonIndex = targetValue.lastIndexOf(":"); //$NON-NLS-1$
+ if (colonIndex >= 0)
+ {
+ targetValue = ANDROID + targetValue.substring(colonIndex + 1);
+ }
+ }
+ catch (Exception e)
+ {
+ //Do nothing.
+ }
+ }
+ }
+
+ boolean androidSdkPreferenceDefined = !getSDKPathByPreference().equals(""); //$NON-NLS-1$
+ String sdkPath = getSdkPath(androidSdkPreferenceDefined);
+
+ if (sdkPath != null)
+ {
+ // found sdk path
+ androidTarget =
+ new File(sdkPath + File.separator + PLATFORMS + File.separator
+ + targetValue);
+ if (!androidTarget.exists())
+ {
+ //if not found the exact version, then look for one version of android that is greater than target value (and retrieve platform folder)
+ File baseFolder = new File(sdkPath + File.separator + PLATFORMS);
+ File[] androidPlatforms = baseFolder.listFiles();
+ boolean foundJar = false;
+ if (androidPlatforms.length > 0)
+ {
+ for (File androidPlatform : androidPlatforms)
+ {
+ File sourcePropsFile = new File(androidPlatform, SOURCE_PROPERTIES);
+ File jar = new File(androidPlatform, ANDROID_JAR);
+ if (sourcePropsFile.exists() && jar.exists())
+ {
+ Properties sourceProperties = new Properties();
+ try
+ {
+ fileInputStream = new FileInputStream(sourcePropsFile);
+ sourceProperties.load(fileInputStream);
+ }
+ catch (IOException e)
+ {
+ throw new AndroidException(
+ UtilitiesNLS.AndroidUtils_ErrorReadingDefaultPropertiesFile,
+ e);
+ }
+ finally
+ {
+ if (fileInputStream != null)
+ {
+ try
+ {
+ fileInputStream.close();
+ }
+ catch (Exception e)
+ {
+ //Do nothing.
+ }
+ }
+ }
+ //platform api level
+ String apiLevel =
+ sourceProperties.getProperty(ANDROID_VERSION_API_LEVEL);
+ int index = targetValue.indexOf("-"); //$NON-NLS-1$
+ if ((index >= 0) && (apiLevel != null))
+ {
+ //project target declared
+ String versionName = targetValue.substring(index + 1);
+ try
+ {
+ Integer version = Integer.valueOf(versionName);
+ Integer apiLevelVersion = Integer.valueOf(apiLevel);
+ if (apiLevelVersion >= version)
+ {
+ //found a compatible platform
+ foundJar = true;
+ androidTarget = androidPlatform;
+ break;
+ }
+ }
+ catch (NumberFormatException nfe)
+ {
+ //ignore this folder (add-on or preview)
+ }
+ }
+ }
+ }
+ if (!foundJar)
+ {
+ throw new AndroidException(
+ androidTarget.getAbsolutePath()
+ + UtilitiesNLS.AndroidUtils_ERROR_SDK_TARGETPLATFORM_NOTFOUND);
+ }
+ }
+ }
+ }
+ else
+ {
+ throw new AndroidException(UtilitiesNLS.AndroidUtils_ERROR_SDKPATHNOTFOUND);
+ }
+ }
+ }
+ else
+ {
+ throw new AndroidException(UtilitiesNLS.AndroidUtils_ERROR_DEFAULTPROPERTIESNOTFOUND);
+ }
+ return androidTarget;
+ }
+
+ /**
+ * Returns the path for Android SDK
+ *
+ * @param preferenceDefined
+ * true if ADT preference for Android SDK is defined (take sdk path from it),
+ * false otherwise (take sdk path from environment variable)
+ * @return resolved path to Android SDK
+ */
+ private static String getSdkPath(boolean preferenceDefined)
+ {
+ String sdkPath = null;
+ if (!preferenceDefined)
+ {
+ // sdk was not defined - take from environment variable
+ String pathVariable = System.getenv("PATH"); //$NON-NLS-1$
+ String pathSeparator = System.getProperty("path.separator"); //$NON-NLS-1$
+ String subPath = null;
+ File checkedPath = null;
+ String[] folderList = null;
+
+ StringTokenizer token = new StringTokenizer(pathVariable, pathSeparator);
+ while (token.hasMoreTokens())
+ {
+ subPath = token.nextToken();
+ checkedPath = new File(subPath);
+ if (checkedPath.isDirectory())
+ {
+ folderList = checkedPath.list();
+ for (String s : folderList)
+ {
+ if (s.equals("emulator") || s.equals("emulator.exe") || s.equals("adb") //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ || s.equals("adb.exe")) //$NON-NLS-1$
+ {
+ File root = checkedPath.getParentFile();
+ sdkPath = root.getAbsolutePath();
+ break;
+ }
+ }
+ }
+ }
+
+ }
+ else
+ {
+ // sdk was defined on execution
+ sdkPath = getSDKPathByPreference();
+ }
+ return sdkPath;
+ }
+
+ /**
+ * Retrieves all activity actions from a given {@link IProject}.
+ * @param project The android project.
+ * @return An {@link String} array containing all the activity actions from the given project.
+ * @throws AndroidException if an error occurred while attempting to get the activity actions.
+ */
+ public static String[] getActivityActions(IProject project) throws AndroidException
+ {
+ String[] activityActions = new String[1];
+ File androidTargetFile =
+ AndroidUtils.getAndroidTargetPathForProject(project.getLocation().toFile());
+ TargetDataReader targetDataReader = new TargetDataReader(androidTargetFile);
+ try
+ {
+ activityActions = targetDataReader.getActivityActions().toArray(activityActions);
+ }
+ catch (IOException e)
+ {
+ throw new AndroidException(e);
+ }
+ return activityActions;
+ }
+
+ /**
+ * Retrieves all receiver actions from a given {@link IProject}.
+ * @param project The android project.
+ * @return An {@link String} array containing all the receiver actions from the given project.
+ * @throws AndroidException if an error occurred while attempting to get the receiver actions.
+ */
+ public static String[] getReceiverActions(IProject project) throws AndroidException
+ {
+ String[] receiverActions = new String[1];
+ File androidTargetFile =
+ AndroidUtils.getAndroidTargetPathForProject(project.getLocation().toFile());
+ TargetDataReader targetDataReader = new TargetDataReader(androidTargetFile);
+ try
+ {
+ receiverActions = targetDataReader.getReceiverActions().toArray(receiverActions);
+ }
+ catch (IOException e)
+ {
+ throw new AndroidException(e);
+ }
+ return receiverActions;
+ }
+
+ /**
+ * Retrieves all intent filter categories from a given {@link IProject}.
+ * @param project The android project.
+ * @return An {@link String} array containing all the intent filter categories from the given project.
+ * @throws AndroidException if an error occurred while attempting to get the intent filter categories.
+ */
+ public static String[] getIntentFilterCategories(IProject project) throws AndroidException
+ {
+ String[] intentFilterCategories = new String[1];
+ File androidTargetFile =
+ AndroidUtils.getAndroidTargetPathForProject(project.getLocation().toFile());
+ TargetDataReader targetDataReader = new TargetDataReader(androidTargetFile);
+ try
+ {
+ intentFilterCategories =
+ targetDataReader.getIntentFilterCategories().toArray(intentFilterCategories);
+ }
+ catch (IOException e)
+ {
+ throw new AndroidException(e);
+ }
+ return intentFilterCategories;
+ }
+
+ /**
+ * Retrieves all service actions from a given {@link IProject}.
+ * @param project The android project.
+ * @return An {@link String} array containing all the service actions from the given project.
+ * @throws AndroidException if an error occurred while attempting to get the service actions.
+ */
+ public static String[] getServiceActions(IProject project) throws AndroidException
+ {
+ String[] serviceActions = new String[1];
+ File androidTargetFile =
+ AndroidUtils.getAndroidTargetPathForProject(project.getLocation().toFile());
+ TargetDataReader targetDataReader = new TargetDataReader(androidTargetFile);
+ try
+ {
+ serviceActions = targetDataReader.getServiceActions().toArray(serviceActions);
+ }
+ catch (IOException e)
+ {
+ throw new AndroidException(e);
+ }
+ return serviceActions;
+ }
+
+ /**
+ * Get the api version number for a given project.
+ * This method accesses ADT via reflection. If an error occurred an AndroidException will be thrown.
+ * @param project: the project
+ * @return the api version number or 0 if some error occurs
+ * @throws Exception if any error occurred while attempting to access the ADT via reflection.
+ */
+ public static int getApiVersionNumberForProject(IProject project) throws AndroidException
+ {
+ int api = 0;
+
+ try
+ {
+ //reflection for: Sdk sdk = Sdk.getCurrent();
+ Class<?> clsSdk = Class.forName(CLASS_COM_ANDROID_IDE_ECLIPSE_ADT_INTERNAL_SDK_SDK);
+ Method mtdGetCurrent = clsSdk.getMethod("getCurrent", (Class[]) null); //$NON-NLS-1$
+ Object sdk = mtdGetCurrent.invoke(null, (Object[]) null);
+
+ //reflection for: IAndroidTarget target = sdk.getTarget(project);
+ Method mtdGetTarget = clsSdk.getMethod("getTarget", IProject.class); //$NON-NLS-1$
+ Object target = mtdGetTarget.invoke(sdk, project);
+
+ //reflection for: AndroidVersion version = target.getVersion(target);
+ Class<?> clsAndroidVersion = Class.forName(CLASS_COM_ANDROID_SDKLIB_ANDROID_VERSION);
+
+ Class<?> interfaceIAndroidTarget =
+ Class.forName(CLASS_COM_ANDROID_SDKLIB_I_ANDROID_TARGET);
+ Method mtdGetVersion = interfaceIAndroidTarget.getMethod("getVersion", (Class[]) null); //$NON-NLS-1$
+
+ Object version = mtdGetVersion.invoke(target, (Object[]) null);
+
+ if (version != null)
+ {
+ //reflection for: version.getApiLevel();
+ Method mtdGetApiLevel = clsAndroidVersion.getMethod("getApiLevel", (Class[]) null); //$NON-NLS-1$
+ Object apiLevel = mtdGetApiLevel.invoke(version, (Object[]) null);
+ api = (Integer) apiLevel;
+ }
+ }
+ catch (Exception e)
+ {
+ StudioLogger.info("It was not possible to reach ADT methods (reflection break)",
+ e.getMessage());
+ throw new AndroidException(
+ UtilitiesNLS.AndroidUtils_NotPossibleToGetAPIVersionNumber_Error);
+ }
+ return api;
+ }
+
+ /**
+ * Reads permissions.txt file inside plugin and creates the list of permissions available in the target
+ * (the list is based on the class <code>Manifest.Permission</code> from Android)
+ *
+ * <br><br>
+ * It appends android.permission. to the items,
+ * the unique exceptions are:
+ * -com.android.alarm.permission.SET_ALARM
+ * -com.android.browser.permission.READ_HISTORY_BOOKMARKS
+ * -com.android.browser.permission.WRITE_HISTORY_BOOKMARKS
+ * -com.android.voicemail.permission.ADD_VOICEMAIL
+ *
+ * @see http://developer.android.com/reference/android/Manifest.permission.html
+ * @return list of Intent Filters Permissions available
+ * @throws IOException if file not found, or if there is any problem reading the permissions file
+ */
+ private static List<String> getIntentFilterPermissions() throws IOException
+ {
+ //path inside <plugin>\files\permissions.txt
+ URL permissionsURL = null;
+
+ Bundle bundle = Platform.getBundle(CommonPlugin.PLUGIN_ID);
+ permissionsURL =
+ bundle.getEntry((new StringBuilder(IPath.SEPARATOR)).append(
+ "files" + IPath.SEPARATOR + "permissions.txt") //$NON-NLS-1$ //$NON-NLS-2$
+ .toString());
+
+ List<String> items = new ArrayList<String>();
+ InputStream is = null;
+ BufferedReader bufferedReader = null;
+ try
+ {
+ if (permissionsURL != null)
+ {
+ is = permissionsURL.openStream();
+ }
+
+ if (is != null)
+ {
+ bufferedReader = new BufferedReader(new InputStreamReader(is));
+ String line;
+ while ((line = bufferedReader.readLine()) != null)
+ {
+ String prefix = ""; //$NON-NLS-1$
+ if (!permissionNameToPrefixToAppend.containsKey(line.trim()))
+ {
+ //general rule - append android.permission.
+ prefix = "android.permission"; //$NON-NLS-1$
+ }
+ else
+ {
+ //exception rule - append the prefix available in the map
+ prefix = permissionNameToPrefixToAppend.get(line.trim());
+ }
+ items.add(prefix + "." + line.trim()); //$NON-NLS-1$
+ }
+ }
+ }
+ finally
+ {
+ if (is != null)
+ {
+ is.close();
+ }
+ if (bufferedReader != null)
+ {
+ bufferedReader.close();
+ }
+ }
+ return items;
+ }
+}