summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--android/src/com/android/tools/idea/avdmanager/SystemImageList.java2
-rwxr-xr-xandroid/src/com/android/tools/idea/sdk/SdkLifecycleListener.java39
-rwxr-xr-xandroid/src/com/android/tools/idea/sdk/SdkPackages.java167
-rwxr-xr-xandroid/src/com/android/tools/idea/sdk/SdkState.java87
-rw-r--r--android/src/com/android/tools/idea/sdk/remote/UpdatablePkgInfo.java57
-rwxr-xr-xandroid/src/com/android/tools/idea/sdk/remote/Update.java97
-rwxr-xr-xandroid/src/com/android/tools/idea/sdk/remote/UpdateResult.java85
-rwxr-xr-xandroid/src/com/android/tools/idea/sdk/remote/internal/archives/Archive.java4
-rwxr-xr-xandroid/src/com/android/tools/idea/sdk/remote/internal/sources/SdkSources.java86
-rwxr-xr-xandroid/src/com/android/tools/idea/sdk/remote/internal/updater/PkgItem.java207
-rwxr-xr-xandroid/src/com/android/tools/idea/sdk/remote/internal/updater/UpdaterData.java2
-rwxr-xr-xandroid/src/com/android/tools/idea/sdk/wizard/SmwOldApiDirectInstall.java5
-rw-r--r--android/src/com/android/tools/idea/welcome/install/ComponentInstaller.java7
-rw-r--r--android/src/com/android/tools/idea/welcome/wizard/AndroidStudioWelcomeScreenProvider.java2
-rwxr-xr-xandroid/src/com/android/tools/idea/wizard/ConfigureFormFactorStep.java3
-rw-r--r--sdk-updates/sdk-updates.iml14
-rw-r--r--sdk-updates/src/META-INF/plugin.xml38
-rw-r--r--sdk-updates/src/com/android/tools/idea/updater/AndroidSdkUpdaterPlugin.java43
-rw-r--r--sdk-updates/src/com/android/tools/idea/updater/SdkComponentSource.java124
-rw-r--r--sdk-updates/src/com/android/tools/idea/updater/UpdatablePackage.java58
20 files changed, 594 insertions, 533 deletions
diff --git a/android/src/com/android/tools/idea/avdmanager/SystemImageList.java b/android/src/com/android/tools/idea/avdmanager/SystemImageList.java
index 5b39ee79af2..05faf7aaa9a 100644
--- a/android/src/com/android/tools/idea/avdmanager/SystemImageList.java
+++ b/android/src/com/android/tools/idea/avdmanager/SystemImageList.java
@@ -283,7 +283,7 @@ public class SystemImageList extends JPanel implements ListSelectionListener {
@Nullable
private List<SystemImageDescription> getRemoteImages() {
List<SystemImageDescription> items = Lists.newArrayList();
- Set<RemotePkgInfo> infos = mySdkState.getUpdates().getNewPkgs();
+ Set<RemotePkgInfo> infos = mySdkState.getPackages().getNewPkgs();
if (infos.isEmpty()) {
return null;
diff --git a/android/src/com/android/tools/idea/sdk/SdkLifecycleListener.java b/android/src/com/android/tools/idea/sdk/SdkLifecycleListener.java
deleted file mode 100755
index ce2d59899f3..00000000000
--- a/android/src/com/android/tools/idea/sdk/SdkLifecycleListener.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2014 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.tools.idea.sdk;
-
-import com.android.annotations.NonNull;
-import com.intellij.util.messages.Topic;
-import org.jetbrains.android.sdk.AndroidSdkData;
-
-public interface SdkLifecycleListener {
- Topic<SdkLifecycleListener> TOPIC = Topic.create("Android SDK lifecycle notifications", SdkLifecycleListener.class);
-
- void localSdkLoaded (@NonNull AndroidSdkData sdkData);
- void remoteSdkLoaded(@NonNull AndroidSdkData sdkData);
- void updatesComputed(@NonNull AndroidSdkData sdkData);
-
- abstract class Adapter implements SdkLifecycleListener {
- @Override
- public void localSdkLoaded(@NonNull AndroidSdkData sdkData) {}
-
- @Override
- public void remoteSdkLoaded(@NonNull AndroidSdkData sdkData) {}
-
- @Override
- public void updatesComputed(@NonNull AndroidSdkData sdkData) {}
- }
-}
diff --git a/android/src/com/android/tools/idea/sdk/SdkPackages.java b/android/src/com/android/tools/idea/sdk/SdkPackages.java
new file mode 100755
index 00000000000..95e60e88b33
--- /dev/null
+++ b/android/src/com/android/tools/idea/sdk/SdkPackages.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2015 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.tools.idea.sdk;
+
+import com.android.annotations.NonNull;
+import com.android.sdklib.repository.descriptors.IPkgDesc;
+import com.android.sdklib.repository.descriptors.PkgType;
+import com.android.sdklib.repository.local.LocalPkgInfo;
+import com.android.tools.idea.sdk.remote.RemotePkgInfo;
+import com.android.tools.idea.sdk.remote.UpdatablePkgInfo;
+import com.google.common.collect.Multimap;
+import com.google.common.collect.Sets;
+import com.google.common.collect.TreeMultimap;
+
+import java.util.Set;
+
+
+/**
+ * Store of current local and remote packages, in convenient forms.
+ */
+public final class SdkPackages {
+ private final Set<UpdatablePkgInfo> myUpdatedPkgs = Sets.newTreeSet();
+ private final Set<RemotePkgInfo> myNewPkgs = Sets.newTreeSet();
+ private final long myTimestampMs;
+ private Set<UpdatablePkgInfo> myConsolidatedPkgs = Sets.newTreeSet();
+ private LocalPkgInfo[] myLocalPkgInfos = new LocalPkgInfo[0];
+ private Multimap<PkgType, RemotePkgInfo> myRemotePkgInfos = TreeMultimap.create();
+
+ SdkPackages() {
+ myTimestampMs = System.currentTimeMillis();
+ }
+
+ public SdkPackages(LocalPkgInfo[] localPkgs, Multimap<PkgType, RemotePkgInfo> remotePkgs) {
+ this();
+ setLocalPkgInfos(localPkgs);
+ setRemotePkgInfos(remotePkgs);
+ }
+
+ /**
+ * Returns the timestamp (in {@link System#currentTimeMillis()} time) when this object was created.
+ */
+ public long getTimestampMs() {
+ return myTimestampMs;
+ }
+
+ /**
+ * Returns the set of packages that have local updates available.
+ * Use {@link LocalPkgInfo#getUpdate()} to retrieve the computed updated candidate.
+ *
+ * @return A non-null, possibly empty Set of update candidates.
+ */
+ @NonNull
+ public Set<UpdatablePkgInfo> getUpdatedPkgs() {
+ return myUpdatedPkgs;
+ }
+
+ /**
+ * Returns the set of new remote packages that are not locally present
+ * and that the user could install.
+ *
+ * @return A non-null, possibly empty Set of new install candidates.
+ */
+ @NonNull
+ public Set<RemotePkgInfo> getNewPkgs() {
+ return myNewPkgs;
+ }
+
+ /**
+ * Returns a set of {@link UpdatablePackageInfo}s representing all known local and remote packages. Remote packages corresponding
+ * to local packages will be represented by a single item containing both the local and remote info..
+ */
+ @NonNull
+ public Set<UpdatablePkgInfo> getConsolidatedPkgs() {
+ return myConsolidatedPkgs;
+ }
+
+ @NonNull
+ public LocalPkgInfo[] getLocalPkgInfos() {
+ return myLocalPkgInfos;
+ }
+
+ public Multimap<PkgType, RemotePkgInfo> getRemotePkgInfos() {
+ return myRemotePkgInfos;
+ }
+
+ void setLocalPkgInfos(LocalPkgInfo[] packages) {
+ myLocalPkgInfos = packages;
+ computeUpdates();
+ }
+
+ void setRemotePkgInfos(Multimap<PkgType, RemotePkgInfo> packages) {
+ myRemotePkgInfos = packages;
+ computeUpdates();
+ }
+
+ private void computeUpdates() {
+ Set<UpdatablePkgInfo> newConsolidatedPkgs = Sets.newTreeSet();
+ UpdatablePkgInfo[] updatablePkgInfos = new UpdatablePkgInfo[myLocalPkgInfos.length];
+ for (int i = 0; i < myLocalPkgInfos.length; i++) {
+ updatablePkgInfos[i] = new UpdatablePkgInfo(myLocalPkgInfos[i]);
+ }
+ Set<RemotePkgInfo> updates = Sets.newTreeSet();
+
+ // Find updates to locally installed packages
+ for (UpdatablePkgInfo info : updatablePkgInfos) {
+ RemotePkgInfo update = findUpdate(info);
+ if (update != null) {
+ info.setRemote(update);
+ myUpdatedPkgs.add(info);
+ updates.add(update);
+ }
+ newConsolidatedPkgs.add(info);
+ }
+
+ // Find new packages not yet installed
+ nextRemote: for (RemotePkgInfo remote : myRemotePkgInfos.values()) {
+ if (updates.contains(remote)) {
+ // if package is already a known update, it's not new.
+ continue nextRemote;
+ }
+ IPkgDesc remoteDesc = remote.getPkgDesc();
+ for (UpdatablePkgInfo info : updatablePkgInfos) {
+ IPkgDesc localDesc = info.getLocalInfo().getDesc();
+ if (remoteDesc.compareTo(localDesc) == 0 || remoteDesc.isUpdateFor(localDesc)) {
+ // if package is same as an installed or is an update for an installed
+ // one, then it's not new.
+ continue nextRemote;
+ }
+ }
+
+ myNewPkgs.add(remote);
+ newConsolidatedPkgs.add(new UpdatablePkgInfo(remote));
+ }
+ myConsolidatedPkgs = newConsolidatedPkgs;
+ }
+
+ private RemotePkgInfo findUpdate(@NonNull UpdatablePkgInfo info) {
+ RemotePkgInfo currUpdatePkg = null;
+ IPkgDesc currUpdateDesc = null;
+ IPkgDesc localDesc = info.getLocalInfo().getDesc();
+
+ for (RemotePkgInfo remote: myRemotePkgInfos.get(localDesc.getType())) {
+ IPkgDesc remoteDesc = remote.getPkgDesc();
+ if ((currUpdateDesc == null && remoteDesc.isUpdateFor(localDesc)) ||
+ (currUpdateDesc != null && remoteDesc.isUpdateFor(currUpdateDesc))) {
+ currUpdatePkg = remote;
+ currUpdateDesc = remoteDesc;
+ }
+ }
+
+ return currUpdatePkg;
+ }
+}
diff --git a/android/src/com/android/tools/idea/sdk/SdkState.java b/android/src/com/android/tools/idea/sdk/SdkState.java
index d232b75a22b..2927d86af9a 100755
--- a/android/src/com/android/tools/idea/sdk/SdkState.java
+++ b/android/src/com/android/tools/idea/sdk/SdkState.java
@@ -19,28 +19,20 @@ import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.annotations.concurrency.GuardedBy;
import com.android.sdklib.repository.descriptors.PkgType;
-import com.android.sdklib.repository.local.LocalPkgInfo;
import com.android.tools.idea.sdk.remote.RemotePkgInfo;
import com.android.tools.idea.sdk.remote.RemoteSdk;
-import com.android.tools.idea.sdk.remote.Update;
-import com.android.tools.idea.sdk.remote.UpdateResult;
import com.android.tools.idea.sdk.remote.internal.sources.SdkSources;
import com.android.utils.ILogger;
import com.google.common.collect.Lists;
import com.google.common.collect.Multimap;
import com.intellij.openapi.application.ApplicationManager;
-import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.application.ex.ApplicationEx;
import com.intellij.openapi.application.ex.ApplicationManagerEx;
import com.intellij.openapi.diagnostic.Logger;
-import com.intellij.openapi.progress.PerformInBackgroundOption;
-import com.intellij.openapi.progress.ProgressIndicator;
-import com.intellij.openapi.progress.ProgressManager;
-import com.intellij.openapi.progress.Task;
+import com.intellij.openapi.progress.*;
import com.intellij.openapi.progress.impl.BackgroundableProcessIndicator;
import com.intellij.openapi.progress.util.ProgressWindow;
import com.intellij.reference.SoftReference;
-import com.intellij.util.concurrency.FutureResult;
import com.intellij.util.concurrency.Semaphore;
import org.jetbrains.android.sdk.AndroidSdkData;
@@ -48,25 +40,19 @@ import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
-import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
public class SdkState {
- /** Default expiration delay is 24 hours. */
- public final static long DEFAULT_EXPIRATION_PERIOD_MS = 24 * 3600 * 1000;
+ public final static long DEFAULT_EXPIRATION_PERIOD_MS = TimeUnit.DAYS.toMillis(1);
private static final Logger LOG = Logger.getInstance("#com.android.tools.idea.sdk.SdkState");
- @GuardedBy(value = "sSdkStates")
- private static final Set<SoftReference<SdkState>> sSdkStates = new HashSet<SoftReference<SdkState>>();
+ @GuardedBy(value = "sSdkStates") private static final Set<SoftReference<SdkState>> sSdkStates = new HashSet<SoftReference<SdkState>>();
- @Nullable
- private final AndroidSdkData mySdkData;
+ @Nullable private final AndroidSdkData mySdkData;
private final RemoteSdk myRemoteSdk;
- private LocalPkgInfo[] myLocalPkgInfos = new LocalPkgInfo[0];
- private SdkSources mySources;
- private UpdateResult myUpdates;
- private Multimap<PkgType, RemotePkgInfo> myRemotePkgs;
+ private SdkPackages myPackages = null;
private long myLastRefreshMs;
private LoadTask myTask;
@@ -78,6 +64,16 @@ public class SdkState {
myRemoteSdk = new RemoteSdk(new LogWrapper(Logger.getInstance(SdkState.class)));
}
+ /**
+ * This shouldn't be needed unless interacting with the internals of the remote sdk.
+ *
+ * @return
+ */
+ @NonNull
+ public RemoteSdk getRemoteSdk() {
+ return myRemoteSdk;
+ }
+
@NonNull
public static SdkState getInstance(@Nullable AndroidSdkData sdkData) {
synchronized (sSdkStates) {
@@ -106,17 +102,9 @@ public class SdkState {
}
@NonNull
- public LocalPkgInfo[] getLocalPkgInfos() {
- return myLocalPkgInfos;
- }
-
- public Multimap<PkgType, RemotePkgInfo> getRemotePkgInfos() {
- return myRemotePkgs;
- }
-
- @Nullable
- public UpdateResult getUpdates() {
- return myUpdates;
+ public SdkPackages getPackages() {
+ assert myPackages != null;
+ return myPackages;
}
public boolean loadAsync(long timeoutMs,
@@ -152,8 +140,12 @@ public class SdkState {
myTask = new LoadTask(canBeCancelled, onLocalComplete, onSuccess, onError, forceRefresh, sync);
}
- ProgressWindow progress = new BackgroundableProcessIndicator(myTask);
- myTask.setProgress(progress);
+ if (!ApplicationManager.getApplication().isDispatchThread()) {
+ // not dispatch thread, assume progress is being handled elsewhere. Just run the task.
+ myTask.run(new EmptyProgressIndicator());
+ return true;
+ }
+
ProgressManager.getInstance().run(myTask);
return true;
@@ -180,6 +172,10 @@ public class SdkState {
onSuccesses.add(complete);
onErrors.add(complete);
boolean result = load(timeoutMs, canBeCancelled, onLocalCompletes, onSuccesses, onErrors, forceRefresh, true);
+ if (!ApplicationManager.getApplication().isDispatchThread()) {
+ // not dispatch thread, assume progress is being handled elsewhere. We don't have to wait since load() ran in-thread.
+ return result;
+ }
ProgressManager pm = ProgressManager.getInstance();
ProgressIndicator indicator = pm.getProgressIndicator();
indicator = indicator == null ? new ProgressWindow(false, false, null) : indicator;
@@ -228,7 +224,8 @@ public class SdkState {
public void error(@Nullable Throwable t, @Nullable String msgFormat, Object... args) {
if (msgFormat == null && t != null) {
myIndicator.setText2(t.toString());
- } else if (msgFormat != null) {
+ }
+ else if (msgFormat != null) {
myIndicator.setText2(String.format(msgFormat, args));
}
}
@@ -287,14 +284,11 @@ public class SdkState {
@Override
public void run(@NonNull ProgressIndicator indicator) {
- assert myProgress != null;
boolean success = false;
try {
IndicatorLogger logger = new IndicatorLogger(indicator);
- ApplicationEx app = ApplicationManagerEx.getApplicationEx();
- SdkLifecycleListener notifier = app.getMessageBus().syncPublisher(SdkLifecycleListener.TOPIC);
-
+ myPackages = new SdkPackages();
if (mySdkData != null) {
// fetch local sdk
indicator.setText("Loading local SDK...");
@@ -302,8 +296,7 @@ public class SdkState {
if (myForceRefresh) {
mySdkData.getLocalSdk().clearLocalPkg(PkgType.PKG_ALL);
}
- myLocalPkgInfos = mySdkData.getLocalSdk().getPkgsInfos(PkgType.PKG_ALL);
- notifier.localSdkLoaded(mySdkData);
+ myPackages.setLocalPkgInfos(mySdkData.getLocalSdk().getPkgsInfos(PkgType.PKG_ALL));
indicator.setFraction(0.25);
}
if (indicator.isCanceled()) {
@@ -315,11 +308,10 @@ public class SdkState {
}
myOnLocalCompletes.clear();
}
-
// fetch sdk repository sources.
indicator.setText("Find SDK Repository...");
indicator.setText2("");
- mySources = myRemoteSdk.fetchSources(myForceRefresh ? 0 : RemoteSdk.DEFAULT_EXPIRATION_PERIOD_MS, logger);
+ SdkSources sources = myRemoteSdk.fetchSources(myForceRefresh ? 0 : RemoteSdk.DEFAULT_EXPIRATION_PERIOD_MS, logger);
indicator.setFraction(0.50);
if (indicator.isCanceled()) {
@@ -328,18 +320,15 @@ public class SdkState {
// fetch remote sdk
indicator.setText("Check SDK Repository...");
indicator.setText2("");
- myRemotePkgs = myRemoteSdk.fetch(mySources, logger);
- notifier.remoteSdkLoaded(mySdkData);
+ Multimap<PkgType, RemotePkgInfo> remotes = myRemoteSdk.fetch(sources, logger);
+ // compute updates
+ indicator.setText("Compute SDK updates...");
indicator.setFraction(0.75);
-
+ myPackages.setRemotePkgInfos(remotes);
if (indicator.isCanceled()) {
return;
}
- // compute updates
- indicator.setText("Compute SDK updates...");
indicator.setText2("");
- myUpdates = Update.computeUpdates(myLocalPkgInfos, myRemotePkgs);
- notifier.updatesComputed(mySdkData);
indicator.setFraction(1.0);
if (indicator.isCanceled()) {
diff --git a/android/src/com/android/tools/idea/sdk/remote/UpdatablePkgInfo.java b/android/src/com/android/tools/idea/sdk/remote/UpdatablePkgInfo.java
index ff082dbcc31..e25aa70d05e 100644
--- a/android/src/com/android/tools/idea/sdk/remote/UpdatablePkgInfo.java
+++ b/android/src/com/android/tools/idea/sdk/remote/UpdatablePkgInfo.java
@@ -16,38 +16,71 @@
package com.android.tools.idea.sdk.remote;
import com.android.annotations.NonNull;
+import com.android.sdklib.repository.descriptors.IPkgDesc;
import com.android.sdklib.repository.local.LocalPkgInfo;
import org.jetbrains.annotations.Nullable;
/**
- * Created by jbakermalone on 4/3/15.
+ * Represents a (revisionless) package, either local, remote, or both. If both a local and remote package are specified,
+ * they should represent exactly the same package, excepting the revision. That is, the result of installing the remote package
+ * should be (a possibly updated version of) the local package.
*/
public class UpdatablePkgInfo implements Comparable<UpdatablePkgInfo> {
- private final LocalPkgInfo myLocalInfo;
- private RemotePkgInfo myUpdate;
+ private LocalPkgInfo myLocalInfo;
+ private RemotePkgInfo myRemoteInfo;
- public UpdatablePkgInfo(@NonNull LocalPkgInfo localPkg, @Nullable RemotePkgInfo update) {
+ public UpdatablePkgInfo(@NonNull LocalPkgInfo localInfo) {
+ init(localInfo, null);
+ }
+
+ public UpdatablePkgInfo(@NonNull RemotePkgInfo remoteInfo) {
+ init(null, remoteInfo);
+ }
+
+ public UpdatablePkgInfo(@NonNull LocalPkgInfo localInfo, @NonNull RemotePkgInfo remoteInfo) {
+ init(localInfo, remoteInfo);
+ }
+
+ private void init(@Nullable LocalPkgInfo localPkg, @Nullable RemotePkgInfo remotePkg) {
+ assert localPkg != null || remotePkg != null;
myLocalInfo = localPkg;
- myUpdate = update;
+ myRemoteInfo = remotePkg;
}
- public void setUpdate(@NonNull RemotePkgInfo update) {
- assert myUpdate == null;
- myUpdate = update;
+ public void setRemote(@NonNull RemotePkgInfo remote) {
+ assert myRemoteInfo == null;
+ myRemoteInfo = remote;
}
- @NonNull
+ @Nullable
public LocalPkgInfo getLocalInfo() {
return myLocalInfo;
}
@Nullable
- public RemotePkgInfo getUpdate() {
- return myUpdate;
+ public RemotePkgInfo getRemote() {
+ return myRemoteInfo;
+ }
+
+ public boolean hasRemote() {
+ return myRemoteInfo != null;
+ }
+
+ public boolean hasLocal() {
+ return myLocalInfo != null;
}
@Override
public int compareTo(UpdatablePkgInfo o) {
- return getLocalInfo().compareTo(o.getLocalInfo());
+ return getPkgDesc().compareTo(o.getPkgDesc());
+ }
+
+ public IPkgDesc getPkgDesc() {
+ return myLocalInfo == null ? myRemoteInfo.getPkgDesc() : myLocalInfo.getDesc();
+ }
+
+ public boolean isUpdate() {
+ return myLocalInfo != null && myRemoteInfo != null &&
+ myRemoteInfo.getPkgDesc().getPreciseRevision().compareTo(myLocalInfo.getDesc().getPreciseRevision()) > 0;
}
}
diff --git a/android/src/com/android/tools/idea/sdk/remote/Update.java b/android/src/com/android/tools/idea/sdk/remote/Update.java
deleted file mode 100755
index bc6213060f4..00000000000
--- a/android/src/com/android/tools/idea/sdk/remote/Update.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright (C) 2015 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.tools.idea.sdk.remote;
-
-import com.android.annotations.NonNull;
-import com.android.sdklib.repository.descriptors.IPkgDesc;
-import com.android.sdklib.repository.descriptors.PkgType;
-import com.android.sdklib.repository.local.LocalPkgInfo;
-import com.google.common.collect.Multimap;
-import com.google.common.collect.Sets;
-
-import java.util.Set;
-
-
-/**
- * Helper methods to compute updates available for local packages.
- */
-public abstract class Update {
-
- public static UpdateResult computeUpdates(@NonNull LocalPkgInfo[] localPkgs,
- @NonNull Multimap<PkgType, RemotePkgInfo> remotePkgs) {
- UpdatablePkgInfo[] updatablePkgInfos = new UpdatablePkgInfo[localPkgs.length];
- for (int i = 0; i < localPkgs.length; i++) {
- updatablePkgInfos[i] = new UpdatablePkgInfo(localPkgs[i], null);
- }
- UpdateResult result = new UpdateResult();
- Set<RemotePkgInfo> updates = Sets.newTreeSet();
-
- // Find updates to locally installed packages
- for (UpdatablePkgInfo info : updatablePkgInfos) {
- RemotePkgInfo update = findUpdate(info, remotePkgs, result);
- if (update != null) {
- info.setUpdate(update);
- updates.add(update);
- }
- }
-
- // Find new packages not yet installed
- nextRemote: for (RemotePkgInfo remote : remotePkgs.values()) {
- if (updates.contains(remote)) {
- // if package is already a known update, it's not new.
- continue nextRemote;
- }
- IPkgDesc remoteDesc = remote.getPkgDesc();
- for (UpdatablePkgInfo info : updatablePkgInfos) {
- IPkgDesc localDesc = info.getLocalInfo().getDesc();
- if (remoteDesc.compareTo(localDesc) == 0 || remoteDesc.isUpdateFor(localDesc)) {
- // if package is same as an installed or is an update for an installed
- // one, then it's not new.
- continue nextRemote;
- }
- }
-
- result.addNewPkgs(remote);
- }
-
- return result;
- }
-
- private static RemotePkgInfo findUpdate(@NonNull UpdatablePkgInfo info,
- @NonNull Multimap<PkgType, RemotePkgInfo> remotePkgs,
- @NonNull UpdateResult result) {
- RemotePkgInfo currUpdatePkg = null;
- IPkgDesc currUpdateDesc = null;
- IPkgDesc localDesc = info.getLocalInfo().getDesc();
-
- for (RemotePkgInfo remote: remotePkgs.get(localDesc.getType())) {
- IPkgDesc remoteDesc = remote.getPkgDesc();
- if ((currUpdateDesc == null && remoteDesc.isUpdateFor(localDesc)) ||
- (currUpdateDesc != null && remoteDesc.isUpdateFor(currUpdateDesc))) {
- currUpdatePkg = remote;
- currUpdateDesc = remoteDesc;
- }
- }
-
- if (currUpdatePkg != null) {
- result.addUpdatedPkgs(info);
- }
-
- return currUpdatePkg;
- }
-
-}
diff --git a/android/src/com/android/tools/idea/sdk/remote/UpdateResult.java b/android/src/com/android/tools/idea/sdk/remote/UpdateResult.java
deleted file mode 100755
index b6ab1485d86..00000000000
--- a/android/src/com/android/tools/idea/sdk/remote/UpdateResult.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright (C) 2015 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.tools.idea.sdk.remote;
-
-import com.android.annotations.NonNull;
-import com.android.sdklib.repository.local.LocalPkgInfo;
-import com.google.common.collect.Sets;
-
-import java.util.Set;
-
-
-/**
- * Results from {@link Update#computeUpdates(LocalPkgInfo[], com.google.common.collect.Multimap)}.
- */
-public final class UpdateResult {
- private final Set<UpdatablePkgInfo> mUpdatedPkgs = Sets.newTreeSet();
- private final Set<RemotePkgInfo> mNewPkgs = Sets.newTreeSet();
- private final long mTimestampMs;
-
- public UpdateResult() {
- mTimestampMs = System.currentTimeMillis();
- }
-
- /**
- * Returns the timestamp (in {@link System#currentTimeMillis()} time) when this object was created.
- */
- public long getTimestampMs() {
- return mTimestampMs;
- }
-
- /**
- * Returns the set of packages that have local updates available.
- * Use {@link LocalPkgInfo#getUpdate()} to retrieve the computed updated candidate.
- *
- * @return A non-null, possibly empty list of update candidates.
- */
- @NonNull
- public Set<UpdatablePkgInfo> getUpdatedPkgs() {
- return mUpdatedPkgs;
- }
-
- /**
- * Returns the set of new remote packages that are not locally present
- * and that the user could install.
- *
- * @return A non-null, possibly empty list of new install candidates.
- */
- @NonNull
- public Set<RemotePkgInfo> getNewPkgs() {
- return mNewPkgs;
- }
-
- /**
- * Add a package to the set of packages with available updates.
- *
- * @param pkgInfo The {@link LocalPkgInfo} which has an available update.
- */
- void addUpdatedPkgs(@NonNull UpdatablePkgInfo pkgInfo) {
- mUpdatedPkgs.add(pkgInfo);
- }
-
- /**
- * Add a package to the set of new remote packages that are not locally present
- * and that the user could install.
- *
- * @param pkgInfo The {@link RemotePkgInfo} which has an available update.
- */
- void addNewPkgs(@NonNull RemotePkgInfo pkgInfo) {
- mNewPkgs.add(pkgInfo);
- }
-}
diff --git a/android/src/com/android/tools/idea/sdk/remote/internal/archives/Archive.java b/android/src/com/android/tools/idea/sdk/remote/internal/archives/Archive.java
index 0d34b5b50a9..25191050f71 100755
--- a/android/src/com/android/tools/idea/sdk/remote/internal/archives/Archive.java
+++ b/android/src/com/android/tools/idea/sdk/remote/internal/archives/Archive.java
@@ -56,7 +56,7 @@ public class Archive implements IDescription, Comparable<Archive> {
* @param checksum The expected checksum string of the archive. Currently only the
* {@link ChecksumType#SHA1} format is supported.
*/
- public Archive(@Nullable RemotePkgInfo pkg, @Nullable ArchFilter archFilter, @Nullable String url, long size, @NonNull String checksum) {
+ public Archive(@NonNull RemotePkgInfo pkg, @Nullable ArchFilter archFilter, @Nullable String url, long size, @NonNull String checksum) {
mPackage = pkg;
mArchFilter = archFilter != null ? archFilter : new ArchFilter(null);
mUrl = url == null ? null : url.trim();
@@ -76,7 +76,7 @@ public class Archive implements IDescription, Comparable<Archive> {
* Returns the package that created and owns this archive.
* It should generally not be null.
*/
- @Nullable
+ @NonNull
public RemotePkgInfo getParentPackage() {
return mPackage;
}
diff --git a/android/src/com/android/tools/idea/sdk/remote/internal/sources/SdkSources.java b/android/src/com/android/tools/idea/sdk/remote/internal/sources/SdkSources.java
index 34a94013ebb..7f908879a26 100755
--- a/android/src/com/android/tools/idea/sdk/remote/internal/sources/SdkSources.java
+++ b/android/src/com/android/tools/idea/sdk/remote/internal/sources/SdkSources.java
@@ -16,20 +16,19 @@
package com.android.tools.idea.sdk.remote.internal.sources;
+import com.android.annotations.concurrency.GuardedBy;
import com.android.prefs.AndroidLocation;
import com.android.prefs.AndroidLocation.AndroidLocationException;
import com.android.sdklib.repository.SdkSysImgConstants;
import com.android.utils.ILogger;
+import com.google.common.collect.Lists;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
-import java.util.ArrayList;
-import java.util.EnumMap;
-import java.util.Iterator;
+import java.util.*;
import java.util.Map.Entry;
-import java.util.Properties;
/**
* A list of sdk-repository and sdk-addon sources, sorted by {@link SdkSourceCategory}.
@@ -39,10 +38,12 @@ public class SdkSources {
private static final String KEY_COUNT = "count";
private static final String KEY_SRC = "src";
+ private static final String KEY_DISPLAY = "disp";
private static final String SRC_FILENAME = "repositories.cfg"; //$NON-NLS-1$
- private final EnumMap<SdkSourceCategory, ArrayList<SdkSource>> mSources =
+ @GuardedBy("itself")
+ private final EnumMap<SdkSourceCategory, ArrayList<SdkSource>> mySources =
new EnumMap<SdkSourceCategory, ArrayList<SdkSource>>(SdkSourceCategory.class);
public SdkSources() {
@@ -57,11 +58,11 @@ public class SdkSources {
* at the end, not for every single addition.
*/
public void add(SdkSourceCategory category, SdkSource source) {
- synchronized (mSources) {
- ArrayList<SdkSource> list = mSources.get(category);
+ synchronized (mySources) {
+ ArrayList<SdkSource> list = mySources.get(category);
if (list == null) {
list = new ArrayList<SdkSource>();
- mSources.put(category, list);
+ mySources.put(category, list);
}
list.add(source);
@@ -69,14 +70,28 @@ public class SdkSources {
}
/**
+ * Replaces the current collection of sources corresponding to a particular category with the given collection.
+ * <p/>
+ * Implementation detail: {@link SdkSources} doesn't invoke {@link #notifyChangeListeners()}
+ * directly. Callers who use {@code set()} are responsible for notifying the listeners once
+ * they are done modifying the sources list. The intent is to notify the listeners only once
+ * at the end, not for every single addition.
+ */
+ public void set(SdkSourceCategory category, Collection<SdkSource> sources) {
+ synchronized (mySources) {
+ mySources.put(category, Lists.newArrayList(sources));
+ }
+ }
+
+ /**
* Removes a source from the Sources list.
* <p/>
* Callers who remove entries are responsible for notifying the listeners using
* {@link #notifyChangeListeners()} once they are done modifying the sources list.
*/
public void remove(SdkSource source) {
- synchronized (mSources) {
- Iterator<Entry<SdkSourceCategory, ArrayList<SdkSource>>> it = mSources.entrySet().iterator();
+ synchronized (mySources) {
+ Iterator<Entry<SdkSourceCategory, ArrayList<SdkSource>>> it = mySources.entrySet().iterator();
while (it.hasNext()) {
Entry<SdkSourceCategory, ArrayList<SdkSource>> entry = it.next();
ArrayList<SdkSource> list = entry.getValue();
@@ -98,8 +113,8 @@ public class SdkSources {
* {@link #notifyChangeListeners()} once they are done modifying the sources list.
*/
public void removeAll(SdkSourceCategory category) {
- synchronized (mSources) {
- mSources.remove(category);
+ synchronized (mySources) {
+ mySources.remove(category);
}
}
@@ -117,8 +132,8 @@ public class SdkSources {
cats.add(cat);
}
else {
- synchronized (mSources) {
- ArrayList<SdkSource> list = mSources.get(cat);
+ synchronized (mySources) {
+ ArrayList<SdkSource> list = mySources.get(cat);
if (list != null && !list.isEmpty()) {
cats.add(cat);
}
@@ -134,8 +149,8 @@ public class SdkSources {
* Might return an empty array, but never returns null.
*/
public SdkSource[] getSources(SdkSourceCategory category) {
- synchronized (mSources) {
- ArrayList<SdkSource> list = mSources.get(category);
+ synchronized (mySources) {
+ ArrayList<SdkSource> list = mySources.get(category);
if (list == null) {
return new SdkSource[0];
}
@@ -149,8 +164,8 @@ public class SdkSources {
* Returns true if there are sources for the given category.
*/
public boolean hasSources(SdkSourceCategory category) {
- synchronized (mSources) {
- ArrayList<SdkSource> list = mSources.get(category);
+ synchronized (mySources) {
+ ArrayList<SdkSource> list = mySources.get(category);
return list != null && !list.isEmpty();
}
}
@@ -159,17 +174,17 @@ public class SdkSources {
* Returns an array of the sources across all categories. This is never null.
*/
public SdkSource[] getAllSources() {
- synchronized (mSources) {
+ synchronized (mySources) {
int n = 0;
- for (ArrayList<SdkSource> list : mSources.values()) {
+ for (ArrayList<SdkSource> list : mySources.values()) {
n += list.size();
}
SdkSource[] sources = new SdkSource[n];
int i = 0;
- for (ArrayList<SdkSource> list : mSources.values()) {
+ for (ArrayList<SdkSource> list : mySources.values()) {
for (SdkSource source : list) {
sources[i++] = source;
}
@@ -186,8 +201,8 @@ public class SdkSources {
* the remote package list.
*/
public void clearAllPackages() {
- synchronized (mSources) {
- for (ArrayList<SdkSource> list : mSources.values()) {
+ synchronized (mySources) {
+ for (ArrayList<SdkSource> list : mySources.values()) {
for (SdkSource source : list) {
source.clearPackages();
}
@@ -205,8 +220,8 @@ public class SdkSources {
*/
public SdkSourceCategory getCategory(SdkSource source) {
if (source != null) {
- synchronized (mSources) {
- for (Entry<SdkSourceCategory, ArrayList<SdkSource>> entry : mSources.entrySet()) {
+ synchronized (mySources) {
+ for (Entry<SdkSourceCategory, ArrayList<SdkSource>> entry : mySources.entrySet()) {
if (entry.getValue().contains(source)) {
return entry.getKey();
}
@@ -227,8 +242,8 @@ public class SdkSources {
* The search is O(N), which should be acceptable on the expectedly small source list.
*/
public boolean hasSourceUrl(SdkSource source) {
- synchronized (mSources) {
- for (ArrayList<SdkSource> list : mSources.values()) {
+ synchronized (mySources) {
+ for (ArrayList<SdkSource> list : mySources.values()) {
for (SdkSource s : list) {
if (s.equals(source)) {
return true;
@@ -250,8 +265,8 @@ public class SdkSources {
* The search is O(N), which should be acceptable on the expectedly small source list.
*/
public boolean hasSourceUrl(SdkSourceCategory category, SdkSource source) {
- synchronized (mSources) {
- ArrayList<SdkSource> list = mSources.get(category);
+ synchronized (mySources) {
+ ArrayList<SdkSource> list = mySources.get(category);
if (list != null) {
for (SdkSource s : list) {
if (s.equals(source)) {
@@ -276,7 +291,7 @@ public class SdkSources {
// In most cases we do these operation from the UI thread so it's not really
// that necessary. This is more a protection in case of someone calls this
// from a worker thread by mistake.
- synchronized (mSources) {
+ synchronized (mySources) {
// Remove all existing user sources
removeAll(SdkSourceCategory.USER_ADDONS);
@@ -295,6 +310,7 @@ public class SdkSources {
for (int i = 0; i < count; i++) {
String url = props.getProperty(String.format("%s%02d", KEY_SRC, i)); //$NON-NLS-1$
+ String disp = props.getProperty(String.format("%s%02d", KEY_DISPLAY, i)); //$NON-NLS-1$
if (url != null) {
// FIXME: this code originally only dealt with add-on XML sources.
// Now we'd like it to deal with system-image sources too, but we
@@ -307,10 +323,10 @@ public class SdkSources {
// the URI has been fetched.
SdkSource s;
if (url.endsWith(SdkSysImgConstants.URL_DEFAULT_FILENAME)) {
- s = new SdkSysImgSource(url, null/*uiName*/);
+ s = new SdkSysImgSource(url, disp);
}
else {
- s = new SdkAddonSource(url, null/*uiName*/);
+ s = new SdkAddonSource(url, disp);
}
if (!hasSourceUrl(s)) {
add(SdkSourceCategory.USER_ADDONS, s);
@@ -351,7 +367,7 @@ public class SdkSources {
*/
public void saveUserAddons(ILogger log) {
// See the implementation detail note in loadUserAddons() about the synchronization.
- synchronized (mSources) {
+ synchronized (mySources) {
FileOutputStream fos = null;
try {
String folder = AndroidLocation.getFolder();
@@ -365,6 +381,10 @@ public class SdkSources {
for (SdkSource s : getSources(SdkSourceCategory.USER_ADDONS)) {
props.setProperty(String.format("%s%02d", KEY_SRC, count), //$NON-NLS-1$
s.getUrl());
+ if (s.getUiName() != null) {
+ props.setProperty(String.format("%s%02d", KEY_DISPLAY, count), //$NON-NLS-1$
+ s.getUiName());
+ }
count++;
}
props.setProperty(KEY_COUNT, Integer.toString(count));
diff --git a/android/src/com/android/tools/idea/sdk/remote/internal/updater/PkgItem.java b/android/src/com/android/tools/idea/sdk/remote/internal/updater/PkgItem.java
deleted file mode 100755
index 02ea2887ec6..00000000000
--- a/android/src/com/android/tools/idea/sdk/remote/internal/updater/PkgItem.java
+++ /dev/null
@@ -1,207 +0,0 @@
-/*
- * Copyright (C) 2015 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.tools.idea.sdk.remote.internal.updater;
-
-import com.android.annotations.Nullable;
-import com.android.sdklib.AndroidVersion;
-import com.android.sdklib.repository.FullRevision;
-import com.android.sdklib.repository.local.LocalPkgInfo;
-import com.android.tools.idea.sdk.remote.RemotePkgInfo;
-import com.android.tools.idea.sdk.remote.internal.archives.Archive;
-import com.android.tools.idea.sdk.remote.internal.packages.IAndroidVersionProvider;
-import com.android.tools.idea.sdk.remote.internal.sources.SdkSource;
-import com.google.common.base.Objects;
-
-/**
- * A {@link PkgItem} represents one main {@link Package} combined with its state
- * and an optional update package.
- * <p/>
- * The main package is final and cannot change since it's what "defines" this PkgItem.
- * The state or update package can change later.
- */
-public class PkgItem implements Comparable<PkgItem> {
- private final PkgState mState;
- private final LocalPkgInfo mMainPkg;
- private RemotePkgInfo mUpdatePkg;
- private boolean mChecked;
-
- /**
- * The state of the a given {@link PkgItem}, that is the relationship between
- * a given remote package and the local repository.
- */
- public enum PkgState {
- // Implementation detail: if this is changed then PackageDiffLogic#STATES
- // and PackageDiffLogic#processSource() need to be changed accordingly.
-
- /**
- * Package is locally installed and may or may not have an update.
- */
- INSTALLED,
-
- /**
- * There's a new package available on the remote site that isn't installed locally.
- */
- NEW
- }
-
- /**
- * Create a new {@link PkgItem} for this main package.
- * The main package is final and cannot change since it's what "defines" this PkgItem.
- * The state or update package can change later.
- */
- public PkgItem(LocalPkgInfo mainPkg, PkgState state) {
- mMainPkg = mainPkg;
- mState = state;
- assert mMainPkg != null;
- }
-
- public boolean isObsolete() {
- return mMainPkg.getDesc().isObsolete();
- }
-
- public boolean isChecked() {
- return mChecked;
- }
-
- public void setChecked(boolean checked) {
- mChecked = checked;
- }
-
- public RemotePkgInfo getUpdatePkg() {
- return mUpdatePkg;
- }
-
- public boolean hasUpdatePkg() {
- return mUpdatePkg != null;
- }
-
- public String getName() {
- return mMainPkg.getListDescription();
- }
-
- public FullRevision getRevision() {
- return mMainPkg.getDesc().getFullRevision();
- }
-
- public LocalPkgInfo getMainPackage() {
- return mMainPkg;
- }
-
- public PkgState getState() {
- return mState;
- }
-
- @Nullable
- public SdkSource getSource() {
- return mUpdatePkg == null ? null : mUpdatePkg.getParentSource();
- }
-
- @Nullable
- public AndroidVersion getAndroidVersion() {
- return mMainPkg instanceof IAndroidVersionProvider ? ((IAndroidVersionProvider)mMainPkg).getAndroidVersion() : null;
- }
-
- @Nullable
- public Archive[] getArchives() {
- return mUpdatePkg == null ? null : mUpdatePkg.getArchives();
- }
-
- @Override
- public int compareTo(PkgItem pkg) {
- return getMainPackage().compareTo(pkg.getMainPackage());
- }
-
- /**
- * Equality is defined as {@link #isSameItemAs(PkgItem)}: state, main package
- * and update package must be the similar.
- */
- @Override
- public boolean equals(Object obj) {
- if (!(obj instanceof PkgItem)) {
- return false;
- }
- PkgItem other = (PkgItem)obj;
- return mMainPkg.equals(other.mMainPkg)
- && Objects.equal(mUpdatePkg, other.mUpdatePkg)
- && mState.equals(other.mState);
- }
-
- @Override
- public int hashCode() {
- final int prime = 31;
- int result = 1;
- result = prime * result + ((mState == null) ? 0 : mState.hashCode());
- result = prime * result + ((mMainPkg == null) ? 0 : mMainPkg.hashCode());
- result = prime * result + ((mUpdatePkg == null) ? 0 : mUpdatePkg.hashCode());
- return result;
- }
-
- /**
- * Check whether the 'pkg' argument is an update for this package.
- * If it is, record it as an updating package.
- * If there's already an updating package, only keep the most recent update.
- * Returns true if it is update (even if there was already an update and this
- * ended up not being the most recent), false if incompatible or not an update.
- * <p/>
- * This should only be used for installed packages.
- */
- public boolean mergeUpdate(RemotePkgInfo pkg) {
- if (mUpdatePkg == pkg) {
- return true;
- }
- if (pkg.canUpdate(mMainPkg) == RemotePkgInfo.UpdateInfo.UPDATE) {
- if (mUpdatePkg == null) {
- mUpdatePkg = pkg;
- }
- return true;
- }
-
- return false;
- }
-
- public void removeUpdate() {
- mUpdatePkg = null;
- }
-
- /**
- * Returns a string representation of this item, useful when debugging.
- */
- @Override
- public String toString() {
- StringBuilder sb = new StringBuilder();
- sb.append('<');
-
- if (mChecked) {
- sb.append(" * "); //$NON-NLS-1$
- }
-
- sb.append(mState.toString());
-
- if (mMainPkg != null) {
- sb.append(", pkg:"); //$NON-NLS-1$
- sb.append(mMainPkg.toString());
- }
-
- if (mUpdatePkg != null) {
- sb.append(", updated by:"); //$NON-NLS-1$
- sb.append(mUpdatePkg.toString());
- }
-
- sb.append('>');
- return sb.toString();
- }
-}
diff --git a/android/src/com/android/tools/idea/sdk/remote/internal/updater/UpdaterData.java b/android/src/com/android/tools/idea/sdk/remote/internal/updater/UpdaterData.java
index 9a29d7f2bde..53deade3f8d 100755
--- a/android/src/com/android/tools/idea/sdk/remote/internal/updater/UpdaterData.java
+++ b/android/src/com/android/tools/idea/sdk/remote/internal/updater/UpdaterData.java
@@ -606,7 +606,7 @@ public class UpdaterData implements IUpdaterData {
SdkState state = SdkState.getInstance(AndroidSdkUtils.tryToChooseAndroidSdk());
state.loadSynchronously(SdkState.DEFAULT_EXPIRATION_PERIOD_MS, false, null, null, null, false);
List<ArchiveInfo> result = Lists.newArrayList();
- for (RemotePkgInfo remote : state.getRemotePkgInfos().values()) {
+ for (RemotePkgInfo remote : state.getPackages().getRemotePkgInfos().values()) {
if (includeAll || !remote.isObsolete()) {
for (Archive archive : remote.getArchives()) {
if (archive.isCompatible()) {
diff --git a/android/src/com/android/tools/idea/sdk/wizard/SmwOldApiDirectInstall.java b/android/src/com/android/tools/idea/sdk/wizard/SmwOldApiDirectInstall.java
index 4cb9afb906d..d50c865d810 100755
--- a/android/src/com/android/tools/idea/sdk/wizard/SmwOldApiDirectInstall.java
+++ b/android/src/com/android/tools/idea/sdk/wizard/SmwOldApiDirectInstall.java
@@ -26,6 +26,7 @@ import com.android.tools.idea.wizard.DynamicWizardStepWithDescription;
import com.android.utils.ILogger;
import com.google.common.collect.Lists;
import com.intellij.openapi.Disposable;
+import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.progress.PerformInBackgroundOption;
import com.intellij.openapi.progress.ProgressIndicator;
@@ -109,6 +110,10 @@ public class SmwOldApiDirectInstall extends DynamicWizardStepWithDescription {
Runnable onSdkAvailable = new Runnable() {
@Override
public void run() {
+ if (!ApplicationManager.getApplication().isDispatchThread()) {
+ ApplicationManager.getApplication().invokeLater(this);
+ return;
+ }
// TODO: since the local SDK has been parsed, this is now a good time
// to filter requestedPackages to remove current installed packages.
// That's because on Windows trying to update some of the packages in-place
diff --git a/android/src/com/android/tools/idea/welcome/install/ComponentInstaller.java b/android/src/com/android/tools/idea/welcome/install/ComponentInstaller.java
index 24d88da9b5e..f674c17d134 100644
--- a/android/src/com/android/tools/idea/welcome/install/ComponentInstaller.java
+++ b/android/src/com/android/tools/idea/welcome/install/ComponentInstaller.java
@@ -22,8 +22,7 @@ import com.android.sdklib.repository.local.LocalPkgInfo;
import com.android.sdklib.repository.local.LocalSdk;
import com.android.tools.idea.sdk.remote.RemotePkgInfo;
import com.android.tools.idea.sdk.remote.UpdatablePkgInfo;
-import com.android.tools.idea.sdk.remote.Update;
-import com.android.tools.idea.sdk.remote.UpdateResult;
+import com.android.tools.idea.sdk.SdkPackages;
import com.android.tools.idea.sdk.remote.internal.updater.SdkUpdaterNoWindow;
import com.android.utils.ILogger;
import com.android.utils.NullLogger;
@@ -69,7 +68,7 @@ public final class ComponentInstaller {
private Iterable<LocalPkgInfo> getOldPackages(Collection<LocalPkgInfo> installed) {
if (myRemotePackages != null) {
LocalPkgInfo[] packagesArray = ArrayUtil.toObjectArray(installed, LocalPkgInfo.class);
- UpdateResult result = Update.computeUpdates(packagesArray, myRemotePackages);
+ SdkPackages result = new SdkPackages(packagesArray, myRemotePackages);
return Iterables.transform(result.getUpdatedPkgs(), new Function<UpdatablePkgInfo, LocalPkgInfo>() {
@Override
public LocalPkgInfo apply(@Nullable UpdatablePkgInfo input) {
@@ -100,7 +99,7 @@ public final class ComponentInstaller {
*
* @param manager SDK manager instance or <code>null</code> if this is a new install.
* @param defaultUpdateAvailable If true, and if remote package information is not available, assume each package may have an update and
- * try to reinstall. If false and remote package information not available, assume no updates are available.
+ * try to reinstall. If false and remote package information not available, assume no updates are available.
*/
public ArrayList<String> getPackagesToInstall(@Nullable SdkManager manager, @NotNull Iterable<? extends InstallableComponent> components,
boolean defaultUpdateAvailable) {
diff --git a/android/src/com/android/tools/idea/welcome/wizard/AndroidStudioWelcomeScreenProvider.java b/android/src/com/android/tools/idea/welcome/wizard/AndroidStudioWelcomeScreenProvider.java
index 422443c0e4b..7559a740bf8 100644
--- a/android/src/com/android/tools/idea/welcome/wizard/AndroidStudioWelcomeScreenProvider.java
+++ b/android/src/com/android/tools/idea/welcome/wizard/AndroidStudioWelcomeScreenProvider.java
@@ -160,7 +160,7 @@ public final class AndroidStudioWelcomeScreenProvider implements WelcomeScreenPr
SdkState state = SdkState.getInstance(AndroidSdkUtils.tryToChooseAndroidSdk());
state.loadSynchronously(SdkState.DEFAULT_EXPIRATION_PERIOD_MS, false, null, null, null, true);
- return state.getRemotePkgInfos();
+ return state.getPackages().getRemotePkgInfos();
}
@Override
diff --git a/android/src/com/android/tools/idea/wizard/ConfigureFormFactorStep.java b/android/src/com/android/tools/idea/wizard/ConfigureFormFactorStep.java
index e4638d017b2..c46aa7e466f 100755
--- a/android/src/com/android/tools/idea/wizard/ConfigureFormFactorStep.java
+++ b/android/src/com/android/tools/idea/wizard/ConfigureFormFactorStep.java
@@ -261,8 +261,7 @@ public class ConfigureFormFactorStep extends DynamicWizardStepWithHeaderAndDescr
ApplicationManager.getApplication().invokeLater(new Runnable() {
@Override
public void run() {
-
- List<RemotePkgInfo> packageList = Lists.newArrayList(state.getUpdates().getNewPkgs());
+ List<RemotePkgInfo> packageList = Lists.newArrayList(state.getPackages().getNewPkgs());
Collections.sort(packageList);
Iterator<RemotePkgInfo> result =
Iterables.filter(packageList, FormFactorUtils.getMinSdkPackageFilter(formFactor, minSdkLevel)).iterator();
diff --git a/sdk-updates/sdk-updates.iml b/sdk-updates/sdk-updates.iml
new file mode 100644
index 00000000000..d8cb223b98f
--- /dev/null
+++ b/sdk-updates/sdk-updates.iml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="JAVA_MODULE" version="4">
+ <component name="NewModuleRootManager" inherit-compiler-output="true">
+ <exclude-output />
+ <content url="file://$MODULE_DIR$">
+ <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
+ </content>
+ <orderEntry type="inheritedJdk" />
+ <orderEntry type="sourceFolder" forTests="false" />
+ <orderEntry type="module" module-name="platform-impl" />
+ <orderEntry type="module" module-name="android" />
+ <orderEntry type="module" module-name="lang-api" />
+ </component>
+</module> \ No newline at end of file
diff --git a/sdk-updates/src/META-INF/plugin.xml b/sdk-updates/src/META-INF/plugin.xml
new file mode 100644
index 00000000000..3408fd88dad
--- /dev/null
+++ b/sdk-updates/src/META-INF/plugin.xml
@@ -0,0 +1,38 @@
+<!--
+ ~ Copyright (C) 2015 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.
+ -->
+<idea-plugin version="2">
+ <id>com.android.tools.idea.updater</id>
+ <name>SDK Updater</name>
+ <version>1.0</version>
+ <vendor>JetBrains</vendor>
+
+ <description><![CDATA[
+ Android SDK Updater Plugin
+ ]]></description>
+
+ <!-- please see http://confluence.jetbrains.com/display/IDEADEV/Build+Number+Ranges for description -->
+ <idea-version since-build="131"/>
+
+ <depends>org.jetbrains.android</depends>
+
+ <application-components>
+ <component>
+ <implementation-class>com.android.tools.idea.updater.AndroidSdkUpdaterPlugin</implementation-class>
+ <interface-class>com.android.tools.idea.updater.AndroidSdkUpdaterPlugin</interface-class>
+ </component>
+ </application-components>
+
+</idea-plugin>
diff --git a/sdk-updates/src/com/android/tools/idea/updater/AndroidSdkUpdaterPlugin.java b/sdk-updates/src/com/android/tools/idea/updater/AndroidSdkUpdaterPlugin.java
new file mode 100644
index 00000000000..e85405e3f60
--- /dev/null
+++ b/sdk-updates/src/com/android/tools/idea/updater/AndroidSdkUpdaterPlugin.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2015 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.tools.idea.updater;
+
+import com.intellij.ide.externalComponents.ExternalComponentManagerImpl;
+import com.intellij.ide.externalComponents.UpdatableExternalComponent;
+import com.intellij.openapi.components.ApplicationComponent;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Plugin to set up the android sdk {@link UpdatableExternalComponent} and
+ * {@link com.android.tools.idea.updater.configure.SdkUpdaterConfigurable}.
+ */
+public class AndroidSdkUpdaterPlugin implements ApplicationComponent {
+ @Override
+ public void initComponent() {
+ ExternalComponentManagerImpl.getInstance().registerComponentSource(new SdkComponentSource());
+ }
+
+ @Override
+ public void disposeComponent() {
+ // nothing
+ }
+
+ @NotNull
+ @Override
+ public String getComponentName() {
+ return "Android Sdk Updater";
+ }
+}
diff --git a/sdk-updates/src/com/android/tools/idea/updater/SdkComponentSource.java b/sdk-updates/src/com/android/tools/idea/updater/SdkComponentSource.java
new file mode 100644
index 00000000000..78afb695e38
--- /dev/null
+++ b/sdk-updates/src/com/android/tools/idea/updater/SdkComponentSource.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2015 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.tools.idea.updater;
+
+import com.android.sdklib.repository.descriptors.IPkgDesc;
+import com.android.sdklib.repository.local.LocalSdk;
+import com.android.tools.idea.sdk.SdkState;
+import com.android.tools.idea.sdk.remote.RemoteSdk;
+import com.android.tools.idea.sdk.remote.UpdatablePkgInfo;
+import com.android.tools.idea.sdk.remote.internal.sources.SdkSources;
+import com.android.tools.idea.sdk.wizard.SdkQuickfixWizard;
+import com.android.tools.idea.wizard.DialogWrapperHost;
+import com.android.utils.ILogger;
+import com.android.utils.StdLogger;
+import com.google.common.collect.Lists;
+import com.intellij.ide.externalComponents.ExternalComponentSource;
+import com.intellij.ide.externalComponents.UpdatableExternalComponent;
+import com.intellij.openapi.progress.ProgressIndicator;
+import com.intellij.openapi.ui.DialogWrapper;
+import org.jetbrains.android.sdk.AndroidSdkData;
+import org.jetbrains.android.sdk.AndroidSdkUtils;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * An {@link ExternalComponentSource} that retrieves information from the {@link LocalSdk} and {@link RemoteSdk} provided
+ * by the Android SDK.
+ */
+public class SdkComponentSource implements ExternalComponentSource {
+ SdkSources mySources;
+ SdkState mySdkState;
+ private static final ILogger ILOG = new StdLogger(StdLogger.Level.ERROR);
+
+ private void initIfNecessary() {
+ if (mySdkState != null) {
+ return;
+ }
+ AndroidSdkData data = AndroidSdkUtils.tryToChooseAndroidSdk();
+ assert data != null;
+ mySdkState = SdkState.getInstance(data);
+
+ mySources = mySdkState.getRemoteSdk().fetchSources(RemoteSdk.DEFAULT_EXPIRATION_PERIOD_MS, ILOG);
+ }
+
+ /**
+ * Install the given new versions of components using the {@link SdkQuickfixWizard}.
+ *
+ * @param request The components to install.
+ */
+ @Override
+ public void installUpdates(@NotNull Collection<UpdatableExternalComponent> request) {
+ final List<IPkgDesc> packages = Lists.newArrayList();
+ for (UpdatableExternalComponent p : request) {
+ packages.add((IPkgDesc)p.getKey());
+ }
+ SdkQuickfixWizard sdkQuickfixWizard =
+ new SdkQuickfixWizard(null, null, packages, new DialogWrapperHost(null, DialogWrapper.IdeModalityType.PROJECT));
+ sdkQuickfixWizard.init();
+ sdkQuickfixWizard.show();
+ }
+
+ /**
+ * Retrieves information on updates available from the {@link RemoteSdk}.
+ *
+ * @param indicator A {@code ProgressIndicator} that can be updated to show progress, or can be used to cancel the process.
+ * @return A collection of {@link UpdatablePackage}s corresponding to the currently installed Packages.
+ */
+ @NotNull
+ @Override
+ public Collection<UpdatableExternalComponent> getAvailableVersions(ProgressIndicator indicator) {
+ return getComponents(indicator, true);
+ }
+
+ /**
+ * Retrieves information on updates installed using the {@link LocalSdk}.
+ *
+ * @return A collection of {@link UpdatablePackage}s corresponding to the currently installed Packages.
+ */
+ @NotNull
+ @Override
+ public Collection<UpdatableExternalComponent> getCurrentVersions() {
+ return getComponents(null, false);
+ }
+
+ private Collection<UpdatableExternalComponent> getComponents(ProgressIndicator indicator, boolean remote) {
+ initIfNecessary();
+ List<UpdatableExternalComponent> result = Lists.newArrayList();
+ mySdkState.loadSynchronously(SdkState.DEFAULT_EXPIRATION_PERIOD_MS, true, null, null, null, true);
+ for (UpdatablePkgInfo info : mySdkState.getPackages().getConsolidatedPkgs()) {
+ if (remote) {
+ if (info.hasRemote()) {
+ result.add(new UpdatablePackage(info.getRemote().getPkgDesc()));
+ }
+ }
+ else {
+ if (info.hasLocal()) {
+ result.add(new UpdatablePackage(info.getLocalInfo().getDesc()));
+ }
+ }
+ }
+ return result;
+ }
+
+ @NotNull
+ @Override
+ public String getName() {
+ return "Android SDK";
+ }
+}
diff --git a/sdk-updates/src/com/android/tools/idea/updater/UpdatablePackage.java b/sdk-updates/src/com/android/tools/idea/updater/UpdatablePackage.java
new file mode 100644
index 00000000000..4523b88a0d0
--- /dev/null
+++ b/sdk-updates/src/com/android/tools/idea/updater/UpdatablePackage.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2015 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.tools.idea.updater;
+
+import com.android.sdklib.repository.descriptors.IPkgDesc;
+import com.intellij.ide.externalComponents.UpdatableExternalComponent;
+
+/**
+ * An {@link UpdatableExternalComponent} that corresponds to an
+ * {@link IPkgDesc} for a local or remote package.
+ */
+public class UpdatablePackage implements UpdatableExternalComponent {
+ private IPkgDesc myPackage;
+
+ public UpdatablePackage(IPkgDesc p) {
+ myPackage = p;
+ }
+
+ @Override
+ public IPkgDesc getKey() {
+ return myPackage;
+ }
+
+ @Override
+ public boolean isUpdateFor(UpdatableExternalComponent c) {
+ if (c == null) {
+ return false;
+ }
+ Object otherKey = c.getKey();
+ if (!(otherKey instanceof IPkgDesc)) {
+ return false;
+ }
+ return myPackage.isUpdateFor((IPkgDesc)otherKey);
+ }
+
+ @Override
+ public String getName() {
+ return myPackage.getListDescription();
+ }
+
+ @Override
+ public String toString() {
+ return getName();
+ }
+}