| /* |
| * 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.internal.androidTarget; |
| |
| import com.android.SdkConstants; |
| import com.android.annotations.NonNull; |
| import com.android.annotations.Nullable; |
| import com.android.sdklib.AndroidTargetHash; |
| import com.android.sdklib.AndroidVersion; |
| import com.android.sdklib.BuildToolInfo; |
| import com.android.sdklib.IAndroidTarget; |
| import com.android.sdklib.ISystemImage; |
| import com.android.sdklib.repository.descriptors.IdDisplay; |
| |
| import java.io.File; |
| import java.io.FileFilter; |
| import java.util.Arrays; |
| import java.util.Collections; |
| import java.util.HashSet; |
| import java.util.List; |
| 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}. |
| */ |
| public final class AddOnTarget implements IAndroidTarget { |
| |
| private static final 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; |
| } |
| |
| @Override |
| public String getJarName() { |
| return mJarName; |
| } |
| |
| @Override |
| public String getJarPath() { |
| return mJarPath; |
| } |
| |
| @Override |
| public String getName() { |
| return mName; |
| } |
| |
| @Override |
| public String getDescription() { |
| return mDescription; |
| } |
| } |
| |
| private final String mLocation; |
| private final PlatformTarget mBasePlatform; |
| private final String mName; |
| private final ISystemImage[] mSystemImages; |
| private final String mVendor; |
| private final int mRevision; |
| private final String mDescription; |
| private final boolean mHasRenderingLibrary; |
| private final boolean mHasRenderingResources; |
| |
| private File[] mSkins; |
| private File mDefaultSkin; |
| private IOptionalLibrary[] mLibraries; |
| private int mVendorId = NO_USB_ID; |
| |
| /** |
| * 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 revision the revision of the add-on |
| * @param description the add-on description |
| * @param systemImages list of supported system images. Can be null or empty. |
| * @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 hasRenderingLibrary whether the addon has a custom layoutlib.jar |
| * @param hasRenderingResources whether the add has custom framework resources. |
| * @param basePlatform the platform the add-on is extending. |
| */ |
| public AddOnTarget( |
| String location, |
| String name, |
| String vendor, |
| int revision, |
| String description, |
| ISystemImage[] systemImages, |
| Map<String, String[]> libMap, |
| boolean hasRenderingLibrary, |
| boolean hasRenderingResources, |
| PlatformTarget basePlatform) { |
| if (location.endsWith(File.separator) == false) { |
| location = location + File.separator; |
| } |
| |
| mLocation = location; |
| mName = name; |
| mVendor = vendor; |
| mRevision = revision; |
| mDescription = description; |
| mHasRenderingLibrary = hasRenderingLibrary; |
| mHasRenderingResources = hasRenderingResources; |
| mBasePlatform = basePlatform; |
| |
| // If the add-on does not have any system-image of its own, the list here |
| // is empty and it's up to the callers to query the parent platform. |
| mSystemImages = systemImages == null ? new ISystemImage[0] : systemImages; |
| Arrays.sort(mSystemImages); |
| |
| // 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); |
| } |
| } |
| } |
| |
| @Override |
| public String getLocation() { |
| return mLocation; |
| } |
| |
| @Override |
| public String getName() { |
| return mName; |
| } |
| |
| @Override |
| @Nullable |
| public ISystemImage getSystemImage(@NonNull IdDisplay tag, @NonNull String abiType) { |
| for (ISystemImage sysImg : mSystemImages) { |
| if (sysImg.getTag().equals(tag) && sysImg.getAbiType().equals(abiType)) { |
| return sysImg; |
| } |
| } |
| return null; |
| } |
| |
| @Override |
| public ISystemImage[] getSystemImages() { |
| return mSystemImages; |
| } |
| |
| @Override |
| public String getVendor() { |
| return mVendor; |
| } |
| |
| @Override |
| public String getFullName() { |
| return String.format("%1$s (%2$s)", mName, mVendor); |
| } |
| |
| @Override |
| public String getClasspathName() { |
| return String.format("%1$s [%2$s]", mName, mBasePlatform.getClasspathName()); |
| } |
| |
| @Override |
| public String getShortClasspathName() { |
| return String.format("%1$s [%2$s]", mName, mBasePlatform.getVersionName()); |
| } |
| |
| @Override |
| public String getDescription() { |
| return mDescription; |
| } |
| |
| @NonNull |
| @Override |
| public AndroidVersion getVersion() { |
| // this is always defined by the base platform |
| return mBasePlatform.getVersion(); |
| } |
| |
| @Override |
| public String getVersionName() { |
| return mBasePlatform.getVersionName(); |
| } |
| |
| @Override |
| public int getRevision() { |
| return mRevision; |
| } |
| |
| @Override |
| public boolean isPlatform() { |
| return false; |
| } |
| |
| @Override |
| public IAndroidTarget getParent() { |
| return mBasePlatform; |
| } |
| |
| @Override |
| public String getPath(int pathId) { |
| switch (pathId) { |
| case SKINS: |
| return mLocation + SdkConstants.OS_SKINS_FOLDER; |
| case DOCS: |
| return mLocation + SdkConstants.FD_DOCS + File.separator |
| + SdkConstants.FD_DOCS_REFERENCE; |
| |
| case LAYOUT_LIB: |
| if (mHasRenderingLibrary) { |
| return mLocation + SdkConstants.FD_DATA + File.separator |
| + SdkConstants.FN_LAYOUTLIB_JAR; |
| } |
| return mBasePlatform.getPath(pathId); |
| |
| case RESOURCES: |
| if (mHasRenderingResources) { |
| return mLocation + SdkConstants.FD_DATA + File.separator |
| + SdkConstants.FD_RES; |
| } |
| return mBasePlatform.getPath(pathId); |
| |
| case FONTS: |
| if (mHasRenderingResources) { |
| return mLocation + SdkConstants.FD_DATA + File.separator |
| + SdkConstants.FD_FONTS; |
| } |
| return mBasePlatform.getPath(pathId); |
| |
| case SAMPLES: |
| // only return the add-on samples folder if there is actually a sample (or more) |
| File sampleLoc = new File(mLocation, SdkConstants.FD_SAMPLES); |
| if (sampleLoc.isDirectory()) { |
| File[] files = sampleLoc.listFiles(new FileFilter() { |
| @Override |
| public boolean accept(File pathname) { |
| return pathname.isDirectory(); |
| } |
| |
| }); |
| if (files != null && files.length > 0) { |
| return sampleLoc.getAbsolutePath(); |
| } |
| } |
| //$FALL-THROUGH$ |
| default : |
| return mBasePlatform.getPath(pathId); |
| } |
| } |
| |
| @Override |
| public File getFile(int pathId) { |
| return new File(getPath(pathId)); |
| } |
| |
| @Override |
| public BuildToolInfo getBuildToolInfo() { |
| return mBasePlatform.getBuildToolInfo(); |
| } |
| |
| @Override @NonNull |
| public List<String> getBootClasspath() { |
| return Collections.singletonList(getPath(IAndroidTarget.ANDROID_JAR)); |
| } |
| |
| @Override |
| public boolean hasRenderingLibrary() { |
| return mHasRenderingLibrary || mHasRenderingResources; |
| } |
| |
| @NonNull |
| @Override |
| public File[] getSkins() { |
| return mSkins; |
| } |
| |
| @Nullable |
| @Override |
| public File getDefaultSkin() { |
| return mDefaultSkin; |
| } |
| |
| @Override |
| public IOptionalLibrary[] getOptionalLibraries() { |
| return mLibraries; |
| } |
| |
| /** |
| * Returns the list of libraries of the underlying platform. |
| * |
| * {@inheritDoc} |
| */ |
| @Override |
| public String[] getPlatformLibraries() { |
| return mBasePlatform.getPlatformLibraries(); |
| } |
| |
| @Override |
| public String getProperty(String name) { |
| return mBasePlatform.getProperty(name); |
| } |
| |
| @Override |
| public Integer getProperty(String name, Integer defaultValue) { |
| return mBasePlatform.getProperty(name, defaultValue); |
| } |
| |
| @Override |
| public Boolean getProperty(String name, Boolean defaultValue) { |
| return mBasePlatform.getProperty(name, defaultValue); |
| } |
| |
| @Override |
| public Map<String, String> getProperties() { |
| return mBasePlatform.getProperties(); |
| } |
| |
| @Override |
| public int getUsbVendorId() { |
| return mVendorId; |
| } |
| |
| @Override |
| public boolean canRunOn(IAndroidTarget target) { |
| // basic test |
| if (target == this) { |
| return true; |
| } |
| |
| /* |
| * The method javadoc indicates: |
| * 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. |
| */ |
| |
| // The receiver is an add-on. There are 2 big use cases: The add-on has libraries |
| // or the add-on doesn't (in which case we consider it a platform). |
| if (mLibraries == null || mLibraries.length == 0) { |
| return mBasePlatform.canRunOn(target); |
| } else { |
| // the only targets that can run the receiver are the same add-on in the same or later |
| // versions. |
| // first check: vendor/name |
| if (!mVendor.equals(target.getVendor()) || !mName.equals(target.getName())) { |
| return false; |
| } |
| |
| // now check the version. At this point since we checked the add-on part, |
| // we can revert to the basic check on version/codename which are done by the |
| // base platform already. |
| return mBasePlatform.canRunOn(target); |
| } |
| |
| } |
| |
| @Override |
| public String hashString() { |
| return String.format(AndroidTargetHash.ADD_ON_FORMAT, mVendor, mName, |
| mBasePlatform.getVersion().getApiString()); |
| } |
| |
| @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.getVersion().equals(addon.mBasePlatform.getVersion()); |
| } |
| |
| return false; |
| } |
| |
| /* |
| * Order by API level (preview/n count as between n and n+1). |
| * At the same API level, order as: Platform first, then add-on ordered by vendor and then name |
| * (non-Javadoc) |
| * @see java.lang.Comparable#compareTo(java.lang.Object) |
| */ |
| @Override |
| public int compareTo(IAndroidTarget target) { |
| // quick check. |
| if (this == target) { |
| return 0; |
| } |
| |
| int versionDiff = getVersion().compareTo(target.getVersion()); |
| |
| // only if the version are the same do we care about platform/add-ons. |
| if (versionDiff == 0) { |
| // platforms go before add-ons. |
| if (target.isPlatform()) { |
| return +1; |
| } else { |
| AddOnTarget targetAddOn = (AddOnTarget)target; |
| |
| // both are add-ons of the same version. Compare per vendor then by name |
| int vendorDiff = mVendor.compareTo(targetAddOn.mVendor); |
| if (vendorDiff == 0) { |
| return mName.compareTo(targetAddOn.mName); |
| } else { |
| return vendorDiff; |
| } |
| } |
| |
| } |
| |
| return versionDiff; |
| } |
| |
| /** |
| * Returns a string representation suitable for debugging. |
| * The representation is not intended for display to the user. |
| * |
| * The representation is also purposely compact. It does not describe _all_ the properties |
| * of the target, only a few key ones. |
| * |
| * @see #getDescription() |
| */ |
| @Override |
| public String toString() { |
| return String.format("AddonTarget %1$s rev %2$d (based on %3$s)", //$NON-NLS-1$ |
| getVersion(), |
| getRevision(), |
| getParent().toString()); |
| } |
| |
| // ---- local methods. |
| |
| public void setSkins(@NonNull File[] skins, @NonNull File defaultSkin) { |
| mDefaultSkin = defaultSkin; |
| |
| // we mix the add-on and base platform skins |
| HashSet<File> skinSet = new HashSet<File>(); |
| skinSet.addAll(Arrays.asList(skins)); |
| skinSet.addAll(Arrays.asList(mBasePlatform.getSkins())); |
| |
| mSkins = skinSet.toArray(new File[skinSet.size()]); |
| } |
| |
| /** |
| * Sets the USB vendor id in the add-on. |
| */ |
| public void setUsbVendorId(int vendorId) { |
| if (vendorId == 0) { |
| throw new IllegalArgumentException( "VendorId must be > 0"); |
| } |
| |
| mVendorId = vendorId; |
| } |
| } |