/* * 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.wizard; import com.android.tools.idea.sdk.DefaultSdks; import com.google.common.base.Objects; import com.intellij.openapi.Disposable; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.fileChooser.FileChooserDescriptor; import com.intellij.openapi.fileChooser.FileChooserDialog; import com.intellij.openapi.fileChooser.FileChooserFactory; import com.intellij.openapi.fileChooser.FileSaverDescriptor; import com.intellij.openapi.fileChooser.ex.FileChooserDialogImpl; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.TextFieldWithBrowseButton; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VfsUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFileWrapper; import com.intellij.platform.templates.github.DownloadUtil; import com.intellij.platform.templates.github.Outcome; import com.intellij.platform.templates.github.ZipUtil; import org.jetbrains.android.sdk.AndroidSdkType; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.File; import java.util.concurrent.Callable; import static com.android.tools.idea.wizard.ScopedStateStore.Key; import static com.android.tools.idea.wizard.ScopedStateStore.Scope.STEP; import static com.android.tools.idea.wizard.ScopedStateStore.createKey; /** * Step to get the sdk location from the user. */ public class GetSdkStep extends DynamicWizardStepWithHeaderAndDescription { private static final Logger LOG = Logger.getInstance(GetSdkStep.class); // TODO: Update these to a stable link private static final String MAC_SDK_URL = "http://dl.google.com/android/android-sdk_r22.6.2-macosx.zip"; private static final String LINUX_SDK_URL = "http://dl.google.com/android/android-sdk_r22.6.2-linux.tgz"; private static final String WINDOWS_SDK_URL = "http://dl.google.com/android/android-sdk_r22.6.2-windows.zip"; private TextFieldWithBrowseButton mySdkLocationField; private JPanel myPanel; private JButton myDownloadANewSDKButton; private static final Key<String> SDK_PATH_KEY = createKey("sdkPath", STEP, String.class); public GetSdkStep(@NotNull Disposable parentDisposable) { super("SDK Setup", "You may use an existing SDK or download a new one", null, parentDisposable); setBodyComponent(myPanel); } @Override public void init() { register(SDK_PATH_KEY, mySdkLocationField); File androidHome = DefaultSdks.getDefaultAndroidHome(); if (androidHome != null) { myState.put(SDK_PATH_KEY, androidHome.getPath()); } mySdkLocationField.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent actionEvent) { String path = getSdkPath(mySdkLocationField.getText()); mySdkLocationField.setText(path == null ? "" : path); } }); myDownloadANewSDKButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent actionEvent) { String sdkPath = getSaveLocation(myState.get(SDK_PATH_KEY)); if (sdkPath != null) { boolean success = downloadSdkToPath(sdkPath); if (success) { myState.put(SDK_PATH_KEY, sdkPath); } else { setErrorHtml("Download failed, please try again."); } } } }); super.init(); } private boolean downloadSdkToPath(@NotNull String sdkPath) { File outputFile = new File(sdkPath); File parentDir = outputFile.getParentFile(); if (parentDir == null) { setErrorHtml("You may not choose an empty (or root) path as an output directory"); return false; } final File tempZipFile = new File(parentDir, outputFile.getName() + ".zip"); Outcome<File> outcome = DownloadUtil.provideDataWithProgressSynchronously( null, "Installing SDK", "Downloading SDK Archive" + DownloadUtil.CONTENT_LENGTH_TEMPLATE + " ...", new Callable<File>() { @Override public File call() throws Exception { ProgressIndicator progress = ProgressManager.getInstance().getProgressIndicator(); String downloadUrl = getDownloadUrl(); if (downloadUrl == null) { setErrorHtml("We cannot recognize your OS. Please visit http://developer.android.com/sdk/index.html and select" + "the appropriate SDK bundle."); return tempZipFile; } DownloadUtil.downloadAtomically(progress, downloadUrl, tempZipFile); return tempZipFile; } }, null ); Exception e = outcome.getException(); if (e != null) { setErrorHtml("Could not download SDK: " + e.getMessage()); return false; } try { ZipUtil.unzipWithProgressSynchronously(null, "Extracting SDK", tempZipFile, outputFile, true); FileUtil.delete(tempZipFile); } catch (Exception e2) { setErrorHtml("Installing SDK failed: " + e2.getMessage()); return false; } return true; } @Override public boolean commitStep() { String sdkPath = myState.get(SDK_PATH_KEY); if (sdkPath == null) { return false; } final File sdkFile = new File(sdkPath); if (!sdkFile.exists() || !AndroidSdkType.validateAndroidSdk(sdkPath).getFirst()) { return false; } ApplicationManager.getApplication().runWriteAction(new Runnable() { @Override public void run() { DefaultSdks.setDefaultAndroidHome(sdkFile); } }); return true; } @Override public boolean validate() { setErrorHtml(""); String path = myState.get(SDK_PATH_KEY); if (path == null || StringUtil.isEmpty(path)) { setErrorHtml("Android SDK path not specified."); return false; } Pair<Boolean, String> validationResult = AndroidSdkType.validateAndroidSdk(path); String error = validationResult.getSecond(); if (!validationResult.getFirst()) { setErrorHtml(String.format("Invalid Android SDK (%1$s): %2$s", path, error)); return false; } else { return true; } } @Override public boolean isStepVisible() { File home = DefaultSdks.getDefaultAndroidHome(); return Objects.equal(Boolean.TRUE, Boolean.getBoolean("android.sdk.prompt")) || home == null; } @NotNull @Override public String getStepName() { return "Find or download SDK"; } @Override public JComponent getPreferredFocusedComponent() { return mySdkLocationField; } @Nullable private String getSdkPath(@Nullable String currentPath) { VirtualFile currentFile = null; if (currentPath != null && !currentPath.isEmpty()) { currentFile = VfsUtil.findFileByIoFile(new File(currentPath), false); } FileChooserDescriptor chooserDescriptor = AndroidSdkType.getInstance().getHomeChooserDescriptor(); FileChooserDialog chooser = new FileChooserDialogImpl(chooserDescriptor, (Project)null); VirtualFile[] files = chooser.choose(null, currentFile); if (files.length == 0) { return null; } else { return files[0].getPath(); } } @Nullable private String getSaveLocation(@Nullable String currentPath) { VirtualFile currentFile = null; if (currentPath != null && !currentPath.isEmpty()) { currentFile = VfsUtil.findFileByIoFile(new File(currentPath), false); } FileSaverDescriptor fileSaverDescriptor = new FileSaverDescriptor("SDK location", "Please choose an installation location for your SDK"); VirtualFileWrapper fileWrapper = FileChooserFactory.getInstance().createSaveFileDialog(fileSaverDescriptor, (Project)null).save(currentFile, "android_sdk"); if (fileWrapper != null) { return fileWrapper.getFile().getPath(); } else { return null; } } @Nullable private String getDownloadUrl() { if (SystemInfo.isLinux) { return LINUX_SDK_URL; } else if (SystemInfo.isWindows) { return WINDOWS_SDK_URL; } else if (SystemInfo.isMac) { return MAC_SDK_URL; } else { return null; } } }