1 /* 2 * Copyright (C) 2007 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; 18 19 import com.android.ddmuilib.StackTracePanel; 20 import com.android.ddmuilib.StackTracePanel.ISourceRevealer; 21 import com.android.ddmuilib.console.DdmConsole; 22 import com.android.ddmuilib.console.IDdmConsole; 23 import com.android.ide.eclipse.adt.internal.VersionCheck; 24 import com.android.ide.eclipse.adt.internal.editors.IconFactory; 25 import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditor; 26 import com.android.ide.eclipse.adt.internal.editors.menu.MenuEditor; 27 import com.android.ide.eclipse.adt.internal.editors.resources.ResourcesEditor; 28 import com.android.ide.eclipse.adt.internal.editors.xml.XmlEditor; 29 import com.android.ide.eclipse.adt.internal.launch.AndroidLaunchController; 30 import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs; 31 import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs.BuildVerbosity; 32 import com.android.ide.eclipse.adt.internal.project.AndroidClasspathContainerInitializer; 33 import com.android.ide.eclipse.adt.internal.project.BaseProjectHelper; 34 import com.android.ide.eclipse.adt.internal.project.ExportHelper; 35 import com.android.ide.eclipse.adt.internal.project.ProjectHelper; 36 import com.android.ide.eclipse.adt.internal.project.ExportHelper.IExportCallback; 37 import com.android.ide.eclipse.adt.internal.resources.manager.GlobalProjectMonitor; 38 import com.android.ide.eclipse.adt.internal.resources.manager.ProjectResources; 39 import com.android.ide.eclipse.adt.internal.resources.manager.ResourceFolder; 40 import com.android.ide.eclipse.adt.internal.resources.manager.ResourceFolderType; 41 import com.android.ide.eclipse.adt.internal.resources.manager.ResourceManager; 42 import com.android.ide.eclipse.adt.internal.resources.manager.GlobalProjectMonitor.IFileListener; 43 import com.android.ide.eclipse.adt.internal.sdk.LoadStatus; 44 import com.android.ide.eclipse.adt.internal.sdk.Sdk; 45 import com.android.ide.eclipse.adt.internal.sdk.Sdk.ITargetChangeListener; 46 import com.android.ide.eclipse.adt.internal.ui.EclipseUiHelper; 47 import com.android.ide.eclipse.adt.internal.wizards.export.ExportWizard; 48 import com.android.ide.eclipse.ddms.DdmsPlugin; 49 import com.android.sdklib.IAndroidTarget; 50 import com.android.sdklib.SdkConstants; 51 import com.android.sdkstats.SdkStatsService; 52 53 import org.eclipse.core.resources.IFile; 54 import org.eclipse.core.resources.IFolder; 55 import org.eclipse.core.resources.IMarkerDelta; 56 import org.eclipse.core.resources.IProject; 57 import org.eclipse.core.resources.IResourceDelta; 58 import org.eclipse.core.resources.IWorkspace; 59 import org.eclipse.core.resources.ResourcesPlugin; 60 import org.eclipse.core.runtime.CoreException; 61 import org.eclipse.core.runtime.IProgressMonitor; 62 import org.eclipse.core.runtime.IStatus; 63 import org.eclipse.core.runtime.Preferences; 64 import org.eclipse.core.runtime.QualifiedName; 65 import org.eclipse.core.runtime.Status; 66 import org.eclipse.core.runtime.SubMonitor; 67 import org.eclipse.core.runtime.Preferences.IPropertyChangeListener; 68 import org.eclipse.core.runtime.Preferences.PropertyChangeEvent; 69 import org.eclipse.core.runtime.jobs.IJobChangeEvent; 70 import org.eclipse.core.runtime.jobs.Job; 71 import org.eclipse.core.runtime.jobs.JobChangeAdapter; 72 import org.eclipse.jdt.core.IJavaProject; 73 import org.eclipse.jface.dialogs.MessageDialog; 74 import org.eclipse.jface.resource.ImageDescriptor; 75 import org.eclipse.jface.viewers.StructuredSelection; 76 import org.eclipse.jface.wizard.WizardDialog; 77 import org.eclipse.swt.graphics.Color; 78 import org.eclipse.swt.graphics.Image; 79 import org.eclipse.swt.widgets.Display; 80 import org.eclipse.swt.widgets.Shell; 81 import org.eclipse.ui.IEditorDescriptor; 82 import org.eclipse.ui.IEditorPart; 83 import org.eclipse.ui.IWorkbench; 84 import org.eclipse.ui.IWorkbenchPage; 85 import org.eclipse.ui.IWorkbenchWindow; 86 import org.eclipse.ui.PlatformUI; 87 import org.eclipse.ui.console.ConsolePlugin; 88 import org.eclipse.ui.console.IConsole; 89 import org.eclipse.ui.console.IConsoleConstants; 90 import org.eclipse.ui.console.MessageConsole; 91 import org.eclipse.ui.console.MessageConsoleStream; 92 import org.eclipse.ui.ide.IDE; 93 import org.eclipse.ui.part.FileEditorInput; 94 import org.eclipse.ui.plugin.AbstractUIPlugin; 95 import org.osgi.framework.Bundle; 96 import org.osgi.framework.BundleContext; 97 import org.osgi.framework.Constants; 98 import org.osgi.framework.Version; 99 100 import java.io.BufferedInputStream; 101 import java.io.BufferedReader; 102 import java.io.File; 103 import java.io.IOException; 104 import java.io.InputStream; 105 import java.io.InputStreamReader; 106 import java.io.OutputStream; 107 import java.io.PrintStream; 108 import java.net.MalformedURLException; 109 import java.net.URL; 110 import java.util.ArrayList; 111 import java.util.Arrays; 112 import java.util.Calendar; 113 import java.util.List; 114 115 /** 116 * The activator class controls the plug-in life cycle 117 */ 118 @SuppressWarnings("deprecation") 119 public class AdtPlugin extends AbstractUIPlugin { 120 /** The plug-in ID */ 121 public static final String PLUGIN_ID = "com.android.ide.eclipse.adt"; //$NON-NLS-1$ 122 123 /** singleton instance */ 124 private static AdtPlugin sPlugin; 125 126 private static Image sAndroidLogo; 127 private static ImageDescriptor sAndroidLogoDesc; 128 129 /** The global android console */ 130 private MessageConsole mAndroidConsole; 131 132 /** Stream to write in the android console */ 133 private MessageConsoleStream mAndroidConsoleStream; 134 135 /** Stream to write error messages to the android console */ 136 private MessageConsoleStream mAndroidConsoleErrorStream; 137 138 /** Color used in the error console */ 139 private Color mRed; 140 141 /** Load status of the SDK. Any access MUST be in a synchronized(mPostLoadProjects) block */ 142 private LoadStatus mSdkIsLoaded = LoadStatus.LOADING; 143 /** Project to update once the SDK is loaded. 144 * Any access MUST be in a synchronized(mPostLoadProjectsToResolve) block */ 145 private final ArrayList<IJavaProject> mPostLoadProjectsToResolve = 146 new ArrayList<IJavaProject>(); 147 /** Project to check validity of cache vs actual once the SDK is loaded. 148 * Any access MUST be in a synchronized(mPostLoadProjectsToResolve) block */ 149 private final ArrayList<IJavaProject> mPostLoadProjectsToCheck = new ArrayList<IJavaProject>(); 150 151 private GlobalProjectMonitor mResourceMonitor; 152 private ArrayList<ITargetChangeListener> mTargetChangeListeners = 153 new ArrayList<ITargetChangeListener>(); 154 155 protected boolean mSdkIsLoading; 156 157 /** 158 * Custom PrintStream for Dx output. This class overrides the method 159 * <code>println()</code> and adds the standard output tag with the 160 * date and the project name in front of every messages. 161 */ 162 private static final class AndroidPrintStream extends PrintStream { 163 private IProject mProject; 164 private String mPrefix; 165 166 /** 167 * Default constructor with project and output stream. 168 * The project is used to get the project name for the output tag. 169 * 170 * @param project The Project 171 * @param prefix A prefix to be printed before the actual message. Can be null 172 * @param stream The Stream 173 */ 174 public AndroidPrintStream(IProject project, String prefix, OutputStream stream) { 175 super(stream); 176 mProject = project; 177 } 178 179 @Override 180 public void println(String message) { 181 // write the date/project tag first. 182 String tag = getMessageTag(mProject != null ? mProject.getName() : null); 183 184 print(tag); 185 print(' '); 186 if (mPrefix != null) { 187 print(mPrefix); 188 } 189 190 // then write the regular message 191 super.println(message); 192 } 193 } 194 195 /** 196 * An error handler for checkSdkLocationAndId() that will handle the generated error 197 * or warning message. Each method must return a boolean that will in turn be returned by 198 * checkSdkLocationAndId. 199 */ 200 public static abstract class CheckSdkErrorHandler { 201 /** Handle an error message during sdk location check. Returns whatever 202 * checkSdkLocationAndId() should returns. 203 */ 204 public abstract boolean handleError(String message); 205 206 /** Handle a warning message during sdk location check. Returns whatever 207 * checkSdkLocationAndId() should returns. 208 */ 209 public abstract boolean handleWarning(String message); 210 } 211 212 /** 213 * The constructor 214 */ 215 public AdtPlugin() { 216 sPlugin = this; 217 } 218 219 /* 220 * (non-Javadoc) 221 * @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext) 222 */ 223 @Override 224 public void start(BundleContext context) throws Exception { 225 super.start(context); 226 227 Display display = getDisplay(); 228 229 // set the default android console. 230 mAndroidConsole = new MessageConsole("Android", null); //$NON-NLS-1$ 231 ConsolePlugin.getDefault().getConsoleManager().addConsoles( 232 new IConsole[] { mAndroidConsole }); 233 234 // get the stream to write in the android console. 235 mAndroidConsoleStream = mAndroidConsole.newMessageStream(); 236 mAndroidConsoleErrorStream = mAndroidConsole.newMessageStream(); 237 mRed = new Color(display, 0xFF, 0x00, 0x00); 238 239 // because this can be run, in some cases, by a non ui thread, and beccause 240 // changing the console properties update the ui, we need to make this change 241 // in the ui thread. 242 display.asyncExec(new Runnable() { 243 public void run() { 244 mAndroidConsoleErrorStream.setColor(mRed); 245 } 246 }); 247 248 // set up the ddms console to use this objects 249 DdmConsole.setConsole(new IDdmConsole() { 250 public void printErrorToConsole(String message) { 251 AdtPlugin.printErrorToConsole((String)null, message); 252 } 253 public void printErrorToConsole(String[] messages) { 254 AdtPlugin.printErrorToConsole((String)null, (Object[])messages); 255 } 256 public void printToConsole(String message) { 257 AdtPlugin.printToConsole((String)null, message); 258 } 259 public void printToConsole(String[] messages) { 260 AdtPlugin.printToConsole((String)null, (Object[])messages); 261 } 262 }); 263 264 // get the eclipse store 265 AdtPrefs.init(getPreferenceStore()); 266 267 // set the listener for the preference change 268 Preferences prefs = getPluginPreferences(); 269 prefs.addPropertyChangeListener(new IPropertyChangeListener() { 270 public void propertyChange(PropertyChangeEvent event) { 271 // load the new preferences 272 AdtPrefs.getPrefs().loadValues(event); 273 274 // if the SDK changed, we have to do some extra work 275 if (AdtPrefs.PREFS_SDK_DIR.equals(event.getProperty())) { 276 277 // finally restart adb, in case it's a different version 278 DdmsPlugin.setAdb(getOsAbsoluteAdb(), true /* startAdb */); 279 280 // get the SDK location and build id. 281 if (checkSdkLocationAndId()) { 282 // if sdk if valid, reparse it 283 284 reparseSdk(); 285 } 286 } 287 } 288 }); 289 290 // load preferences. 291 AdtPrefs.getPrefs().loadValues(null /*event*/); 292 293 // check the location of SDK 294 final boolean isSdkLocationValid = checkSdkLocationAndId(); 295 296 // start the DdmsPlugin by setting the adb location, only if it is set already. 297 String osSdkLocation = AdtPrefs.getPrefs().getOsSdkFolder(); 298 if (osSdkLocation.length() > 0) { 299 DdmsPlugin.setAdb(getOsAbsoluteAdb(), true); 300 } 301 302 // and give it the debug launcher for android projects 303 DdmsPlugin.setRunningAppDebugLauncher(new DdmsPlugin.IDebugLauncher() { 304 public boolean debug(String appName, int port) { 305 // search for an android project matching the process name 306 IProject project = ProjectHelper.findAndroidProjectByAppName(appName); 307 if (project != null) { 308 AndroidLaunchController.debugRunningApp(project, port); 309 return true; 310 } else { 311 // check to see if there's a platform project defined by an env var. 312 String var = System.getenv("ANDROID_PLATFORM_PROJECT"); //$NON-NLS-1$ 313 if (var != null && var.length() > 0) { 314 boolean auto = "AUTO".equals(var); //$NON-NLS-1$ 315 316 // Get the list of project for the current workspace 317 IWorkspace workspace = ResourcesPlugin.getWorkspace(); 318 IProject[] projects = workspace.getRoot().getProjects(); 319 320 // look for a project that matches the env var or take the first 321 // one if in automatic mode. 322 for (IProject p : projects) { 323 if (p.isOpen()) { 324 if (auto || p.getName().equals(var)) { 325 AndroidLaunchController.debugRunningApp(p, port); 326 return true; 327 } 328 } 329 } 330 331 } 332 return false; 333 } 334 } 335 }); 336 337 StackTracePanel.setSourceRevealer(new ISourceRevealer() { 338 public void reveal(String applicationName, String className, int line) { 339 IProject project = ProjectHelper.findAndroidProjectByAppName(applicationName); 340 if (project != null) { 341 BaseProjectHelper.revealSource(project, className, line); 342 } 343 } 344 }); 345 346 // setup export callback for editors 347 ExportHelper.setCallback(new IExportCallback() { 348 public void startExportWizard(IProject project) { 349 StructuredSelection selection = new StructuredSelection(project); 350 351 ExportWizard wizard = new ExportWizard(); 352 wizard.init(PlatformUI.getWorkbench(), selection); 353 WizardDialog dialog = new WizardDialog(getDisplay().getActiveShell(), 354 wizard); 355 dialog.open(); 356 } 357 }); 358 359 // initialize editors 360 startEditors(); 361 362 // Ping the usage server and parse the SDK content. 363 // This is deferred in separate jobs to avoid blocking the bundle start. 364 // We also serialize them to avoid too many parallel jobs when Eclipse starts. 365 Job pingJob = createPingUsageServerJob(); 366 pingJob.addJobChangeListener(new JobChangeAdapter() { 367 @Override 368 public void done(IJobChangeEvent event) { 369 super.done(event); 370 371 // Once the ping job is finished, start the SDK parser 372 if (isSdkLocationValid) { 373 // parse the SDK resources. 374 parseSdkContent(); 375 } 376 } 377 }); 378 // build jobs are run after other interactive jobs 379 pingJob.setPriority(Job.BUILD); 380 // Wait 2 seconds before starting the ping job. This leaves some time to the 381 // other bundles to initialize. 382 pingJob.schedule(2000 /*milliseconds*/); 383 } 384 385 /* 386 * (non-Javadoc) 387 * 388 * @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext) 389 */ 390 @Override 391 public void stop(BundleContext context) throws Exception { 392 super.stop(context); 393 394 stopEditors(); 395 396 mRed.dispose(); 397 synchronized (AdtPlugin.class) { 398 sPlugin = null; 399 } 400 } 401 402 /** 403 * Returns the shared instance 404 * 405 * @return the shared instance 406 */ 407 public static synchronized AdtPlugin getDefault() { 408 return sPlugin; 409 } 410 411 public static Display getDisplay() { 412 IWorkbench bench = null; 413 synchronized (AdtPlugin.class) { 414 bench = sPlugin.getWorkbench(); 415 } 416 417 if (bench != null) { 418 return bench.getDisplay(); 419 } 420 return null; 421 } 422 423 /** Returns the adb path relative to the sdk folder */ 424 public static String getOsRelativeAdb() { 425 return SdkConstants.OS_SDK_TOOLS_FOLDER + SdkConstants.FN_ADB; 426 } 427 428 /** Returns the zipalign path relative to the sdk folder */ 429 public static String getOsRelativeZipAlign() { 430 return SdkConstants.OS_SDK_TOOLS_FOLDER + SdkConstants.FN_ZIPALIGN; 431 } 432 433 /** Returns the emulator path relative to the sdk folder */ 434 public static String getOsRelativeEmulator() { 435 return SdkConstants.OS_SDK_TOOLS_FOLDER + SdkConstants.FN_EMULATOR; 436 } 437 438 /** Returns the absolute adb path */ 439 public static String getOsAbsoluteAdb() { 440 return getOsSdkFolder() + getOsRelativeAdb(); 441 } 442 443 /** Returns the absolute zipalign path */ 444 public static String getOsAbsoluteZipAlign() { 445 return getOsSdkFolder() + getOsRelativeZipAlign(); 446 } 447 448 /** Returns the absolute traceview path */ 449 public static String getOsAbsoluteTraceview() { 450 return getOsSdkFolder() + SdkConstants.OS_SDK_TOOLS_FOLDER + 451 AndroidConstants.FN_TRACEVIEW; 452 } 453 454 /** Returns the absolute emulator path */ 455 public static String getOsAbsoluteEmulator() { 456 return getOsSdkFolder() + getOsRelativeEmulator(); 457 } 458 459 /** 460 * Returns a Url file path to the javaDoc folder. 461 */ 462 public static String getUrlDoc() { 463 return ProjectHelper.getJavaDocPath( 464 getOsSdkFolder() + AndroidConstants.WS_JAVADOC_FOLDER_LEAF); 465 } 466 467 /** 468 * Returns the SDK folder. 469 * Guaranteed to be terminated by a platform-specific path separator. 470 */ 471 public static synchronized String getOsSdkFolder() { 472 if (sPlugin == null) { 473 return null; 474 } 475 476 return AdtPrefs.getPrefs().getOsSdkFolder(); 477 } 478 479 public static String getOsSdkToolsFolder() { 480 return getOsSdkFolder() + SdkConstants.OS_SDK_TOOLS_FOLDER; 481 } 482 483 /** 484 * Returns an image descriptor for the image file at the given 485 * plug-in relative path 486 * 487 * @param path the path 488 * @return the image descriptor 489 */ 490 public static ImageDescriptor getImageDescriptor(String path) { 491 return imageDescriptorFromPlugin(PLUGIN_ID, path); 492 } 493 494 /** 495 * Reads and returns the content of a text file embedded in the plugin jar 496 * file. 497 * @param filepath the file path to the text file 498 * @return null if the file could not be read 499 */ 500 public static String readEmbeddedTextFile(String filepath) { 501 try { 502 InputStream is = readEmbeddedFileAsStream(filepath); 503 if (is != null) { 504 BufferedReader reader = new BufferedReader(new InputStreamReader(is)); 505 506 String line; 507 StringBuilder total = new StringBuilder(reader.readLine()); 508 while ((line = reader.readLine()) != null) { 509 total.append('\n'); 510 total.append(line); 511 } 512 513 return total.toString(); 514 } 515 } catch (IOException e) { 516 // we'll just return null 517 AdtPlugin.log(e, "Failed to read text file '%s'", filepath); //$NON-NLS-1$ 518 } 519 520 return null; 521 } 522 523 /** 524 * Reads and returns the content of a binary file embedded in the plugin jar 525 * file. 526 * @param filepath the file path to the text file 527 * @return null if the file could not be read 528 */ 529 public static byte[] readEmbeddedFile(String filepath) { 530 try { 531 InputStream is = readEmbeddedFileAsStream(filepath); 532 if (is != null) { 533 // create a buffered reader to facilitate reading. 534 BufferedInputStream stream = new BufferedInputStream(is); 535 536 // get the size to read. 537 int avail = stream.available(); 538 539 // create the buffer and reads it. 540 byte[] buffer = new byte[avail]; 541 stream.read(buffer); 542 543 // and return. 544 return buffer; 545 } 546 } catch (IOException e) { 547 // we'll just return null;. 548 AdtPlugin.log(e, "Failed to read binary file '%s'", filepath); //$NON-NLS-1$ 549 } 550 551 return null; 552 } 553 554 /** 555 * Reads and returns the content of a binary file embedded in the plugin jar 556 * file. 557 * @param filepath the file path to the text file 558 * @return null if the file could not be read 559 */ 560 public static InputStream readEmbeddedFileAsStream(String filepath) { 561 // attempt to read an embedded file 562 try { 563 URL url = getEmbeddedFileUrl(AndroidConstants.WS_SEP + filepath); 564 if (url != null) { 565 return url.openStream(); 566 } 567 } catch (MalformedURLException e) { 568 // we'll just return null. 569 AdtPlugin.log(e, "Failed to read stream '%s'", filepath); //$NON-NLS-1$ 570 } catch (IOException e) { 571 // we'll just return null;. 572 AdtPlugin.log(e, "Failed to read stream '%s'", filepath); //$NON-NLS-1$ 573 } 574 575 return null; 576 } 577 578 /** 579 * Returns the URL of a binary file embedded in the plugin jar file. 580 * @param filepath the file path to the text file 581 * @return null if the file was not found. 582 */ 583 public static URL getEmbeddedFileUrl(String filepath) { 584 Bundle bundle = null; 585 synchronized (AdtPlugin.class) { 586 if (sPlugin != null) { 587 bundle = sPlugin.getBundle(); 588 } else { 589 AdtPlugin.log(IStatus.WARNING, "ADT Plugin is missing"); //$NON-NLS-1$ 590 return null; 591 } 592 } 593 594 // attempt to get a file to one of the template. 595 String path = filepath; 596 if (!path.startsWith(AndroidConstants.WS_SEP)) { 597 path = AndroidConstants.WS_SEP + path; 598 } 599 600 URL url = bundle.getEntry(path); 601 602 if (url == null) { 603 AdtPlugin.log(IStatus.INFO, "Bundle file URL not found at path '%s'", path); //$NON-NLS-1$ 604 } 605 606 return url; 607 } 608 609 /** 610 * Displays an error dialog box. This dialog box is ran asynchronously in the ui thread, 611 * therefore this method can be called from any thread. 612 * @param title The title of the dialog box 613 * @param message The error message 614 */ 615 public final static void displayError(final String title, final String message) { 616 // get the current Display 617 final Display display = getDisplay(); 618 619 // dialog box only run in ui thread.. 620 display.asyncExec(new Runnable() { 621 public void run() { 622 Shell shell = display.getActiveShell(); 623 MessageDialog.openError(shell, title, message); 624 } 625 }); 626 } 627 628 /** 629 * Displays a warning dialog box. This dialog box is ran asynchronously in the ui thread, 630 * therefore this method can be called from any thread. 631 * @param title The title of the dialog box 632 * @param message The warning message 633 */ 634 public final static void displayWarning(final String title, final String message) { 635 // get the current Display 636 final Display display = getDisplay(); 637 638 // dialog box only run in ui thread.. 639 display.asyncExec(new Runnable() { 640 public void run() { 641 Shell shell = display.getActiveShell(); 642 MessageDialog.openWarning(shell, title, message); 643 } 644 }); 645 } 646 647 /** 648 * Display a yes/no question dialog box. This dialog is opened synchronously in the ui thread, 649 * therefore this message can be called from any thread. 650 * @param title The title of the dialog box 651 * @param message The error message 652 * @return true if OK was clicked. 653 */ 654 public final static boolean displayPrompt(final String title, final String message) { 655 // get the current Display and Shell 656 final Display display = getDisplay(); 657 658 // we need to ask the user what he wants to do. 659 final boolean[] result = new boolean[1]; 660 display.syncExec(new Runnable() { 661 public void run() { 662 Shell shell = display.getActiveShell(); 663 result[0] = MessageDialog.openQuestion(shell, title, message); 664 } 665 }); 666 return result[0]; 667 } 668 669 /** 670 * Logs a message to the default Eclipse log. 671 * 672 * @param severity The severity code. Valid values are: {@link IStatus#OK}, 673 * {@link IStatus#ERROR}, {@link IStatus#INFO}, {@link IStatus#WARNING} or 674 * {@link IStatus#CANCEL}. 675 * @param format The format string, like for {@link String#format(String, Object...)}. 676 * @param args The arguments for the format string, like for 677 * {@link String#format(String, Object...)}. 678 */ 679 public static void log(int severity, String format, Object ... args) { 680 String message = String.format(format, args); 681 Status status = new Status(severity, PLUGIN_ID, message); 682 getDefault().getLog().log(status); 683 } 684 685 /** 686 * Logs an exception to the default Eclipse log. 687 * <p/> 688 * The status severity is always set to ERROR. 689 * 690 * @param exception the exception to log. 691 * @param format The format string, like for {@link String#format(String, Object...)}. 692 * @param args The arguments for the format string, like for 693 * {@link String#format(String, Object...)}. 694 */ 695 public static void log(Throwable exception, String format, Object ... args) { 696 String message = String.format(format, args); 697 Status status = new Status(IStatus.ERROR, PLUGIN_ID, message, exception); 698 getDefault().getLog().log(status); 699 } 700 701 /** 702 * This is a mix between log(Throwable) and printErrorToConsole. 703 * <p/> 704 * This logs the exception with an ERROR severity and the given printf-like format message. 705 * The same message is then printed on the Android error console with the associated tag. 706 * 707 * @param exception the exception to log. 708 * @param format The format string, like for {@link String#format(String, Object...)}. 709 * @param args The arguments for the format string, like for 710 * {@link String#format(String, Object...)}. 711 */ 712 public static synchronized void logAndPrintError(Throwable exception, String tag, 713 String format, Object ... args) { 714 if (sPlugin != null) { 715 String message = String.format(format, args); 716 Status status = new Status(IStatus.ERROR, PLUGIN_ID, message, exception); 717 getDefault().getLog().log(status); 718 printToStream(sPlugin.mAndroidConsoleErrorStream, tag, message); 719 showAndroidConsole(); 720 } 721 } 722 723 /** 724 * Prints one or more error message to the android console. 725 * @param tag A tag to be associated with the message. Can be null. 726 * @param objects the objects to print through their <code>toString</code> method. 727 */ 728 public static synchronized void printErrorToConsole(String tag, Object... objects) { 729 if (sPlugin != null) { 730 printToStream(sPlugin.mAndroidConsoleErrorStream, tag, objects); 731 732 showAndroidConsole(); 733 } 734 } 735 736 /** 737 * Prints one or more error message to the android console. 738 * @param objects the objects to print through their <code>toString</code> method. 739 */ 740 public static void printErrorToConsole(Object... objects) { 741 printErrorToConsole((String)null, objects); 742 } 743 744 /** 745 * Prints one or more error message to the android console. 746 * @param project The project to which the message is associated. Can be null. 747 * @param objects the objects to print through their <code>toString</code> method. 748 */ 749 public static void printErrorToConsole(IProject project, Object... objects) { 750 String tag = project != null ? project.getName() : null; 751 printErrorToConsole(tag, objects); 752 } 753 754 /** 755 * Prints one or more build messages to the android console, filtered by Build output verbosity. 756 * @param level {@link BuildVerbosity} level of the message. 757 * @param project The project to which the message is associated. Can be null. 758 * @param objects the objects to print through their <code>toString</code> method. 759 * @see BuildVerbosity#ALWAYS 760 * @see BuildVerbosity#NORMAL 761 * @see BuildVerbosity#VERBOSE 762 */ 763 public static synchronized void printBuildToConsole(BuildVerbosity level, IProject project, 764 Object... objects) { 765 if (sPlugin != null) { 766 if (level.getLevel() <= AdtPrefs.getPrefs().getBuildVerbosity().getLevel()) { 767 String tag = project != null ? project.getName() : null; 768 printToStream(sPlugin.mAndroidConsoleStream, tag, objects); 769 } 770 } 771 } 772 773 /** 774 * Prints one or more message to the android console. 775 * @param tag The tag to be associated with the message. Can be null. 776 * @param objects the objects to print through their <code>toString</code> method. 777 */ 778 public static synchronized void printToConsole(String tag, Object... objects) { 779 if (sPlugin != null) { 780 printToStream(sPlugin.mAndroidConsoleStream, tag, objects); 781 } 782 } 783 784 /** 785 * Prints one or more message to the android console. 786 * @param project The project to which the message is associated. Can be null. 787 * @param objects the objects to print through their <code>toString</code> method. 788 */ 789 public static void printToConsole(IProject project, Object... objects) { 790 String tag = project != null ? project.getName() : null; 791 printToConsole(tag, objects); 792 } 793 794 /** Force the display of the android console */ 795 public static void showAndroidConsole() { 796 // first make sure the console is in the workbench 797 EclipseUiHelper.showView(IConsoleConstants.ID_CONSOLE_VIEW, true); 798 799 // now make sure it's not docked. 800 ConsolePlugin.getDefault().getConsoleManager().showConsoleView( 801 AdtPlugin.getDefault().getAndroidConsole()); 802 } 803 804 /** 805 * Returns an standard PrintStream object for a specific project.<br> 806 * This PrintStream will add a date/project at the beginning of every 807 * <code>println()</code> output. 808 * 809 * @param project The project object 810 * @param prefix The prefix to be added to the message. Can be null. 811 * @return a new PrintStream 812 */ 813 public static synchronized PrintStream getOutPrintStream(IProject project, String prefix) { 814 if (sPlugin != null) { 815 return new AndroidPrintStream(project, prefix, sPlugin.mAndroidConsoleStream); 816 } 817 818 return null; 819 } 820 821 /** 822 * Returns an error PrintStream object for a specific project.<br> 823 * This PrintStream will add a date/project at the beginning of every 824 * <code>println()</code> output. 825 * 826 * @param project The project object 827 * @param prefix The prefix to be added to the message. Can be null. 828 * @return a new PrintStream 829 */ 830 public static synchronized PrintStream getErrPrintStream(IProject project, String prefix) { 831 if (sPlugin != null) { 832 return new AndroidPrintStream(project, prefix, sPlugin.mAndroidConsoleErrorStream); 833 } 834 835 return null; 836 } 837 838 /** 839 * Returns whether the {@link IAndroidTarget}s have been loaded from the SDK. 840 */ 841 public final LoadStatus getSdkLoadStatus() { 842 synchronized (Sdk.getLock()) { 843 return mSdkIsLoaded; 844 } 845 } 846 847 /** 848 * Sets the given {@link IJavaProject} to have its target resolved again once the SDK finishes 849 * to load. 850 */ 851 public final void setProjectToResolve(IJavaProject javaProject) { 852 synchronized (Sdk.getLock()) { 853 mPostLoadProjectsToResolve.add(javaProject); 854 } 855 } 856 857 /** 858 * Sets the given {@link IJavaProject} to have its target checked for consistency 859 * once the SDK finishes to load. This is used if the target is resolved using cached 860 * information while the SDK is loading. 861 */ 862 public final void setProjectToCheck(IJavaProject javaProject) { 863 // only lock on 864 synchronized (Sdk.getLock()) { 865 mPostLoadProjectsToCheck.add(javaProject); 866 } 867 } 868 869 /** 870 * Checks the location of the SDK is valid and if it is, grab the SDK API version 871 * from the SDK. 872 * @return false if the location is not correct. 873 */ 874 private boolean checkSdkLocationAndId() { 875 String sdkLocation = AdtPrefs.getPrefs().getOsSdkFolder(); 876 if (sdkLocation == null || sdkLocation.length() == 0) { 877 return false; 878 } 879 880 return checkSdkLocationAndId(sdkLocation, new CheckSdkErrorHandler() { 881 @Override 882 public boolean handleError(String message) { 883 return false; 884 } 885 886 @Override 887 public boolean handleWarning(String message) { 888 return true; 889 } 890 }); 891 } 892 893 /** 894 * Internal helper to perform the actual sdk location and id check. 895 * 896 * @param osSdkLocation The sdk directory, an OS path. 897 * @param errorHandler An checkSdkErrorHandler that can display a warning or an error. 898 * @return False if there was an error or the result from the errorHandler invocation. 899 */ 900 public boolean checkSdkLocationAndId(String osSdkLocation, CheckSdkErrorHandler errorHandler) { 901 if (osSdkLocation.endsWith(File.separator) == false) { 902 osSdkLocation = osSdkLocation + File.separator; 903 } 904 905 File osSdkFolder = new File(osSdkLocation); 906 if (osSdkFolder.isDirectory() == false) { 907 return errorHandler.handleError( 908 String.format(Messages.Could_Not_Find_Folder, osSdkLocation)); 909 } 910 911 String osTools = osSdkLocation + SdkConstants.OS_SDK_TOOLS_FOLDER; 912 File toolsFolder = new File(osTools); 913 if (toolsFolder.isDirectory() == false) { 914 return errorHandler.handleError( 915 String.format(Messages.Could_Not_Find_Folder_In_SDK, 916 SdkConstants.FD_TOOLS, osSdkLocation)); 917 } 918 919 // check the path to various tools we use 920 String[] filesToCheck = new String[] { 921 osSdkLocation + getOsRelativeAdb(), 922 osSdkLocation + getOsRelativeEmulator() 923 }; 924 for (String file : filesToCheck) { 925 if (checkFile(file) == false) { 926 return errorHandler.handleError(String.format(Messages.Could_Not_Find, file)); 927 } 928 } 929 930 // check the SDK build id/version and the plugin version. 931 return VersionCheck.checkVersion(osSdkLocation, errorHandler); 932 } 933 934 /** 935 * Checks if a path reference a valid existing file. 936 * @param osPath the os path to check. 937 * @return true if the file exists and is, in fact, a file. 938 */ 939 private boolean checkFile(String osPath) { 940 File file = new File(osPath); 941 if (file.isFile() == false) { 942 return false; 943 } 944 945 return true; 946 } 947 948 /** 949 * Creates a job than can ping the usage server. 950 */ 951 private Job createPingUsageServerJob() { 952 // In order to not block the plugin loading, so we spawn another thread. 953 Job job = new Job("Android SDK Ping") { // Job name, visible in progress view 954 @Override 955 protected IStatus run(IProgressMonitor monitor) { 956 try { 957 pingUsageServer(); //$NON-NLS-1$ 958 959 return Status.OK_STATUS; 960 } catch (Throwable t) { 961 log(t, "pingUsageServer failed"); //$NON-NLS-1$ 962 return new Status(IStatus.ERROR, PLUGIN_ID, 963 "pingUsageServer failed", t); //$NON-NLS-1$ 964 } 965 } 966 }; 967 return job; 968 } 969 970 /** 971 * Parses the SDK resources. 972 */ 973 private void parseSdkContent() { 974 // Perform the update in a thread (here an Eclipse runtime job) 975 // since this should never block the caller (especially the start method) 976 Job job = new Job(Messages.AdtPlugin_Android_SDK_Content_Loader) { 977 @SuppressWarnings("unchecked") 978 @Override 979 protected IStatus run(IProgressMonitor monitor) { 980 try { 981 982 if (mSdkIsLoading) { 983 return new Status(IStatus.WARNING, PLUGIN_ID, 984 "An Android SDK is already being loaded. Please try again later."); 985 } 986 987 mSdkIsLoading = true; 988 989 SubMonitor progress = SubMonitor.convert(monitor, 990 "Initialize SDK Manager", 100); 991 992 Sdk sdk = Sdk.loadSdk(AdtPrefs.getPrefs().getOsSdkFolder()); 993 994 if (sdk != null) { 995 996 ArrayList<IJavaProject> list = new ArrayList<IJavaProject>(); 997 synchronized (Sdk.getLock()) { 998 mSdkIsLoaded = LoadStatus.LOADED; 999 1000 progress.setTaskName("Check Projects"); 1001 1002 for (IJavaProject javaProject : mPostLoadProjectsToResolve) { 1003 IProject iProject = javaProject.getProject(); 1004 if (iProject.isOpen()) { 1005 // project that have been resolved before the sdk was loaded 1006 // will have a ProjectState where the IAndroidTarget is null 1007 // so we load the target now that the SDK is loaded. 1008 sdk.loadTarget(Sdk.getProjectState(iProject)); 1009 list.add(javaProject); 1010 } 1011 } 1012 1013 // done with this list. 1014 mPostLoadProjectsToResolve.clear(); 1015 } 1016 1017 // check the projects that need checking. 1018 // The method modifies the list (it removes the project that 1019 // do not need to be resolved again). 1020 AndroidClasspathContainerInitializer.checkProjectsCache( 1021 mPostLoadProjectsToCheck); 1022 1023 list.addAll(mPostLoadProjectsToCheck); 1024 1025 // update the project that needs recompiling. 1026 if (list.size() > 0) { 1027 IJavaProject[] array = list.toArray( 1028 new IJavaProject[list.size()]); 1029 AndroidClasspathContainerInitializer.updateProjects(array); 1030 } 1031 1032 progress.worked(10); 1033 } else { 1034 // SDK failed to Load! 1035 // Sdk#loadSdk() has already displayed an error. 1036 synchronized (Sdk.getLock()) { 1037 mSdkIsLoaded = LoadStatus.FAILED; 1038 } 1039 } 1040 1041 // Notify resource changed listeners 1042 progress.setTaskName("Refresh UI"); 1043 progress.setWorkRemaining(mTargetChangeListeners.size()); 1044 1045 // Clone the list before iterating, to avoid ConcurrentModification 1046 // exceptions 1047 final List<ITargetChangeListener> listeners = 1048 (List<ITargetChangeListener>)mTargetChangeListeners.clone(); 1049 final SubMonitor progress2 = progress; 1050 AdtPlugin.getDisplay().asyncExec(new Runnable() { 1051 public void run() { 1052 for (ITargetChangeListener listener : listeners) { 1053 try { 1054 listener.onSdkLoaded(); 1055 } catch (Exception e) { 1056 AdtPlugin.log(e, "Failed to update a TargetChangeListener."); //$NON-NLS-1$ 1057 } finally { 1058 progress2.worked(1); 1059 } 1060 } 1061 } 1062 }); 1063 } catch (Throwable t) { 1064 log(t, "Unknown exception in parseSdkContent."); //$NON-NLS-1$ 1065 return new Status(IStatus.ERROR, PLUGIN_ID, 1066 "parseSdkContent failed", t); //$NON-NLS-1$ 1067 1068 } finally { 1069 mSdkIsLoading = false; 1070 if (monitor != null) { 1071 monitor.done(); 1072 } 1073 } 1074 1075 return Status.OK_STATUS; 1076 } 1077 }; 1078 job.setPriority(Job.BUILD); // build jobs are run after other interactive jobs 1079 job.schedule(); 1080 } 1081 1082 /** Returns the global android console */ 1083 public MessageConsole getAndroidConsole() { 1084 return mAndroidConsole; 1085 } 1086 1087 // ----- Methods for Editors ------- 1088 1089 public void startEditors() { 1090 sAndroidLogoDesc = imageDescriptorFromPlugin(AdtPlugin.PLUGIN_ID, 1091 "/icons/android.png"); //$NON-NLS-1$ 1092 sAndroidLogo = sAndroidLogoDesc.createImage(); 1093 1094 // Add a resource listener to handle compiled resources. 1095 IWorkspace ws = ResourcesPlugin.getWorkspace(); 1096 mResourceMonitor = GlobalProjectMonitor.startMonitoring(ws); 1097 1098 if (mResourceMonitor != null) { 1099 try { 1100 setupDefaultEditor(mResourceMonitor); 1101 ResourceManager.setup(mResourceMonitor); 1102 } catch (Throwable t) { 1103 log(t, "ResourceManager.setup failed"); //$NON-NLS-1$ 1104 } 1105 } 1106 } 1107 1108 /** 1109 * The <code>AbstractUIPlugin</code> implementation of this <code>Plugin</code> 1110 * method saves this plug-in's preference and dialog stores and shuts down 1111 * its image registry (if they are in use). Subclasses may extend this 1112 * method, but must send super <b>last</b>. A try-finally statement should 1113 * be used where necessary to ensure that <code>super.shutdown()</code> is 1114 * always done. 1115 * 1116 * @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext) 1117 */ 1118 public void stopEditors() { 1119 sAndroidLogo.dispose(); 1120 1121 IconFactory.getInstance().Dispose(); 1122 1123 // Remove the resource listener that handles compiled resources. 1124 IWorkspace ws = ResourcesPlugin.getWorkspace(); 1125 GlobalProjectMonitor.stopMonitoring(ws); 1126 1127 mRed.dispose(); 1128 } 1129 1130 /** 1131 * Returns an Image for the small Android logo. 1132 * 1133 * Callers should not dispose it. 1134 */ 1135 public static Image getAndroidLogo() { 1136 return sAndroidLogo; 1137 } 1138 1139 /** 1140 * Returns an {@link ImageDescriptor} for the small Android logo. 1141 * 1142 * Callers should not dispose it. 1143 */ 1144 public static ImageDescriptor getAndroidLogoDesc() { 1145 return sAndroidLogoDesc; 1146 } 1147 1148 /** 1149 * Returns the ResourceMonitor object. 1150 */ 1151 public GlobalProjectMonitor getResourceMonitor() { 1152 return mResourceMonitor; 1153 } 1154 1155 /** 1156 * Sets up the editor to register default editors for resource files when needed. 1157 * 1158 * This is called by the {@link AdtPlugin} during initialization. 1159 * 1160 * @param monitor The main Resource Monitor object. 1161 */ 1162 public void setupDefaultEditor(GlobalProjectMonitor monitor) { 1163 monitor.addFileListener(new IFileListener() { 1164 1165 private static final String UNKNOWN_EDITOR = "unknown-editor"; //$NON-NLS-1$ 1166 1167 /* (non-Javadoc) 1168 * Sent when a file changed. 1169 * @param file The file that changed. 1170 * @param markerDeltas The marker deltas for the file. 1171 * @param kind The change kind. This is equivalent to 1172 * {@link IResourceDelta#accept(IResourceDeltaVisitor)} 1173 * 1174 * @see IFileListener#fileChanged 1175 */ 1176 public void fileChanged(IFile file, IMarkerDelta[] markerDeltas, int kind) { 1177 if (AndroidConstants.EXT_XML.equals(file.getFileExtension())) { 1178 // The resources files must have a file path similar to 1179 // project/res/.../*.xml 1180 // There is no support for sub folders, so the segment count must be 4 1181 if (file.getFullPath().segmentCount() == 4) { 1182 // check if we are inside the res folder. 1183 String segment = file.getFullPath().segment(1); 1184 if (segment.equalsIgnoreCase(SdkConstants.FD_RESOURCES)) { 1185 // we are inside a res/ folder, get the actual ResourceFolder 1186 ProjectResources resources = ResourceManager.getInstance(). 1187 getProjectResources(file.getProject()); 1188 1189 // This happens when importing old Android projects in Eclipse 1190 // that lack the container (probably because resources fail to build 1191 // properly.) 1192 if (resources == null) { 1193 log(IStatus.INFO, 1194 "getProjectResources failed for path %1$s in project %2$s", //$NON-NLS-1$ 1195 file.getFullPath().toOSString(), 1196 file.getProject().getName()); 1197 return; 1198 } 1199 1200 ResourceFolder resFolder = resources.getResourceFolder( 1201 (IFolder)file.getParent()); 1202 1203 if (resFolder != null) { 1204 if (kind == IResourceDelta.ADDED) { 1205 resourceAdded(file, resFolder.getType()); 1206 } else if (kind == IResourceDelta.CHANGED) { 1207 resourceChanged(file, resFolder.getType()); 1208 } 1209 } else { 1210 // if the res folder is null, this means the name is invalid, 1211 // in this case we remove whatever android editors that was set 1212 // as the default editor. 1213 IEditorDescriptor desc = IDE.getDefaultEditor(file); 1214 String editorId = desc.getId(); 1215 if (editorId.startsWith(AndroidConstants.EDITORS_NAMESPACE)) { 1216 // reset the default editor. 1217 IDE.setDefaultEditor(file, null); 1218 } 1219 } 1220 } 1221 } 1222 } 1223 } 1224 1225 private void resourceAdded(IFile file, ResourceFolderType type) { 1226 // set the default editor based on the type. 1227 if (type == ResourceFolderType.LAYOUT) { 1228 IDE.setDefaultEditor(file, LayoutEditor.ID); 1229 } else if (type == ResourceFolderType.DRAWABLE 1230 || type == ResourceFolderType.VALUES) { 1231 IDE.setDefaultEditor(file, ResourcesEditor.ID); 1232 } else if (type == ResourceFolderType.MENU) { 1233 IDE.setDefaultEditor(file, MenuEditor.ID); 1234 } else if (type == ResourceFolderType.XML) { 1235 if (XmlEditor.canHandleFile(file)) { 1236 IDE.setDefaultEditor(file, XmlEditor.ID); 1237 } else { 1238 // set a property to determine later if the XML can be handled 1239 QualifiedName qname = new QualifiedName( 1240 AdtPlugin.PLUGIN_ID, 1241 UNKNOWN_EDITOR); 1242 try { 1243 file.setPersistentProperty(qname, "1"); //$NON-NLS-1$ 1244 } catch (CoreException e) { 1245 // pass 1246 } 1247 } 1248 } 1249 } 1250 1251 private void resourceChanged(IFile file, ResourceFolderType type) { 1252 if (type == ResourceFolderType.XML) { 1253 IEditorDescriptor ed = IDE.getDefaultEditor(file); 1254 if (ed == null || ed.getId() != XmlEditor.ID) { 1255 QualifiedName qname = new QualifiedName( 1256 AdtPlugin.PLUGIN_ID, 1257 UNKNOWN_EDITOR); 1258 String prop = null; 1259 try { 1260 prop = file.getPersistentProperty(qname); 1261 } catch (CoreException e) { 1262 // pass 1263 } 1264 if (prop != null && XmlEditor.canHandleFile(file)) { 1265 try { 1266 // remove the property & set editor 1267 file.setPersistentProperty(qname, null); 1268 1269 // the window can be null sometimes 1270 IWorkbench wb = PlatformUI.getWorkbench(); 1271 IWorkbenchWindow win = wb == null ? null : 1272 wb.getActiveWorkbenchWindow(); 1273 IWorkbenchPage page = win == null ? null : 1274 win.getActivePage(); 1275 1276 IEditorPart oldEditor = page == null ? null : 1277 page.findEditor(new FileEditorInput(file)); 1278 if (page != null && 1279 oldEditor != null && 1280 AdtPlugin.displayPrompt("Android XML Editor", 1281 String.format("The file you just saved as been recognized as a file that could be better handled using the Android XML Editor. Do you want to edit '%1$s' using the Android XML editor instead?", 1282 file.getFullPath()))) { 1283 IDE.setDefaultEditor(file, XmlEditor.ID); 1284 IEditorPart newEditor = page.openEditor( 1285 new FileEditorInput(file), 1286 XmlEditor.ID, 1287 true, /* activate */ 1288 IWorkbenchPage.MATCH_NONE); 1289 1290 if (newEditor != null) { 1291 page.closeEditor(oldEditor, true /* save */); 1292 } 1293 } 1294 } catch (CoreException e) { 1295 // setPersistentProperty or page.openEditor may have failed 1296 } 1297 } 1298 } 1299 } 1300 } 1301 1302 }, IResourceDelta.ADDED | IResourceDelta.CHANGED); 1303 } 1304 1305 /** 1306 * Adds a new {@link ITargetChangeListener} to be notified when a new SDK is loaded, or when 1307 * a project has its target changed. 1308 */ 1309 public void addTargetListener(ITargetChangeListener listener) { 1310 mTargetChangeListeners.add(listener); 1311 } 1312 1313 /** 1314 * Removes an existing {@link ITargetChangeListener}. 1315 * @see #addTargetListener(ITargetChangeListener) 1316 */ 1317 public void removeTargetListener(ITargetChangeListener listener) { 1318 mTargetChangeListeners.remove(listener); 1319 } 1320 1321 /** 1322 * Updates all the {@link ITargetChangeListener}s that a target has changed for a given project. 1323 * <p/>Only editors related to that project should reload. 1324 */ 1325 @SuppressWarnings("unchecked") 1326 public void updateTargetListeners(final IProject project) { 1327 final List<ITargetChangeListener> listeners = 1328 (List<ITargetChangeListener>)mTargetChangeListeners.clone(); 1329 1330 AdtPlugin.getDisplay().asyncExec(new Runnable() { 1331 public void run() { 1332 for (ITargetChangeListener listener : listeners) { 1333 try { 1334 listener.onProjectTargetChange(project); 1335 } catch (Exception e) { 1336 AdtPlugin.log(e, "Failed to update a TargetChangeListener."); //$NON-NLS-1$ 1337 } 1338 } 1339 } 1340 }); 1341 } 1342 1343 /** 1344 * Updates all the {@link ITargetChangeListener}s that a target data was loaded. 1345 * <p/>Only editors related to a project using this target should reload. 1346 */ 1347 @SuppressWarnings("unchecked") 1348 public void updateTargetListeners(final IAndroidTarget target) { 1349 final List<ITargetChangeListener> listeners = 1350 (List<ITargetChangeListener>)mTargetChangeListeners.clone(); 1351 1352 AdtPlugin.getDisplay().asyncExec(new Runnable() { 1353 public void run() { 1354 for (ITargetChangeListener listener : listeners) { 1355 try { 1356 listener.onTargetLoaded(target); 1357 } catch (Exception e) { 1358 AdtPlugin.log(e, "Failed to update a TargetChangeListener."); //$NON-NLS-1$ 1359 } 1360 } 1361 } 1362 }); 1363 } 1364 1365 public static synchronized OutputStream getErrorStream() { 1366 return sPlugin.mAndroidConsoleErrorStream; 1367 } 1368 1369 /** 1370 * Pings the usage start server. 1371 */ 1372 private void pingUsageServer() { 1373 // get the version of the plugin 1374 String versionString = (String) getBundle().getHeaders().get( 1375 Constants.BUNDLE_VERSION); 1376 Version version = new Version(versionString); 1377 1378 versionString = String.format("%1$d.%2$d.%3$d", version.getMajor(), //$NON-NLS-1$ 1379 version.getMinor(), version.getMicro()); 1380 1381 SdkStatsService.ping("adt", versionString, getDisplay()); //$NON-NLS-1$ 1382 } 1383 1384 /** 1385 * Reparses the content of the SDK and updates opened projects. 1386 */ 1387 public void reparseSdk() { 1388 // add all the opened Android projects to the list of projects to be updated 1389 // after the SDK is reloaded 1390 synchronized (Sdk.getLock()) { 1391 // get the project to refresh. 1392 IJavaProject[] androidProjects = BaseProjectHelper.getAndroidProjects(null /*filter*/); 1393 mPostLoadProjectsToResolve.addAll(Arrays.asList(androidProjects)); 1394 } 1395 1396 // parse the SDK resources at the new location 1397 parseSdkContent(); 1398 } 1399 1400 /** 1401 * Prints messages, associated with a project to the specified stream 1402 * @param stream The stream to write to 1403 * @param tag The tag associated to the message. Can be null 1404 * @param objects The objects to print through their toString() method (or directly for 1405 * {@link String} objects. 1406 */ 1407 public static synchronized void printToStream(MessageConsoleStream stream, String tag, 1408 Object... objects) { 1409 String dateTag = getMessageTag(tag); 1410 1411 for (Object obj : objects) { 1412 stream.print(dateTag); 1413 stream.print(" "); //$NON-NLS-1$ 1414 if (obj instanceof String) { 1415 stream.println((String)obj); 1416 } else if (obj == null) { 1417 stream.println("(null)"); //$NON-NLS-1$ 1418 } else { 1419 stream.println(obj.toString()); 1420 } 1421 } 1422 } 1423 1424 /** 1425 * Creates a string containing the current date/time, and the tag. 1426 * The tag does not end with a whitespace. 1427 * @param tag The tag associated to the message. Can be null 1428 * @return The dateTag 1429 */ 1430 public static String getMessageTag(String tag) { 1431 Calendar c = Calendar.getInstance(); 1432 1433 if (tag == null) { 1434 return String.format(Messages.Console_Date_Tag, c); 1435 } 1436 1437 return String.format(Messages.Console_Data_Project_Tag, c, tag); 1438 } 1439 1440 } 1441