1 /* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Eclipse Public License, Version 1.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.eclipse.org/org/documents/epl-v10.php 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.ide.eclipse.adt.internal.welcome; 18 19 import com.android.ide.eclipse.adt.AdtPlugin; 20 import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs; 21 import com.android.ide.eclipse.adt.internal.sdk.AdtConsoleSdkLog; 22 import com.android.sdkstats.DdmsPreferenceStore; 23 import com.android.sdkuilib.internal.repository.ui.AdtUpdateDialog; 24 25 import org.eclipse.core.runtime.IStatus; 26 import org.eclipse.jface.resource.ImageDescriptor; 27 import org.eclipse.jface.wizard.Wizard; 28 import org.eclipse.swt.widgets.Display; 29 import org.eclipse.swt.widgets.Shell; 30 import org.eclipse.ui.IWorkbench; 31 import org.eclipse.ui.IWorkbenchWindow; 32 import org.eclipse.ui.PlatformUI; 33 34 import java.io.File; 35 import java.util.HashSet; 36 import java.util.Set; 37 38 /** 39 * Wizard shown on first start for new users: configure SDK location, accept or 40 * reject usage data collection, etc 41 */ 42 public class WelcomeWizard extends Wizard { 43 private final DdmsPreferenceStore mStore; 44 45 private WelcomeWizardPage mWelcomePage; 46 private UsagePermissionPage mUsagePage; 47 48 private final boolean mShowWelcomePage; 49 private final boolean mShowUsagePage; 50 51 /** 52 * Creates a new {@link WelcomeWizard} 53 * 54 * @param store preferences for usage statistics collection etc 55 * @param showInstallSdkPage show page to install SDK's 56 * @param showUsageOptinPage show page to get user consent for usage data collection 57 */ WelcomeWizard(DdmsPreferenceStore store, boolean showInstallSdkPage, boolean showUsageOptinPage)58 public WelcomeWizard(DdmsPreferenceStore store, boolean showInstallSdkPage, 59 boolean showUsageOptinPage) { 60 mStore = store; 61 mShowWelcomePage = showInstallSdkPage; 62 mShowUsagePage = showUsageOptinPage; 63 64 setWindowTitle("Welcome to Android Development"); 65 ImageDescriptor image = AdtPlugin.getImageDescriptor("icons/android-64.png"); //$NON-NLS-1$ 66 setDefaultPageImageDescriptor(image); 67 } 68 69 @Override addPages()70 public void addPages() { 71 if (mShowWelcomePage) { 72 mWelcomePage = new WelcomeWizardPage(); 73 addPage(mWelcomePage); 74 } 75 76 // It's possible that the user has already run the command line tools 77 // such as ddms and has agreed to usage statistics collection, but has never 78 // run ADT which is why the wizard was opened. No need to ask again. 79 if (mShowUsagePage && !mStore.hasPingId()) { 80 mUsagePage = new UsagePermissionPage(); 81 addPage(mUsagePage); 82 } 83 } 84 85 @Override performFinish()86 public boolean performFinish() { 87 if (mUsagePage != null) { 88 boolean isUsageCollectionApproved = mUsagePage.isUsageCollectionApproved(); 89 DdmsPreferenceStore store = new DdmsPreferenceStore(); 90 91 // Workaround: Store a new ping id if one doesn't exist, regardless of 92 // whether usage statistics gathering is enabled, to ensure that ddms and 93 // ADT agree upon whether usage data collection is enabled. The reason this 94 // is necessary is that the Eclipse PreferenceStore optimizes out writing 95 // property values that equal their default values, and in our case, the 96 // default value for usage-collection is "false", so it just doesn't write 97 // it into the config file is the user opts out - which means that nothing 98 // is written in ddms.config. That works in the sense that the getter returns 99 // "usage collection"=false, but it doesn't work in the sense that it looks 100 // like the property has not yet been decided by the user. DDMS will look at 101 // the existence of a ping id to see whether we've already considered the 102 // question, so do the same here. 103 if (!store.hasPingId()) { 104 store.generateNewPingId(); 105 } 106 107 store.setPingOptIn(isUsageCollectionApproved); 108 } 109 110 if (mWelcomePage != null) { 111 // Read out wizard settings immediately; we will perform the actual work 112 // after the wizard window has been taken down and it's too late to read the 113 // settings then 114 final File path = mWelcomePage.getPath(); 115 final boolean installCommon = mWelcomePage.isInstallCommon(); 116 final boolean installLatest = mWelcomePage.isInstallLatest(); 117 final boolean createNew = mWelcomePage.isCreateNew(); 118 119 // Perform installation asynchronously since it takes a while. 120 getShell().getDisplay().asyncExec(new Runnable() { 121 @Override 122 public void run() { 123 if (createNew) { 124 try { 125 Set<Integer> apiLevels = new HashSet<Integer>(); 126 if (installCommon) { 127 apiLevels.add(8); 128 } 129 if (installLatest) { 130 apiLevels.add(AdtUpdateDialog.USE_MAX_REMOTE_API_LEVEL); 131 } 132 installSdk(path, apiLevels); 133 } catch (Exception e) { 134 AdtPlugin.logAndPrintError(e, "ADT Welcome Wizard", 135 "Installation failed"); 136 } 137 } 138 139 // Set SDK path after installation since this will trigger a SDK refresh. 140 AdtPrefs.getPrefs().setSdkLocation(path); 141 } 142 }); 143 } 144 145 // The wizard always succeeds, even if installation fails or is aborted 146 return true; 147 } 148 149 /** 150 * Trigger the install window. It will connect to the repository, display 151 * a confirmation window showing which packages are selected for install 152 * and display a progress dialog during installation. 153 */ installSdk(File path, Set<Integer> apiLevels)154 private boolean installSdk(File path, Set<Integer> apiLevels) { 155 if (!path.isDirectory()) { 156 if (!path.mkdirs()) { 157 AdtPlugin.logAndPrintError(null, "ADT Welcome Wizard", 158 "Failed to create directory %1$s", 159 path.getAbsolutePath()); 160 return false; 161 } 162 } 163 164 // Get a shell to use for the SDK installation. There are cases where getActiveShell 165 // returns null so attempt to obtain it through other means. 166 Display display = AdtPlugin.getDisplay(); 167 Shell shell = display.getActiveShell(); 168 if (shell == null) { 169 IWorkbench workbench = PlatformUI.getWorkbench(); 170 IWorkbenchWindow window = workbench.getActiveWorkbenchWindow(); 171 if (window != null) { 172 shell = window.getShell(); 173 } 174 } 175 boolean disposeShell = false; 176 if (shell == null) { 177 shell = new Shell(display); 178 AdtPlugin.log(IStatus.WARNING, "No parent shell for SDK installation dialog"); 179 disposeShell = true; 180 } 181 182 AdtUpdateDialog updater = new AdtUpdateDialog( 183 shell, 184 new AdtConsoleSdkLog(), 185 path.getAbsolutePath()); 186 // Note: we don't have to specify tools & platform-tools since they 187 // are required dependencies of any platform. 188 boolean result = updater.installNewSdk(apiLevels); 189 190 // TODO: Install extra package here as well since it is now core to most of 191 // the templates 192 // if (result) { 193 // updater.installExtraPackage(vendor, path); 194 // } 195 196 if (disposeShell) { 197 shell.dispose(); 198 } 199 200 if (!result) { 201 AdtPlugin.printErrorToConsole("Failed to install Android SDK."); 202 return false; 203 } 204 205 return true; 206 } 207 } 208