blob: dc86c454da3dc713cbc452e4bfddfb654b2f09ae [file] [log] [blame]
/*
* 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.gradle.project.compatibility;
import com.android.annotations.VisibleForTesting;
import com.intellij.ide.AppLifecycleListener;
import com.intellij.ide.util.PropertiesComponent;
import com.intellij.openapi.application.Application;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.components.ApplicationComponent;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.ActionCallback;
import com.intellij.openapi.util.Ref;
import com.intellij.util.io.HttpRequests;
import org.jdom.Document;
import org.jdom.JDOMException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.IOException;
import static com.intellij.openapi.util.JDOMUtil.loadDocument;
import static com.intellij.openapi.util.text.StringUtil.isNotEmpty;
import static java.util.concurrent.TimeUnit.DAYS;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
/**
* Checks if there are component version metadata updates from a remote server. The check is perform on IDE startup, once, every day.
*/
public class VersionMetadataUpdater extends ApplicationComponent.Adapter {
private static final Logger LOG = Logger.getInstance(VersionMetadataUpdater.class);
private static final String LAST_CHECK_TIMESTAMP_PROPERTY_NAME = "android-component-compatibility-check";
public VersionMetadataUpdater() {
String checkIntervalProperty = System.getProperty("android.version.compatibility.check.interval");
final CheckInterval checkInterval = CheckInterval.find(checkIntervalProperty);
if (checkInterval != CheckInterval.NONE) {
Application app = ApplicationManager.getApplication();
app.getMessageBus().connect(app).subscribe(AppLifecycleListener.TOPIC, new AppLifecycleListener.Adapter() {
@Override
public void appFrameCreated(String[] commandLineArgs, @NotNull Ref<Boolean> willOpenProject) {
long lastUpdateCheck = PropertiesComponent.getInstance().getOrInitLong(LAST_CHECK_TIMESTAMP_PROPERTY_NAME, -1);
if (checkInterval.needsUpdate(lastUpdateCheck)) {
fetchVersionMetadataUpdate(false);
}
}
});
}
}
public void fetchVersionMetadataUpdate() {
fetchVersionMetadataUpdate(true);
}
private static void fetchVersionMetadataUpdate(final boolean startedByUser) {
fetchMetadata().doWhenDone(new Runnable() {
@Override
public void run() {
long now = System.currentTimeMillis();
PropertiesComponent.getInstance().setValue(LAST_CHECK_TIMESTAMP_PROPERTY_NAME, String.valueOf(now));
}
});
}
@NotNull
private static ActionCallback fetchMetadata() {
final ActionCallback callback = new ActionCallback();
ApplicationManager.getApplication().executeOnPooledThread(new Runnable() {
@Override
public void run() {
String url = "https://dl.google.com/android/studio/metadata/android-component-compatibility.xml";
try {
Document metadata = HttpRequests.request(url).connect(new HttpRequests.RequestProcessor<Document>() {
@Override
public Document process(@NotNull HttpRequests.Request request) throws IOException {
try {
return loadDocument(request.getReader());
}
catch (JDOMException e) {
LOG.info("Failed to parse XML metadata", e);
return null;
}
}
});
if (metadata != null) {
VersionCompatibilityService.getInstance().updateMetadata(metadata);
callback.setDone();
}
}
catch (IOException e) {
LOG.info(String.format("Failed to connect to '%1$s'", url), e);
}
callback.setRejected();
}
});
return callback;
}
@VisibleForTesting
enum CheckInterval {
NONE(Long.MAX_VALUE), DAILY(MILLISECONDS.convert(1, DAYS)), WEEKLY(MILLISECONDS.convert(7, DAYS)), TESTING(-1L);
private final long myIntervalInMs;
CheckInterval(long intervalInMs) {
myIntervalInMs = intervalInMs;
}
boolean needsUpdate(long lastUpdateTimestampInMs) {
return System.currentTimeMillis() - lastUpdateTimestampInMs >= myIntervalInMs;
}
@NotNull
static CheckInterval find(@Nullable String value) {
if (isNotEmpty(value)) {
for (CheckInterval checkInterval : values()) {
if (value.equalsIgnoreCase(checkInterval.name())) {
return checkInterval;
}
}
}
return WEEKLY;
}
}
}