SDK Updater: parse local packages, download and install remote packages.
The install phase is still work in progress. The local part needs to display descriptions. Buttons callback are generally not implemented yet.
This commit is contained in:
@@ -90,7 +90,7 @@ public class AndroidConstants {
|
||||
|
||||
/** Name of the android sources directory */
|
||||
public static final String FD_ANDROID_SOURCES = "sources"; //$NON-NLS-1$
|
||||
|
||||
|
||||
/** Resource java class filename, i.e. "R.java" */
|
||||
public final static String FN_RESOURCE_CLASS = "R.java"; //$NON-NLS-1$
|
||||
/** Resource class file filename, i.e. "R.class" */
|
||||
@@ -104,15 +104,11 @@ public class AndroidConstants {
|
||||
/** Temporary packaged resources file name for a specific set of configuration */
|
||||
public final static String FN_RESOURCES_S_AP_ = "resources-%s.ap_"; //$NON-NLS-1$
|
||||
public final static Pattern PATTERN_RESOURCES_S_AP_ =
|
||||
Pattern.compile("resources-.*\\.ap_", Pattern.CASE_INSENSITIVE);
|
||||
Pattern.compile("resources-.*\\.ap_", Pattern.CASE_INSENSITIVE); //$NON-NLS-1$
|
||||
|
||||
public final static String FN_ADB =
|
||||
(SdkConstants.CURRENT_PLATFORM == SdkConstants.PLATFORM_WINDOWS) ?
|
||||
"adb.exe" : "adb"; //$NON-NLS-1$ //$NON-NLS-2$
|
||||
public final static String FN_ADB = SdkConstants.FN_ADB;
|
||||
|
||||
public final static String FN_EMULATOR =
|
||||
(SdkConstants.CURRENT_PLATFORM == SdkConstants.PLATFORM_WINDOWS) ?
|
||||
"emulator.exe" : "emulator"; //$NON-NLS-1$ //$NON-NLS-2$
|
||||
public final static String FN_EMULATOR = SdkConstants.FN_EMULATOR;
|
||||
|
||||
public final static String FN_TRACEVIEW =
|
||||
(SdkConstants.CURRENT_PLATFORM == SdkConstants.PLATFORM_WINDOWS) ?
|
||||
@@ -128,8 +124,8 @@ public class AndroidConstants {
|
||||
public final static String WS_ASSETS = WS_SEP + SdkConstants.FD_ASSETS;
|
||||
|
||||
/** Leaf of the javaDoc folder. Does not start with a separator. */
|
||||
public final static String WS_JAVADOC_FOLDER_LEAF = SdkConstants.FD_DOCS + "/" +
|
||||
SdkConstants.FD_DOCS_REFERENCE; //$NON-NLS-1$
|
||||
public final static String WS_JAVADOC_FOLDER_LEAF = SdkConstants.FD_DOCS + "/" + //$NON-NLS-1$
|
||||
SdkConstants.FD_DOCS_REFERENCE;
|
||||
|
||||
/** Path of the samples directory relative to the sdk folder.
|
||||
* This is an OS path, ending with a separator.
|
||||
@@ -159,10 +155,10 @@ public class AndroidConstants {
|
||||
|
||||
/** aidl marker error. */
|
||||
public final static String MARKER_AIDL = COMMON_PLUGIN_ID + ".aidlProblem"; //$NON-NLS-1$
|
||||
|
||||
|
||||
/** android marker error */
|
||||
public final static String MARKER_ANDROID = COMMON_PLUGIN_ID + ".androidProblem"; //$NON-NLS-1$
|
||||
|
||||
|
||||
/** Name for the "type" marker attribute */
|
||||
public final static String MARKER_ATTR_TYPE = "android.type"; //$NON-NLS-1$
|
||||
/** Name for the "class" marker attribute */
|
||||
@@ -176,9 +172,9 @@ public class AndroidConstants {
|
||||
/** provider value for marker attribute "type" */
|
||||
public final static String MARKER_ATTR_TYPE_PROVIDER = "provider"; //$NON-NLS-1$
|
||||
|
||||
public final static String CLASS_ACTIVITY = "android.app.Activity"; //$NON-NLS-1$
|
||||
public final static String CLASS_SERVICE = "android.app.Service"; //$NON-NLS-1$
|
||||
public final static String CLASS_BROADCASTRECEIVER = "android.content.BroadcastReceiver"; //$NON-NLS-1$
|
||||
public final static String CLASS_ACTIVITY = "android.app.Activity"; //$NON-NLS-1$
|
||||
public final static String CLASS_SERVICE = "android.app.Service"; //$NON-NLS-1$
|
||||
public final static String CLASS_BROADCASTRECEIVER = "android.content.BroadcastReceiver"; //$NON-NLS-1$
|
||||
public final static String CLASS_CONTENTPROVIDER = "android.content.ContentProvider"; //$NON-NLS-1$
|
||||
public final static String CLASS_INSTRUMENTATION = "android.app.Instrumentation"; //$NON-NLS-1$
|
||||
public final static String CLASS_INSTRUMENTATION_RUNNER =
|
||||
@@ -202,7 +198,7 @@ public class AndroidConstants {
|
||||
"android.preference." + CLASS_NAME_PREFERENCE_SCREEN; //$NON-NLS-1$
|
||||
public final static String CLASS_PREFERENCEGROUP = "android.preference.PreferenceGroup"; //$NON-NLS-1$
|
||||
public final static String CLASS_PARCELABLE = "android.os.Parcelable"; //$NON-NLS-1$
|
||||
|
||||
|
||||
public final static String CLASS_BRIDGE = "com.android.layoutlib.bridge.Bridge"; //$NON-NLS-1$
|
||||
|
||||
/**
|
||||
@@ -219,6 +215,6 @@ public class AndroidConstants {
|
||||
|
||||
/** The base URL where to find the Android class & manifest documentation */
|
||||
public static final String CODESITE_BASE_URL = "http://code.google.com/android"; //$NON-NLS-1$
|
||||
|
||||
|
||||
public static final String LIBRARY_TEST_RUNNER = "android.test.runner"; // $NON-NLS-1$
|
||||
}
|
||||
|
||||
@@ -82,18 +82,26 @@ public final class SdkConstants {
|
||||
/** dex.jar file */
|
||||
public static final String FN_DX_JAR = "dx.jar"; //$NON-NLS-1$
|
||||
|
||||
/** dx executable */
|
||||
/** dx executable (with extension for the current OS) */
|
||||
public final static String FN_DX = (CURRENT_PLATFORM == PLATFORM_WINDOWS) ?
|
||||
"dx.bat" : "dx"; //$NON-NLS-1$ //$NON-NLS-2$
|
||||
|
||||
/** aapt executable */
|
||||
/** aapt executable (with extension for the current OS) */
|
||||
public final static String FN_AAPT = (CURRENT_PLATFORM == PLATFORM_WINDOWS) ?
|
||||
"aapt.exe" : "aapt"; //$NON-NLS-1$ //$NON-NLS-2$
|
||||
|
||||
/** aidl executable */
|
||||
/** aidl executable (with extension for the current OS) */
|
||||
public final static String FN_AIDL = (CURRENT_PLATFORM == PLATFORM_WINDOWS) ?
|
||||
"aidl.exe" : "aidl"; //$NON-NLS-1$ //$NON-NLS-2$
|
||||
|
||||
/** adb executable (with extension for the current OS) */
|
||||
public final static String FN_ADB = (CURRENT_PLATFORM == PLATFORM_WINDOWS) ?
|
||||
"adb.exe" : "adb"; //$NON-NLS-1$ //$NON-NLS-2$
|
||||
|
||||
/** emulator executable (with extension for the current OS) */
|
||||
public final static String FN_EMULATOR = (CURRENT_PLATFORM == PLATFORM_WINDOWS) ?
|
||||
"emulator.exe" : "emulator"; //$NON-NLS-1$ //$NON-NLS-2$
|
||||
|
||||
/* Folder Names for Android Projects . */
|
||||
|
||||
/** Resources folder name, i.e. "res". */
|
||||
|
||||
@@ -16,10 +16,17 @@
|
||||
|
||||
package com.android.sdklib.internal.repository;
|
||||
|
||||
import com.android.sdklib.IAndroidTarget;
|
||||
import com.android.sdklib.SdkConstants;
|
||||
import com.android.sdklib.SdkManager;
|
||||
import com.android.sdklib.IAndroidTarget.IOptionalLibrary;
|
||||
import com.android.sdklib.internal.repository.Archive.Arch;
|
||||
import com.android.sdklib.internal.repository.Archive.Os;
|
||||
import com.android.sdklib.repository.SdkRepository;
|
||||
|
||||
import org.w3c.dom.Node;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
@@ -57,8 +64,8 @@ public class AddonPackage extends Package {
|
||||
* <p/>
|
||||
* This constructor should throw an exception if the package cannot be created.
|
||||
*/
|
||||
AddonPackage(Node packageNode) {
|
||||
super(packageNode);
|
||||
AddonPackage(RepoSource source, Node packageNode) {
|
||||
super(source, packageNode);
|
||||
mVendor = getXmlString(packageNode, SdkRepository.NODE_VENDOR);
|
||||
mName = getXmlString(packageNode, SdkRepository.NODE_NAME);
|
||||
mApiLevel = getXmlInt (packageNode, SdkRepository.NODE_API_LEVEL, 0);
|
||||
@@ -66,6 +73,41 @@ public class AddonPackage extends Package {
|
||||
mLibs = parseLibs(getFirstChild(packageNode, SdkRepository.NODE_LIBS));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new platform package based on an actual {@link IAndroidTarget} (with
|
||||
* {@link IAndroidTarget#isPlatform()} false) from the {@link SdkManager}.
|
||||
* This is used to list local SDK folders.
|
||||
*/
|
||||
AddonPackage(IAndroidTarget target) {
|
||||
super( null, //source
|
||||
0, //revision
|
||||
target.getDescription(), //description
|
||||
null, //descUrl
|
||||
Os.getCurrentOs(), //archiveOs
|
||||
Arch.getCurrentArch(), //archiveArch
|
||||
"", //archiveUrl //$NON-NLS-1$
|
||||
0, //archiveSize
|
||||
null //archiveChecksum
|
||||
);
|
||||
|
||||
mApiLevel = target.getApiVersionNumber();
|
||||
mName = target.getName();
|
||||
mVendor = target.getVendor();
|
||||
|
||||
IOptionalLibrary[] optLibs = target.getOptionalLibraries();
|
||||
if (optLibs == null || optLibs.length == 0) {
|
||||
mLibs = new Lib[0];
|
||||
} else {
|
||||
mLibs = new Lib[optLibs.length];
|
||||
for (int i = 0; i < optLibs.length; i++) {
|
||||
mLibs[i] = new Lib(optLibs[i].getName(), optLibs[i].getDescription());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a <libs> element.
|
||||
*/
|
||||
private Lib[] parseLibs(Node libsNode) {
|
||||
ArrayList<Lib> libs = new ArrayList<Lib>();
|
||||
|
||||
@@ -85,6 +127,9 @@ public class AddonPackage extends Package {
|
||||
return libs.toArray(new Lib[libs.size()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a <lib> element from a <libs> container.
|
||||
*/
|
||||
private Lib parseLib(Node libNode) {
|
||||
return new Lib(getXmlString(libNode, SdkRepository.NODE_NAME),
|
||||
getXmlString(libNode, SdkRepository.NODE_DESCRIPTION));
|
||||
@@ -126,4 +171,31 @@ public class AddonPackage extends Package {
|
||||
getShortDescription(),
|
||||
super.getLongDescription());
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes a potential installation folder if an archive of this package were
|
||||
* to be installed right away in the given SDK root.
|
||||
* <p/>
|
||||
* An add-on package is typically installed in SDK/add-ons/"addon-name"-"api-level".
|
||||
* The name needs to be sanitized to be acceptable as a directory name.
|
||||
* However if we can find a different directory under SDK/add-ons that already
|
||||
* has this add-ons installed, we'll use that one.
|
||||
*
|
||||
* @param osSdkRoot The OS path of the SDK root folder.
|
||||
* @return A new {@link File} corresponding to the directory to use to install this package.
|
||||
*/
|
||||
@Override
|
||||
public File getInstallFolder(String osSdkRoot) {
|
||||
File addons = new File(osSdkRoot, SdkConstants.FD_ADDONS);
|
||||
|
||||
String name = String.format("%s-%d", getName(), getApiLevel()); // $NON-NLS-1$
|
||||
|
||||
name = name.replaceAll("[^a-zA-Z0-9_-]+", "_"); // $NON-NLS-1$
|
||||
name = name.replaceAll("_+", "_"); // $NON-NLS-1$
|
||||
|
||||
File folder = new File(addons, name);
|
||||
|
||||
// TODO find similar existing addon in addons folder
|
||||
return folder;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,9 @@
|
||||
|
||||
package com.android.sdklib.internal.repository;
|
||||
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
|
||||
/**
|
||||
* A {@link Archive} is the base class for "something" that can be downloaded from
|
||||
@@ -32,23 +35,106 @@ public class Archive implements IDescription {
|
||||
/** The checksum type. */
|
||||
public enum ChecksumType {
|
||||
/** A SHA1 checksum, represented as a 40-hex string. */
|
||||
SHA1
|
||||
SHA1("SHA-1"); //$NON-NLS-1$
|
||||
|
||||
private final String mAlgorithmName;
|
||||
|
||||
/**
|
||||
* Constructs a {@link ChecksumType} with the algorigth name
|
||||
* suitable for {@link MessageDigest#getInstance(String)}.
|
||||
* <p/>
|
||||
* These names are officially documented at
|
||||
* http://java.sun.com/javase/6/docs/technotes/guides/security/StandardNames.html#MessageDigest
|
||||
*/
|
||||
private ChecksumType(String algorithmName) {
|
||||
mAlgorithmName = algorithmName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new {@link MessageDigest} instance for this checksum type.
|
||||
* @throws NoSuchAlgorithmException if this algorithm is not available.
|
||||
*/
|
||||
public MessageDigest getMessageDigest() throws NoSuchAlgorithmException {
|
||||
return MessageDigest.getInstance(mAlgorithmName);
|
||||
}
|
||||
}
|
||||
|
||||
/** The OS that this archive can be downloaded on. */
|
||||
public enum Os {
|
||||
ANY,
|
||||
LINUX,
|
||||
MACOSX,
|
||||
WINDOWS
|
||||
ANY("Any"),
|
||||
LINUX("Linux"),
|
||||
MACOSX("MacOS X"),
|
||||
WINDOWS("Windows");
|
||||
|
||||
private final String mUiName;
|
||||
|
||||
private Os(String uiName) {
|
||||
mUiName = uiName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return mUiName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current OS as one of the {@link Os} enum values or null.
|
||||
*/
|
||||
public static Os getCurrentOs() {
|
||||
String os = System.getProperty("os.name"); //$NON-NLS-1$
|
||||
if (os.startsWith("Mac OS")) { //$NON-NLS-1$
|
||||
return Os.MACOSX;
|
||||
|
||||
} else if (os.startsWith("Windows")) { //$NON-NLS-1$
|
||||
return Os.WINDOWS;
|
||||
|
||||
} else if (os.startsWith("Linux")) { //$NON-NLS-1$
|
||||
return Os.LINUX;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/** The Architecture that this archvie can be downloaded on. */
|
||||
/** The Architecture that this archive can be downloaded on. */
|
||||
public enum Arch {
|
||||
ANY,
|
||||
PPC,
|
||||
X86,
|
||||
X86_64
|
||||
ANY("Any"),
|
||||
PPC("PowerPC"),
|
||||
X86("x86"),
|
||||
X86_64("x86_64");
|
||||
|
||||
private final String mUiName;
|
||||
|
||||
private Arch(String uiName) {
|
||||
mUiName = uiName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return mUiName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current architecture as one of the {@link Arch} enum values or null.
|
||||
*/
|
||||
public static Arch getCurrentArch() {
|
||||
// Values listed from http://lopica.sourceforge.net/os.html
|
||||
String arch = System.getProperty("os.arch");
|
||||
|
||||
if (arch.equalsIgnoreCase("x86_64") || arch.equalsIgnoreCase("amd64")) {
|
||||
return Arch.X86_64;
|
||||
|
||||
} else if (arch.equalsIgnoreCase("x86")
|
||||
|| arch.equalsIgnoreCase("i386")
|
||||
|| arch.equalsIgnoreCase("i686")) {
|
||||
return Arch.X86;
|
||||
|
||||
} else if (arch.equalsIgnoreCase("ppc") || arch.equalsIgnoreCase("PowerPC")) {
|
||||
return Arch.PPC;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private final Os mOs;
|
||||
@@ -57,11 +143,13 @@ public class Archive implements IDescription {
|
||||
private final long mSize;
|
||||
private final String mChecksum;
|
||||
private final ChecksumType mChecksumType = ChecksumType.SHA1;
|
||||
private final Package mPackage;
|
||||
|
||||
/**
|
||||
* Creates a new archive.
|
||||
*/
|
||||
Archive(Os os, Arch arch, String url, long size, String checksum) {
|
||||
Archive(Package pkg, Os os, Arch arch, String url, long size, String checksum) {
|
||||
mPackage = pkg;
|
||||
mOs = os;
|
||||
mArch = arch;
|
||||
mUrl = url;
|
||||
@@ -69,63 +157,117 @@ public class Archive implements IDescription {
|
||||
mChecksum = checksum;
|
||||
}
|
||||
|
||||
/** Returns the archive size, an int > 0. */
|
||||
/**
|
||||
* Returns the package that created and owns this archive.
|
||||
* It should generally not be null.
|
||||
*/
|
||||
public Package getParentPackage() {
|
||||
return mPackage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the archive size, an int > 0.
|
||||
* Size will be 0 if this a local installed folder of unknown size.
|
||||
*/
|
||||
public long getSize() {
|
||||
return mSize;
|
||||
}
|
||||
|
||||
/** Returns the SHA1 archive checksum, as a 40-char hex. */
|
||||
/**
|
||||
* Returns the SHA1 archive checksum, as a 40-char hex.
|
||||
* Can be empty but not null for local installed folders.
|
||||
*/
|
||||
public String getChecksum() {
|
||||
return mChecksum;
|
||||
}
|
||||
|
||||
/** Returns the checksum type, always {@link ChecksumType#SHA1} right now. */
|
||||
/**
|
||||
* Returns the checksum type, always {@link ChecksumType#SHA1} right now.
|
||||
*/
|
||||
public ChecksumType getChecksumType() {
|
||||
return mChecksumType;
|
||||
}
|
||||
|
||||
/** Returns the optional description URL for all packages (platform, add-on, tool, doc).
|
||||
* Can be empty but not null. */
|
||||
public String getDescUrl() {
|
||||
/**
|
||||
* Returns the download archive URL, either absolute or relative to the repository xml.
|
||||
* For a local installed folder, an URL is frabricated from the folder path.
|
||||
*/
|
||||
public String getUrl() {
|
||||
return mUrl;
|
||||
}
|
||||
|
||||
/** Returns the archive {@link Os} enum. */
|
||||
/**
|
||||
* Returns the archive {@link Os} enum.
|
||||
* Can be null for a local installed folder on an unknown OS.
|
||||
*/
|
||||
public Os getOs() {
|
||||
return mOs;
|
||||
}
|
||||
|
||||
/** Returns the archive {@link Arch} enum. */
|
||||
/**
|
||||
* Returns the archive {@link Arch} enum.
|
||||
* Can be null for a local installed folder on an unknown architecture.
|
||||
*/
|
||||
public Arch getArch() {
|
||||
return mArch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a short description for this archive.
|
||||
*/
|
||||
public String getShortDescription() {
|
||||
String os = "any OS";
|
||||
if (mOs != Os.ANY) {
|
||||
os = capitalize(mOs.toString());
|
||||
String os;
|
||||
if (mOs == null) {
|
||||
os = "unknown OS";
|
||||
} else if (mOs == Os.ANY) {
|
||||
os = "any OS";
|
||||
} else {
|
||||
os = mOs.toString();
|
||||
}
|
||||
|
||||
String arch = "";
|
||||
if (mArch != Arch.ANY) {
|
||||
arch = mArch.toString().toLowerCase();
|
||||
if (mArch != null && mArch != Arch.ANY) {
|
||||
arch = mArch.toString();
|
||||
}
|
||||
|
||||
return String.format("Archive for %1$s %2$s", os, arch);
|
||||
}
|
||||
|
||||
private String capitalize(String string) {
|
||||
if (string.length() > 1) {
|
||||
return string.substring(0, 1).toUpperCase() + string.substring(1).toLowerCase();
|
||||
} else {
|
||||
return string.toUpperCase();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a longer description for this archive.
|
||||
*/
|
||||
public String getLongDescription() {
|
||||
return String.format("%1$s\nSize: %2$d MiB\nSHA1: %3$s",
|
||||
getShortDescription(),
|
||||
Math.round(getSize() / (1024*1024)),
|
||||
getChecksum());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this archive can be installed on the current platform.
|
||||
*/
|
||||
public boolean isCompatible() {
|
||||
// Check OS
|
||||
Os os = getOs();
|
||||
|
||||
if (os != Os.ANY) {
|
||||
Os os2 = Os.getCurrentOs();
|
||||
if (os2 != os) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Check Arch
|
||||
Arch arch = getArch();
|
||||
|
||||
if (arch != Arch.ANY) {
|
||||
Arch arch2 = Arch.getCurrentArch();
|
||||
if (arch2 != arch) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,10 +16,15 @@
|
||||
|
||||
package com.android.sdklib.internal.repository;
|
||||
|
||||
import com.android.sdklib.SdkConstants;
|
||||
import com.android.sdklib.internal.repository.Archive.Arch;
|
||||
import com.android.sdklib.internal.repository.Archive.Os;
|
||||
import com.android.sdklib.repository.SdkRepository;
|
||||
|
||||
import org.w3c.dom.Node;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* Represents a doc XML node in an SDK repository.
|
||||
*/
|
||||
@@ -32,12 +37,39 @@ public class DocPackage extends Package {
|
||||
* <p/>
|
||||
* This constructor should throw an exception if the package cannot be created.
|
||||
*/
|
||||
DocPackage(Node packageNode) {
|
||||
super(packageNode);
|
||||
DocPackage(RepoSource source, Node packageNode) {
|
||||
super(source, packageNode);
|
||||
mApiLevel = getXmlInt(packageNode, SdkRepository.NODE_API_LEVEL, 0);
|
||||
}
|
||||
|
||||
/** Returns the api-level, an int > 0, for platform, add-on and doc packages. */
|
||||
/**
|
||||
* Manually create a new package with one archive and the given attributes.
|
||||
* This is used to create packages from local directories.
|
||||
*/
|
||||
DocPackage(RepoSource source,
|
||||
int apiLevel,
|
||||
int revision,
|
||||
String description,
|
||||
String descUrl,
|
||||
Os archiveOs,
|
||||
Arch archiveArch,
|
||||
String archiveUrl,
|
||||
long archiveSize,
|
||||
String archiveChecksum) {
|
||||
super(source,
|
||||
revision,
|
||||
description,
|
||||
descUrl,
|
||||
archiveOs,
|
||||
archiveArch,
|
||||
archiveUrl,
|
||||
archiveSize,
|
||||
archiveChecksum);
|
||||
mApiLevel = apiLevel;
|
||||
}
|
||||
|
||||
/** Returns the api-level, an int > 0, for platform, add-on and doc packages.
|
||||
* Can be 0 if this is a local package of unknown api-level. */
|
||||
public int getApiLevel() {
|
||||
return mApiLevel;
|
||||
}
|
||||
@@ -45,7 +77,11 @@ public class DocPackage extends Package {
|
||||
/** Returns a short description for an {@link IDescription}. */
|
||||
@Override
|
||||
public String getShortDescription() {
|
||||
return String.format("Documentation for SDK Android API %1$d", getApiLevel());
|
||||
if (mApiLevel != 0) {
|
||||
return String.format("Documentation for Android SDK, API %1$d", mApiLevel);
|
||||
} else {
|
||||
return String.format("Documentation for Android SDK");
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns a long description for an {@link IDescription}. */
|
||||
@@ -55,4 +91,18 @@ public class DocPackage extends Package {
|
||||
getShortDescription(),
|
||||
super.getLongDescription());
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes a potential installation folder if an archive of this package were
|
||||
* to be installed right away in the given SDK root.
|
||||
* <p/>
|
||||
* A "doc" package should always be located in SDK/docs.
|
||||
*
|
||||
* @param osSdkRoot The OS path of the SDK root folder.
|
||||
* @return A new {@link File} corresponding to the directory to use to install this package.
|
||||
*/
|
||||
@Override
|
||||
public File getInstallFolder(String osSdkRoot) {
|
||||
return new File(osSdkRoot, SdkConstants.FD_DOCS);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,13 +34,13 @@ public interface ITaskMonitor {
|
||||
* Sets the description in the current task dialog.
|
||||
* This method can be invoked from a non-UI thread.
|
||||
*/
|
||||
public void setDescription(String description);
|
||||
public void setDescription(String descriptionFormat, Object...args);
|
||||
|
||||
/**
|
||||
* Sets the result text in the current task dialog.
|
||||
* This method can be invoked from a non-UI thread.
|
||||
*/
|
||||
public void setResult(String result);
|
||||
public void setResult(String resultFormat, Object...args);
|
||||
|
||||
/**
|
||||
* Sets the max value of the progress bar.
|
||||
|
||||
@@ -0,0 +1,415 @@
|
||||
/*
|
||||
* 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.internal.repository;
|
||||
|
||||
import com.android.sdklib.IAndroidTarget;
|
||||
import com.android.sdklib.ISdkLog;
|
||||
import com.android.sdklib.SdkConstants;
|
||||
import com.android.sdklib.SdkManager;
|
||||
import com.android.sdklib.internal.repository.Archive.Arch;
|
||||
import com.android.sdklib.internal.repository.Archive.Os;
|
||||
import com.android.sdklib.repository.SdkRepository;
|
||||
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Node;
|
||||
import org.xml.sax.InputSource;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.StringReader;
|
||||
import java.net.MalformedURLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.xml.XMLConstants;
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import javax.xml.transform.stream.StreamSource;
|
||||
import javax.xml.validation.Schema;
|
||||
import javax.xml.validation.SchemaFactory;
|
||||
import javax.xml.validation.Validator;
|
||||
|
||||
/**
|
||||
* Scans a local SDK to find which packages are currently installed.
|
||||
*/
|
||||
public class LocalSdkParser {
|
||||
|
||||
private static final String SOURCE_XML = "source.xml"; //$NON-NLS-1$ // TODO move to global constants
|
||||
private Package[] mPackages;
|
||||
|
||||
public LocalSdkParser() {
|
||||
// TODO Auto-generated constructor stub
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the packages found by the last call to {@link #parseSdk(String)}.
|
||||
*/
|
||||
public Package[] getPackages() {
|
||||
return mPackages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the internal packages list. After this call, {@link #getPackages()} will return
|
||||
* null till {@link #parseSdk(String)} is called.
|
||||
*/
|
||||
public void clearPackages() {
|
||||
mPackages = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Scan the give SDK to find all the packages already installed at this location.
|
||||
* <p/>
|
||||
* Store the packages internally. You can use {@link #getPackages()} to retrieve them
|
||||
* at any time later.
|
||||
*
|
||||
* @param osSdkRoot The path to the SDK folder.
|
||||
* @return The packages found. Can be retrieved later using {@link #getPackages()}.
|
||||
*/
|
||||
public Package[] parseSdk(String osSdkRoot) {
|
||||
ArrayList<Package> packages = new ArrayList<Package>();
|
||||
|
||||
Package pkg = scanDoc(new File(osSdkRoot, SdkConstants.FD_DOCS));
|
||||
if (pkg != null) {
|
||||
packages.add(pkg);
|
||||
}
|
||||
|
||||
pkg = scanTools(new File(osSdkRoot, SdkConstants.FD_TOOLS));
|
||||
if (pkg != null) {
|
||||
packages.add(pkg);
|
||||
}
|
||||
|
||||
// for platforms and add-ons, rely on the SdkManager parser
|
||||
SdkManager sdkman = SdkManager.createManager(osSdkRoot, new ISdkLog() {
|
||||
// A dummy sdk logger that doesn't log anything.
|
||||
public void error(Throwable t, String errorFormat, Object... args) {
|
||||
// pass
|
||||
}
|
||||
public void printf(String msgFormat, Object... args) {
|
||||
// pass
|
||||
}
|
||||
public void warning(String warningFormat, Object... args) {
|
||||
// pass
|
||||
}
|
||||
});
|
||||
|
||||
for(IAndroidTarget target : sdkman.getTargets()) {
|
||||
pkg = null;
|
||||
|
||||
if (target.isPlatform()) {
|
||||
pkg = parseXml(new File(target.getLocation(), SOURCE_XML),
|
||||
SdkRepository.NODE_PLATFORM);
|
||||
if (pkg == null) {
|
||||
pkg = new PlatformPackage(target);
|
||||
}
|
||||
|
||||
} else {
|
||||
pkg = parseXml(new File(target.getLocation(), SOURCE_XML),
|
||||
SdkRepository.NODE_ADD_ON);
|
||||
|
||||
if (pkg == null) {
|
||||
pkg = new AddonPackage(target);
|
||||
}
|
||||
}
|
||||
|
||||
if (pkg != null) {
|
||||
packages.add(pkg);
|
||||
}
|
||||
}
|
||||
|
||||
mPackages = packages.toArray(new Package[packages.size()]);
|
||||
return mPackages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to find a tools package at the given location.
|
||||
* Returns null if not found.
|
||||
*/
|
||||
private Package scanTools(File toolFolder) {
|
||||
// Can we find a source.xml?
|
||||
Package pkg = parseXml(new File(toolFolder, SOURCE_XML), SdkRepository.NODE_TOOL);
|
||||
|
||||
// We're not going to check that all tools are present. At the very least
|
||||
// we should expect to find adb, android and an emulator adapted to the current OS.
|
||||
Set<String> names = new HashSet<String>();
|
||||
for (File file : toolFolder.listFiles()) {
|
||||
names.add(file.getName());
|
||||
}
|
||||
if (!names.contains(SdkConstants.FN_ADB) ||
|
||||
!names.contains(SdkConstants.androidCmdName()) ||
|
||||
!names.contains(SdkConstants.FN_EMULATOR)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// if we don't have the package info, make one up
|
||||
if (pkg == null) {
|
||||
pkg = new ToolPackage(
|
||||
null, //source
|
||||
0, //revision
|
||||
"Tools", //description
|
||||
null, //descUrl
|
||||
Os.getCurrentOs(), //archiveOs
|
||||
Arch.getCurrentArch(), //archiveArch
|
||||
"", //archiveUrl //$NON-NLS-1$
|
||||
0, //archiveSize
|
||||
null //archiveChecksum
|
||||
);
|
||||
}
|
||||
|
||||
return pkg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to find a docs package at the given location.
|
||||
* Returns null if not found.
|
||||
*/
|
||||
private Package scanDoc(File docFolder) {
|
||||
// Can we find a source.xml?
|
||||
Package pkg = parseXml(new File(docFolder, SOURCE_XML), SdkRepository.NODE_DOC);
|
||||
|
||||
// To start with, a doc folder should have an "index.html" to be acceptable.
|
||||
String html = readFile(new File(docFolder, "index.html"));
|
||||
if (html != null) {
|
||||
// Try to find something that looks like this line:
|
||||
// <a href="./sdk/1.5_r1/index.html">
|
||||
// We should find one or more of these and we want the highest version
|
||||
// and release numbers. Note that unfortunately that doesn't give us
|
||||
// the api-level we care about for the doc package.
|
||||
|
||||
String found = null;
|
||||
Pattern re = Pattern.compile(
|
||||
"<a\\s+href=\"./sdk/(\\d\\.\\d_r\\d)/index.html\">",
|
||||
Pattern.DOTALL);
|
||||
Matcher m = re.matcher(html);
|
||||
while(m.find()) {
|
||||
String v = m.group(1);
|
||||
if (found == null || v.compareTo(found) == 1) {
|
||||
found = v;
|
||||
}
|
||||
}
|
||||
|
||||
if (found == null) {
|
||||
// That doesn't look like a doc folder.
|
||||
return null;
|
||||
}
|
||||
|
||||
// We found the line, so it seems like an SDK doc.
|
||||
// Create a pkg if we don't have one yet.
|
||||
|
||||
if (pkg == null) {
|
||||
String url = null;
|
||||
try {
|
||||
url = docFolder.toURI().toURL().toString();
|
||||
} catch (MalformedURLException e) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
pkg = new DocPackage(
|
||||
null, //source
|
||||
0, //apiLevel
|
||||
0, //revision
|
||||
String.format("Documentation for %1$s", found), //description
|
||||
null, //descUrl
|
||||
Os.getCurrentOs(), //archiveOs
|
||||
Arch.getCurrentArch(), //archiveArch
|
||||
url, //archiveUrl
|
||||
0, //archiveSize
|
||||
null //archiveChecksum
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return pkg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the given XML file for the specific element filter.
|
||||
* The element must one of the package type local names: doc, tool, platform or addon.
|
||||
* Returns null if no such package was found.
|
||||
*/
|
||||
private Package parseXml(File sourceXmlFile, String elementFilter) {
|
||||
|
||||
String xml = readFile(sourceXmlFile);
|
||||
if (xml != null) {
|
||||
if (validateXml(xml)) {
|
||||
return parsePackages(xml, elementFilter);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the given XML to find the specific element filter.
|
||||
* The element must one of the package type local names: doc, tool, platform or addon.
|
||||
* Returns null if no such package was found.
|
||||
*/
|
||||
private Package parsePackages(String xml, String elementFilter) {
|
||||
|
||||
try {
|
||||
Document doc = getDocument(xml);
|
||||
|
||||
Node root = getFirstChild(doc, SdkRepository.NODE_SDK_REPOSITORY);
|
||||
if (root != null) {
|
||||
|
||||
for (Node child = root.getFirstChild();
|
||||
child != null;
|
||||
child = child.getNextSibling()) {
|
||||
if (child.getNodeType() == Node.ELEMENT_NODE &&
|
||||
SdkRepository.NS_SDK_REPOSITORY.equals(child.getNamespaceURI()) &&
|
||||
elementFilter.equals(child.getLocalName())) {
|
||||
String name = child.getLocalName();
|
||||
Package p = null;
|
||||
|
||||
try {
|
||||
if (SdkRepository.NODE_ADD_ON.equals(name)) {
|
||||
return new AddonPackage(null /*source*/, child);
|
||||
|
||||
} else if (SdkRepository.NODE_PLATFORM.equals(name)) {
|
||||
return new PlatformPackage(null /*source*/, child);
|
||||
|
||||
} else if (SdkRepository.NODE_DOC.equals(name)) {
|
||||
return new DocPackage(null /*source*/, child);
|
||||
|
||||
} else if (SdkRepository.NODE_TOOL.equals(name)) {
|
||||
return new ToolPackage(null /*source*/, child);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// Ignore invalid packages
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a file as a string.
|
||||
* Returns null if the file could not be read.
|
||||
*/
|
||||
private String readFile(File sourceXmlFile) {
|
||||
FileReader fr = null;
|
||||
try {
|
||||
fr = new FileReader(sourceXmlFile);
|
||||
BufferedReader br = new BufferedReader(fr);
|
||||
StringBuilder dest = new StringBuilder();
|
||||
char[] buf = new char[65536];
|
||||
int n;
|
||||
while ((n = br.read(buf)) > 0) {
|
||||
if (n > 0) {
|
||||
dest.append(buf, 0, n);
|
||||
}
|
||||
}
|
||||
return dest.toString();
|
||||
|
||||
} catch (IOException e) {
|
||||
// ignore
|
||||
|
||||
} finally {
|
||||
if (fr != null) {
|
||||
try {
|
||||
fr.close();
|
||||
} catch (IOException e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates this XML against the SDK Repository schema.
|
||||
* Returns true if the XML was correctly validated.
|
||||
*/
|
||||
private boolean validateXml(String xml) {
|
||||
|
||||
try {
|
||||
Validator validator = getValidator();
|
||||
validator.validate(new StreamSource(new StringReader(xml)));
|
||||
return true;
|
||||
|
||||
} catch (SAXException e) {
|
||||
// ignore
|
||||
|
||||
} catch (IOException e) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method that returns a validator for our XSD
|
||||
*/
|
||||
private Validator getValidator() throws SAXException {
|
||||
InputStream xsdStream = SdkRepository.getXsdStream();
|
||||
SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
|
||||
|
||||
// This may throw a SAX Exception if the schema itself is not a valid XSD
|
||||
Schema schema = factory.newSchema(new StreamSource(xsdStream));
|
||||
|
||||
Validator validator = schema.newValidator();
|
||||
|
||||
return validator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the first child element with the given XML local name.
|
||||
* If xmlLocalName is null, returns the very first child element.
|
||||
*/
|
||||
private Node getFirstChild(Node node, String xmlLocalName) {
|
||||
|
||||
for(Node child = node.getFirstChild(); child != null; child = child.getNextSibling()) {
|
||||
if (child.getNodeType() == Node.ELEMENT_NODE &&
|
||||
SdkRepository.NS_SDK_REPOSITORY.equals(child.getNamespaceURI())) {
|
||||
if (xmlLocalName == null || child.getLocalName().equals(xmlLocalName)) {
|
||||
return child;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes an XML document as a string as parameter and returns a DOM for it.
|
||||
*/
|
||||
private Document getDocument(String xml)
|
||||
throws ParserConfigurationException, SAXException, IOException {
|
||||
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
|
||||
factory.setIgnoringComments(true);
|
||||
factory.setNamespaceAware(true);
|
||||
|
||||
DocumentBuilder builder = factory.newDocumentBuilder();
|
||||
Document doc = builder.parse(new InputSource(new StringReader(xml)));
|
||||
|
||||
return doc;
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,7 @@ import com.android.sdklib.repository.SdkRepository;
|
||||
|
||||
import org.w3c.dom.Node;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
@@ -42,13 +43,15 @@ public abstract class Package implements IDescription {
|
||||
private final String mDescription;
|
||||
private final String mDescUrl;
|
||||
private final Archive[] mArchives;
|
||||
private final RepoSource mSource;
|
||||
|
||||
/**
|
||||
* Creates a new package from the attributes and elements of the given XML node.
|
||||
* <p/>
|
||||
* This constructor should throw an exception if the package cannot be created.
|
||||
*/
|
||||
Package(Node packageNode) {
|
||||
Package(RepoSource source, Node packageNode) {
|
||||
mSource = source;
|
||||
mRevision = getXmlInt (packageNode, SdkRepository.NODE_REVISION, 0);
|
||||
mDescription = getXmlString(packageNode, SdkRepository.NODE_DESCRIPTION);
|
||||
mDescUrl = getXmlString(packageNode, SdkRepository.NODE_DESC_URL);
|
||||
@@ -56,6 +59,35 @@ public abstract class Package implements IDescription {
|
||||
mArchives = parseArchives(getFirstChild(packageNode, SdkRepository.NODE_ARCHIVES));
|
||||
}
|
||||
|
||||
/**
|
||||
* Manually create a new package with one archive and the given attributes.
|
||||
* This is used to create packages from local directories.
|
||||
*/
|
||||
public Package(RepoSource source,
|
||||
int revision,
|
||||
String description,
|
||||
String descUrl,
|
||||
Os archiveOs,
|
||||
Arch archiveArch,
|
||||
String archiveUrl,
|
||||
long archiveSize,
|
||||
String archiveChecksum) {
|
||||
mSource = source;
|
||||
mRevision = revision;
|
||||
mDescription = description;
|
||||
mDescUrl = descUrl;
|
||||
mArchives = new Archive[1];
|
||||
mArchives[0] = new Archive(this,
|
||||
archiveOs,
|
||||
archiveArch,
|
||||
archiveUrl,
|
||||
archiveSize,
|
||||
archiveChecksum);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses an XML node to process the <archives> element.
|
||||
*/
|
||||
private Archive[] parseArchives(Node archivesNode) {
|
||||
ArrayList<Archive> archives = new ArrayList<Archive>();
|
||||
|
||||
@@ -75,52 +107,99 @@ public abstract class Package implements IDescription {
|
||||
return archives.toArray(new Archive[archives.size()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses one <archive> element from an <archives> container.
|
||||
*/
|
||||
private Archive parseArchive(Node archiveNode) {
|
||||
Archive a = new Archive(
|
||||
this,
|
||||
(Os) getEnumAttribute(archiveNode, SdkRepository.ATTR_OS,
|
||||
Os.values(), null),
|
||||
(Arch) getEnumAttribute(archiveNode, SdkRepository.ATTR_ARCH,
|
||||
Arch.values(), Arch.ANY),
|
||||
getXmlString(archiveNode, SdkRepository.NODE_URL),
|
||||
getXmlInt(archiveNode, SdkRepository.NODE_SIZE, 0),
|
||||
getXmlLong(archiveNode, SdkRepository.NODE_SIZE, 0),
|
||||
getXmlString(archiveNode, SdkRepository.NODE_CHECKSUM)
|
||||
);
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
/** Returns the revision, an int > 0, for all packages (platform, add-on, tool, doc). */
|
||||
/**
|
||||
* Returns the source that created (and owns) this package. Can be null.
|
||||
*/
|
||||
public RepoSource getParentSource() {
|
||||
return mSource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the revision, an int > 0, for all packages (platform, add-on, tool, doc).
|
||||
* Can be 0 if this is a local package of unknown revision.
|
||||
*/
|
||||
public int getRevision() {
|
||||
return mRevision;
|
||||
}
|
||||
|
||||
/** Returns the optional description for all packages (platform, add-on, tool, doc) or
|
||||
* for a lib. */
|
||||
/**
|
||||
* Returns the optional description for all packages (platform, add-on, tool, doc) or
|
||||
* for a lib. Can be empty but not null.
|
||||
*/
|
||||
public String getDescription() {
|
||||
return mDescription;
|
||||
}
|
||||
|
||||
/** Returns the optional description URL for all packages (platform, add-on, tool, doc).
|
||||
* Can be empty but not null. */
|
||||
/**
|
||||
* Returns the optional description URL for all packages (platform, add-on, tool, doc).
|
||||
* Can be empty but not null.
|
||||
*/
|
||||
public String getDescUrl() {
|
||||
return mDescUrl;
|
||||
}
|
||||
|
||||
/** Returns the archives defined in this package. Can be an empty array but not null. */
|
||||
/**
|
||||
* Returns the archives defined in this package.
|
||||
* Can be an empty array but not null.
|
||||
*/
|
||||
public Archive[] getArchives() {
|
||||
return mArchives;
|
||||
}
|
||||
|
||||
/** Returns a short description for an {@link IDescription}. */
|
||||
/**
|
||||
* Returns a short description for an {@link IDescription}.
|
||||
* Can be empty but not null.
|
||||
*/
|
||||
public abstract String getShortDescription();
|
||||
|
||||
/** Returns a long description for an {@link IDescription}. */
|
||||
/**
|
||||
* Returns a long description for an {@link IDescription}.
|
||||
* Can be empty but not null.
|
||||
*/
|
||||
public String getLongDescription() {
|
||||
return String.format("%1$s\nRevision %2$d", getDescription(), getRevision());
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes a potential installation folder if an archive of this package were
|
||||
* to be installed right away in the given SDK root.
|
||||
* <p/>
|
||||
* Some types of packages install in a fix location, for example docs and tools.
|
||||
* In this case the returned folder may already exist with a different archive installed
|
||||
* at the desired location.
|
||||
* For other packages types, such as add-on or platform, the folder name is only partially
|
||||
* relevant to determine the content and thus a real check will be done to provide an
|
||||
* existing or new folder depending on the current content of the SDK.
|
||||
*
|
||||
* @param osSdkRoot The OS path of the SDK root folder.
|
||||
* @return A new {@link File} corresponding to the directory to use to install this package.
|
||||
*/
|
||||
public abstract File getInstallFolder(String osSdkRoot);
|
||||
|
||||
//---
|
||||
|
||||
/**
|
||||
* Returns the first child element with the given XML local name.
|
||||
* If xmlLocalName is null, returns the very first child element.
|
||||
*/
|
||||
protected static Node getFirstChild(Node node, String xmlLocalName) {
|
||||
|
||||
for(Node child = node.getFirstChild(); child != null; child = child.getNextSibling()) {
|
||||
@@ -158,6 +237,19 @@ public abstract class Package implements IDescription {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the value of that XML element as a long.
|
||||
* Returns the default value when the element is missing or is not an integer.
|
||||
*/
|
||||
protected static long getXmlLong(Node node, String xmlLocalName, long defaultValue) {
|
||||
String s = getXmlString(node, xmlLocalName);
|
||||
try {
|
||||
return Long.parseLong(s);
|
||||
} catch (NumberFormatException e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve an attribute which value must match one of the given enums using a
|
||||
* case-insensitive name match.
|
||||
|
||||
@@ -16,10 +16,17 @@
|
||||
|
||||
package com.android.sdklib.internal.repository;
|
||||
|
||||
import com.android.sdklib.IAndroidTarget;
|
||||
import com.android.sdklib.SdkConstants;
|
||||
import com.android.sdklib.SdkManager;
|
||||
import com.android.sdklib.internal.repository.Archive.Arch;
|
||||
import com.android.sdklib.internal.repository.Archive.Os;
|
||||
import com.android.sdklib.repository.SdkRepository;
|
||||
|
||||
import org.w3c.dom.Node;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* Represents a platform XML node in an SDK repository.
|
||||
*/
|
||||
@@ -33,12 +40,33 @@ public class PlatformPackage extends Package {
|
||||
* <p/>
|
||||
* This constructor should throw an exception if the package cannot be created.
|
||||
*/
|
||||
PlatformPackage(Node packageNode) {
|
||||
super(packageNode);
|
||||
PlatformPackage(RepoSource source, Node packageNode) {
|
||||
super(source, packageNode);
|
||||
mVersion = getXmlString(packageNode, SdkRepository.NODE_VERSION);
|
||||
mApiLevel = getXmlInt (packageNode, SdkRepository.NODE_API_LEVEL, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new platform package based on an actual {@link IAndroidTarget} (with
|
||||
* must have {@link IAndroidTarget#isPlatform()} true) from the {@link SdkManager}.
|
||||
* This is used to list local SDK folders.
|
||||
*/
|
||||
PlatformPackage(IAndroidTarget target) {
|
||||
super( null, //source
|
||||
0, //revision
|
||||
target.getDescription(), //description
|
||||
null, //descUrl
|
||||
Os.getCurrentOs(), //archiveOs
|
||||
Arch.getCurrentArch(), //archiveArch
|
||||
"", //archiveUrl //$NON-NLS-1$
|
||||
0, //archiveSize
|
||||
null //archiveChecksum
|
||||
);
|
||||
|
||||
mApiLevel = target.getApiVersionNumber();
|
||||
mVersion = target.getApiVersionName();
|
||||
}
|
||||
|
||||
/** Returns the version, a string, for platform packages. */
|
||||
public String getVersion() {
|
||||
return mVersion;
|
||||
@@ -64,4 +92,23 @@ public class PlatformPackage extends Package {
|
||||
getShortDescription(),
|
||||
super.getLongDescription());
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes a potential installation folder if an archive of this package were
|
||||
* to be installed right away in the given SDK root.
|
||||
* <p/>
|
||||
* A platform package is typically installed in SDK/platforms/android-"version".
|
||||
* However if we can find a different directory under SDK/platform that already
|
||||
* has this platform version installed, we'll use that one.
|
||||
*
|
||||
* @param osSdkRoot The OS path of the SDK root folder.
|
||||
* @return A new {@link File} corresponding to the directory to use to install this package.
|
||||
*/
|
||||
@Override
|
||||
public File getInstallFolder(String osSdkRoot) {
|
||||
File platforms = new File(osSdkRoot, SdkConstants.FD_PLATFORMS);
|
||||
File folder = new File(platforms, String.format("android-%s", getVersion())); //$NON-NLS-1$
|
||||
// TODO find similar existing platform in platforms folder
|
||||
return folder;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,12 +68,21 @@ public class RepoSource implements IDescription {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of known packages. This is null when the source hasn't been loaded yet.
|
||||
* Returns the list of known packages found by the last call to {@link #load(ITaskFactory)}.
|
||||
* This is null when the source hasn't been loaded yet.
|
||||
*/
|
||||
public Package[] getPackages() {
|
||||
return mPackages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the internal packages list. After this call, {@link #getPackages()} will return
|
||||
* null till {@link #load(ITaskFactory)} is called.
|
||||
*/
|
||||
public void clearPackages() {
|
||||
mPackages = null;
|
||||
}
|
||||
|
||||
public String getShortDescription() {
|
||||
return mUrl;
|
||||
}
|
||||
@@ -93,7 +102,7 @@ public class RepoSource implements IDescription {
|
||||
|
||||
setDefaultDescription();
|
||||
|
||||
monitor.setDescription(String.format("Fetching %1$s", mUrl));
|
||||
monitor.setDescription("Fetching %1$s", mUrl);
|
||||
monitor.incProgress(1);
|
||||
|
||||
String xml = fetchUrl(mUrl, monitor);
|
||||
@@ -136,7 +145,10 @@ public class RepoSource implements IDescription {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* Fetches the document at the given URL and returns it as a string.
|
||||
* Returns null if anything wrong happens and write errors to the monitor.
|
||||
*
|
||||
* References:
|
||||
* Java URL Connection: http://java.sun.com/docs/books/tutorial/networking/urls/readingWriting.html
|
||||
* Java URL Reader: http://java.sun.com/docs/books/tutorial/networking/urls/readingURL.html
|
||||
@@ -186,6 +198,10 @@ public class RepoSource implements IDescription {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates this XML against the SDK Repository schema.
|
||||
* Returns true if the XML was correctly validated.
|
||||
*/
|
||||
private boolean validateXml(String xml, ITaskMonitor monitor) {
|
||||
|
||||
try {
|
||||
@@ -203,7 +219,9 @@ public class RepoSource implements IDescription {
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Helper method that returns a validator for our XSD */
|
||||
/**
|
||||
* Helper method that returns a validator for our XSD
|
||||
*/
|
||||
private Validator getValidator() throws SAXException {
|
||||
InputStream xsdStream = SdkRepository.getXsdStream();
|
||||
SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
|
||||
@@ -217,6 +235,10 @@ public class RepoSource implements IDescription {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parse all packages defined in the SDK Repository XML and creates
|
||||
* a new mPackages array with them.
|
||||
*/
|
||||
private boolean parsePackages(String xml, ITaskMonitor monitor) {
|
||||
|
||||
try {
|
||||
@@ -237,22 +259,21 @@ public class RepoSource implements IDescription {
|
||||
|
||||
try {
|
||||
if (SdkRepository.NODE_ADD_ON.equals(name)) {
|
||||
p = new AddonPackage(child);
|
||||
p = new AddonPackage(this, child);
|
||||
|
||||
} else if (!mAddonOnly) {
|
||||
if (SdkRepository.NODE_PLATFORM.equals(name)) {
|
||||
p = new PlatformPackage(child);
|
||||
p = new PlatformPackage(this, child);
|
||||
} else if (SdkRepository.NODE_DOC.equals(name)) {
|
||||
p = new DocPackage(child);
|
||||
p = new DocPackage(this, child);
|
||||
} else if (SdkRepository.NODE_TOOL.equals(name)) {
|
||||
p = new ToolPackage(child);
|
||||
p = new ToolPackage(this, child);
|
||||
}
|
||||
}
|
||||
|
||||
if (p != null) {
|
||||
packages.add(p);
|
||||
monitor.setDescription(
|
||||
String.format("Found %1$s", p.getShortDescription()));
|
||||
monitor.setDescription("Found %1$s", p.getShortDescription());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// Ignore invalid packages
|
||||
@@ -278,6 +299,10 @@ public class RepoSource implements IDescription {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the first child element with the given XML local name.
|
||||
* If xmlLocalName is null, returns the very first child element.
|
||||
*/
|
||||
private Node getFirstChild(Node node, String xmlLocalName) {
|
||||
|
||||
for(Node child = node.getFirstChild(); child != null; child = child.getNextSibling()) {
|
||||
@@ -292,6 +317,9 @@ public class RepoSource implements IDescription {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes an XML document as a string as parameter and returns a DOM for it.
|
||||
*/
|
||||
private Document getDocument(String xml)
|
||||
throws ParserConfigurationException, SAXException, IOException {
|
||||
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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.internal.repository;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* A list of sdk-repository sources.
|
||||
*/
|
||||
public class RepoSources {
|
||||
|
||||
private ArrayList<RepoSource> mSources = new ArrayList<RepoSource>();
|
||||
private ITaskFactory mTaskFactory;
|
||||
|
||||
public RepoSources() {
|
||||
}
|
||||
|
||||
public void setTaskFactory(ITaskFactory taskFactory) {
|
||||
mTaskFactory = taskFactory;
|
||||
}
|
||||
|
||||
public ITaskFactory getTaskFactory() {
|
||||
return mTaskFactory;
|
||||
}
|
||||
|
||||
public void add(RepoSource source) {
|
||||
mSources.add(source);
|
||||
}
|
||||
|
||||
public ArrayList<RepoSource> getSources() {
|
||||
return mSources;
|
||||
}
|
||||
}
|
||||
@@ -16,8 +16,14 @@
|
||||
|
||||
package com.android.sdklib.internal.repository;
|
||||
|
||||
import com.android.sdklib.SdkConstants;
|
||||
import com.android.sdklib.internal.repository.Archive.Arch;
|
||||
import com.android.sdklib.internal.repository.Archive.Os;
|
||||
|
||||
import org.w3c.dom.Node;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* Represents a tool XML node in an SDK repository.
|
||||
*/
|
||||
@@ -28,8 +34,32 @@ public class ToolPackage extends Package {
|
||||
* <p/>
|
||||
* This constructor should throw an exception if the package cannot be created.
|
||||
*/
|
||||
ToolPackage(Node packageNode) {
|
||||
super(packageNode);
|
||||
ToolPackage(RepoSource source, Node packageNode) {
|
||||
super(source, packageNode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Manually create a new package with one archive and the given attributes.
|
||||
* This is used to create packages from local directories.
|
||||
*/
|
||||
ToolPackage(RepoSource source,
|
||||
int revision,
|
||||
String description,
|
||||
String descUrl,
|
||||
Os archiveOs,
|
||||
Arch archiveArch,
|
||||
String archiveUrl,
|
||||
long archiveSize,
|
||||
String archiveChecksum) {
|
||||
super(source,
|
||||
revision,
|
||||
description,
|
||||
descUrl,
|
||||
archiveOs,
|
||||
archiveArch,
|
||||
archiveUrl,
|
||||
archiveSize,
|
||||
archiveChecksum);
|
||||
}
|
||||
|
||||
/** Returns a short description for an {@link IDescription}. */
|
||||
@@ -45,4 +75,18 @@ public class ToolPackage extends Package {
|
||||
getRevision(),
|
||||
super.getLongDescription());
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes a potential installation folder if an archive of this package were
|
||||
* to be installed right away in the given SDK root.
|
||||
* <p/>
|
||||
* A "tool" package should always be located in SDK/tools.
|
||||
*
|
||||
* @param osSdkRoot The OS path of the SDK root folder.
|
||||
* @return A new {@link File} corresponding to the directory to use to install this package.
|
||||
*/
|
||||
@Override
|
||||
public File getInstallFolder(String osSdkRoot) {
|
||||
return new File(osSdkRoot, SdkConstants.FD_TOOLS);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package com.android.sdklib.repository;
|
||||
|
||||
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
@@ -23,6 +24,10 @@ import java.io.InputStream;
|
||||
*/
|
||||
public class SdkRepository {
|
||||
|
||||
/** The URL of the official Google sdk-repository site. */
|
||||
public static final String URL_GOOGLE_SDK_REPO_SITE =
|
||||
"https://dl.google.com/android/eclipse/repository/index.xml"; //$NON-NLS-1$
|
||||
|
||||
/** The XML namespace of the sdk-repository XML. */
|
||||
public static final String NS_SDK_REPOSITORY =
|
||||
"http://schemas.android.com/sdk/android/repository/1"; //$NON-NLS-1$
|
||||
|
||||
@@ -21,8 +21,11 @@ import com.android.sdklib.internal.repository.ITaskMonitor;
|
||||
|
||||
import org.eclipse.jface.viewers.TableViewer;
|
||||
import org.eclipse.swt.SWT;
|
||||
import org.eclipse.swt.events.ControlAdapter;
|
||||
import org.eclipse.swt.events.ControlEvent;
|
||||
import org.eclipse.swt.events.SelectionAdapter;
|
||||
import org.eclipse.swt.events.SelectionEvent;
|
||||
import org.eclipse.swt.graphics.Rectangle;
|
||||
import org.eclipse.swt.layout.GridData;
|
||||
import org.eclipse.swt.layout.GridLayout;
|
||||
import org.eclipse.swt.widgets.Button;
|
||||
@@ -44,25 +47,22 @@ import org.eclipse.swt.widgets.Text;
|
||||
* - refresh callback
|
||||
*/
|
||||
|
||||
public class InstalledPackagesPage extends Composite {
|
||||
public class LocalPackagesPage extends Composite {
|
||||
private UpdaterData mUpdaterData;
|
||||
|
||||
private Label mSdkLocLabel;
|
||||
private Text mSdkLocText;
|
||||
private Button mSdkLocBrowse;
|
||||
private Label mInstalledPkgLabel;
|
||||
private TableViewer mTableViewerInstPkg;
|
||||
private Table mTableInstPkg;
|
||||
private TableColumn mColumnInstSummary;
|
||||
private TableColumn mColumnInstApiLevel;
|
||||
private TableColumn mColumnInstRevision;
|
||||
private TableViewer mTableViewerPackages;
|
||||
private Table mTablePackages;
|
||||
private TableColumn mColumnPackages;
|
||||
private Group mDescriptionContainer;
|
||||
private Composite mInstButtons;
|
||||
private Button mInstUpdate;
|
||||
private Composite mContainerButtons;
|
||||
private Button mUpdateButton;
|
||||
private Label mPlaceholder1;
|
||||
private Button mInstDelete;
|
||||
private Button mDeleteButton;
|
||||
private Label mPlaceholder2;
|
||||
private Button mInstHomePage;
|
||||
private Button mHomePageButton;
|
||||
private Label mDescriptionLabel;
|
||||
|
||||
/**
|
||||
@@ -71,12 +71,13 @@ public class InstalledPackagesPage extends Composite {
|
||||
* @param updaterData An instance of {@link UpdaterData}. If null, a local
|
||||
* one will be allocated just to help with the SWT Designer.
|
||||
*/
|
||||
public InstalledPackagesPage(Composite parent, UpdaterData updaterData) {
|
||||
public LocalPackagesPage(Composite parent, UpdaterData updaterData) {
|
||||
super(parent, SWT.BORDER);
|
||||
|
||||
mUpdaterData = updaterData != null ? updaterData : new UpdaterData();
|
||||
|
||||
createContents(this);
|
||||
postCreate(); //$hide$
|
||||
}
|
||||
|
||||
private void createContents(Composite parent) {
|
||||
@@ -84,26 +85,14 @@ public class InstalledPackagesPage extends Composite {
|
||||
|
||||
createSdkLocation(parent);
|
||||
|
||||
mInstalledPkgLabel = new Label(parent, SWT.NONE);
|
||||
mInstalledPkgLabel.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 3, 1));
|
||||
mInstalledPkgLabel.setText("Installed Packages:");
|
||||
mTableViewerPackages = new TableViewer(parent, SWT.BORDER | SWT.FULL_SELECTION);
|
||||
mTablePackages = mTableViewerPackages.getTable();
|
||||
mTablePackages.setHeaderVisible(true);
|
||||
mTablePackages.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 3, 1));
|
||||
|
||||
mTableViewerInstPkg = new TableViewer(parent, SWT.BORDER | SWT.FULL_SELECTION);
|
||||
mTableInstPkg = mTableViewerInstPkg.getTable();
|
||||
mTableInstPkg.setHeaderVisible(true);
|
||||
mTableInstPkg.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 3, 1));
|
||||
|
||||
mColumnInstSummary = new TableColumn(mTableInstPkg, SWT.NONE);
|
||||
mColumnInstSummary.setWidth(377);
|
||||
mColumnInstSummary.setText("Summary");
|
||||
|
||||
mColumnInstApiLevel = new TableColumn(mTableInstPkg, SWT.NONE);
|
||||
mColumnInstApiLevel.setWidth(100);
|
||||
mColumnInstApiLevel.setText("API Level");
|
||||
|
||||
mColumnInstRevision = new TableColumn(mTableInstPkg, SWT.NONE);
|
||||
mColumnInstRevision.setWidth(100);
|
||||
mColumnInstRevision.setText("Revision");
|
||||
mColumnPackages = new TableColumn(mTablePackages, SWT.NONE);
|
||||
mColumnPackages.setWidth(377);
|
||||
mColumnPackages.setText("Installed Packages");
|
||||
|
||||
mDescriptionContainer = new Group(parent, SWT.NONE);
|
||||
mDescriptionContainer.setLayout(new GridLayout(1, false));
|
||||
@@ -114,32 +103,32 @@ public class InstalledPackagesPage extends Composite {
|
||||
mDescriptionLabel.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, true, 1, 1));
|
||||
mDescriptionLabel.setText("Line1\nLine2\nLine3");
|
||||
|
||||
mInstButtons = new Composite(parent, SWT.NONE);
|
||||
mInstButtons.setLayout(new GridLayout(5, false));
|
||||
mInstButtons.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 3, 1));
|
||||
mContainerButtons = new Composite(parent, SWT.NONE);
|
||||
mContainerButtons.setLayout(new GridLayout(5, false));
|
||||
mContainerButtons.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 3, 1));
|
||||
|
||||
mInstUpdate = new Button(mInstButtons, SWT.NONE);
|
||||
mInstUpdate.addSelectionListener(new SelectionAdapter() {
|
||||
mUpdateButton = new Button(mContainerButtons, SWT.NONE);
|
||||
mUpdateButton.addSelectionListener(new SelectionAdapter() {
|
||||
@Override
|
||||
public void widgetSelected(SelectionEvent e) {
|
||||
onUpdateInstalledPackage(); //$hide$ (hide from SWT designer)
|
||||
}
|
||||
});
|
||||
mInstUpdate.setText("Update...");
|
||||
mUpdateButton.setText("Update...");
|
||||
|
||||
mPlaceholder1 = new Label(mInstButtons, SWT.NONE);
|
||||
mPlaceholder1 = new Label(mContainerButtons, SWT.NONE);
|
||||
mPlaceholder1.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, true, false, 1, 1));
|
||||
|
||||
mInstDelete = new Button(mInstButtons, SWT.NONE);
|
||||
mInstDelete.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, false, false, 1, 1));
|
||||
mInstDelete.setText("Delete...");
|
||||
mDeleteButton = new Button(mContainerButtons, SWT.NONE);
|
||||
mDeleteButton.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, false, false, 1, 1));
|
||||
mDeleteButton.setText("Delete...");
|
||||
|
||||
mPlaceholder2 = new Label(mInstButtons, SWT.NONE);
|
||||
mPlaceholder2 = new Label(mContainerButtons, SWT.NONE);
|
||||
mPlaceholder2.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, true, false, 1, 1));
|
||||
|
||||
mInstHomePage = new Button(mInstButtons, SWT.NONE);
|
||||
mInstHomePage.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));
|
||||
mInstHomePage.setText("Home Page...");
|
||||
mHomePageButton = new Button(mContainerButtons, SWT.NONE);
|
||||
mHomePageButton.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));
|
||||
mHomePageButton.setText("Home Page...");
|
||||
}
|
||||
|
||||
private void createSdkLocation(Composite parent) {
|
||||
@@ -175,7 +164,36 @@ public class InstalledPackagesPage extends Composite {
|
||||
// Hide everything down-below from SWT designer
|
||||
//$hide>>$
|
||||
|
||||
private void postCreate() {
|
||||
adjustColumnsWidth();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds a listener to adjust the columns width when the parent is resized.
|
||||
* <p/>
|
||||
* If we need something more fancy, we might want to use this:
|
||||
* http://dev.eclipse.org/viewcvs/index.cgi/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/Snippet77.java?view=co
|
||||
*/
|
||||
private void adjustColumnsWidth() {
|
||||
// Add a listener to resize the column to the full width of the table
|
||||
mTablePackages.addControlListener(new ControlAdapter() {
|
||||
@Override
|
||||
public void controlResized(ControlEvent e) {
|
||||
Rectangle r = mTablePackages.getClientArea();
|
||||
mColumnPackages.setWidth(r.width);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void setInput(LocalSdkAdapter localSdkAdapter) {
|
||||
mTableViewerPackages.setLabelProvider( localSdkAdapter.getLabelProvider());
|
||||
mTableViewerPackages.setContentProvider(localSdkAdapter.getContentProvider());
|
||||
mTableViewerPackages.setInput(localSdkAdapter);
|
||||
}
|
||||
|
||||
protected void onUpdateInstalledPackage() {
|
||||
// TODO just a test, needs to be removed later.
|
||||
ProgressTask.start(getShell(), "Test", new ITask() {
|
||||
public void run(ITaskMonitor monitor) {
|
||||
monitor.setDescription("Test");
|
||||
@@ -189,6 +207,7 @@ public class InstalledPackagesPage extends Composite {
|
||||
try {
|
||||
Thread.sleep(5);
|
||||
} catch (InterruptedException e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* 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.sdkuilib.internal.repository;
|
||||
|
||||
import com.android.sdklib.internal.repository.IDescription;
|
||||
import com.android.sdklib.internal.repository.LocalSdkParser;
|
||||
import com.android.sdklib.internal.repository.Package;
|
||||
import com.android.sdklib.internal.repository.RepoSource;
|
||||
|
||||
import org.eclipse.jface.viewers.IContentProvider;
|
||||
import org.eclipse.jface.viewers.ILabelProvider;
|
||||
import org.eclipse.jface.viewers.IStructuredContentProvider;
|
||||
import org.eclipse.jface.viewers.LabelProvider;
|
||||
import org.eclipse.jface.viewers.Viewer;
|
||||
import org.eclipse.swt.graphics.Image;
|
||||
|
||||
/**
|
||||
* Table adapters to use the local SDK list.
|
||||
*/
|
||||
class LocalSdkAdapter {
|
||||
|
||||
private final LocalSdkParser mLocalSdkParser;
|
||||
private String mOsSdkRoot;
|
||||
|
||||
public LocalSdkAdapter(LocalSdkParser localSdkParser) {
|
||||
mLocalSdkParser = localSdkParser;
|
||||
}
|
||||
|
||||
public void setSdkRoot(String osSdkRoot) {
|
||||
mOsSdkRoot = osSdkRoot;
|
||||
mLocalSdkParser.clearPackages();
|
||||
}
|
||||
|
||||
public ILabelProvider getLabelProvider() {
|
||||
return new ViewerLabelProvider();
|
||||
}
|
||||
|
||||
|
||||
public IContentProvider getContentProvider() {
|
||||
return new TableContentProvider();
|
||||
}
|
||||
|
||||
// ------------
|
||||
|
||||
public static class ViewerLabelProvider extends LabelProvider {
|
||||
/** Returns null by default */
|
||||
@Override
|
||||
public Image getImage(Object element) {
|
||||
return super.getImage(element);
|
||||
}
|
||||
|
||||
/** Returns the toString of the element. */
|
||||
@Override
|
||||
public String getText(Object element) {
|
||||
if (element instanceof IDescription) {
|
||||
return ((IDescription) element).getShortDescription();
|
||||
}
|
||||
return super.getText(element);
|
||||
}
|
||||
}
|
||||
|
||||
// ------------
|
||||
|
||||
private static class TableContentProvider implements IStructuredContentProvider {
|
||||
|
||||
// Called when the viewer is disposed
|
||||
public void dispose() {
|
||||
// pass
|
||||
}
|
||||
|
||||
// Called when the input is set or changed on the provider
|
||||
public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
|
||||
// pass
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to collect the root elements for the given input.
|
||||
* The input here is a {@link LocalSdkAdapter} object, this returns an array
|
||||
* of {@link RepoSource}.
|
||||
*/
|
||||
public Object[] getElements(Object inputElement) {
|
||||
if (inputElement instanceof LocalSdkAdapter) {
|
||||
LocalSdkAdapter adapter = (LocalSdkAdapter) inputElement;
|
||||
LocalSdkParser parser = adapter.mLocalSdkParser;
|
||||
|
||||
Package[] packages = parser.getPackages();
|
||||
|
||||
if (packages == null) {
|
||||
// load on demand the first time
|
||||
packages = parser.parseSdk(adapter.mOsSdkRoot);
|
||||
}
|
||||
|
||||
if (packages != null) {
|
||||
return packages;
|
||||
}
|
||||
}
|
||||
|
||||
return new Object[0];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -152,11 +152,11 @@ class ProgressTask extends Dialog
|
||||
* Sets the description in the current task dialog.
|
||||
* This method can be invoke from a non-UI thread.
|
||||
*/
|
||||
public void setDescription(final String description) {
|
||||
public void setDescription(final String descriptionFormat, final Object...args) {
|
||||
mDialogShell.getDisplay().asyncExec(new Runnable() {
|
||||
public void run() {
|
||||
if (!mLabel.isDisposed()) {
|
||||
mLabel.setText(description);
|
||||
mLabel.setText(String.format(descriptionFormat, args));
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -166,14 +166,14 @@ class ProgressTask extends Dialog
|
||||
* Sets the description in the current task dialog.
|
||||
* This method can be invoke from a non-UI thread.
|
||||
*/
|
||||
public void setResult(final String result) {
|
||||
public void setResult(final String resultFormat, final Object...args) {
|
||||
mAutomaticallyCloseOnTaskCompletion = false;
|
||||
if (!mDialogShell.isDisposed()) {
|
||||
mDialogShell.getDisplay().asyncExec(new Runnable() {
|
||||
public void run() {
|
||||
if (!mResultText.isDisposed()) {
|
||||
mResultText.setVisible(true);
|
||||
mResultText.setText(result);
|
||||
mResultText.setText(String.format(resultFormat, args));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -17,9 +17,14 @@
|
||||
package com.android.sdkuilib.internal.repository;
|
||||
|
||||
|
||||
import com.android.sdklib.internal.repository.Archive;
|
||||
import com.android.sdklib.internal.repository.IDescription;
|
||||
|
||||
import org.eclipse.jface.viewers.CheckStateChangedEvent;
|
||||
import org.eclipse.jface.viewers.CheckboxTreeViewer;
|
||||
import org.eclipse.jface.viewers.DoubleClickEvent;
|
||||
import org.eclipse.jface.viewers.ICheckStateListener;
|
||||
import org.eclipse.jface.viewers.IDoubleClickListener;
|
||||
import org.eclipse.jface.viewers.ISelection;
|
||||
import org.eclipse.jface.viewers.ITreeSelection;
|
||||
import org.eclipse.swt.SWT;
|
||||
@@ -36,10 +41,8 @@ import org.eclipse.swt.widgets.Group;
|
||||
import org.eclipse.swt.widgets.Label;
|
||||
import org.eclipse.swt.widgets.Tree;
|
||||
import org.eclipse.swt.widgets.TreeColumn;
|
||||
import org.eclipse.jface.viewers.ICheckStateListener;
|
||||
import org.eclipse.jface.viewers.CheckStateChangedEvent;
|
||||
import org.eclipse.jface.viewers.IDoubleClickListener;
|
||||
import org.eclipse.jface.viewers.DoubleClickEvent;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/*
|
||||
* TODO list
|
||||
@@ -54,8 +57,9 @@ import org.eclipse.jface.viewers.DoubleClickEvent;
|
||||
* - install selected callback
|
||||
*/
|
||||
|
||||
public class AvailablePackagesPage extends Composite {
|
||||
public class RemotePackagesPage extends Composite {
|
||||
|
||||
private final UpdaterWindowImpl mUpdaterWindow;
|
||||
private final UpdaterData mUpdaterData;
|
||||
|
||||
private CheckboxTreeViewer mTreeViewerSources;
|
||||
@@ -70,14 +74,18 @@ public class AvailablePackagesPage extends Composite {
|
||||
private Label mDescriptionLabel;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Create the composite.
|
||||
* @param parent The parent of the composite.
|
||||
* @param updaterData An instance of {@link UpdaterData}. If null, a local
|
||||
* one will be allocated just to help with the SWT Designer.
|
||||
*/
|
||||
public AvailablePackagesPage(Composite parent, UpdaterData updaterData) {
|
||||
public RemotePackagesPage(UpdaterWindowImpl updaterWindow,
|
||||
Composite parent,
|
||||
UpdaterData updaterData) {
|
||||
super(parent, SWT.BORDER);
|
||||
mUpdaterWindow = updaterWindow;
|
||||
|
||||
mUpdaterData = updaterData != null ? updaterData : new UpdaterData();
|
||||
|
||||
@@ -91,16 +99,14 @@ public class AvailablePackagesPage extends Composite {
|
||||
mTreeViewerSources = new CheckboxTreeViewer(parent, SWT.BORDER);
|
||||
mTreeViewerSources.addDoubleClickListener(new IDoubleClickListener() {
|
||||
public void doubleClick(DoubleClickEvent event) {
|
||||
doTreeDoubleClick(event); //$hide$
|
||||
onTreeDoubleClick(event); //$hide$
|
||||
}
|
||||
});
|
||||
mTreeViewerSources.addCheckStateListener(new ICheckStateListener() {
|
||||
public void checkStateChanged(CheckStateChangedEvent event) {
|
||||
doTreeCeckStateChanged(event); //$hide$
|
||||
onTreeCheckStateChanged(event); //$hide$
|
||||
}
|
||||
});
|
||||
mTreeViewerSources.setContentProvider(mUpdaterData.getSources().getContentProvider());
|
||||
mTreeViewerSources.setLabelProvider(mUpdaterData.getSources().getLabelProvider());
|
||||
mTreeSources = mTreeViewerSources.getTree();
|
||||
mTreeSources.addSelectionListener(new SelectionAdapter() {
|
||||
@Override
|
||||
@@ -137,6 +143,12 @@ public class AvailablePackagesPage extends Composite {
|
||||
mRefreshButton.setText("Refresh");
|
||||
|
||||
mInstallSelectedButton = new Button(parent, SWT.NONE);
|
||||
mInstallSelectedButton.addSelectionListener(new SelectionAdapter() {
|
||||
@Override
|
||||
public void widgetSelected(SelectionEvent e) {
|
||||
onInstallSelectedArchives(); //$hide$
|
||||
}
|
||||
});
|
||||
mInstallSelectedButton.setText("Install Selected");
|
||||
}
|
||||
|
||||
@@ -171,7 +183,9 @@ public class AvailablePackagesPage extends Composite {
|
||||
});
|
||||
}
|
||||
|
||||
public void setInput(RepoSources sources) {
|
||||
public void setInput(RepoSourcesAdapter sources) {
|
||||
mTreeViewerSources.setContentProvider(sources.getContentProvider());
|
||||
mTreeViewerSources.setLabelProvider( sources.getLabelProvider());
|
||||
mTreeViewerSources.setInput(sources);
|
||||
onTreeSelected();
|
||||
}
|
||||
@@ -189,13 +203,27 @@ public class AvailablePackagesPage extends Composite {
|
||||
mDescriptionLabel.setText(""); //$NON-NLS1-$
|
||||
}
|
||||
|
||||
private void doTreeCeckStateChanged(CheckStateChangedEvent event) {
|
||||
private void onTreeCheckStateChanged(CheckStateChangedEvent event) {
|
||||
boolean b = event.getChecked();
|
||||
Object elem = event.getElement();
|
||||
Object elem = event.getElement(); // Will be Archive or Package or RepoSource
|
||||
Object src = event.getSource();
|
||||
// TODO
|
||||
}
|
||||
|
||||
private void doTreeDoubleClick(DoubleClickEvent event) {
|
||||
private void onTreeDoubleClick(DoubleClickEvent event) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
private void onInstallSelectedArchives() {
|
||||
|
||||
ArrayList<Archive> archives = new ArrayList<Archive>();
|
||||
for (Object element : mTreeViewerSources.getCheckedElements()) {
|
||||
if (element instanceof Archive) {
|
||||
archives.add((Archive) element);
|
||||
}
|
||||
}
|
||||
|
||||
mUpdaterWindow.installArchives(archives);
|
||||
}
|
||||
|
||||
// End of hiding from SWT Designer
|
||||
@@ -18,9 +18,9 @@ package com.android.sdkuilib.internal.repository;
|
||||
|
||||
import com.android.sdklib.internal.repository.Archive;
|
||||
import com.android.sdklib.internal.repository.IDescription;
|
||||
import com.android.sdklib.internal.repository.ITaskFactory;
|
||||
import com.android.sdklib.internal.repository.Package;
|
||||
import com.android.sdklib.internal.repository.RepoSource;
|
||||
import com.android.sdklib.internal.repository.RepoSources;
|
||||
|
||||
import org.eclipse.jface.viewers.IContentProvider;
|
||||
import org.eclipse.jface.viewers.ILabelProvider;
|
||||
@@ -29,27 +29,17 @@ import org.eclipse.jface.viewers.LabelProvider;
|
||||
import org.eclipse.jface.viewers.Viewer;
|
||||
import org.eclipse.swt.graphics.Image;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* A list of sdk-repository sources.
|
||||
*
|
||||
* This implementation is UI dependent.
|
||||
*/
|
||||
class RepoSources {
|
||||
class RepoSourcesAdapter {
|
||||
|
||||
private ArrayList<RepoSource> mSources = new ArrayList<RepoSource>();
|
||||
private ITaskFactory mTaskFactory;
|
||||
private final RepoSources mRepoSources;
|
||||
|
||||
public RepoSources() {
|
||||
}
|
||||
|
||||
public void setTaskFactory(ITaskFactory taskFactory) {
|
||||
mTaskFactory = taskFactory;
|
||||
}
|
||||
|
||||
public void add(RepoSource source) {
|
||||
mSources.add(source);
|
||||
public RepoSourcesAdapter(RepoSources repoSources) {
|
||||
mRepoSources = repoSources;
|
||||
}
|
||||
|
||||
public ILabelProvider getLabelProvider() {
|
||||
@@ -63,7 +53,7 @@ class RepoSources {
|
||||
|
||||
// ------------
|
||||
|
||||
public class ViewerLabelProvider extends LabelProvider {
|
||||
public static class ViewerLabelProvider extends LabelProvider {
|
||||
/** Returns null by default */
|
||||
@Override
|
||||
public Image getImage(Object element) {
|
||||
@@ -82,9 +72,9 @@ class RepoSources {
|
||||
|
||||
// ------------
|
||||
|
||||
private class TreeContentProvider implements ITreeContentProvider {
|
||||
private static class TreeContentProvider implements ITreeContentProvider {
|
||||
|
||||
private Object mInput;
|
||||
private RepoSourcesAdapter mInput;
|
||||
|
||||
// Called when the viewer is disposed
|
||||
public void dispose() {
|
||||
@@ -93,13 +83,14 @@ class RepoSources {
|
||||
|
||||
// Called when the input is set or changed on the provider
|
||||
public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
|
||||
mInput = newInput;
|
||||
assert newInput == null || newInput instanceof RepoSourcesAdapter;
|
||||
mInput = (RepoSourcesAdapter) newInput;
|
||||
// pass
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to collect the root elements for the given input.
|
||||
* The input here is a {@link RepoSources} object, this returns an array
|
||||
* The input here is a {@link RepoSourcesAdapter} object, this returns an array
|
||||
* of {@link RepoSource}.
|
||||
*/
|
||||
public Object[] getElements(Object inputElement) {
|
||||
@@ -110,20 +101,20 @@ class RepoSources {
|
||||
* Get the children of the given parent. This is requested on-demand as
|
||||
* nodes are expanded.
|
||||
*
|
||||
* For a {@link RepoSources} object, returns an array of {@link RepoSource}s.
|
||||
* For a {@link RepoSourcesAdapter} object, returns an array of {@link RepoSource}s.
|
||||
* For a {@link RepoSource}, returns an array of {@link Package}s.
|
||||
* For a {@link Package}, returns an array of {@link Archive}s.
|
||||
*/
|
||||
public Object[] getChildren(Object parentElement) {
|
||||
if (parentElement instanceof RepoSources) {
|
||||
return ((RepoSources) parentElement).mSources.toArray();
|
||||
if (parentElement instanceof RepoSourcesAdapter) {
|
||||
return ((RepoSourcesAdapter) parentElement).mRepoSources.getSources().toArray();
|
||||
|
||||
} else if (parentElement instanceof RepoSource) {
|
||||
RepoSource source = (RepoSource) parentElement;
|
||||
Package[] packages = source.getPackages();
|
||||
|
||||
if (packages == null) {
|
||||
source.load(mTaskFactory);
|
||||
source.load(mInput.mRepoSources.getTaskFactory());
|
||||
packages = source.getPackages();
|
||||
}
|
||||
if (packages != null) {
|
||||
@@ -134,17 +125,20 @@ class RepoSources {
|
||||
return ((Package) parentElement).getArchives();
|
||||
}
|
||||
|
||||
|
||||
return new Object[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the parent of a given element.
|
||||
* The input {@link RepoSources} is the parent of all {@link RepoSource} elements.
|
||||
* The input {@link RepoSourcesAdapter} is the parent of all {@link RepoSource} elements.
|
||||
*/
|
||||
public Object getParent(Object element) {
|
||||
|
||||
if (element instanceof RepoSource) {
|
||||
return mInput;
|
||||
|
||||
} else if (element instanceof Package) {
|
||||
return ((Package) element).getParentSource();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -152,7 +146,8 @@ class RepoSources {
|
||||
/**
|
||||
* Returns true if a given element has children, which is used to display a
|
||||
* "+/expand" box next to the tree node.
|
||||
* All {@link RepoSource} are expandable, whether they actually have any childre or not.
|
||||
* All {@link RepoSource} and {@link Package} are expandable, whether they actually
|
||||
* have any children or not.
|
||||
*/
|
||||
public boolean hasChildren(Object element) {
|
||||
return element instanceof RepoSource || element instanceof Package;
|
||||
@@ -16,13 +16,21 @@
|
||||
|
||||
package com.android.sdkuilib.internal.repository;
|
||||
|
||||
import com.android.sdklib.internal.repository.LocalSdkParser;
|
||||
import com.android.sdklib.internal.repository.RepoSources;
|
||||
|
||||
/**
|
||||
* Data shared between {@link UpdaterWindowImpl} and its pages.
|
||||
*/
|
||||
class UpdaterData {
|
||||
private String mOsSdkRoot;
|
||||
private boolean mUserCanChangeSdkRoot;
|
||||
private RepoSources mSources = new RepoSources();
|
||||
|
||||
private final LocalSdkParser mLocalSdkParser = new LocalSdkParser();
|
||||
private final RepoSources mSources = new RepoSources();
|
||||
|
||||
private final LocalSdkAdapter mLocalSdkAdapter = new LocalSdkAdapter(mLocalSdkParser);
|
||||
private final RepoSourcesAdapter mSourcesAdapter = new RepoSourcesAdapter(mSources);
|
||||
|
||||
public void setOsSdkRoot(String osSdkRoot) {
|
||||
mOsSdkRoot = osSdkRoot;
|
||||
@@ -40,12 +48,20 @@ class UpdaterData {
|
||||
return mUserCanChangeSdkRoot;
|
||||
}
|
||||
|
||||
public void setSources(RepoSources sources) {
|
||||
mSources = sources;
|
||||
}
|
||||
|
||||
public RepoSources getSources() {
|
||||
return mSources;
|
||||
}
|
||||
|
||||
public RepoSourcesAdapter getSourcesAdapter() {
|
||||
return mSourcesAdapter;
|
||||
}
|
||||
|
||||
public LocalSdkParser getLocalSdkParser() {
|
||||
return mLocalSdkParser;
|
||||
}
|
||||
|
||||
public LocalSdkAdapter getLocalSdkAdapter() {
|
||||
return mLocalSdkAdapter;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -17,7 +17,12 @@
|
||||
package com.android.sdkuilib.internal.repository;
|
||||
|
||||
|
||||
import com.android.sdklib.internal.repository.Archive;
|
||||
import com.android.sdklib.internal.repository.ITask;
|
||||
import com.android.sdklib.internal.repository.ITaskMonitor;
|
||||
import com.android.sdklib.internal.repository.Package;
|
||||
import com.android.sdklib.internal.repository.RepoSource;
|
||||
import com.android.sdklib.repository.SdkRepository;
|
||||
|
||||
import org.eclipse.swt.SWT;
|
||||
import org.eclipse.swt.SWTException;
|
||||
@@ -36,14 +41,22 @@ import org.eclipse.swt.widgets.Display;
|
||||
import org.eclipse.swt.widgets.List;
|
||||
import org.eclipse.swt.widgets.Shell;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.security.MessageDigest;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* This is the private implementation of the UpdateWindow.
|
||||
*/
|
||||
public class UpdaterWindowImpl {
|
||||
|
||||
private static final int NUM_FETCH_URL_MONITOR_INC = 10;
|
||||
|
||||
private final UpdaterData mUpdaterData = new UpdaterData();
|
||||
private ArrayList<Composite> mPages = new ArrayList<Composite>();
|
||||
private boolean mInternalPageChange;
|
||||
@@ -54,10 +67,11 @@ public class UpdaterWindowImpl {
|
||||
private SashForm mSashForm;
|
||||
private List mPageList;
|
||||
private Composite mPagesRootComposite;
|
||||
private InstalledPackagesPage mInstalledPackagePage;
|
||||
private AvailablePackagesPage mAvailablePackagesPage;
|
||||
private LocalPackagesPage mLocalPackagePage;
|
||||
private RemotePackagesPage mRemotePackagesPage;
|
||||
private StackLayout mStackLayout;
|
||||
private Image mIconImage;
|
||||
private ProgressTaskFactory mTaskFactory;
|
||||
|
||||
public UpdaterWindowImpl(String osSdkRoot, boolean userCanChangeSdkRoot) {
|
||||
mUpdaterData.setOsSdkRoot(osSdkRoot);
|
||||
@@ -114,8 +128,8 @@ public class UpdaterWindowImpl {
|
||||
mStackLayout = new StackLayout();
|
||||
mPagesRootComposite.setLayout(mStackLayout);
|
||||
|
||||
mInstalledPackagePage = new InstalledPackagesPage(mPagesRootComposite, mUpdaterData);
|
||||
mAvailablePackagesPage = new AvailablePackagesPage(mPagesRootComposite, mUpdaterData);
|
||||
mLocalPackagePage = new LocalPackagesPage(mPagesRootComposite, mUpdaterData);
|
||||
mRemotePackagesPage = new RemotePackagesPage(this, mPagesRootComposite, mUpdaterData);
|
||||
mSashForm.setWeights(new int[] {150, 576});
|
||||
}
|
||||
|
||||
@@ -158,8 +172,10 @@ public class UpdaterWindowImpl {
|
||||
* Once the UI has been created, initialize the content
|
||||
*/
|
||||
private void firstInit() {
|
||||
addPage(mInstalledPackagePage, "Installed Packages");
|
||||
addPage(mAvailablePackagesPage, "Available Packages");
|
||||
mTaskFactory = new ProgressTaskFactory(getShell());
|
||||
|
||||
addPage(mLocalPackagePage, "Installed Packages");
|
||||
addPage(mRemotePackagesPage, "Available Packages");
|
||||
displayPage(0);
|
||||
mPageList.setSelection(0);
|
||||
|
||||
@@ -199,23 +215,214 @@ public class UpdaterWindowImpl {
|
||||
}
|
||||
|
||||
private void setupSources() {
|
||||
mUpdaterData.getSources().setTaskFactory(new ProgressTaskFactory(getShell()));
|
||||
mUpdaterData.getSources().setTaskFactory(mTaskFactory);
|
||||
|
||||
mUpdaterData.getSources().add(new RepoSource(
|
||||
"https://dl.google.com/android/eclipse/repository/index.xml", //$NON-NLS-1$
|
||||
false /* addonOnly */));
|
||||
mUpdaterData.getSources().add(
|
||||
new RepoSource(SdkRepository.URL_GOOGLE_SDK_REPO_SITE, false /* addonOnly */));
|
||||
|
||||
String url = System.getenv("TEMP_SDK_URL"); // TODO STOPSHIP temporary remove before shipping
|
||||
if (url != null) {
|
||||
mUpdaterData.getSources().add(new RepoSource(url, false /* addonOnly */));
|
||||
}
|
||||
|
||||
mAvailablePackagesPage.setInput(mUpdaterData.getSources());
|
||||
mRemotePackagesPage.setInput(mUpdaterData.getSourcesAdapter());
|
||||
}
|
||||
|
||||
private void scanLocalSdkFolders() {
|
||||
// TODO Auto-generated method stub
|
||||
mUpdaterData.getLocalSdkAdapter().setSdkRoot(mUpdaterData.getOsSdkRoot());
|
||||
|
||||
mLocalPackagePage.setInput(mUpdaterData.getLocalSdkAdapter());
|
||||
}
|
||||
|
||||
public void installArchives(final Collection<Archive> archives) {
|
||||
// TODO move most parts to SdkLib, maybe as part of Archive, making archives self-installing.
|
||||
mTaskFactory.start("Installing Archives", new ITask() {
|
||||
public void run(ITaskMonitor monitor) {
|
||||
|
||||
monitor.setProgressMax(archives.size() * (NUM_FETCH_URL_MONITOR_INC + 3));
|
||||
monitor.setDescription("Preparing to install archives");
|
||||
|
||||
int num_installed = 0;
|
||||
for (Archive archive : archives) {
|
||||
|
||||
if (!archive.isCompatible()) {
|
||||
monitor.setResult("Skipping incompatible archive: %1$s",
|
||||
archive.getShortDescription());
|
||||
monitor.incProgress(3);
|
||||
continue;
|
||||
}
|
||||
|
||||
File archiveFile = null;
|
||||
try {
|
||||
archiveFile = downloadArchive(archive, monitor);
|
||||
monitor.incProgress(1);
|
||||
if (archiveFile != null) {
|
||||
if (installArchive(archive, archiveFile, monitor)) {
|
||||
num_installed++;
|
||||
}
|
||||
}
|
||||
monitor.incProgress(1);
|
||||
} finally {
|
||||
if (archiveFile != null) {
|
||||
if (!archiveFile.delete()) {
|
||||
archiveFile.deleteOnExit();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (num_installed == 0) {
|
||||
monitor.setResult("Nothing was installed.");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Downloads an archive and returns the temp file with it.
|
||||
* Caller is responsible with deleting the temp file when done.
|
||||
*/
|
||||
private File downloadArchive(Archive archive, ITaskMonitor monitor) {
|
||||
|
||||
try {
|
||||
File tmpFile = File.createTempFile("sdkupload", "bin"); //$NON-NLS-1$ //$NON-NLS-2$
|
||||
|
||||
monitor.setDescription("Downloading %1$s", archive.getShortDescription());
|
||||
|
||||
String link = archive.getUrl();
|
||||
if (!link.startsWith("http://") //$NON-NLS-1$
|
||||
&& !link.startsWith("https://") //$NON-NLS-1$
|
||||
&& !link.startsWith("ftp://")) { //$NON-NLS-1$
|
||||
// Make the URL absolute by prepending the source
|
||||
Package pkg = archive.getParentPackage();
|
||||
RepoSource src = pkg.getParentSource();
|
||||
if (src == null) {
|
||||
monitor.setResult("Internal error: no source for archive %1$s",
|
||||
archive.getShortDescription());
|
||||
return null;
|
||||
}
|
||||
|
||||
String base = src.getUrl();
|
||||
if (!base.endsWith("/") && !link.startsWith("/")) { //$NON-NLS-1$ //$NON-NLS-2$
|
||||
base += "/"; //$NON-NLS-1$
|
||||
}
|
||||
|
||||
link = base + link;
|
||||
}
|
||||
|
||||
fetchUrl(tmpFile, archive, link, monitor);
|
||||
|
||||
} catch (IOException e) {
|
||||
monitor.setResult(e.getMessage());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Actually performs the download.
|
||||
* Also computes the SHA1 of the file on the fly.
|
||||
* <p/>
|
||||
* Success is defined as downloading as many bytes as was expected and having the same
|
||||
* SHA1 as expected. Returns true on success or false if any of those checks fail.
|
||||
* <p/>
|
||||
* Increments the monitor by {@link #NUM_FETCH_URL_MONITOR_INC} (which is 10).
|
||||
*/
|
||||
private boolean fetchUrl(File tmpFile, Archive archive, String urlString, ITaskMonitor monitor) {
|
||||
URL url;
|
||||
|
||||
FileOutputStream os = null;
|
||||
InputStream is = null;
|
||||
try {
|
||||
url = new URL(urlString);
|
||||
is = url.openStream();
|
||||
os = new FileOutputStream(tmpFile);
|
||||
|
||||
MessageDigest digester = archive.getChecksumType().getMessageDigest();
|
||||
|
||||
byte[] buf = new byte[65536];
|
||||
int n;
|
||||
|
||||
long total = 0;
|
||||
long size = archive.getSize();
|
||||
long inc = size / NUM_FETCH_URL_MONITOR_INC;
|
||||
long next_inc = inc;
|
||||
|
||||
while ((n = is.read(buf)) >= 0) {
|
||||
if (n > 0) {
|
||||
os.write(buf, 0, n);
|
||||
digester.update(buf, 0, n);
|
||||
}
|
||||
|
||||
total += n;
|
||||
if (total >= next_inc) {
|
||||
monitor.incProgress(1);
|
||||
next_inc += inc;
|
||||
}
|
||||
|
||||
if (monitor.cancelRequested()) {
|
||||
monitor.setResult("Download aborted by user at %1$d bytes.", total);
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (total != size) {
|
||||
monitor.setResult("Download finished with wrong size. Expected %1$d bytes, got %2$d bytes.",
|
||||
size, total);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create an hex string from the digest
|
||||
byte[] digest = digester.digest();
|
||||
n = digest.length;
|
||||
String hex = "0123456789abcdef"; //$NON-NLS-1$
|
||||
char[] hexDigest = new char[n * 2];
|
||||
for (int i = 0; i < n; i++) {
|
||||
byte b = digest[i];
|
||||
hexDigest[i*2 + 0] = hex.charAt(b >>> 4);
|
||||
hexDigest[i*2 + 1] = hex.charAt(b & 0x0f);
|
||||
}
|
||||
|
||||
String expected = archive.getChecksum();
|
||||
String actual = new String(hexDigest);
|
||||
if (!actual.equalsIgnoreCase(expected)) {
|
||||
monitor.setResult("Download finished with wrong checksum. Expected %1$s, got %2$s.",
|
||||
expected, actual);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
} catch (Exception e) {
|
||||
monitor.setResult(e.getMessage());
|
||||
|
||||
} finally {
|
||||
if (os != null) {
|
||||
try {
|
||||
os.close();
|
||||
} catch (IOException e) {
|
||||
// pass
|
||||
}
|
||||
}
|
||||
|
||||
if (is != null) {
|
||||
try {
|
||||
is.close();
|
||||
} catch (IOException e) {
|
||||
// pass
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean installArchive(Archive archive, File archiveFile, ITaskMonitor monitor) {
|
||||
monitor.setDescription("Installing %1$s", archive.getShortDescription());
|
||||
|
||||
File destFolder = archive.getParentPackage().getInstallFolder(mUpdaterData.getOsSdkRoot());
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// End of hiding from SWT Designer
|
||||
|
||||
Reference in New Issue
Block a user