SDK Updater: platform dependency on tools, addon dependency on platform.
SDK BUG 2040986 Change-Id: Ica46d14939bb3a9bf499899a0bf571456d4c6017
This commit is contained in:
@@ -40,13 +40,14 @@ import java.util.Properties;
|
|||||||
public class AndroidVersion {
|
public class AndroidVersion {
|
||||||
|
|
||||||
private static final String PROP_API_LEVEL = "AndroidVersion.ApiLevel"; //$NON-NLS-1$
|
private static final String PROP_API_LEVEL = "AndroidVersion.ApiLevel"; //$NON-NLS-1$
|
||||||
private static final String PROP_CODENAME = "AndroidVersion.CodeName"; //$NON-NLS-1$
|
private static final String PROP_CODENAME = "AndroidVersion.CodeName"; //$NON-NLS-1$
|
||||||
|
|
||||||
private final int mApiLevel;
|
private final int mApiLevel;
|
||||||
private final String mCodename;
|
private final String mCodename;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an {@link AndroidVersion} with the given api level and codename.
|
* Creates an {@link AndroidVersion} with the given api level and codename.
|
||||||
|
* Codename should be null for a release version, otherwise it's a preview codename.
|
||||||
*/
|
*/
|
||||||
public AndroidVersion(int apiLevel, String codename) {
|
public AndroidVersion(int apiLevel, String codename) {
|
||||||
mApiLevel = apiLevel;
|
mApiLevel = apiLevel;
|
||||||
|
|||||||
@@ -89,6 +89,8 @@ public class AddonPackage extends Package {
|
|||||||
* {@link IAndroidTarget#isPlatform()} false) from the {@link SdkManager}.
|
* {@link IAndroidTarget#isPlatform()} false) from the {@link SdkManager}.
|
||||||
* This is used to list local SDK folders in which case there is one archive which
|
* This is used to list local SDK folders in which case there is one archive which
|
||||||
* URL is the actual target location.
|
* URL is the actual target location.
|
||||||
|
* <p/>
|
||||||
|
* By design, this creates a package with one and only one archive.
|
||||||
*/
|
*/
|
||||||
AddonPackage(IAndroidTarget target, Properties props) {
|
AddonPackage(IAndroidTarget target, Properties props) {
|
||||||
super( null, //source
|
super( null, //source
|
||||||
|
|||||||
@@ -56,6 +56,8 @@ public class DocPackage extends Package {
|
|||||||
* Manually create a new package with one archive and the given attributes.
|
* Manually create a new package with one archive and the given attributes.
|
||||||
* This is used to create packages from local directories in which case there must be
|
* This is used to create packages from local directories in which case there must be
|
||||||
* one archive which URL is the actual target location.
|
* one archive which URL is the actual target location.
|
||||||
|
* <p/>
|
||||||
|
* By design, this creates a package with one and only one archive.
|
||||||
*/
|
*/
|
||||||
DocPackage(RepoSource source,
|
DocPackage(RepoSource source,
|
||||||
Properties props,
|
Properties props,
|
||||||
|
|||||||
@@ -72,6 +72,8 @@ public class ExtraPackage extends Package {
|
|||||||
* Manually create a new package with one archive and the given attributes or properties.
|
* Manually create a new package with one archive and the given attributes or properties.
|
||||||
* This is used to create packages from local directories in which case there must be
|
* This is used to create packages from local directories in which case there must be
|
||||||
* one archive which URL is the actual target location.
|
* one archive which URL is the actual target location.
|
||||||
|
* <p/>
|
||||||
|
* By design, this creates a package with one and only one archive.
|
||||||
*/
|
*/
|
||||||
ExtraPackage(RepoSource source,
|
ExtraPackage(RepoSource source,
|
||||||
Properties props,
|
Properties props,
|
||||||
|
|||||||
@@ -96,8 +96,10 @@ public abstract class Package implements IDescription {
|
|||||||
* Manually create a new package with one archive and the given attributes.
|
* Manually create a new package with one archive and the given attributes.
|
||||||
* This is used to create packages from local directories in which case there must be
|
* This is used to create packages from local directories in which case there must be
|
||||||
* one archive which URL is the actual target location.
|
* one archive which URL is the actual target location.
|
||||||
*
|
* <p/>
|
||||||
* Properties from props are used first when possible, e.g. if props is non null.
|
* Properties from props are used first when possible, e.g. if props is non null.
|
||||||
|
* <p/>
|
||||||
|
* By design, this creates a package with one and only one archive.
|
||||||
*/
|
*/
|
||||||
public Package(
|
public Package(
|
||||||
RepoSource source,
|
RepoSource source,
|
||||||
|
|||||||
@@ -35,8 +35,8 @@ import java.util.Properties;
|
|||||||
*/
|
*/
|
||||||
public class PlatformPackage extends Package {
|
public class PlatformPackage extends Package {
|
||||||
|
|
||||||
private static final String PROP_VERSION = "Platform.Version"; //$NON-NLS-1$
|
protected static final String PROP_VERSION = "Platform.Version"; //$NON-NLS-1$
|
||||||
private static final String PROP_MIN_TOOLS_REV = "Platform.MinToolsRev"; //$NON-NLS-1$
|
protected static final String PROP_MIN_TOOLS_REV = "Platform.MinToolsRev"; //$NON-NLS-1$
|
||||||
|
|
||||||
/** The package version, for platform, add-on and doc packages. */
|
/** The package version, for platform, add-on and doc packages. */
|
||||||
private final AndroidVersion mVersion;
|
private final AndroidVersion mVersion;
|
||||||
@@ -80,6 +80,8 @@ public class PlatformPackage extends Package {
|
|||||||
* must have {@link IAndroidTarget#isPlatform()} true) from the {@link SdkManager}.
|
* must have {@link IAndroidTarget#isPlatform()} true) from the {@link SdkManager}.
|
||||||
* This is used to list local SDK folders in which case there is one archive which
|
* This is used to list local SDK folders in which case there is one archive which
|
||||||
* URL is the actual target location.
|
* URL is the actual target location.
|
||||||
|
* <p/>
|
||||||
|
* By design, this creates a package with one and only one archive.
|
||||||
*/
|
*/
|
||||||
PlatformPackage(IAndroidTarget target, Properties props) {
|
PlatformPackage(IAndroidTarget target, Properties props) {
|
||||||
super( null, //source
|
super( null, //source
|
||||||
|
|||||||
@@ -45,6 +45,8 @@ public class ToolPackage extends Package {
|
|||||||
* Manually create a new package with one archive and the given attributes or properties.
|
* Manually create a new package with one archive and the given attributes or properties.
|
||||||
* This is used to create packages from local directories in which case there must be
|
* This is used to create packages from local directories in which case there must be
|
||||||
* one archive which URL is the actual target location.
|
* one archive which URL is the actual target location.
|
||||||
|
* <p/>
|
||||||
|
* By design, this creates a package with one and only one archive.
|
||||||
*/
|
*/
|
||||||
ToolPackage(
|
ToolPackage(
|
||||||
RepoSource source,
|
RepoSource source,
|
||||||
|
|||||||
@@ -0,0 +1,136 @@
|
|||||||
|
/*
|
||||||
|
* 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.AndroidVersion;
|
||||||
|
import com.android.sdklib.IAndroidTarget;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A mock {@link AddonPackage} for testing.
|
||||||
|
*
|
||||||
|
* By design, this package contains one and only one archive.
|
||||||
|
*/
|
||||||
|
public class MockAddonPackage extends AddonPackage {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a {@link MockAddonTarget} with the requested base platform and addon revision
|
||||||
|
* and then a {@link MockAddonPackage} wrapping it.
|
||||||
|
*
|
||||||
|
* By design, this package contains one and only one archive.
|
||||||
|
*/
|
||||||
|
public MockAddonPackage(MockPlatformPackage basePlatform, int revision) {
|
||||||
|
super(new MockAddonTarget(basePlatform.getTarget(), revision), null /*props*/);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A mock AddonTarget.
|
||||||
|
* This reimplements the minimum needed from the interface for our limited testing needs.
|
||||||
|
*/
|
||||||
|
static class MockAddonTarget implements IAndroidTarget {
|
||||||
|
|
||||||
|
private final IAndroidTarget mParentTarget;
|
||||||
|
private final int mRevision;
|
||||||
|
|
||||||
|
public MockAddonTarget(IAndroidTarget parentTarget, int revision) {
|
||||||
|
mParentTarget = parentTarget;
|
||||||
|
mRevision = revision;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getClasspathName() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDefaultSkin() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDescription() {
|
||||||
|
return "mock addon target";
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFullName() {
|
||||||
|
return "mock addon target";
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLocation() {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return "mock addon target";
|
||||||
|
}
|
||||||
|
|
||||||
|
public IOptionalLibrary[] getOptionalLibraries() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IAndroidTarget getParent() {
|
||||||
|
return mParentTarget;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPath(int pathId) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String[] getPlatformLibraries() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getRevision() {
|
||||||
|
return mRevision;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String[] getSkins() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getUsbVendorId() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getVendor() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AndroidVersion getVersion() {
|
||||||
|
return mParentTarget.getVersion();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getVersionName() {
|
||||||
|
return String.format("mock-addon-%1$d", getVersion().getApiLevel());
|
||||||
|
}
|
||||||
|
|
||||||
|
public String hashString() {
|
||||||
|
return getVersionName();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns false for an addon. */
|
||||||
|
public boolean isPlatform() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isCompatibleBaseFor(IAndroidTarget target) {
|
||||||
|
throw new UnsupportedOperationException("Implement this as needed for tests");
|
||||||
|
}
|
||||||
|
|
||||||
|
public int compareTo(IAndroidTarget o) {
|
||||||
|
throw new UnsupportedOperationException("Implement this as needed for tests");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,169 @@
|
|||||||
|
/*
|
||||||
|
* 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.AndroidVersion;
|
||||||
|
import com.android.sdklib.IAndroidTarget;
|
||||||
|
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A mock {@link PlatformPackage} for testing.
|
||||||
|
*
|
||||||
|
* By design, this package contains one and only one archive.
|
||||||
|
*/
|
||||||
|
public class MockPlatformPackage extends PlatformPackage {
|
||||||
|
|
||||||
|
private final IAndroidTarget mTarget;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a {@link MockPlatformTarget} with the requested API and revision
|
||||||
|
* and then a {@link MockPlatformPackage} wrapping it.
|
||||||
|
*
|
||||||
|
* By design, this package contains one and only one archive.
|
||||||
|
*/
|
||||||
|
public MockPlatformPackage(int apiLevel, int revision) {
|
||||||
|
this(new MockPlatformTarget(apiLevel, revision), null /*props*/);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a {@link MockPlatformTarget} with the requested API and revision
|
||||||
|
* and then a {@link MockPlatformPackage} wrapping it.
|
||||||
|
*
|
||||||
|
* Also sets the min-tools-rev of the platform.
|
||||||
|
*
|
||||||
|
* By design, this package contains one and only one archive.
|
||||||
|
*/
|
||||||
|
public MockPlatformPackage(int apiLevel, int revision, int min_tools_rev) {
|
||||||
|
this(new MockPlatformTarget(apiLevel, revision), createProps(min_tools_rev));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** A little trick to be able to capture the target new after passing it to the super. */
|
||||||
|
private MockPlatformPackage(IAndroidTarget target, Properties props) {
|
||||||
|
super(target, props);
|
||||||
|
mTarget = target;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Properties createProps(int min_tools_rev) {
|
||||||
|
Properties props = new Properties();
|
||||||
|
props.setProperty(PlatformPackage.PROP_MIN_TOOLS_REV, Integer.toString((min_tools_rev)));
|
||||||
|
return props;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IAndroidTarget getTarget() {
|
||||||
|
return mTarget;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A mock PlatformTarget.
|
||||||
|
* This reimplements the minimum needed from the interface for our limited testing needs.
|
||||||
|
*/
|
||||||
|
static class MockPlatformTarget implements IAndroidTarget {
|
||||||
|
|
||||||
|
private final int mApiLevel;
|
||||||
|
private final int mRevision;
|
||||||
|
|
||||||
|
public MockPlatformTarget(int apiLevel, int revision) {
|
||||||
|
mApiLevel = apiLevel;
|
||||||
|
mRevision = revision;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getClasspathName() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDefaultSkin() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDescription() {
|
||||||
|
return "mock platform target";
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFullName() {
|
||||||
|
return "mock platform target";
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLocation() {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return "mock platform target";
|
||||||
|
}
|
||||||
|
|
||||||
|
public IOptionalLibrary[] getOptionalLibraries() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IAndroidTarget getParent() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPath(int pathId) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String[] getPlatformLibraries() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getRevision() {
|
||||||
|
return mRevision;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String[] getSkins() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getUsbVendorId() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getVendor() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AndroidVersion getVersion() {
|
||||||
|
return new AndroidVersion(mApiLevel, null /*codename*/);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getVersionName() {
|
||||||
|
return String.format("android-%1$d", mApiLevel);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String hashString() {
|
||||||
|
return getVersionName();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns true for a platform. */
|
||||||
|
public boolean isPlatform() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isCompatibleBaseFor(IAndroidTarget target) {
|
||||||
|
throw new UnsupportedOperationException("Implement this as needed for tests");
|
||||||
|
}
|
||||||
|
|
||||||
|
public int compareTo(IAndroidTarget o) {
|
||||||
|
throw new UnsupportedOperationException("Implement this as needed for tests");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
* 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.internal.repository.Archive.Arch;
|
||||||
|
import com.android.sdklib.internal.repository.Archive.Os;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A mock {@link ToolPackage} for testing.
|
||||||
|
*
|
||||||
|
* By design, this package contains one and only one archive.
|
||||||
|
*/
|
||||||
|
public class MockToolPackage extends ToolPackage {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a {@link MockToolPackage} with the given revision and hardcoded defaults
|
||||||
|
* for everything else.
|
||||||
|
* <p/>
|
||||||
|
* By design, this creates a package with one and only one archive.
|
||||||
|
*/
|
||||||
|
public MockToolPackage(int revision) {
|
||||||
|
super(
|
||||||
|
null, // source,
|
||||||
|
null, // props,
|
||||||
|
revision,
|
||||||
|
null, // license,
|
||||||
|
"desc", // description,
|
||||||
|
"url", // descUrl,
|
||||||
|
Os.getCurrentOs(), // archiveOs,
|
||||||
|
Arch.getCurrentArch(), // archiveArch,
|
||||||
|
"foo" // archiveOsPath
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,9 +1,11 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<classpath>
|
<classpath>
|
||||||
<classpathentry kind="src" path="src"/>
|
<classpathentry kind="src" path="src"/>
|
||||||
|
<classpathentry kind="src" path="tests"/>
|
||||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
|
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
|
||||||
<classpathentry exported="true" kind="con" path="org.eclipse.jdt.USER_LIBRARY/ANDROID_SWT"/>
|
<classpathentry exported="true" kind="con" path="org.eclipse.jdt.USER_LIBRARY/ANDROID_SWT"/>
|
||||||
<classpathentry combineaccessrules="false" kind="src" path="/SdkLib"/>
|
<classpathentry combineaccessrules="false" kind="src" path="/SdkLib"/>
|
||||||
<classpathentry combineaccessrules="false" kind="src" path="/AndroidPrefs"/>
|
<classpathentry combineaccessrules="false" kind="src" path="/AndroidPrefs"/>
|
||||||
|
<classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/3"/>
|
||||||
<classpathentry kind="output" path="bin"/>
|
<classpathentry kind="output" path="bin"/>
|
||||||
</classpath>
|
</classpath>
|
||||||
|
|||||||
@@ -0,0 +1,139 @@
|
|||||||
|
/*
|
||||||
|
* 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.Archive;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents an archive that we want to install.
|
||||||
|
* Note that the installer deals with archives whereas the user mostly sees packages
|
||||||
|
* but as far as we are concerned for installation there's a 1-to-1 mapping.
|
||||||
|
* <p/>
|
||||||
|
* A new archive is always a remote archive that needs to be downloaded and then
|
||||||
|
* installed. It can replace an existing local one. It can also depends on another
|
||||||
|
* (new or local) archive, which means the dependent archive needs to be successfully
|
||||||
|
* installed first. Finally this archive can also be a dependency for another one.
|
||||||
|
*
|
||||||
|
* @see ArchiveInfo#ArchiveInfo(Archive, Archive, ArchiveInfo)
|
||||||
|
*/
|
||||||
|
class ArchiveInfo {
|
||||||
|
|
||||||
|
private final Archive mNewArchive;
|
||||||
|
private final Archive mReplaced;
|
||||||
|
private final ArchiveInfo mDependsOn;
|
||||||
|
private final ArrayList<ArchiveInfo> mDependencyFor = new ArrayList<ArchiveInfo>();
|
||||||
|
private boolean mAccepted;
|
||||||
|
private boolean mRejected;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param newArchive A "new archive" to be installed. This is always an archive
|
||||||
|
* that comes from a remote site. This can not be null.
|
||||||
|
* @param replaced An optional local archive that the new one will replace.
|
||||||
|
* Can be null if this archive does not replace anything.
|
||||||
|
* @param dependsOn An optional new or local dependency, that is an archive that
|
||||||
|
* <em>this</em> archive depends upon. In other words, we can only install
|
||||||
|
* this archive if the dependency has been successfully installed. It also
|
||||||
|
* means we need to install the dependency first.
|
||||||
|
*/
|
||||||
|
public ArchiveInfo(Archive newArchive, Archive replaced, ArchiveInfo dependsOn) {
|
||||||
|
mNewArchive = newArchive;
|
||||||
|
mReplaced = replaced;
|
||||||
|
mDependsOn = dependsOn;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the "new archive" to be installed.
|
||||||
|
* This is always an archive that comes from a remote site.
|
||||||
|
*/
|
||||||
|
public Archive getNewArchive() {
|
||||||
|
return mNewArchive;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an optional local archive that the new one will replace.
|
||||||
|
* Can be null if this archive does not replace anything.
|
||||||
|
*/
|
||||||
|
public Archive getReplaced() {
|
||||||
|
return mReplaced;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an optional new or local dependency, that is an archive that <em>this</em>
|
||||||
|
* archive depends upon. In other words, we can only install this archive if the
|
||||||
|
* dependency has been successfully installed. It also means we need to install the
|
||||||
|
* dependency first.
|
||||||
|
*/
|
||||||
|
public ArchiveInfo getDependsOn() {
|
||||||
|
return mDependsOn;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if this new archive is a dependency for <em>another</em> one that we
|
||||||
|
* want to install.
|
||||||
|
*/
|
||||||
|
public boolean isDependencyFor() {
|
||||||
|
return mDependencyFor.size() > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set to true if this new archive is a dependency for <em>another</em> one that we
|
||||||
|
* want to install.
|
||||||
|
*/
|
||||||
|
public void addDependencyFor(ArchiveInfo dependencyFor) {
|
||||||
|
mDependencyFor.add(dependencyFor);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Collection<ArchiveInfo> getDependenciesFor() {
|
||||||
|
return mDependencyFor;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets whether this archive was accepted (either manually by the user or
|
||||||
|
* automatically if it doesn't have a license) for installation.
|
||||||
|
*/
|
||||||
|
public void setAccepted(boolean accepted) {
|
||||||
|
mAccepted = accepted;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether this archive was accepted (either manually by the user or
|
||||||
|
* automatically if it doesn't have a license) for installation.
|
||||||
|
*/
|
||||||
|
public boolean isAccepted() {
|
||||||
|
return mAccepted;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets whether this archive was rejected manually by the user.
|
||||||
|
* An archive can neither accepted nor rejected.
|
||||||
|
*/
|
||||||
|
public void setRejected(boolean rejected) {
|
||||||
|
mRejected = rejected;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether this archive was rejected manually by the user.
|
||||||
|
* An archive can neither accepted nor rejected.
|
||||||
|
*/
|
||||||
|
public boolean isRejected() {
|
||||||
|
return mRejected;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -52,11 +52,6 @@ import org.eclipse.swt.widgets.Table;
|
|||||||
import org.eclipse.swt.widgets.TableColumn;
|
import org.eclipse.swt.widgets.TableColumn;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Comparator;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.TreeMap;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -64,21 +59,16 @@ import java.util.TreeMap;
|
|||||||
*/
|
*/
|
||||||
final class UpdateChooserDialog extends Dialog {
|
final class UpdateChooserDialog extends Dialog {
|
||||||
|
|
||||||
/**
|
/** Min Y location for dialog. Need to deal with the menu bar on mac os. */
|
||||||
* Min Y location for dialog. Need to deal with the menu bar on mac os.
|
private final static int MIN_Y =
|
||||||
*/
|
SdkConstants.CURRENT_PLATFORM == SdkConstants.PLATFORM_DARWIN ? 20 : 0;
|
||||||
private final static int MIN_Y = SdkConstants.CURRENT_PLATFORM == SdkConstants.PLATFORM_DARWIN ?
|
|
||||||
20 : 0;
|
|
||||||
|
|
||||||
/** Last dialog size for this session. */
|
/** Last dialog size for this session. */
|
||||||
private static Point sLastSize;
|
private static Point sLastSize;
|
||||||
|
private boolean mCancelled = true;
|
||||||
private boolean mCompleted;
|
private boolean mCompleted;
|
||||||
private final Map<Archive, Archive> mNewToOldArchiveMap;
|
|
||||||
private boolean mLicenseAcceptAll;
|
private boolean mLicenseAcceptAll;
|
||||||
private boolean mInternalLicenseRadioUpdate;
|
private boolean mInternalLicenseRadioUpdate;
|
||||||
private HashSet<Archive> mAccepted = new HashSet<Archive>();
|
|
||||||
private HashSet<Archive> mRejected = new HashSet<Archive>();
|
|
||||||
private ArrayList<Archive> mResult = new ArrayList<Archive>();
|
|
||||||
|
|
||||||
// UI fields
|
// UI fields
|
||||||
private Shell mDialogShell;
|
private Shell mDialogShell;
|
||||||
@@ -96,40 +86,56 @@ final class UpdateChooserDialog extends Dialog {
|
|||||||
private Group mPackageTextGroup;
|
private Group mPackageTextGroup;
|
||||||
private final UpdaterData mUpdaterData;
|
private final UpdaterData mUpdaterData;
|
||||||
private Group mTableGroup;
|
private Group mTableGroup;
|
||||||
|
private Label mErrorLabel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List of all archives to be installed with dependency information.
|
||||||
|
*
|
||||||
|
* Note: in a lot of cases, we need to find the archive info for a given archive. This
|
||||||
|
* is currently done using a simple linear search, which is fine since we only have a very
|
||||||
|
* limited number of archives to deal with (e.g. < 10 now). We might want to revisit
|
||||||
|
* this later if it becomes an issue. Right now just do the simple thing.
|
||||||
|
*
|
||||||
|
* Typically we could add a map Archive=>ArchiveInfo later.
|
||||||
|
*/
|
||||||
|
private final ArrayList<ArchiveInfo> mArchives;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create the dialog.
|
* Create the dialog.
|
||||||
* @param parentShell The shell to use, typically updaterData.getWindowShell()
|
* @param parentShell The shell to use, typically updaterData.getWindowShell()
|
||||||
* @param updaterData The updater data
|
* @param updaterData The updater data
|
||||||
* @param newToOldUpdates The map [new archive => old archive] of potential updates
|
* @param archives The archives to be installed
|
||||||
*/
|
*/
|
||||||
public UpdateChooserDialog(Shell parentShell,
|
public UpdateChooserDialog(Shell parentShell,
|
||||||
UpdaterData updaterData,
|
UpdaterData updaterData,
|
||||||
Map<Archive, Archive> newToOldUpdates) {
|
ArrayList<ArchiveInfo> archives) {
|
||||||
super(parentShell,
|
super(parentShell,
|
||||||
SWT.APPLICATION_MODAL);
|
SWT.APPLICATION_MODAL);
|
||||||
mUpdaterData = updaterData;
|
mUpdaterData = updaterData;
|
||||||
|
mArchives = archives;
|
||||||
mNewToOldArchiveMap = new TreeMap<Archive, Archive>(new Comparator<Archive>() {
|
|
||||||
public int compare(Archive a1, Archive a2) {
|
|
||||||
// The items are archive but what we show are packages so we'll
|
|
||||||
// sort of packages short descriptions
|
|
||||||
String desc1 = a1.getParentPackage().getShortDescription();
|
|
||||||
String desc2 = a2.getParentPackage().getShortDescription();
|
|
||||||
return desc1.compareTo(desc2);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
mNewToOldArchiveMap.putAll(newToOldUpdates);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the results, i.e. the list of selected new archives to install.
|
* Returns the results, i.e. the list of selected new archives to install.
|
||||||
* The list is always non null. It is empty when cancel is selected or when
|
* This is similar to the {@link ArchiveInfo} list instance given to the constructor
|
||||||
* all potential updates have been refused.
|
* except only accepted archives are present.
|
||||||
|
*
|
||||||
|
* An empty list is returned if cancel was choosen.
|
||||||
*/
|
*/
|
||||||
public Collection<Archive> getResult() {
|
public ArrayList<ArchiveInfo> getResult() {
|
||||||
return mResult;
|
ArrayList<ArchiveInfo> ais = new ArrayList<ArchiveInfo>();
|
||||||
|
|
||||||
|
if (!mCancelled) {
|
||||||
|
for (ArchiveInfo ai : mArchives) {
|
||||||
|
if (ai.isAccepted()) {
|
||||||
|
ais.add(ai);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ais;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -249,7 +255,14 @@ final class UpdateChooserDialog extends Dialog {
|
|||||||
});
|
});
|
||||||
|
|
||||||
mSashForm.setWeights(new int[] {200, 300});
|
mSashForm.setWeights(new int[] {200, 300});
|
||||||
|
|
||||||
|
// Error message area
|
||||||
|
|
||||||
|
mErrorLabel = new Label(mDialogShell, SWT.NONE);
|
||||||
|
mErrorLabel.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 3, 1));
|
||||||
|
|
||||||
|
|
||||||
|
// Bottom buttons area
|
||||||
|
|
||||||
placeholder = new Label(mDialogShell, SWT.NONE);
|
placeholder = new Label(mDialogShell, SWT.NONE);
|
||||||
placeholder.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, true, false, 1, 1));
|
placeholder.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, true, false, 1, 1));
|
||||||
@@ -293,24 +306,19 @@ final class UpdateChooserDialog extends Dialog {
|
|||||||
*/
|
*/
|
||||||
private void postCreate() {
|
private void postCreate() {
|
||||||
setWindowImage();
|
setWindowImage();
|
||||||
|
|
||||||
// Automatically accept those with an empty license
|
// Automatically accept those with an empty license or no license
|
||||||
|
for (ArchiveInfo ai : mArchives) {
|
||||||
|
Archive a = ai.getNewArchive();
|
||||||
assert a != null;
|
assert a != null;
|
||||||
|
|
||||||
String license = a.getParentPackage().getLicense();
|
String license = a.getParentPackage().getLicense();
|
||||||
if (license != null) {
|
|
||||||
license = license.trim();
|
|
||||||
if (license.length() == 0) {
|
|
||||||
mAccepted.add(a);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
mAccepted.add(a);
|
|
||||||
ai.setAccepted(license == null || license.trim().length() == 0);
|
ai.setAccepted(license == null || license.trim().length() == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fill the list with the replacement packages
|
// Fill the list with the replacement packages
|
||||||
mTableViewPackage.setLabelProvider(new NewArchivesLabelProvider());
|
mTableViewPackage.setLabelProvider(new NewArchivesLabelProvider());
|
||||||
mTableViewPackage.setContentProvider(new NewArchivesContentProvider());
|
mTableViewPackage.setContentProvider(new NewArchivesContentProvider());
|
||||||
mTableViewPackage.setInput(mArchives);
|
mTableViewPackage.setInput(mArchives);
|
||||||
|
|
||||||
adjustColumnsWidth();
|
adjustColumnsWidth();
|
||||||
@@ -403,12 +411,10 @@ final class UpdateChooserDialog extends Dialog {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Callback invoked when the Install button is selected. Fills {@link #mResult} and
|
|
||||||
* Callback invoked when the Install button is selected. Completes the dialog.
|
* Callback invoked when the Install button is selected. Completes the dialog.
|
||||||
*/
|
*/
|
||||||
private void onInstallSelected() {
|
private void onInstallSelected() {
|
||||||
// get list of accepted items
|
|
||||||
mCancelled = false;
|
mCancelled = false;
|
||||||
mCompleted = true;
|
mCompleted = true;
|
||||||
}
|
}
|
||||||
@@ -416,6 +422,7 @@ final class UpdateChooserDialog extends Dialog {
|
|||||||
/**
|
/**
|
||||||
* Callback invoked when the Cancel button is selected.
|
* Callback invoked when the Cancel button is selected.
|
||||||
*/
|
*/
|
||||||
|
private void onCancelSelected() {
|
||||||
mCancelled = true;
|
mCancelled = true;
|
||||||
mCompleted = true;
|
mCompleted = true;
|
||||||
}
|
}
|
||||||
@@ -423,49 +430,123 @@ final class UpdateChooserDialog extends Dialog {
|
|||||||
/**
|
/**
|
||||||
* Callback invoked when a package item is selected in the list.
|
* Callback invoked when a package item is selected in the list.
|
||||||
*/
|
*/
|
||||||
private void onPackageSelected() {
|
private void onPackageSelected() {
|
||||||
Archive a = getSelectedArchive();
|
ArchiveInfo ai = getSelectedArchive();
|
||||||
displayInformation(a);
|
displayInformation(ai);
|
||||||
|
displayMissingDependency(ai);
|
||||||
updateLicenceRadios(ai);
|
updateLicenceRadios(ai);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns the currently selected Archive or null. */
|
/** Returns the currently selected {@link ArchiveInfo} or null. */
|
||||||
private ArchiveInfo getSelectedArchive() {
|
private ArchiveInfo getSelectedArchive() {
|
||||||
ISelection sel = mTableViewPackage.getSelection();
|
ISelection sel = mTableViewPackage.getSelection();
|
||||||
if (sel instanceof IStructuredSelection) {
|
if (sel instanceof IStructuredSelection) {
|
||||||
Object elem = ((IStructuredSelection) sel).getFirstElement();
|
Object elem = ((IStructuredSelection) sel).getFirstElement();
|
||||||
if (elem instanceof Archive) {
|
if (elem instanceof ArchiveInfo) {
|
||||||
return (ArchiveInfo) elem;
|
return (ArchiveInfo) elem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void displayInformation(Archive a) {
|
/**
|
||||||
|
* Updates the package description and license text depending on the selected package.
|
||||||
|
*/
|
||||||
|
private void displayInformation(ArchiveInfo ai) {
|
||||||
if (ai == null) {
|
if (ai == null) {
|
||||||
mPackageText.setText("Please select a package.");
|
mPackageText.setText("Please select a package.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Archive anew = ai.getNewArchive();
|
||||||
|
|
||||||
mPackageText.setText(""); //$NON-NLS-1$
|
mPackageText.setText(""); //$NON-NLS-1$
|
||||||
|
|
||||||
addSectionTitle("Package Description\n");
|
addSectionTitle("Package Description\n");
|
||||||
addText(anew.getParentPackage().getLongDescription(), "\n\n"); //$NON-NLS-1$
|
addText(anew.getParentPackage().getLongDescription(), "\n\n"); //$NON-NLS-1$
|
||||||
|
|
||||||
Archive aold = ai.getReplaced();
|
Archive aold = ai.getReplaced();
|
||||||
if (aold != null) {
|
if (aold != null) {
|
||||||
addText(String.format("This update will replace revision %1$s with revision %2$s.\n\n",
|
addText(String.format("This update will replace revision %1$s with revision %2$s.\n\n",
|
||||||
aold.getParentPackage().getRevision(),
|
aold.getParentPackage().getRevision(),
|
||||||
anew.getParentPackage().getRevision()));
|
anew.getParentPackage().getRevision()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ArchiveInfo adep = ai.getDependsOn();
|
||||||
|
if (adep != null || ai.isDependencyFor()) {
|
||||||
|
addSectionTitle("Dependencies\n");
|
||||||
|
|
||||||
|
if (adep != null) {
|
||||||
|
addText(String.format("This package depends on %1$s.\n\n",
|
||||||
|
adep.getNewArchive().getParentPackage().getShortDescription()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ai.isDependencyFor()) {
|
||||||
|
addText("This package is a dependency for:");
|
||||||
|
for (ArchiveInfo ai2 : ai.getDependenciesFor()) {
|
||||||
|
addText("\n- " +
|
||||||
|
ai2.getNewArchive().getParentPackage().getShortDescription());
|
||||||
|
}
|
||||||
|
addText("\n\n");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
addSectionTitle("Archive Description\n");
|
addSectionTitle("Archive Description\n");
|
||||||
addText(anew.getLongDescription(), "\n\n"); //$NON-NLS-1$
|
addText(anew.getLongDescription(), "\n\n"); //$NON-NLS-1$
|
||||||
|
|
||||||
String license = anew.getParentPackage().getLicense();
|
String license = anew.getParentPackage().getLicense();
|
||||||
if (license != null) {
|
if (license != null) {
|
||||||
addSectionTitle("License\n");
|
addSectionTitle("License\n");
|
||||||
|
addText(license.trim(), "\n"); //$NON-NLS-1$
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes and display missing dependency.
|
||||||
|
* If there's a selected package, check the dependency for that one.
|
||||||
|
* Otherwise display the first missing dependency.
|
||||||
|
*/
|
||||||
|
private void displayMissingDependency(ArchiveInfo ai) {
|
||||||
|
String error = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (ai != null) {
|
||||||
|
|
||||||
|
if (!ai.isAccepted()) {
|
||||||
|
// Case where this package blocks another one when not accepted
|
||||||
|
for (ArchiveInfo ai2 : ai.getDependenciesFor()) {
|
||||||
|
// It only matters if the blocked one is accepted
|
||||||
|
if (ai2.isAccepted()) {
|
||||||
|
error = String.format("Package '%1$s' depends on this one.",
|
||||||
|
ai2.getNewArchive().getParentPackage().getShortDescription());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Case where this package is accepted but blocked by another non-accepted one
|
||||||
|
ArchiveInfo adep = ai.getDependsOn();
|
||||||
|
if (adep != null && !adep.isAccepted()) {
|
||||||
|
error = String.format("This package depends on '%1$s'.",
|
||||||
|
adep.getNewArchive().getParentPackage().getShortDescription());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there's no selection, just find the first missing dependency of any accepted
|
||||||
|
// package.
|
||||||
|
for (ArchiveInfo ai2 : mArchives) {
|
||||||
|
if (ai2.isAccepted()) {
|
||||||
|
ArchiveInfo adep = ai.getDependsOn();
|
||||||
|
if (adep != null && !adep.isAccepted()) {
|
||||||
|
error = String.format("Package '%1$s' depends on '%2$s'",
|
||||||
|
ai2.getNewArchive().getParentPackage().getShortDescription(),
|
||||||
|
adep.getNewArchive().getParentPackage().getShortDescription());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
mErrorLabel.setText(error == null ? "" : error); //$NON-NLS-1$
|
mErrorLabel.setText(error == null ? "" : error); //$NON-NLS-1$
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -488,25 +569,41 @@ final class UpdateChooserDialog extends Dialog {
|
|||||||
sr.underline = true;
|
sr.underline = true;
|
||||||
mPackageText.setStyleRange(sr);
|
mPackageText.setStyleRange(sr);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateLicenceRadios(ArchiveInfo ai) {
|
private void updateLicenceRadios(ArchiveInfo ai) {
|
||||||
if (mInternalLicenseRadioUpdate) {
|
if (mInternalLicenseRadioUpdate) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
mInternalLicenseRadioUpdate = true;
|
mInternalLicenseRadioUpdate = true;
|
||||||
|
|
||||||
|
boolean oneAccepted = false;
|
||||||
|
|
||||||
if (mLicenseAcceptAll) {
|
if (mLicenseAcceptAll) {
|
||||||
|
mLicenseRadioAcceptAll.setSelection(true);
|
||||||
|
mLicenseRadioAccept.setEnabled(true);
|
||||||
mLicenseRadioReject.setEnabled(true);
|
mLicenseRadioReject.setEnabled(true);
|
||||||
mLicenseRadioAccept.setSelection(false);
|
mLicenseRadioAccept.setSelection(false);
|
||||||
mLicenseRadioReject.setSelection(false);
|
mLicenseRadioReject.setSelection(false);
|
||||||
} else {
|
} else {
|
||||||
mLicenseRadioAcceptAll.setSelection(false);
|
mLicenseRadioAcceptAll.setSelection(false);
|
||||||
mLicenseRadioAccept.setSelection(mAccepted.contains(a));
|
oneAccepted = ai != null && ai.isAccepted();
|
||||||
|
mLicenseRadioAccept.setEnabled(ai != null);
|
||||||
|
mLicenseRadioReject.setEnabled(ai != null);
|
||||||
|
mLicenseRadioAccept.setSelection(oneAccepted);
|
||||||
mLicenseRadioReject.setSelection(ai != null && ai.isRejected());
|
mLicenseRadioReject.setSelection(ai != null && ai.isRejected());
|
||||||
}
|
}
|
||||||
|
|
||||||
// The install button is enabled if there's at least one
|
// The install button is enabled if there's at least one package accepted.
|
||||||
// package accepted.
|
// If the current one isn't, look for another one.
|
||||||
|
boolean missing = mErrorLabel.getText() != null && mErrorLabel.getText().length() > 0;
|
||||||
|
if (!missing && !oneAccepted) {
|
||||||
|
for(ArchiveInfo ai2 : mArchives) {
|
||||||
|
if (ai2.isAccepted()) {
|
||||||
|
oneAccepted = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
mInstallButton.setEnabled(!missing && oneAccepted);
|
mInstallButton.setEnabled(!missing && oneAccepted);
|
||||||
|
|
||||||
mInternalLicenseRadioUpdate = false;
|
mInternalLicenseRadioUpdate = false;
|
||||||
@@ -523,26 +620,28 @@ final class UpdateChooserDialog extends Dialog {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
mInternalLicenseRadioUpdate = true;
|
mInternalLicenseRadioUpdate = true;
|
||||||
|
|
||||||
ArchiveInfo ai = getSelectedArchive();
|
ArchiveInfo ai = getSelectedArchive();
|
||||||
boolean needUpdate = true;
|
boolean needUpdate = true;
|
||||||
|
|
||||||
if (!mLicenseAcceptAll && mLicenseRadioAcceptAll.getSelection()) {
|
if (!mLicenseAcceptAll && mLicenseRadioAcceptAll.getSelection()) {
|
||||||
// Accept all has been switched on. Mark all packages as accepted
|
// Accept all has been switched on. Mark all packages as accepted
|
||||||
mLicenseAcceptAll = true;
|
mLicenseAcceptAll = true;
|
||||||
mAccepted.addAll(mNewToOldArchiveMap.keySet());
|
for(ArchiveInfo ai2 : mArchives) {
|
||||||
|
ai2.setAccepted(true);
|
||||||
|
ai2.setRejected(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (mLicenseRadioAccept.getSelection()) {
|
} else if (mLicenseRadioAccept.getSelection()) {
|
||||||
// Accept only this one
|
// Accept only this one
|
||||||
mLicenseAcceptAll = false;
|
mLicenseAcceptAll = false;
|
||||||
mAccepted.add(a);
|
ai.setAccepted(true);
|
||||||
ai.setRejected(false);
|
ai.setRejected(false);
|
||||||
|
|
||||||
} else if (mLicenseRadioReject.getSelection()) {
|
} else if (mLicenseRadioReject.getSelection()) {
|
||||||
// Reject only this one
|
// Reject only this one
|
||||||
mLicenseAcceptAll = false;
|
mLicenseAcceptAll = false;
|
||||||
mAccepted.remove(a);
|
ai.setAccepted(false);
|
||||||
ai.setRejected(true);
|
ai.setRejected(true);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
@@ -554,9 +653,10 @@ final class UpdateChooserDialog extends Dialog {
|
|||||||
if (needUpdate) {
|
if (needUpdate) {
|
||||||
if (mLicenseAcceptAll) {
|
if (mLicenseAcceptAll) {
|
||||||
mTableViewPackage.refresh();
|
mTableViewPackage.refresh();
|
||||||
} else {
|
} else {
|
||||||
mTableViewPackage.refresh(ai);
|
mTableViewPackage.refresh(ai);
|
||||||
}
|
}
|
||||||
|
displayMissingDependency(ai);
|
||||||
updateLicenceRadios(ai);
|
updateLicenceRadios(ai);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -564,32 +664,29 @@ final class UpdateChooserDialog extends Dialog {
|
|||||||
/**
|
/**
|
||||||
* Callback invoked when a package item is double-clicked in the list.
|
* Callback invoked when a package item is double-clicked in the list.
|
||||||
*/
|
*/
|
||||||
private void onPackageDoubleClick() {
|
private void onPackageDoubleClick() {
|
||||||
ArchiveInfo ai = getSelectedArchive();
|
ArchiveInfo ai = getSelectedArchive();
|
||||||
|
|
||||||
if (mAccepted.contains(a)) {
|
boolean wasAccepted = ai.isAccepted();
|
||||||
// toggle from accepted to rejected
|
ai.setAccepted(!wasAccepted);
|
||||||
mAccepted.remove(a);
|
|
||||||
mRejected.add(a);
|
|
||||||
} else {
|
|
||||||
// toggle from rejected or unknown to accepted
|
|
||||||
mAccepted.add(a);
|
|
||||||
mRejected.remove(a);
|
|
||||||
ai.setRejected(wasAccepted);
|
ai.setRejected(wasAccepted);
|
||||||
|
|
||||||
// update state
|
// update state
|
||||||
mLicenseAcceptAll = false;
|
mLicenseAcceptAll = false;
|
||||||
mTableViewPackage.refresh(a);
|
mTableViewPackage.refresh(ai);
|
||||||
updateLicenceRadios(ai);
|
updateLicenceRadios(ai);
|
||||||
}
|
}
|
||||||
|
|
||||||
private class NewArchivesLabelProvider extends LabelProvider {
|
private class NewArchivesLabelProvider extends LabelProvider {
|
||||||
@Override
|
@Override
|
||||||
|
public Image getImage(Object element) {
|
||||||
|
assert element instanceof ArchiveInfo;
|
||||||
|
ArchiveInfo ai = (ArchiveInfo) element;
|
||||||
|
|
||||||
ImageFactory imgFactory = mUpdaterData.getImageFactory();
|
ImageFactory imgFactory = mUpdaterData.getImageFactory();
|
||||||
if (imgFactory != null) {
|
if (imgFactory != null) {
|
||||||
if (ai.isAccepted()) {
|
if (ai.isAccepted()) {
|
||||||
return imgFactory.getImageByName("accept_icon16.png");
|
return imgFactory.getImageByName("accept_icon16.png");
|
||||||
} else if (ai.isRejected()) {
|
} else if (ai.isRejected()) {
|
||||||
return imgFactory.getImageByName("reject_icon16.png");
|
return imgFactory.getImageByName("reject_icon16.png");
|
||||||
}
|
}
|
||||||
@@ -599,10 +696,16 @@ final class UpdateChooserDialog extends Dialog {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getText(Object element) {
|
public String getText(Object element) {
|
||||||
if (element instanceof Archive) {
|
assert element instanceof ArchiveInfo;
|
||||||
|
ArchiveInfo ai = (ArchiveInfo) element;
|
||||||
|
|
||||||
|
String desc = ai.getNewArchive().getParentPackage().getShortDescription();
|
||||||
|
|
||||||
|
if (ai.isDependencyFor()) {
|
||||||
desc += " [*]";
|
desc += " [*]";
|
||||||
}
|
}
|
||||||
|
|
||||||
return desc;
|
return desc;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -613,11 +716,11 @@ final class UpdateChooserDialog extends Dialog {
|
|||||||
// pass
|
// pass
|
||||||
}
|
}
|
||||||
|
|
||||||
public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
|
public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
|
||||||
// Ignore. The input is always mArchives
|
// Ignore. The input is always mArchives
|
||||||
}
|
}
|
||||||
|
|
||||||
public Object[] getElements(Object inputElement) {
|
public Object[] getElements(Object inputElement) {
|
||||||
return mArchives.toArray();
|
return mArchives.toArray();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,6 @@ import com.android.sdklib.internal.repository.Package;
|
|||||||
import com.android.sdklib.internal.repository.RepoSource;
|
import com.android.sdklib.internal.repository.RepoSource;
|
||||||
import com.android.sdklib.internal.repository.RepoSources;
|
import com.android.sdklib.internal.repository.RepoSources;
|
||||||
import com.android.sdklib.internal.repository.ToolPackage;
|
import com.android.sdklib.internal.repository.ToolPackage;
|
||||||
import com.android.sdklib.internal.repository.Package.UpdateInfo;
|
|
||||||
import com.android.sdkuilib.internal.repository.icons.ImageFactory;
|
import com.android.sdkuilib.internal.repository.icons.ImageFactory;
|
||||||
import com.android.sdkuilib.repository.UpdaterWindow.ISdkListener;
|
import com.android.sdkuilib.repository.UpdaterWindow.ISdkListener;
|
||||||
|
|
||||||
@@ -42,8 +41,7 @@ import java.io.ByteArrayOutputStream;
|
|||||||
import java.io.PrintStream;
|
import java.io.PrintStream;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashSet;
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Data shared between {@link UpdaterWindowImpl} and its pages.
|
* Data shared between {@link UpdaterWindowImpl} and its pages.
|
||||||
@@ -268,9 +266,9 @@ class UpdaterData {
|
|||||||
* Install the list of given {@link Archive}s. This is invoked by the user selecting some
|
* Install the list of given {@link Archive}s. This is invoked by the user selecting some
|
||||||
* packages in the remote page and then clicking "install selected".
|
* packages in the remote page and then clicking "install selected".
|
||||||
*
|
*
|
||||||
* @param archives The archives to install. Incompatible ones will be skipped.
|
* @param result The archives to install. Incompatible ones will be skipped.
|
||||||
*/
|
*/
|
||||||
public void installArchives(final Collection<Archive> archives) {
|
public void installArchives(final ArrayList<ArchiveInfo> result) {
|
||||||
if (mTaskFactory == null) {
|
if (mTaskFactory == null) {
|
||||||
throw new IllegalArgumentException("Task Factory is null");
|
throw new IllegalArgumentException("Task Factory is null");
|
||||||
}
|
}
|
||||||
@@ -281,14 +279,23 @@ class UpdaterData {
|
|||||||
public void run(ITaskMonitor monitor) {
|
public void run(ITaskMonitor monitor) {
|
||||||
|
|
||||||
final int progressPerArchive = 2 * Archive.NUM_MONITOR_INC;
|
final int progressPerArchive = 2 * Archive.NUM_MONITOR_INC;
|
||||||
monitor.setProgressMax(archives.size() * progressPerArchive);
|
monitor.setProgressMax(result.size() * progressPerArchive);
|
||||||
monitor.setDescription("Preparing to install archives");
|
monitor.setDescription("Preparing to install archives");
|
||||||
|
|
||||||
boolean installedAddon = false;
|
boolean installedAddon = false;
|
||||||
boolean installedTools = false;
|
boolean installedTools = false;
|
||||||
|
|
||||||
|
// Mark all current local archives as already installed.
|
||||||
|
HashSet<Archive> installedArchives = new HashSet<Archive>();
|
||||||
|
for (Package p : getInstalledPackage()) {
|
||||||
|
for (Archive a : p.getArchives()) {
|
||||||
|
installedArchives.add(a);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int numInstalled = 0;
|
int numInstalled = 0;
|
||||||
for (Archive archive : archives) {
|
for (ArchiveInfo ai : result) {
|
||||||
|
Archive archive = ai.getNewArchive();
|
||||||
|
|
||||||
int nextProgress = monitor.getProgress() + progressPerArchive;
|
int nextProgress = monitor.getProgress() + progressPerArchive;
|
||||||
try {
|
try {
|
||||||
@@ -296,9 +303,24 @@ class UpdaterData {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ArchiveInfo adep = ai.getDependsOn();
|
||||||
|
if (adep != null && !installedArchives.contains(adep)) {
|
||||||
|
// This archive depends on another one that was not installed.
|
||||||
|
// Skip it.
|
||||||
|
monitor.setResult("Skipping '%1$s'; it depends on '%2$s' which was not installed.",
|
||||||
|
archive.getParentPackage().getShortDescription(),
|
||||||
|
adep.getNewArchive().getParentPackage().getShortDescription());
|
||||||
|
}
|
||||||
|
|
||||||
if (archive.install(mOsSdkRoot, forceHttp, mSdkManager, monitor)) {
|
if (archive.install(mOsSdkRoot, forceHttp, mSdkManager, monitor)) {
|
||||||
|
// We installed this archive.
|
||||||
|
installedArchives.add(archive);
|
||||||
numInstalled++;
|
numInstalled++;
|
||||||
|
|
||||||
|
// If this package was replacing an existing one, the old one
|
||||||
|
// is no longer installed.
|
||||||
|
installedArchives.remove(ai.getReplaced());
|
||||||
|
|
||||||
// Check if we successfully installed a tool or add-on package.
|
// Check if we successfully installed a tool or add-on package.
|
||||||
if (archive.getParentPackage() instanceof AddonPackage) {
|
if (archive.getParentPackage() instanceof AddonPackage) {
|
||||||
installedAddon = true;
|
installedAddon = true;
|
||||||
@@ -435,28 +457,20 @@ class UpdaterData {
|
|||||||
refreshSources(true);
|
refreshSources(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
final Map<Archive, Archive> updates = findUpdates(selectedArchives);
|
UpdaterLogic ul = new UpdaterLogic();
|
||||||
|
ArrayList<ArchiveInfo> archives = ul.computeUpdates(
|
||||||
|
selectedArchives,
|
||||||
|
getSources(),
|
||||||
|
getLocalSdkParser().getPackages());
|
||||||
|
|
||||||
if (selectedArchives != null) {
|
UpdateChooserDialog dialog = new UpdateChooserDialog(getWindowShell(), this, archives);
|
||||||
// Not only we want to perform updates but we also want to install the
|
|
||||||
// selected archives. If they do not match an update, list them anyway
|
|
||||||
// except they map themselves to null (no "old" archive)
|
|
||||||
for (Archive a : selectedArchives) {
|
|
||||||
if (!updates.containsKey(a)) {
|
|
||||||
updates.put(a, null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
UpdateChooserDialog dialog = new UpdateChooserDialog(getWindowShell(), this, updates);
|
|
||||||
dialog.open();
|
dialog.open();
|
||||||
|
|
||||||
Collection<Archive> result = dialog.getResult();
|
ArrayList<ArchiveInfo> result = dialog.getResult();
|
||||||
if (result != null && result.size() > 0) {
|
if (result != null && result.size() > 0) {
|
||||||
installArchives(result);
|
installArchives(result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Refresh all sources. This is invoked either internally (reusing an existing monitor)
|
* Refresh all sources. This is invoked either internally (reusing an existing monitor)
|
||||||
* or as a UI callback on the remote page "Refresh" button (in which case the monitor is
|
* or as a UI callback on the remote page "Refresh" button (in which case the monitor is
|
||||||
@@ -485,108 +499,4 @@ class UpdaterData {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Check the local archives vs the remote available packages to find potential updates.
|
|
||||||
* Return a map [remote archive => local archive] of suitable update candidates.
|
|
||||||
* Returns null if there's an unexpected error. Otherwise returns a map that can be
|
|
||||||
* empty but not null.
|
|
||||||
*
|
|
||||||
* @param selectedArchives The list of remote archive to consider for the update.
|
|
||||||
* This can be null, in which case a list of remote archive is fetched from all
|
|
||||||
* available sources.
|
|
||||||
*/
|
|
||||||
private Map<Archive, Archive> findUpdates(Collection<Archive> selectedArchives) {
|
|
||||||
// Map [remote archive => local archive] of suitable update candidates
|
|
||||||
Map<Archive, Archive> result = new HashMap<Archive, Archive>();
|
|
||||||
|
|
||||||
// First go thru all sources and make a list of all available remote archives
|
|
||||||
// sorted by package class.
|
|
||||||
HashMap<Class<? extends Package>, ArrayList<Archive>> availablePkgs =
|
|
||||||
new HashMap<Class<? extends Package>, ArrayList<Archive>>();
|
|
||||||
|
|
||||||
if (selectedArchives != null) {
|
|
||||||
// Only consider the archives given
|
|
||||||
|
|
||||||
for (Archive a : selectedArchives) {
|
|
||||||
// Only add compatible archives
|
|
||||||
if (a.isCompatible()) {
|
|
||||||
Class<? extends Package> clazz = a.getParentPackage().getClass();
|
|
||||||
|
|
||||||
ArrayList<Archive> list = availablePkgs.get(clazz);
|
|
||||||
if (list == null) {
|
|
||||||
availablePkgs.put(clazz, list = new ArrayList<Archive>());
|
|
||||||
}
|
|
||||||
|
|
||||||
list.add(a);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// Get all the available archives from all loaded sources
|
|
||||||
RepoSource[] remoteSources = getSources().getSources();
|
|
||||||
|
|
||||||
for (RepoSource remoteSrc : remoteSources) {
|
|
||||||
Package[] remotePkgs = remoteSrc.getPackages();
|
|
||||||
if (remotePkgs != null) {
|
|
||||||
for (Package remotePkg : remotePkgs) {
|
|
||||||
Class<? extends Package> clazz = remotePkg.getClass();
|
|
||||||
|
|
||||||
ArrayList<Archive> list = availablePkgs.get(clazz);
|
|
||||||
if (list == null) {
|
|
||||||
availablePkgs.put(clazz, list = new ArrayList<Archive>());
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Archive a : remotePkg.getArchives()) {
|
|
||||||
// Only add compatible archives
|
|
||||||
if (a.isCompatible()) {
|
|
||||||
list.add(a);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Package[] localPkgs = getLocalSdkParser().getPackages();
|
|
||||||
if (localPkgs == null) {
|
|
||||||
// This is unexpected. The local sdk parser should have been called first.
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Package localPkg : localPkgs) {
|
|
||||||
// get the available archive list for this package type
|
|
||||||
ArrayList<Archive> list = availablePkgs.get(localPkg.getClass());
|
|
||||||
|
|
||||||
// if this list is empty, we'll never find anything that matches
|
|
||||||
if (list == null || list.size() == 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// local packages should have one archive at most
|
|
||||||
Archive[] localArchives = localPkg.getArchives();
|
|
||||||
if (localArchives != null && localArchives.length > 0) {
|
|
||||||
Archive localArchive = localArchives[0];
|
|
||||||
// only consider archives compatible with the current platform
|
|
||||||
if (localArchive != null && localArchive.isCompatible()) {
|
|
||||||
|
|
||||||
// We checked all this archive stuff because that's what eventually gets
|
|
||||||
// installed, but the "update" mechanism really works on packages. So now
|
|
||||||
// the real question: is there a remote package that can update this
|
|
||||||
// local package?
|
|
||||||
|
|
||||||
for (Archive availArchive : list) {
|
|
||||||
UpdateInfo info = localPkg.canBeUpdatedBy(availArchive.getParentPackage());
|
|
||||||
if (info == UpdateInfo.UPDATE) {
|
|
||||||
// Found one!
|
|
||||||
result.put(availArchive, localArchive);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,323 @@
|
|||||||
|
/*
|
||||||
|
* 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.AndroidVersion;
|
||||||
|
import com.android.sdklib.internal.repository.AddonPackage;
|
||||||
|
import com.android.sdklib.internal.repository.Archive;
|
||||||
|
import com.android.sdklib.internal.repository.Package;
|
||||||
|
import com.android.sdklib.internal.repository.PlatformPackage;
|
||||||
|
import com.android.sdklib.internal.repository.RepoSource;
|
||||||
|
import com.android.sdklib.internal.repository.RepoSources;
|
||||||
|
import com.android.sdklib.internal.repository.ToolPackage;
|
||||||
|
import com.android.sdklib.internal.repository.Package.UpdateInfo;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
class UpdaterLogic {
|
||||||
|
|
||||||
|
private RepoSources mSources;
|
||||||
|
|
||||||
|
public ArrayList<ArchiveInfo> computeUpdates(
|
||||||
|
Collection<Archive> selectedArchives,
|
||||||
|
RepoSources sources,
|
||||||
|
Package[] localPkgs) {
|
||||||
|
|
||||||
|
mSources = sources;
|
||||||
|
ArrayList<ArchiveInfo> archives = new ArrayList<ArchiveInfo>();
|
||||||
|
ArrayList<Package> remotePkgs = new ArrayList<Package>();
|
||||||
|
|
||||||
|
if (selectedArchives == null) {
|
||||||
|
selectedArchives = findUpdates(localPkgs, remotePkgs);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Archive a : selectedArchives) {
|
||||||
|
insertArchive(a, archives, selectedArchives, remotePkgs, localPkgs, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return archives;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find suitable updates to all current local packages.
|
||||||
|
*/
|
||||||
|
private Collection<Archive> findUpdates(Package[] localPkgs, ArrayList<Package> remotePkgs) {
|
||||||
|
ArrayList<Archive> updates = new ArrayList<Archive>();
|
||||||
|
|
||||||
|
fetchRemotePackages(remotePkgs);
|
||||||
|
|
||||||
|
for (Package localPkg : localPkgs) {
|
||||||
|
for (Package remotePkg : remotePkgs) {
|
||||||
|
if (localPkg.canBeUpdatedBy(remotePkg) == UpdateInfo.UPDATE) {
|
||||||
|
// Found a suitable update. Only accept the remote package
|
||||||
|
// if it provides at least one compatible archive.
|
||||||
|
|
||||||
|
for (Archive a : remotePkg.getArchives()) {
|
||||||
|
if (a.isCompatible()) {
|
||||||
|
updates.add(a);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return updates;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ArchiveInfo insertArchive(Archive archive,
|
||||||
|
ArrayList<ArchiveInfo> outArchives,
|
||||||
|
Collection<Archive> selectedArchives,
|
||||||
|
ArrayList<Package> remotePkgs,
|
||||||
|
Package[] localPkgs,
|
||||||
|
boolean automated) {
|
||||||
|
Package p = archive.getParentPackage();
|
||||||
|
|
||||||
|
// Is this an update?
|
||||||
|
Archive updatedArchive = null;
|
||||||
|
for (Package lp : localPkgs) {
|
||||||
|
assert lp.getArchives().length == 1;
|
||||||
|
if (lp.getArchives().length > 0 && lp.canBeUpdatedBy(p) == UpdateInfo.UPDATE) {
|
||||||
|
updatedArchive = lp.getArchives()[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// find dependencies
|
||||||
|
ArchiveInfo dep = findDependency(p, outArchives, selectedArchives, remotePkgs, localPkgs);
|
||||||
|
|
||||||
|
ArchiveInfo ai = new ArchiveInfo(
|
||||||
|
archive, //newArchive
|
||||||
|
updatedArchive, //replaced
|
||||||
|
dep //dependsOn
|
||||||
|
);
|
||||||
|
|
||||||
|
outArchives.add(ai);
|
||||||
|
if (dep != null) {
|
||||||
|
dep.addDependencyFor(ai);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ai;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ArchiveInfo findDependency(Package pkg,
|
||||||
|
ArrayList<ArchiveInfo> outArchives,
|
||||||
|
Collection<Archive> selectedArchives,
|
||||||
|
ArrayList<Package> remotePkgs,
|
||||||
|
Package[] localPkgs) {
|
||||||
|
|
||||||
|
// Current dependencies can be:
|
||||||
|
// - addon: *always* depends on platform of same API level
|
||||||
|
// - platform: *might* depends on tools of rev >= min-tools-rev
|
||||||
|
|
||||||
|
if (pkg instanceof AddonPackage) {
|
||||||
|
AddonPackage addon = (AddonPackage) pkg;
|
||||||
|
|
||||||
|
return findAddonDependency(
|
||||||
|
addon, outArchives, selectedArchives, remotePkgs, localPkgs);
|
||||||
|
|
||||||
|
} else if (pkg instanceof PlatformPackage) {
|
||||||
|
PlatformPackage platform = (PlatformPackage) pkg;
|
||||||
|
|
||||||
|
return findPlatformDependency(
|
||||||
|
platform, outArchives, selectedArchives, remotePkgs, localPkgs);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A platform can have a min-tools-rev, in which case it depends on having a tools package
|
||||||
|
* of the requested revision.
|
||||||
|
* Finds the tools dependency. If found, add it to the list of things to install.
|
||||||
|
* Returns the archive info dependency, if any.
|
||||||
|
*/
|
||||||
|
protected ArchiveInfo findPlatformDependency(PlatformPackage platform,
|
||||||
|
ArrayList<ArchiveInfo> outArchives,
|
||||||
|
Collection<Archive> selectedArchives,
|
||||||
|
ArrayList<Package> remotePkgs,
|
||||||
|
Package[] localPkgs) {
|
||||||
|
// This is the requirement to match.
|
||||||
|
int rev = platform.getMinToolsRevision();
|
||||||
|
|
||||||
|
if (rev == PlatformPackage.MIN_TOOLS_REV_NOT_SPECIFIED) {
|
||||||
|
// Well actually there's no requirement.
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// First look in local packages.
|
||||||
|
for (Package p : localPkgs) {
|
||||||
|
if (p instanceof ToolPackage) {
|
||||||
|
if (((ToolPackage) p).getRevision() >= rev) {
|
||||||
|
// We found one already installed. We don't report this dependency
|
||||||
|
// as the UI only cares about resolving "newly added dependencies".
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Look in archives already scheduled for install
|
||||||
|
for (ArchiveInfo ai : outArchives) {
|
||||||
|
Package p = ai.getNewArchive().getParentPackage();
|
||||||
|
if (p instanceof PlatformPackage) {
|
||||||
|
if (((ToolPackage) p).getRevision() >= rev) {
|
||||||
|
// The dependency is already scheduled for install, nothing else to do.
|
||||||
|
return ai;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise look in the selected archives.
|
||||||
|
for (Archive a : selectedArchives) {
|
||||||
|
Package p = a.getParentPackage();
|
||||||
|
if (p instanceof ToolPackage) {
|
||||||
|
if (((ToolPackage) p).getRevision() >= rev) {
|
||||||
|
// It's not already in the list of things to install, so add it now
|
||||||
|
return insertArchive(a, outArchives,
|
||||||
|
selectedArchives, remotePkgs, localPkgs,
|
||||||
|
true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally nothing matched, so let's look at all available remote packages
|
||||||
|
fetchRemotePackages(remotePkgs);
|
||||||
|
for (Package p : remotePkgs) {
|
||||||
|
if (p instanceof ToolPackage) {
|
||||||
|
if (((ToolPackage) p).getRevision() >= rev) {
|
||||||
|
// It's not already in the list of things to install, so add the
|
||||||
|
// first compatible archive we can find.
|
||||||
|
for (Archive a : p.getArchives()) {
|
||||||
|
if (a.isCompatible()) {
|
||||||
|
return insertArchive(a, outArchives,
|
||||||
|
selectedArchives, remotePkgs, localPkgs,
|
||||||
|
true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We end up here if nothing matches. We don't have a good tools to match.
|
||||||
|
// Seriously, that can't happens unless we totally screwed our repo manifest.
|
||||||
|
// We'll let this one go through anyway.
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An addon depends on having a platform with the same API version.
|
||||||
|
* Finds the platform dependency. If found, add it to the list of things to install.
|
||||||
|
* Returns the archive info dependency, if any.
|
||||||
|
*/
|
||||||
|
protected ArchiveInfo findAddonDependency(AddonPackage addon,
|
||||||
|
ArrayList<ArchiveInfo> outArchives,
|
||||||
|
Collection<Archive> selectedArchives,
|
||||||
|
ArrayList<Package> remotePkgs,
|
||||||
|
Package[] localPkgs) {
|
||||||
|
// This is the requirement to match.
|
||||||
|
AndroidVersion v = addon.getVersion();
|
||||||
|
|
||||||
|
// Find a platform that would satisfy the requirement.
|
||||||
|
|
||||||
|
// First look in local packages.
|
||||||
|
for (Package p : localPkgs) {
|
||||||
|
if (p instanceof PlatformPackage) {
|
||||||
|
if (v.equals(((PlatformPackage) p).getVersion())) {
|
||||||
|
// We found one already installed. We don't report this dependency
|
||||||
|
// as the UI only cares about resolving "newly added dependencies".
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Look in archives already scheduled for install
|
||||||
|
for (ArchiveInfo ai : outArchives) {
|
||||||
|
Package p = ai.getNewArchive().getParentPackage();
|
||||||
|
if (p instanceof PlatformPackage) {
|
||||||
|
if (v.equals(((PlatformPackage) p).getVersion())) {
|
||||||
|
// The dependency is already scheduled for install, nothing else to do.
|
||||||
|
return ai;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise look in the selected archives.
|
||||||
|
for (Archive a : selectedArchives) {
|
||||||
|
Package p = a.getParentPackage();
|
||||||
|
if (p instanceof PlatformPackage) {
|
||||||
|
if (v.equals(((PlatformPackage) p).getVersion())) {
|
||||||
|
// It's not already in the list of things to install, so add it now
|
||||||
|
return insertArchive(a, outArchives,
|
||||||
|
selectedArchives, remotePkgs, localPkgs,
|
||||||
|
true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally nothing matched, so let's look at all available remote packages
|
||||||
|
fetchRemotePackages(remotePkgs);
|
||||||
|
for (Package p : remotePkgs) {
|
||||||
|
if (p instanceof PlatformPackage) {
|
||||||
|
if (v.equals(((PlatformPackage) p).getVersion())) {
|
||||||
|
// It's not already in the list of things to install, so add the
|
||||||
|
// first compatible archive we can find.
|
||||||
|
for (Archive a : p.getArchives()) {
|
||||||
|
if (a.isCompatible()) {
|
||||||
|
return insertArchive(a, outArchives,
|
||||||
|
selectedArchives, remotePkgs, localPkgs,
|
||||||
|
true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We end up here if nothing matches. We don't have a good platform to match.
|
||||||
|
// Seriously, that can't happens unless the repository contains a bogus addon
|
||||||
|
// entry that does not match any existing platform API level.
|
||||||
|
// It's conceivable that a 3rd part addon repo might have error, in which case
|
||||||
|
// we'll let this one go through anyway.
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Fetch all remote packages only if really needed. */
|
||||||
|
protected void fetchRemotePackages(ArrayList<Package> remotePkgs) {
|
||||||
|
if (remotePkgs.size() > 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get all the available packages from all loaded sources
|
||||||
|
RepoSource[] remoteSources = mSources.getSources();
|
||||||
|
|
||||||
|
for (RepoSource remoteSrc : remoteSources) {
|
||||||
|
Package[] pkgs = remoteSrc.getPackages();
|
||||||
|
if (pkgs != null) {
|
||||||
|
nextPackage: for (Package pkg : pkgs) {
|
||||||
|
for (Archive a : pkg.getArchives()) {
|
||||||
|
// Only add a package if it contains at least one compatible archive
|
||||||
|
if (a.isCompatible()) {
|
||||||
|
remotePkgs.add(pkg);
|
||||||
|
continue nextPackage;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
/*
|
||||||
|
* 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.Archive;
|
||||||
|
import com.android.sdklib.internal.repository.MockAddonPackage;
|
||||||
|
import com.android.sdklib.internal.repository.MockPlatformPackage;
|
||||||
|
import com.android.sdklib.internal.repository.MockToolPackage;
|
||||||
|
import com.android.sdklib.internal.repository.Package;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
|
public class UpdaterLogicTest extends TestCase {
|
||||||
|
|
||||||
|
private static class MockUpdaterLogic extends UpdaterLogic {
|
||||||
|
private final Package[] mRemotePackages;
|
||||||
|
|
||||||
|
public MockUpdaterLogic(Package[] remotePackages) {
|
||||||
|
mRemotePackages = remotePackages;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void fetchRemotePackages(ArrayList<Package> remotePkgs) {
|
||||||
|
if (mRemotePackages != null) {
|
||||||
|
remotePkgs.addAll(Arrays.asList(mRemotePackages));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testFindAddonDependency() throws Exception {
|
||||||
|
MockUpdaterLogic mul = new MockUpdaterLogic(null);
|
||||||
|
|
||||||
|
MockPlatformPackage p1 = new MockPlatformPackage(1, 1);
|
||||||
|
MockPlatformPackage p2 = new MockPlatformPackage(2, 1);
|
||||||
|
|
||||||
|
MockAddonPackage a1 = new MockAddonPackage(p1, 1);
|
||||||
|
MockAddonPackage a2 = new MockAddonPackage(p2, 2);
|
||||||
|
|
||||||
|
ArrayList<ArchiveInfo> out = new ArrayList<ArchiveInfo>();
|
||||||
|
ArrayList<Archive> selected = new ArrayList<Archive>();
|
||||||
|
ArrayList<Package> remote = new ArrayList<Package>();
|
||||||
|
|
||||||
|
// a2 depends on p2, which is not in the locals
|
||||||
|
Package[] locals = { p1, a1 };
|
||||||
|
assertNull(mul.findAddonDependency(a2, out, selected, remote, locals));
|
||||||
|
assertEquals(0, out.size());
|
||||||
|
|
||||||
|
// p2 is now selected, and should be scheduled for install in out
|
||||||
|
Archive p2_archive = p2.getArchives()[0];
|
||||||
|
selected.add(p2_archive);
|
||||||
|
ArchiveInfo ai2 = mul.findAddonDependency(a2, out, selected, remote, locals);
|
||||||
|
assertNotNull(ai2);
|
||||||
|
assertSame(p2_archive, ai2.getNewArchive());
|
||||||
|
assertEquals(1, out.size());
|
||||||
|
assertSame(p2_archive, out.get(0).getNewArchive());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testFindPlatformDependency() throws Exception {
|
||||||
|
MockUpdaterLogic mul = new MockUpdaterLogic(null);
|
||||||
|
|
||||||
|
MockToolPackage t1 = new MockToolPackage(1);
|
||||||
|
MockToolPackage t2 = new MockToolPackage(2);
|
||||||
|
|
||||||
|
MockPlatformPackage p2 = new MockPlatformPackage(2, 1, 2);
|
||||||
|
|
||||||
|
ArrayList<ArchiveInfo> out = new ArrayList<ArchiveInfo>();
|
||||||
|
ArrayList<Archive> selected = new ArrayList<Archive>();
|
||||||
|
ArrayList<Package> remote = new ArrayList<Package>();
|
||||||
|
|
||||||
|
// p2 depends on t2, which is not locally installed
|
||||||
|
Package[] locals = { t1 };
|
||||||
|
assertNull(mul.findPlatformDependency(p2, out, selected, remote, locals));
|
||||||
|
assertEquals(0, out.size());
|
||||||
|
|
||||||
|
// t2 is now selected and can be used as a dependency
|
||||||
|
Archive t2_archive = t2.getArchives()[0];
|
||||||
|
selected.add(t2_archive);
|
||||||
|
ArchiveInfo ai2 = mul.findPlatformDependency(p2, out, selected, remote, locals);
|
||||||
|
assertNotNull(ai2);
|
||||||
|
assertSame(t2_archive, ai2.getNewArchive());
|
||||||
|
assertEquals(1, out.size());
|
||||||
|
assertSame(t2_archive, out.get(0).getNewArchive());
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user