- Change Archive to have a "isLocal" mode.
- In local mode, Archive.getLocalOsPath gives the install folder.
- In remote mode, Archive.getUrl gives the download URL.
- Implement delete on local archive.
- Started refreshing all sources. Need to revamp the progress
dialog to share it accross methods first.
/**\r
* Creates a new platform package based on an actual {@link IAndroidTarget} (which\r
* {@link IAndroidTarget#isPlatform()} false) from the {@link SdkManager}.\r
- * This is used to list local SDK folders.\r
+ * This is used to list local SDK folders in which case there is one archive which\r
+ * URL is the actual target location.\r
*/\r
AddonPackage(IAndroidTarget target) {\r
super( null, //source\r
null, //descUrl\r
Os.getCurrentOs(), //archiveOs\r
Arch.getCurrentArch(), //archiveArch\r
- "", //archiveUrl //$NON-NLS-1$\r
- 0, //archiveSize\r
- null //archiveChecksum\r
+ target.getLocation() //archiveOsPath\r
);\r
\r
mApiLevel = target.getApiVersionNumber();\r
private final String mChecksum;\r
private final ChecksumType mChecksumType = ChecksumType.SHA1;\r
private final Package mPackage;\r
+ private final String mLocalOsPath;\r
+ private final boolean mIsLocal;\r
\r
/**\r
- * Creates a new archive.\r
+ * Creates a new remote archive.\r
*/\r
Archive(Package pkg, Os os, Arch arch, String url, long size, String checksum) {\r
mPackage = pkg;\r
mOs = os;\r
mArch = arch;\r
mUrl = url;\r
+ mLocalOsPath = null;\r
mSize = size;\r
mChecksum = checksum;\r
+ mIsLocal = false;\r
+ }\r
+\r
+ /**\r
+ * Creates a new local archive.\r
+ */\r
+ Archive(Package pkg, Os os, Arch arch, String localOsPath) {\r
+ mPackage = pkg;\r
+ mOs = os;\r
+ mArch = arch;\r
+ mUrl = null;\r
+ mLocalOsPath = localOsPath;\r
+ mSize = 0;\r
+ mChecksum = "";\r
+ mIsLocal = true;\r
+ }\r
+\r
+ /**\r
+ * Returns true if this is a locally installed archive.\r
+ * Returns false if this is a remote archive that needs to be downloaded.\r
+ */\r
+ public boolean isLocal() {\r
+ return mIsLocal;\r
}\r
\r
/**\r
\r
/**\r
* Returns the download archive URL, either absolute or relative to the repository xml.\r
- * For a local installed folder, an URL is frabricated from the folder path.\r
+ * Always return null for a local installed folder.\r
+ * @see #getLocalOsPath()\r
*/\r
public String getUrl() {\r
return mUrl;\r
}\r
\r
/**\r
+ * Returns the local OS folder where a local archive is installed.\r
+ * Always return null for remote archives.\r
+ * @see #getUrl()\r
+ */\r
+ public String getLocalOsPath() {\r
+ return mLocalOsPath;\r
+ }\r
+\r
+ /**\r
* Returns the archive {@link Os} enum.\r
* Can be null for a local installed folder on an unknown OS.\r
*/\r
}\r
\r
/**\r
+ * Delete the archive folder if this is a local archive.\r
+ */\r
+ public void deleteLocal() {\r
+ if (isLocal()) {\r
+ deleteFileOrFolder(new File(getLocalOsPath()));\r
+ }\r
+ }\r
+\r
+ /**\r
* Install this {@link Archive}s.\r
* The archive will be skipped if it is incompatible.\r
*\r
try {\r
String name = getParentPackage().getShortDescription();\r
\r
- // TODO: we should not see this test fail if we had the filter UI above.\r
+ if (isLocal()) {\r
+ // This should never happen.\r
+ monitor.setResult("Skipping already installed archive: %1$s for %2$s",\r
+ name,\r
+ getOsDescription());\r
+ return false;\r
+ }\r
+\r
if (!isCompatible()) {\r
monitor.setResult("Skipping incompatible archive: %1$s for %2$s",\r
name,\r
\r
/**\r
* Manually create a new package with one archive and the given attributes.\r
- * This is used to create packages from local directories.\r
+ * This is used to create packages from local directories in which case there must be\r
+ * one archive which URL is the actual target location.\r
*/\r
DocPackage(RepoSource source,\r
int apiLevel,\r
String descUrl,\r
Os archiveOs,\r
Arch archiveArch,\r
- String archiveUrl,\r
- long archiveSize,\r
- String archiveChecksum) {\r
+ String archiveOsPath) {\r
super(source,\r
revision,\r
license,\r
descUrl,\r
archiveOs,\r
archiveArch,\r
- archiveUrl,\r
- archiveSize,\r
- archiveChecksum);\r
+ archiveOsPath);\r
mApiLevel = apiLevel;\r
}\r
\r
import java.io.IOException;\r
import java.io.InputStream;\r
import java.io.StringReader;\r
-import java.net.MalformedURLException;\r
import java.util.ArrayList;\r
import java.util.HashSet;\r
import java.util.Set;\r
private Package[] mPackages;\r
\r
public LocalSdkParser() {\r
- // TODO Auto-generated constructor stub\r
+ // pass\r
}\r
\r
/**\r
null, //descUrl\r
Os.getCurrentOs(), //archiveOs\r
Arch.getCurrentArch(), //archiveArch\r
- "", //archiveUrl //$NON-NLS-1$\r
- 0, //archiveSize\r
- null //archiveChecksum\r
+ toolFolder.getPath() //archiveOsPath\r
);\r
}\r
\r
// Create a pkg if we don't have one yet.\r
\r
if (pkg == null) {\r
- String url = null;\r
- try {\r
- url = docFolder.toURI().toURL().toString();\r
- } catch (MalformedURLException e) {\r
- // ignore\r
- }\r
-\r
pkg = new DocPackage(\r
null, //source\r
0, //apiLevel\r
null, //descUrl\r
Os.getCurrentOs(), //archiveOs\r
Arch.getCurrentArch(), //archiveArch\r
- url, //archiveUrl\r
- 0, //archiveSize\r
- null //archiveChecksum\r
+ docFolder.getPath() //archiveOsPath\r
);\r
}\r
}\r
\r
/**\r
* Manually create a new package with one archive and the given attributes.\r
- * This is used to create packages from local directories.\r
+ * This is used to create packages from local directories in which case there must be\r
+ * one archive which URL is the actual target location.\r
*/\r
public Package(RepoSource source,\r
int revision,\r
String descUrl,\r
Os archiveOs,\r
Arch archiveArch,\r
- String archiveUrl,\r
- long archiveSize,\r
- String archiveChecksum) {\r
+ String archiveOsPath) {\r
mSource = source;\r
mRevision = revision;\r
mLicense = license;\r
mArchives[0] = new Archive(this,\r
archiveOs,\r
archiveArch,\r
- archiveUrl,\r
- archiveSize,\r
- archiveChecksum);\r
+ archiveOsPath);\r
}\r
\r
/**\r
/**\r
* Creates a new platform package based on an actual {@link IAndroidTarget} (which\r
* must have {@link IAndroidTarget#isPlatform()} true) from the {@link SdkManager}.\r
- * This is used to list local SDK folders.\r
+ * This is used to list local SDK folders in which case there is one archive which\r
+ * URL is the actual target location.\r
*/\r
PlatformPackage(IAndroidTarget target) {\r
super( null, //source\r
null, //descUrl\r
Os.getCurrentOs(), //archiveOs\r
Arch.getCurrentArch(), //archiveArch\r
- "", //archiveUrl //$NON-NLS-1$\r
- 0, //archiveSize\r
- null //archiveChecksum\r
+ target.getLocation() //archiveOsPath\r
);\r
\r
mApiLevel = target.getApiVersionNumber();\r
\r
/**\r
* Manually create a new package with one archive and the given attributes.\r
- * This is used to create packages from local directories.\r
+ * This is used to create packages from local directories in which case there must be\r
+ * one archive which URL is the actual target location.\r
*/\r
ToolPackage(RepoSource source,\r
int revision,\r
String descUrl,\r
Os archiveOs,\r
Arch archiveArch,\r
- String archiveUrl,\r
- long archiveSize,\r
- String archiveChecksum) {\r
+ String archiveOsPath) {\r
super(source,\r
revision,\r
license,\r
descUrl,\r
archiveOs,\r
archiveArch,\r
- archiveUrl,\r
- archiveSize,\r
- archiveChecksum);\r
+ archiveOsPath);\r
}\r
\r
/** Returns a short description for an {@link IDescription}. */\r
\r
package com.android.sdkuilib.internal.repository;\r
\r
+import com.android.sdklib.internal.repository.Archive;\r
import com.android.sdklib.internal.repository.IDescription;\r
import com.android.sdklib.internal.repository.ITask;\r
import com.android.sdklib.internal.repository.ITaskMonitor;\r
+import com.android.sdklib.internal.repository.Package;\r
\r
+import org.eclipse.jface.dialogs.MessageDialog;\r
import org.eclipse.jface.viewers.ISelection;\r
import org.eclipse.jface.viewers.IStructuredSelection;\r
import org.eclipse.jface.viewers.TableViewer;\r
import org.eclipse.swt.widgets.TableColumn;\r
import org.eclipse.swt.widgets.Text;\r
\r
+import java.io.File;\r
+\r
/*\r
* TODO list\r
* - select => update desc, enable update + delete, enable home page if url\r
*/\r
\r
public class LocalPackagesPage extends Composite {\r
- private UpdaterData mUpdaterData;\r
+\r
+ private final UpdaterWindowImpl mUpdaterWindow;\r
+ private final UpdaterData mUpdaterData;\r
\r
private Label mSdkLocLabel;\r
private Text mSdkLocText;\r
private Label mPlaceholder1;\r
private Button mDeleteButton;\r
private Label mPlaceholder2;\r
- private Button mHomePageButton;\r
+ private Button mRefreshButton;\r
private Label mDescriptionLabel;\r
\r
+\r
/**\r
* Create the composite.\r
* @param parent The parent of the composite.\r
* @param updaterData An instance of {@link UpdaterData}. If null, a local\r
* one will be allocated just to help with the SWT Designer.\r
+ * @param updaterWindow The parent window.\r
*/\r
- public LocalPackagesPage(Composite parent, UpdaterData updaterData) {\r
+ public LocalPackagesPage(Composite parent,\r
+ UpdaterData updaterData,\r
+ UpdaterWindowImpl updaterWindow) {\r
super(parent, SWT.BORDER);\r
+ mUpdaterWindow = updaterWindow;\r
\r
mUpdaterData = updaterData != null ? updaterData : new UpdaterData();\r
\r
mPlaceholder1.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, true, false, 1, 1));\r
\r
mDeleteButton = new Button(mContainerButtons, SWT.NONE);\r
+ mDeleteButton.addSelectionListener(new SelectionAdapter() {\r
+ @Override\r
+ public void widgetSelected(SelectionEvent e) {\r
+ onDeleteSelected(); //$hide$ (hide from SWT designer)\r
+ }\r
+ });\r
mDeleteButton.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, false, false, 1, 1));\r
mDeleteButton.setText("Delete...");\r
\r
mPlaceholder2 = new Label(mContainerButtons, SWT.NONE);\r
mPlaceholder2.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, true, false, 1, 1));\r
\r
- mHomePageButton = new Button(mContainerButtons, SWT.NONE);\r
- mHomePageButton.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));\r
- mHomePageButton.setText("Home Page...");\r
+ mRefreshButton = new Button(mContainerButtons, SWT.NONE);\r
+ mRefreshButton.addSelectionListener(new SelectionAdapter() {\r
+ @Override\r
+ public void widgetSelected(SelectionEvent e) {\r
+ onRefreshSelected(); //$hide$ (hide from SWT designer)\r
+ }\r
+ });\r
+ mRefreshButton.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));\r
+ mRefreshButton.setText("Refresh");\r
}\r
\r
private void createSdkLocation(Composite parent) {\r
}\r
\r
private void onUpdateInstalledPackage() {\r
- // TODO just a test, needs to be removed later.\r
- new ProgressTask(getShell(), "Test", new ITask() {\r
- public void run(ITaskMonitor monitor) {\r
- monitor.setDescription("Test");\r
- monitor.setProgressMax(100);\r
- int n = 0;\r
- int d = 1;\r
- while(!monitor.isCancelRequested()) {\r
- monitor.incProgress(d);\r
- n += d;\r
- if (n == 0 || n == 100) d = -d;\r
- try {\r
- Thread.sleep(5);\r
- } catch (InterruptedException e) {\r
- // ignore\r
+ if (mUpdaterWindow != null) {\r
+ mUpdaterWindow.updateAll();\r
+ }\r
+ }\r
+\r
+ private void onDeleteSelected() {\r
+ ISelection sel = mTableViewerPackages.getSelection();\r
+ if (sel instanceof IStructuredSelection) {\r
+ Object elem = ((IStructuredSelection) sel).getFirstElement();\r
+ if (elem instanceof Package) {\r
+\r
+ String title = "Delete SDK Package";\r
+ String error = null;\r
+\r
+ Package p = (Package) elem;\r
+ Archive[] archives = p.getArchives();\r
+ if (archives.length == 1 && archives[0] != null && archives[0].isLocal()) {\r
+ Archive archive = archives[0];\r
+ String osPath = archive.getLocalOsPath();\r
+\r
+ File dir = new File(osPath);\r
+ if (dir.isDirectory()) {\r
+ String msg = String.format("Are you sure you want to delete '%1$s' at '%2$s'? This cannot be undone.",\r
+ p.getShortDescription(), osPath);\r
+\r
+ if (MessageDialog.openQuestion(getShell(), title, msg)) {\r
+ archive.deleteLocal();\r
+\r
+ // refresh list\r
+ onRefreshSelected();\r
+ }\r
+ } else {\r
+ error = "Directory not found for this package";\r
}\r
+ } else {\r
+ error = "No local archive found for this package";\r
}\r
+\r
+ if (error != null) {\r
+ MessageDialog.openError(getShell(), title, error);\r
+ }\r
+\r
+ return;\r
}\r
- });\r
+ }\r
+ }\r
+\r
+ private void onRefreshSelected() {\r
+ if (mUpdaterWindow != null) {\r
+ mUpdaterWindow.scanLocalSdkFolders();\r
+ }\r
}\r
\r
// End of hiding from SWT Designer\r
mDescriptionLabel.setText("Line1\nLine2\nLine3");\r
\r
mAddSiteButton = new Button(parent, SWT.NONE);\r
+ mAddSiteButton.addSelectionListener(new SelectionAdapter() {\r
+ @Override\r
+ public void widgetSelected(SelectionEvent e) {\r
+ onAddSiteSelected(); //$hide$\r
+ }\r
+ });\r
mAddSiteButton.setText("Add Site...");\r
\r
mRemoveSiteButton = new Button(parent, SWT.NONE);\r
+ mRemoveSiteButton.addSelectionListener(new SelectionAdapter() {\r
+ @Override\r
+ public void widgetSelected(SelectionEvent e) {\r
+ onRemoveSiteSelected(); //$hide$\r
+ }\r
+ });\r
mRemoveSiteButton.setText("Delete Site...");\r
\r
mPlaceholder3 = new Label(parent, SWT.NONE);\r
mPlaceholder3.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true, false, 1, 1));\r
\r
mRefreshButton = new Button(parent, SWT.NONE);\r
+ mRefreshButton.addSelectionListener(new SelectionAdapter() {\r
+ @Override\r
+ public void widgetSelected(SelectionEvent e) {\r
+ onRefreshSelected(); //$hide$\r
+ }\r
+ });\r
mRefreshButton.setText("Refresh");\r
\r
mInstallSelectedButton = new Button(parent, SWT.NONE);\r
}\r
}\r
\r
- mUpdaterWindow.installArchives(archives);\r
+ if (mUpdaterWindow != null) {\r
+ mUpdaterWindow.installArchives(archives);\r
+ }\r
+ }\r
+\r
+ private void onAddSiteSelected() {\r
+ }\r
+\r
+ private void onRemoveSiteSelected() {\r
+ }\r
+\r
+ private void onRefreshSelected() {\r
+ if (mUpdaterWindow != null) {\r
+ mUpdaterWindow.refreshSources(false /*forceFetching*/);\r
+ }\r
}\r
\r
// End of hiding from SWT Designer\r
import com.android.sdklib.internal.repository.ITask;\r
import com.android.sdklib.internal.repository.ITaskMonitor;\r
import com.android.sdklib.internal.repository.RepoSource;\r
+import com.android.sdklib.internal.repository.RepoSources;\r
import com.android.sdklib.repository.SdkRepository;\r
\r
import org.eclipse.swt.SWT;\r
mStackLayout = new StackLayout();\r
mPagesRootComposite.setLayout(mStackLayout);\r
\r
- mLocalPackagePage = new LocalPackagesPage(mPagesRootComposite, mUpdaterData);\r
+ mLocalPackagePage = new LocalPackagesPage(mPagesRootComposite, mUpdaterData, this);\r
mRemotePackagesPage = new RemotePackagesPage(mPagesRootComposite, mUpdaterData, this);\r
mSashForm.setWeights(new int[] {150, 576});\r
}\r
/**\r
* Used to scan the local SDK folders the first time.\r
*/\r
- private void scanLocalSdkFolders() {\r
+ public void scanLocalSdkFolders() {\r
mUpdaterData.getLocalSdkAdapter().setSdkRoot(mUpdaterData.getOsSdkRoot());\r
\r
mLocalPackagePage.setInput(mUpdaterData.getLocalSdkAdapter());\r
});\r
}\r
\r
+ public void updateAll() {\r
+ refreshSources(true);\r
+\r
+ // TODO have refreshSources reuse the current progress task\r
+// mTaskFactory.start("Update Archives", new ITask() {\r
+// public void run(ITaskMonitor monitor) {\r
+// monitor.setProgressMax(3);\r
+//\r
+// monitor.setDescription("Refresh sources");\r
+// refreshSources(true);\r
+// }\r
+// });\r
+ }\r
+\r
+ /**\r
+ * Refresh sources\r
+ *\r
+ * @param forceFetching When true, load sources that haven't been loaded yet. When\r
+ * false, only refresh sources that have been loaded yet.\r
+ */\r
+ public void refreshSources(final boolean forceFetching) {\r
+ ArrayList<RepoSource> sources = mUpdaterData.getSources().getSources();\r
+ for (RepoSource source : sources) {\r
+ if (forceFetching || source.getPackages() != null) {\r
+ source.load(mTaskFactory);\r
+ }\r
+\r
+ }\r
+\r
+ // TODO have source.load reuse the current progress task\r
+// mTaskFactory.start("Refresh sources", new ITask() {\r
+// public void run(ITaskMonitor monitor) {\r
+// ArrayList<RepoSource> sources = mUpdaterData.getSources().getSources();\r
+// monitor.setProgressMax(sources.size());\r
+//\r
+// for (RepoSource source : sources) {\r
+// if (forceFetching || source.getPackages() != null) {\r
+// monitor.setDescription(String.format("Refresh %1$s",\r
+// source.getShortDescription()));\r
+// source.load(mTaskFactory);\r
+// }\r
+// monitor.incProgress(1);\r
+// }\r
+// }\r
+// });\r
+\r
+ }\r
+\r
// End of hiding from SWT Designer\r
//$hide<<$\r
}\r