Merge change Id49751eb into eclair-sdk
* changes: SDK Manager: reuse complete downloads, retry Windows locks.
This commit is contained in:
@@ -23,6 +23,7 @@ import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
|
|||||||
import org.apache.commons.compress.archivers.zip.ZipFile;
|
import org.apache.commons.compress.archivers.zip.ZipFile;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -369,42 +370,38 @@ public class Archive implements IDescription {
|
|||||||
Package pkg = getParentPackage();
|
Package pkg = getParentPackage();
|
||||||
|
|
||||||
File archiveFile = null;
|
File archiveFile = null;
|
||||||
try {
|
String name = pkg.getShortDescription();
|
||||||
String name = pkg.getShortDescription();
|
|
||||||
|
|
||||||
if (pkg instanceof ExtraPackage && !((ExtraPackage) pkg).isPathValid()) {
|
if (pkg instanceof ExtraPackage && !((ExtraPackage) pkg).isPathValid()) {
|
||||||
monitor.setResult("Skipping %1$s: %2$s is not a valid install path.",
|
monitor.setResult("Skipping %1$s: %2$s is not a valid install path.",
|
||||||
name,
|
name,
|
||||||
((ExtraPackage) pkg).getPath());
|
((ExtraPackage) pkg).getPath());
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isLocal()) {
|
||||||
|
// This should never happen.
|
||||||
|
monitor.setResult("Skipping already installed archive: %1$s for %2$s",
|
||||||
|
name,
|
||||||
|
getOsDescription());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isCompatible()) {
|
||||||
|
monitor.setResult("Skipping incompatible archive: %1$s for %2$s",
|
||||||
|
name,
|
||||||
|
getOsDescription());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
archiveFile = downloadFile(osSdkRoot, monitor, forceHttp);
|
||||||
|
if (archiveFile != null) {
|
||||||
|
if (unarchive(osSdkRoot, archiveFile, sdkManager, monitor)) {
|
||||||
|
monitor.setResult("Installed %1$s", name);
|
||||||
|
// Delete the temp archive if it exists, only on success
|
||||||
|
deleteFileOrFolder(archiveFile);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isLocal()) {
|
|
||||||
// This should never happen.
|
|
||||||
monitor.setResult("Skipping already installed archive: %1$s for %2$s",
|
|
||||||
name,
|
|
||||||
getOsDescription());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isCompatible()) {
|
|
||||||
monitor.setResult("Skipping incompatible archive: %1$s for %2$s",
|
|
||||||
name,
|
|
||||||
getOsDescription());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
archiveFile = downloadFile(monitor, forceHttp);
|
|
||||||
if (archiveFile != null) {
|
|
||||||
if (unarchive(osSdkRoot, archiveFile, sdkManager, monitor)) {
|
|
||||||
monitor.setResult("Installed %1$s", name);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} finally {
|
|
||||||
// Delete the temp archive if it exists
|
|
||||||
deleteFileOrFolder(archiveFile);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@@ -414,57 +411,134 @@ public class Archive implements IDescription {
|
|||||||
* Downloads an archive and returns the temp file with it.
|
* Downloads an archive and returns the temp file with it.
|
||||||
* Caller is responsible with deleting the temp file when done.
|
* Caller is responsible with deleting the temp file when done.
|
||||||
*/
|
*/
|
||||||
private File downloadFile(ITaskMonitor monitor, boolean forceHttp) {
|
private File downloadFile(String osSdkRoot, ITaskMonitor monitor, boolean forceHttp) {
|
||||||
|
|
||||||
File tmpFileToDelete = null;
|
String name = getParentPackage().getShortDescription();
|
||||||
try {
|
String desc = String.format("Downloading %1$s", name);
|
||||||
File tmpFile = File.createTempFile("sdkupload", ".bin"); //$NON-NLS-1$ //$NON-NLS-2$
|
monitor.setDescription(desc);
|
||||||
tmpFileToDelete = tmpFile;
|
monitor.setResult(desc);
|
||||||
|
|
||||||
String name = getParentPackage().getShortDescription();
|
String link = getUrl();
|
||||||
String desc = String.format("Downloading %1$s", name);
|
if (!link.startsWith("http://") //$NON-NLS-1$
|
||||||
monitor.setDescription(desc);
|
&& !link.startsWith("https://") //$NON-NLS-1$
|
||||||
monitor.setResult(desc);
|
&& !link.startsWith("ftp://")) { //$NON-NLS-1$
|
||||||
|
// Make the URL absolute by prepending the source
|
||||||
String link = getUrl();
|
Package pkg = getParentPackage();
|
||||||
if (!link.startsWith("http://") //$NON-NLS-1$
|
RepoSource src = pkg.getParentSource();
|
||||||
&& !link.startsWith("https://") //$NON-NLS-1$
|
if (src == null) {
|
||||||
&& !link.startsWith("ftp://")) { //$NON-NLS-1$
|
monitor.setResult("Internal error: no source for archive %1$s", name);
|
||||||
// Make the URL absolute by prepending the source
|
return null;
|
||||||
Package pkg = getParentPackage();
|
|
||||||
RepoSource src = pkg.getParentSource();
|
|
||||||
if (src == null) {
|
|
||||||
monitor.setResult("Internal error: no source for archive %1$s", name);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// take the URL to the repository.xml and remove the last component
|
|
||||||
// to get the base
|
|
||||||
String repoXml = src.getUrl();
|
|
||||||
int pos = repoXml.lastIndexOf('/');
|
|
||||||
String base = repoXml.substring(0, pos + 1);
|
|
||||||
|
|
||||||
link = base + link;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (forceHttp) {
|
// take the URL to the repository.xml and remove the last component
|
||||||
link = link.replaceAll("https://", "http://"); //$NON-NLS-1$ //$NON-NLS-2$
|
// to get the base
|
||||||
}
|
String repoXml = src.getUrl();
|
||||||
|
int pos = repoXml.lastIndexOf('/');
|
||||||
|
String base = repoXml.substring(0, pos + 1);
|
||||||
|
|
||||||
if (fetchUrl(tmpFile, link, desc, monitor)) {
|
link = base + link;
|
||||||
// Fetching was successful, don't delete the temp file here!
|
}
|
||||||
tmpFileToDelete = null;
|
|
||||||
|
if (forceHttp) {
|
||||||
|
link = link.replaceAll("https://", "http://"); //$NON-NLS-1$ //$NON-NLS-2$
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the basename of the file we're downloading, i.e. the last component
|
||||||
|
// of the URL
|
||||||
|
int pos = link.lastIndexOf('/');
|
||||||
|
String base = link.substring(pos + 1);
|
||||||
|
|
||||||
|
// Rather than create a real temp file in the system, we simply use our
|
||||||
|
// temp folder (in the SDK base folder) and use the archive name for the
|
||||||
|
// download. This allows us to reuse or continue downloads.
|
||||||
|
|
||||||
|
File tmpFile = new File(getTempFolder(osSdkRoot), base);
|
||||||
|
|
||||||
|
// if the file exists, check if its checksum & size. Use it if complete
|
||||||
|
if (tmpFile.exists()) {
|
||||||
|
if (tmpFile.length() == getSize() &&
|
||||||
|
fileChecksum(tmpFile, monitor).equalsIgnoreCase(getChecksum())) {
|
||||||
|
// File is good, let's use it.
|
||||||
return tmpFile;
|
return tmpFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (IOException e) {
|
// Existing file is either of different size or content.
|
||||||
|
// TODO: continue download when we support continue mode.
|
||||||
|
// Right now, let's simply remove the file and start over.
|
||||||
|
deleteFileOrFolder(tmpFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fetchUrl(tmpFile, link, desc, monitor)) {
|
||||||
|
// Fetching was successful, let's use this file.
|
||||||
|
return tmpFile;
|
||||||
|
} else {
|
||||||
|
// Delete the temp file if we aborted the download
|
||||||
|
// TODO: disable this when we want to support partial downloads!
|
||||||
|
deleteFileOrFolder(tmpFile);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes the SHA-1 checksum of the content of the given file.
|
||||||
|
* Returns an empty string on error (rather than null).
|
||||||
|
*/
|
||||||
|
private String fileChecksum(File tmpFile, ITaskMonitor monitor) {
|
||||||
|
InputStream is = null;
|
||||||
|
try {
|
||||||
|
is = new FileInputStream(tmpFile);
|
||||||
|
|
||||||
|
MessageDigest digester = getChecksumType().getMessageDigest();
|
||||||
|
|
||||||
|
byte[] buf = new byte[65536];
|
||||||
|
int n;
|
||||||
|
|
||||||
|
while ((n = is.read(buf)) >= 0) {
|
||||||
|
if (n > 0) {
|
||||||
|
digester.update(buf, 0, n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return getDigestChecksum(digester);
|
||||||
|
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
// The FNF message is just the URL. Make it a bit more useful.
|
||||||
|
monitor.setResult("File not found: %1$s", e.getMessage());
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
monitor.setResult(e.getMessage());
|
monitor.setResult(e.getMessage());
|
||||||
|
|
||||||
} finally {
|
} finally {
|
||||||
deleteFileOrFolder(tmpFileToDelete);
|
if (is != null) {
|
||||||
|
try {
|
||||||
|
is.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
// pass
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return ""; //$NON-NLS-1$
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the SHA-1 from a {@link MessageDigest} as an hex string
|
||||||
|
* that can be compared with {@link #getChecksum()}.
|
||||||
|
*/
|
||||||
|
private String getDigestChecksum(MessageDigest digester) {
|
||||||
|
int n;
|
||||||
|
// Create an hex string from the digest
|
||||||
|
byte[] digest = digester.digest();
|
||||||
|
n = digest.length;
|
||||||
|
String hex = "0123456789abcdef"; //$NON-NLS-1$
|
||||||
|
char[] hexDigest = new char[n * 2];
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
int b = digest[i] & 0x0FF;
|
||||||
|
hexDigest[i*2 + 0] = hex.charAt(b >>> 4);
|
||||||
|
hexDigest[i*2 + 1] = hex.charAt(b & 0x0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new String(hexDigest);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -554,18 +628,8 @@ public class Archive implements IDescription {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create an hex string from the digest
|
// Create an hex string from the digest
|
||||||
byte[] digest = digester.digest();
|
String actual = getDigestChecksum(digester);
|
||||||
n = digest.length;
|
|
||||||
String hex = "0123456789abcdef"; //$NON-NLS-1$
|
|
||||||
char[] hexDigest = new char[n * 2];
|
|
||||||
for (int i = 0; i < n; i++) {
|
|
||||||
int b = digest[i] & 0x0FF;
|
|
||||||
hexDigest[i*2 + 0] = hex.charAt(b >>> 4);
|
|
||||||
hexDigest[i*2 + 1] = hex.charAt(b & 0x0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
String expected = getChecksum();
|
String expected = getChecksum();
|
||||||
String actual = new String(hexDigest);
|
|
||||||
if (!actual.equalsIgnoreCase(expected)) {
|
if (!actual.equalsIgnoreCase(expected)) {
|
||||||
monitor.setResult("Download finished with wrong checksum. Expected %1$s, got %2$s.",
|
monitor.setResult("Download finished with wrong checksum. Expected %1$s, got %2$s.",
|
||||||
expected, actual);
|
expected, actual);
|
||||||
@@ -627,7 +691,7 @@ public class Archive implements IDescription {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
// Find a new temp folder that doesn't exist yet
|
// Find a new temp folder that doesn't exist yet
|
||||||
unzipDestFolder = findTempFolder(osSdkRoot, pkgKind, "new"); //$NON-NLS-1$
|
unzipDestFolder = createTempFolder(osSdkRoot, pkgKind, "new"); //$NON-NLS-1$
|
||||||
|
|
||||||
if (unzipDestFolder == null) {
|
if (unzipDestFolder == null) {
|
||||||
// this should not seriously happen.
|
// this should not seriously happen.
|
||||||
@@ -662,39 +726,72 @@ public class Archive implements IDescription {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Swap the old folder by the new one.
|
// Swap the old folder by the new one.
|
||||||
File renameFailedForDir = null;
|
// We have 2 "folder rename" (aka moves) to do.
|
||||||
if (destFolder.isDirectory()) {
|
// They must both succeed in the right order.
|
||||||
renamedDestFolder = findTempFolder(osSdkRoot, pkgKind, "old"); //$NON-NLS-1$
|
boolean move1done = false;
|
||||||
if (renamedDestFolder == null) {
|
boolean move2done = false;
|
||||||
// this should not seriously happen.
|
while (!move1done || !move2done) {
|
||||||
monitor.setResult("Failed to find a temp directory in %1$s.", osSdkRoot);
|
File renameFailedForDir = null;
|
||||||
|
|
||||||
|
// Case where the dest dir already exists
|
||||||
|
if (!move1done) {
|
||||||
|
if (destFolder.isDirectory()) {
|
||||||
|
// Create a new temp/old dir
|
||||||
|
if (renamedDestFolder == null) {
|
||||||
|
renamedDestFolder = createTempFolder(osSdkRoot, pkgKind, "old"); //$NON-NLS-1$
|
||||||
|
}
|
||||||
|
if (renamedDestFolder == null) {
|
||||||
|
// this should not seriously happen.
|
||||||
|
monitor.setResult("Failed to find a temp directory in %1$s.", osSdkRoot);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// try to move the current dest dir to the temp/old one
|
||||||
|
if (!destFolder.renameTo(renamedDestFolder)) {
|
||||||
|
monitor.setResult("Failed to rename directory %1$s to %2$s.",
|
||||||
|
destFolder.getPath(), renamedDestFolder.getPath());
|
||||||
|
renameFailedForDir = destFolder;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
move1done = (renameFailedForDir == null);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Case where there's no dest dir or we successfully moved it to temp/old
|
||||||
|
// We not try to move the temp/unzip to the dest dir
|
||||||
|
if (move1done && !move2done) {
|
||||||
|
if (renameFailedForDir == null && !unzipDestFolder.renameTo(destFolder)) {
|
||||||
|
monitor.setResult("Failed to rename directory %1$s to %2$s",
|
||||||
|
unzipDestFolder.getPath(), destFolder.getPath());
|
||||||
|
renameFailedForDir = unzipDestFolder;
|
||||||
|
}
|
||||||
|
|
||||||
|
move2done = (renameFailedForDir == null);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (renameFailedForDir != null) {
|
||||||
|
if (SdkConstants.CURRENT_PLATFORM == SdkConstants.PLATFORM_WINDOWS) {
|
||||||
|
|
||||||
|
String msg = String.format(
|
||||||
|
"-= Warning ! =-\n" +
|
||||||
|
"A folder failed to be renamed or moved. On Windows this " +
|
||||||
|
"typically means that a program is using that folder (for example " +
|
||||||
|
"Windows Explorer or your anti-virus software.)\n" +
|
||||||
|
"Please momentarily deactivate your anti-virus software.\n" +
|
||||||
|
"Please also close any running programs that may be accessing " +
|
||||||
|
"the directory '%1$s'.\n" +
|
||||||
|
"When ready, press YES to try again.",
|
||||||
|
renameFailedForDir.getPath());
|
||||||
|
|
||||||
|
if (monitor.displayPrompt("SDK Manager: failed to install", msg)) {
|
||||||
|
// loop, trying to rename the temp dir into the destination
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
if (!destFolder.renameTo(renamedDestFolder)) {
|
|
||||||
monitor.setResult("Failed to rename directory %1$s to %2$s",
|
|
||||||
destFolder.getPath(), renamedDestFolder.getPath());
|
|
||||||
renameFailedForDir = destFolder;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (renameFailedForDir == null && !unzipDestFolder.renameTo(destFolder)) {
|
|
||||||
monitor.setResult("Failed to rename directory %1$s to %2$s",
|
|
||||||
unzipDestFolder.getPath(), destFolder.getPath());
|
|
||||||
renameFailedForDir = unzipDestFolder;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (renameFailedForDir != null) {
|
|
||||||
if (SdkConstants.CURRENT_PLATFORM == SdkConstants.PLATFORM_WINDOWS) {
|
|
||||||
monitor.setResult(
|
|
||||||
"-= Warning ! =-\n" +
|
|
||||||
"A folder failed to be renamed or moved. On Windows this " +
|
|
||||||
"typically means that a program is using that folder (for example " +
|
|
||||||
"Windows Explorer.) Please close all running programs that may be " +
|
|
||||||
"locking the directory '%1$s' and try again.",
|
|
||||||
renameFailedForDir.getPath());
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
unzipDestFolder = null;
|
unzipDestFolder = null;
|
||||||
@@ -850,7 +947,7 @@ public class Archive implements IDescription {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finds a temp folder in the form of osBasePath/temp/prefix.suffixNNN.
|
* Creates a temp folder in the form of osBasePath/temp/prefix.suffixNNN.
|
||||||
* <p/>
|
* <p/>
|
||||||
* This operation is not atomic so there's no guarantee the folder can't get
|
* This operation is not atomic so there's no guarantee the folder can't get
|
||||||
* created in between. This is however unlikely and the caller can assume the
|
* created in between. This is however unlikely and the caller can assume the
|
||||||
@@ -859,8 +956,8 @@ public class Archive implements IDescription {
|
|||||||
* Returns null if no such folder can be found (e.g. if all candidates exist,
|
* Returns null if no such folder can be found (e.g. if all candidates exist,
|
||||||
* which is rather unlikely) or if the base temp folder cannot be created.
|
* which is rather unlikely) or if the base temp folder cannot be created.
|
||||||
*/
|
*/
|
||||||
private File findTempFolder(String osBasePath, String prefix, String suffix) {
|
private File createTempFolder(String osBasePath, String prefix, String suffix) {
|
||||||
File baseTempFolder = new File(osBasePath, "temp");
|
File baseTempFolder = getTempFolder(osBasePath);
|
||||||
|
|
||||||
if (!baseTempFolder.isDirectory()) {
|
if (!baseTempFolder.isDirectory()) {
|
||||||
if (!baseTempFolder.mkdirs()) {
|
if (!baseTempFolder.mkdirs()) {
|
||||||
@@ -878,6 +975,15 @@ public class Archive implements IDescription {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the temp folder used by the SDK Manager.
|
||||||
|
* This folder is always at osBasePath/temp.
|
||||||
|
*/
|
||||||
|
private File getTempFolder(String osBasePath) {
|
||||||
|
File baseTempFolder = new File(osBasePath, "temp"); //$NON-NLS-1$
|
||||||
|
return baseTempFolder;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deletes a file or a directory.
|
* Deletes a file or a directory.
|
||||||
* Directories are deleted recursively.
|
* Directories are deleted recursively.
|
||||||
|
|||||||
@@ -81,4 +81,17 @@ public interface ITaskMonitor {
|
|||||||
* tickCount must be 1 or more.
|
* tickCount must be 1 or more.
|
||||||
*/
|
*/
|
||||||
public ITaskMonitor createSubMonitor(int tickCount);
|
public ITaskMonitor createSubMonitor(int tickCount);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display a yes/no question dialog box.
|
||||||
|
*
|
||||||
|
* Implementations MUST allow this to be called from any thread, e.g. by
|
||||||
|
* making sure the dialog is opened synchronously in the ui thread.
|
||||||
|
*
|
||||||
|
* @param title The title of the dialog box
|
||||||
|
* @param message The error message
|
||||||
|
* @return true if YES was clicked.
|
||||||
|
*/
|
||||||
|
public boolean displayPrompt(final String title, final String message);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ package com.android.sdkuilib.internal.tasks;
|
|||||||
import com.android.sdklib.internal.repository.ITask;
|
import com.android.sdklib.internal.repository.ITask;
|
||||||
import com.android.sdklib.internal.repository.ITaskMonitor;
|
import com.android.sdklib.internal.repository.ITaskMonitor;
|
||||||
|
|
||||||
|
import org.eclipse.jface.dialogs.MessageDialog;
|
||||||
|
import org.eclipse.swt.widgets.Display;
|
||||||
import org.eclipse.swt.widgets.ProgressBar;
|
import org.eclipse.swt.widgets.ProgressBar;
|
||||||
import org.eclipse.swt.widgets.Shell;
|
import org.eclipse.swt.widgets.Shell;
|
||||||
|
|
||||||
@@ -142,6 +144,30 @@ public final class ProgressTask implements ITaskMonitor {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display a yes/no question dialog box.
|
||||||
|
*
|
||||||
|
* This implementation allow this to be called from any thread, it
|
||||||
|
* makes sure the dialog is opened synchronously in the ui thread.
|
||||||
|
*
|
||||||
|
* @param title The title of the dialog box
|
||||||
|
* @param message The error message
|
||||||
|
* @return true if YES was clicked.
|
||||||
|
*/
|
||||||
|
public boolean displayPrompt(final String title, final String message) {
|
||||||
|
final Shell shell = mDialog.getParent();
|
||||||
|
Display display = shell.getDisplay();
|
||||||
|
|
||||||
|
// we need to ask the user what he wants to do.
|
||||||
|
final boolean[] result = new boolean[] { false };
|
||||||
|
display.syncExec(new Runnable() {
|
||||||
|
public void run() {
|
||||||
|
result[0] = MessageDialog.openQuestion(shell, title, message);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return result[0];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a sub-monitor that will use up to tickCount on the progress bar.
|
* Creates a sub-monitor that will use up to tickCount on the progress bar.
|
||||||
* tickCount must be 1 or more.
|
* tickCount must be 1 or more.
|
||||||
@@ -222,6 +248,10 @@ public final class ProgressTask implements ITaskMonitor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean displayPrompt(String title, String message) {
|
||||||
|
return mRoot.displayPrompt(title, message);
|
||||||
|
}
|
||||||
|
|
||||||
public ITaskMonitor createSubMonitor(int tickCount) {
|
public ITaskMonitor createSubMonitor(int tickCount) {
|
||||||
assert mSubCoef > 0;
|
assert mSubCoef > 0;
|
||||||
assert tickCount > 0;
|
assert tickCount > 0;
|
||||||
|
|||||||
Reference in New Issue
Block a user