SDK Updater: parse local packages, download and install remote packages.
The install phase is still work in progress. The local part needs to display descriptions. Buttons callback are generally not implemented yet.
This commit is contained in:
@@ -21,8 +21,11 @@ import com.android.sdklib.internal.repository.ITaskMonitor;
|
||||
|
||||
import org.eclipse.jface.viewers.TableViewer;
|
||||
import org.eclipse.swt.SWT;
|
||||
import org.eclipse.swt.events.ControlAdapter;
|
||||
import org.eclipse.swt.events.ControlEvent;
|
||||
import org.eclipse.swt.events.SelectionAdapter;
|
||||
import org.eclipse.swt.events.SelectionEvent;
|
||||
import org.eclipse.swt.graphics.Rectangle;
|
||||
import org.eclipse.swt.layout.GridData;
|
||||
import org.eclipse.swt.layout.GridLayout;
|
||||
import org.eclipse.swt.widgets.Button;
|
||||
@@ -44,25 +47,22 @@ import org.eclipse.swt.widgets.Text;
|
||||
* - refresh callback
|
||||
*/
|
||||
|
||||
public class InstalledPackagesPage extends Composite {
|
||||
public class LocalPackagesPage extends Composite {
|
||||
private UpdaterData mUpdaterData;
|
||||
|
||||
private Label mSdkLocLabel;
|
||||
private Text mSdkLocText;
|
||||
private Button mSdkLocBrowse;
|
||||
private Label mInstalledPkgLabel;
|
||||
private TableViewer mTableViewerInstPkg;
|
||||
private Table mTableInstPkg;
|
||||
private TableColumn mColumnInstSummary;
|
||||
private TableColumn mColumnInstApiLevel;
|
||||
private TableColumn mColumnInstRevision;
|
||||
private TableViewer mTableViewerPackages;
|
||||
private Table mTablePackages;
|
||||
private TableColumn mColumnPackages;
|
||||
private Group mDescriptionContainer;
|
||||
private Composite mInstButtons;
|
||||
private Button mInstUpdate;
|
||||
private Composite mContainerButtons;
|
||||
private Button mUpdateButton;
|
||||
private Label mPlaceholder1;
|
||||
private Button mInstDelete;
|
||||
private Button mDeleteButton;
|
||||
private Label mPlaceholder2;
|
||||
private Button mInstHomePage;
|
||||
private Button mHomePageButton;
|
||||
private Label mDescriptionLabel;
|
||||
|
||||
/**
|
||||
@@ -71,12 +71,13 @@ public class InstalledPackagesPage extends Composite {
|
||||
* @param updaterData An instance of {@link UpdaterData}. If null, a local
|
||||
* one will be allocated just to help with the SWT Designer.
|
||||
*/
|
||||
public InstalledPackagesPage(Composite parent, UpdaterData updaterData) {
|
||||
public LocalPackagesPage(Composite parent, UpdaterData updaterData) {
|
||||
super(parent, SWT.BORDER);
|
||||
|
||||
mUpdaterData = updaterData != null ? updaterData : new UpdaterData();
|
||||
|
||||
createContents(this);
|
||||
postCreate(); //$hide$
|
||||
}
|
||||
|
||||
private void createContents(Composite parent) {
|
||||
@@ -84,26 +85,14 @@ public class InstalledPackagesPage extends Composite {
|
||||
|
||||
createSdkLocation(parent);
|
||||
|
||||
mInstalledPkgLabel = new Label(parent, SWT.NONE);
|
||||
mInstalledPkgLabel.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 3, 1));
|
||||
mInstalledPkgLabel.setText("Installed Packages:");
|
||||
mTableViewerPackages = new TableViewer(parent, SWT.BORDER | SWT.FULL_SELECTION);
|
||||
mTablePackages = mTableViewerPackages.getTable();
|
||||
mTablePackages.setHeaderVisible(true);
|
||||
mTablePackages.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 3, 1));
|
||||
|
||||
mTableViewerInstPkg = new TableViewer(parent, SWT.BORDER | SWT.FULL_SELECTION);
|
||||
mTableInstPkg = mTableViewerInstPkg.getTable();
|
||||
mTableInstPkg.setHeaderVisible(true);
|
||||
mTableInstPkg.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 3, 1));
|
||||
|
||||
mColumnInstSummary = new TableColumn(mTableInstPkg, SWT.NONE);
|
||||
mColumnInstSummary.setWidth(377);
|
||||
mColumnInstSummary.setText("Summary");
|
||||
|
||||
mColumnInstApiLevel = new TableColumn(mTableInstPkg, SWT.NONE);
|
||||
mColumnInstApiLevel.setWidth(100);
|
||||
mColumnInstApiLevel.setText("API Level");
|
||||
|
||||
mColumnInstRevision = new TableColumn(mTableInstPkg, SWT.NONE);
|
||||
mColumnInstRevision.setWidth(100);
|
||||
mColumnInstRevision.setText("Revision");
|
||||
mColumnPackages = new TableColumn(mTablePackages, SWT.NONE);
|
||||
mColumnPackages.setWidth(377);
|
||||
mColumnPackages.setText("Installed Packages");
|
||||
|
||||
mDescriptionContainer = new Group(parent, SWT.NONE);
|
||||
mDescriptionContainer.setLayout(new GridLayout(1, false));
|
||||
@@ -114,32 +103,32 @@ public class InstalledPackagesPage extends Composite {
|
||||
mDescriptionLabel.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, true, 1, 1));
|
||||
mDescriptionLabel.setText("Line1\nLine2\nLine3");
|
||||
|
||||
mInstButtons = new Composite(parent, SWT.NONE);
|
||||
mInstButtons.setLayout(new GridLayout(5, false));
|
||||
mInstButtons.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 3, 1));
|
||||
mContainerButtons = new Composite(parent, SWT.NONE);
|
||||
mContainerButtons.setLayout(new GridLayout(5, false));
|
||||
mContainerButtons.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 3, 1));
|
||||
|
||||
mInstUpdate = new Button(mInstButtons, SWT.NONE);
|
||||
mInstUpdate.addSelectionListener(new SelectionAdapter() {
|
||||
mUpdateButton = new Button(mContainerButtons, SWT.NONE);
|
||||
mUpdateButton.addSelectionListener(new SelectionAdapter() {
|
||||
@Override
|
||||
public void widgetSelected(SelectionEvent e) {
|
||||
onUpdateInstalledPackage(); //$hide$ (hide from SWT designer)
|
||||
}
|
||||
});
|
||||
mInstUpdate.setText("Update...");
|
||||
mUpdateButton.setText("Update...");
|
||||
|
||||
mPlaceholder1 = new Label(mInstButtons, SWT.NONE);
|
||||
mPlaceholder1 = new Label(mContainerButtons, SWT.NONE);
|
||||
mPlaceholder1.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, true, false, 1, 1));
|
||||
|
||||
mInstDelete = new Button(mInstButtons, SWT.NONE);
|
||||
mInstDelete.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, false, false, 1, 1));
|
||||
mInstDelete.setText("Delete...");
|
||||
mDeleteButton = new Button(mContainerButtons, SWT.NONE);
|
||||
mDeleteButton.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, false, false, 1, 1));
|
||||
mDeleteButton.setText("Delete...");
|
||||
|
||||
mPlaceholder2 = new Label(mInstButtons, SWT.NONE);
|
||||
mPlaceholder2 = new Label(mContainerButtons, SWT.NONE);
|
||||
mPlaceholder2.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, true, false, 1, 1));
|
||||
|
||||
mInstHomePage = new Button(mInstButtons, SWT.NONE);
|
||||
mInstHomePage.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));
|
||||
mInstHomePage.setText("Home Page...");
|
||||
mHomePageButton = new Button(mContainerButtons, SWT.NONE);
|
||||
mHomePageButton.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));
|
||||
mHomePageButton.setText("Home Page...");
|
||||
}
|
||||
|
||||
private void createSdkLocation(Composite parent) {
|
||||
@@ -175,7 +164,36 @@ public class InstalledPackagesPage extends Composite {
|
||||
// Hide everything down-below from SWT designer
|
||||
//$hide>>$
|
||||
|
||||
private void postCreate() {
|
||||
adjustColumnsWidth();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds a listener to adjust the columns width when the parent is resized.
|
||||
* <p/>
|
||||
* If we need something more fancy, we might want to use this:
|
||||
* http://dev.eclipse.org/viewcvs/index.cgi/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/Snippet77.java?view=co
|
||||
*/
|
||||
private void adjustColumnsWidth() {
|
||||
// Add a listener to resize the column to the full width of the table
|
||||
mTablePackages.addControlListener(new ControlAdapter() {
|
||||
@Override
|
||||
public void controlResized(ControlEvent e) {
|
||||
Rectangle r = mTablePackages.getClientArea();
|
||||
mColumnPackages.setWidth(r.width);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void setInput(LocalSdkAdapter localSdkAdapter) {
|
||||
mTableViewerPackages.setLabelProvider( localSdkAdapter.getLabelProvider());
|
||||
mTableViewerPackages.setContentProvider(localSdkAdapter.getContentProvider());
|
||||
mTableViewerPackages.setInput(localSdkAdapter);
|
||||
}
|
||||
|
||||
protected void onUpdateInstalledPackage() {
|
||||
// TODO just a test, needs to be removed later.
|
||||
ProgressTask.start(getShell(), "Test", new ITask() {
|
||||
public void run(ITaskMonitor monitor) {
|
||||
monitor.setDescription("Test");
|
||||
@@ -189,6 +207,7 @@ public class InstalledPackagesPage extends Composite {
|
||||
try {
|
||||
Thread.sleep(5);
|
||||
} catch (InterruptedException e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* Copyright (C) 2009 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.sdkuilib.internal.repository;
|
||||
|
||||
import com.android.sdklib.internal.repository.IDescription;
|
||||
import com.android.sdklib.internal.repository.LocalSdkParser;
|
||||
import com.android.sdklib.internal.repository.Package;
|
||||
import com.android.sdklib.internal.repository.RepoSource;
|
||||
|
||||
import org.eclipse.jface.viewers.IContentProvider;
|
||||
import org.eclipse.jface.viewers.ILabelProvider;
|
||||
import org.eclipse.jface.viewers.IStructuredContentProvider;
|
||||
import org.eclipse.jface.viewers.LabelProvider;
|
||||
import org.eclipse.jface.viewers.Viewer;
|
||||
import org.eclipse.swt.graphics.Image;
|
||||
|
||||
/**
|
||||
* Table adapters to use the local SDK list.
|
||||
*/
|
||||
class LocalSdkAdapter {
|
||||
|
||||
private final LocalSdkParser mLocalSdkParser;
|
||||
private String mOsSdkRoot;
|
||||
|
||||
public LocalSdkAdapter(LocalSdkParser localSdkParser) {
|
||||
mLocalSdkParser = localSdkParser;
|
||||
}
|
||||
|
||||
public void setSdkRoot(String osSdkRoot) {
|
||||
mOsSdkRoot = osSdkRoot;
|
||||
mLocalSdkParser.clearPackages();
|
||||
}
|
||||
|
||||
public ILabelProvider getLabelProvider() {
|
||||
return new ViewerLabelProvider();
|
||||
}
|
||||
|
||||
|
||||
public IContentProvider getContentProvider() {
|
||||
return new TableContentProvider();
|
||||
}
|
||||
|
||||
// ------------
|
||||
|
||||
public static class ViewerLabelProvider extends LabelProvider {
|
||||
/** Returns null by default */
|
||||
@Override
|
||||
public Image getImage(Object element) {
|
||||
return super.getImage(element);
|
||||
}
|
||||
|
||||
/** Returns the toString of the element. */
|
||||
@Override
|
||||
public String getText(Object element) {
|
||||
if (element instanceof IDescription) {
|
||||
return ((IDescription) element).getShortDescription();
|
||||
}
|
||||
return super.getText(element);
|
||||
}
|
||||
}
|
||||
|
||||
// ------------
|
||||
|
||||
private static class TableContentProvider implements IStructuredContentProvider {
|
||||
|
||||
// Called when the viewer is disposed
|
||||
public void dispose() {
|
||||
// pass
|
||||
}
|
||||
|
||||
// Called when the input is set or changed on the provider
|
||||
public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
|
||||
// pass
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to collect the root elements for the given input.
|
||||
* The input here is a {@link LocalSdkAdapter} object, this returns an array
|
||||
* of {@link RepoSource}.
|
||||
*/
|
||||
public Object[] getElements(Object inputElement) {
|
||||
if (inputElement instanceof LocalSdkAdapter) {
|
||||
LocalSdkAdapter adapter = (LocalSdkAdapter) inputElement;
|
||||
LocalSdkParser parser = adapter.mLocalSdkParser;
|
||||
|
||||
Package[] packages = parser.getPackages();
|
||||
|
||||
if (packages == null) {
|
||||
// load on demand the first time
|
||||
packages = parser.parseSdk(adapter.mOsSdkRoot);
|
||||
}
|
||||
|
||||
if (packages != null) {
|
||||
return packages;
|
||||
}
|
||||
}
|
||||
|
||||
return new Object[0];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -152,11 +152,11 @@ class ProgressTask extends Dialog
|
||||
* Sets the description in the current task dialog.
|
||||
* This method can be invoke from a non-UI thread.
|
||||
*/
|
||||
public void setDescription(final String description) {
|
||||
public void setDescription(final String descriptionFormat, final Object...args) {
|
||||
mDialogShell.getDisplay().asyncExec(new Runnable() {
|
||||
public void run() {
|
||||
if (!mLabel.isDisposed()) {
|
||||
mLabel.setText(description);
|
||||
mLabel.setText(String.format(descriptionFormat, args));
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -166,14 +166,14 @@ class ProgressTask extends Dialog
|
||||
* Sets the description in the current task dialog.
|
||||
* This method can be invoke from a non-UI thread.
|
||||
*/
|
||||
public void setResult(final String result) {
|
||||
public void setResult(final String resultFormat, final Object...args) {
|
||||
mAutomaticallyCloseOnTaskCompletion = false;
|
||||
if (!mDialogShell.isDisposed()) {
|
||||
mDialogShell.getDisplay().asyncExec(new Runnable() {
|
||||
public void run() {
|
||||
if (!mResultText.isDisposed()) {
|
||||
mResultText.setVisible(true);
|
||||
mResultText.setText(result);
|
||||
mResultText.setText(String.format(resultFormat, args));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -17,9 +17,14 @@
|
||||
package com.android.sdkuilib.internal.repository;
|
||||
|
||||
|
||||
import com.android.sdklib.internal.repository.Archive;
|
||||
import com.android.sdklib.internal.repository.IDescription;
|
||||
|
||||
import org.eclipse.jface.viewers.CheckStateChangedEvent;
|
||||
import org.eclipse.jface.viewers.CheckboxTreeViewer;
|
||||
import org.eclipse.jface.viewers.DoubleClickEvent;
|
||||
import org.eclipse.jface.viewers.ICheckStateListener;
|
||||
import org.eclipse.jface.viewers.IDoubleClickListener;
|
||||
import org.eclipse.jface.viewers.ISelection;
|
||||
import org.eclipse.jface.viewers.ITreeSelection;
|
||||
import org.eclipse.swt.SWT;
|
||||
@@ -36,10 +41,8 @@ import org.eclipse.swt.widgets.Group;
|
||||
import org.eclipse.swt.widgets.Label;
|
||||
import org.eclipse.swt.widgets.Tree;
|
||||
import org.eclipse.swt.widgets.TreeColumn;
|
||||
import org.eclipse.jface.viewers.ICheckStateListener;
|
||||
import org.eclipse.jface.viewers.CheckStateChangedEvent;
|
||||
import org.eclipse.jface.viewers.IDoubleClickListener;
|
||||
import org.eclipse.jface.viewers.DoubleClickEvent;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/*
|
||||
* TODO list
|
||||
@@ -54,8 +57,9 @@ import org.eclipse.jface.viewers.DoubleClickEvent;
|
||||
* - install selected callback
|
||||
*/
|
||||
|
||||
public class AvailablePackagesPage extends Composite {
|
||||
public class RemotePackagesPage extends Composite {
|
||||
|
||||
private final UpdaterWindowImpl mUpdaterWindow;
|
||||
private final UpdaterData mUpdaterData;
|
||||
|
||||
private CheckboxTreeViewer mTreeViewerSources;
|
||||
@@ -70,14 +74,18 @@ public class AvailablePackagesPage extends Composite {
|
||||
private Label mDescriptionLabel;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Create the composite.
|
||||
* @param parent The parent of the composite.
|
||||
* @param updaterData An instance of {@link UpdaterData}. If null, a local
|
||||
* one will be allocated just to help with the SWT Designer.
|
||||
*/
|
||||
public AvailablePackagesPage(Composite parent, UpdaterData updaterData) {
|
||||
public RemotePackagesPage(UpdaterWindowImpl updaterWindow,
|
||||
Composite parent,
|
||||
UpdaterData updaterData) {
|
||||
super(parent, SWT.BORDER);
|
||||
mUpdaterWindow = updaterWindow;
|
||||
|
||||
mUpdaterData = updaterData != null ? updaterData : new UpdaterData();
|
||||
|
||||
@@ -91,16 +99,14 @@ public class AvailablePackagesPage extends Composite {
|
||||
mTreeViewerSources = new CheckboxTreeViewer(parent, SWT.BORDER);
|
||||
mTreeViewerSources.addDoubleClickListener(new IDoubleClickListener() {
|
||||
public void doubleClick(DoubleClickEvent event) {
|
||||
doTreeDoubleClick(event); //$hide$
|
||||
onTreeDoubleClick(event); //$hide$
|
||||
}
|
||||
});
|
||||
mTreeViewerSources.addCheckStateListener(new ICheckStateListener() {
|
||||
public void checkStateChanged(CheckStateChangedEvent event) {
|
||||
doTreeCeckStateChanged(event); //$hide$
|
||||
onTreeCheckStateChanged(event); //$hide$
|
||||
}
|
||||
});
|
||||
mTreeViewerSources.setContentProvider(mUpdaterData.getSources().getContentProvider());
|
||||
mTreeViewerSources.setLabelProvider(mUpdaterData.getSources().getLabelProvider());
|
||||
mTreeSources = mTreeViewerSources.getTree();
|
||||
mTreeSources.addSelectionListener(new SelectionAdapter() {
|
||||
@Override
|
||||
@@ -137,6 +143,12 @@ public class AvailablePackagesPage extends Composite {
|
||||
mRefreshButton.setText("Refresh");
|
||||
|
||||
mInstallSelectedButton = new Button(parent, SWT.NONE);
|
||||
mInstallSelectedButton.addSelectionListener(new SelectionAdapter() {
|
||||
@Override
|
||||
public void widgetSelected(SelectionEvent e) {
|
||||
onInstallSelectedArchives(); //$hide$
|
||||
}
|
||||
});
|
||||
mInstallSelectedButton.setText("Install Selected");
|
||||
}
|
||||
|
||||
@@ -171,7 +183,9 @@ public class AvailablePackagesPage extends Composite {
|
||||
});
|
||||
}
|
||||
|
||||
public void setInput(RepoSources sources) {
|
||||
public void setInput(RepoSourcesAdapter sources) {
|
||||
mTreeViewerSources.setContentProvider(sources.getContentProvider());
|
||||
mTreeViewerSources.setLabelProvider( sources.getLabelProvider());
|
||||
mTreeViewerSources.setInput(sources);
|
||||
onTreeSelected();
|
||||
}
|
||||
@@ -189,13 +203,27 @@ public class AvailablePackagesPage extends Composite {
|
||||
mDescriptionLabel.setText(""); //$NON-NLS1-$
|
||||
}
|
||||
|
||||
private void doTreeCeckStateChanged(CheckStateChangedEvent event) {
|
||||
private void onTreeCheckStateChanged(CheckStateChangedEvent event) {
|
||||
boolean b = event.getChecked();
|
||||
Object elem = event.getElement();
|
||||
Object elem = event.getElement(); // Will be Archive or Package or RepoSource
|
||||
Object src = event.getSource();
|
||||
// TODO
|
||||
}
|
||||
|
||||
private void doTreeDoubleClick(DoubleClickEvent event) {
|
||||
private void onTreeDoubleClick(DoubleClickEvent event) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
private void onInstallSelectedArchives() {
|
||||
|
||||
ArrayList<Archive> archives = new ArrayList<Archive>();
|
||||
for (Object element : mTreeViewerSources.getCheckedElements()) {
|
||||
if (element instanceof Archive) {
|
||||
archives.add((Archive) element);
|
||||
}
|
||||
}
|
||||
|
||||
mUpdaterWindow.installArchives(archives);
|
||||
}
|
||||
|
||||
// End of hiding from SWT Designer
|
||||
@@ -18,9 +18,9 @@ package com.android.sdkuilib.internal.repository;
|
||||
|
||||
import com.android.sdklib.internal.repository.Archive;
|
||||
import com.android.sdklib.internal.repository.IDescription;
|
||||
import com.android.sdklib.internal.repository.ITaskFactory;
|
||||
import com.android.sdklib.internal.repository.Package;
|
||||
import com.android.sdklib.internal.repository.RepoSource;
|
||||
import com.android.sdklib.internal.repository.RepoSources;
|
||||
|
||||
import org.eclipse.jface.viewers.IContentProvider;
|
||||
import org.eclipse.jface.viewers.ILabelProvider;
|
||||
@@ -29,27 +29,17 @@ import org.eclipse.jface.viewers.LabelProvider;
|
||||
import org.eclipse.jface.viewers.Viewer;
|
||||
import org.eclipse.swt.graphics.Image;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* A list of sdk-repository sources.
|
||||
*
|
||||
* This implementation is UI dependent.
|
||||
*/
|
||||
class RepoSources {
|
||||
class RepoSourcesAdapter {
|
||||
|
||||
private ArrayList<RepoSource> mSources = new ArrayList<RepoSource>();
|
||||
private ITaskFactory mTaskFactory;
|
||||
private final RepoSources mRepoSources;
|
||||
|
||||
public RepoSources() {
|
||||
}
|
||||
|
||||
public void setTaskFactory(ITaskFactory taskFactory) {
|
||||
mTaskFactory = taskFactory;
|
||||
}
|
||||
|
||||
public void add(RepoSource source) {
|
||||
mSources.add(source);
|
||||
public RepoSourcesAdapter(RepoSources repoSources) {
|
||||
mRepoSources = repoSources;
|
||||
}
|
||||
|
||||
public ILabelProvider getLabelProvider() {
|
||||
@@ -63,7 +53,7 @@ class RepoSources {
|
||||
|
||||
// ------------
|
||||
|
||||
public class ViewerLabelProvider extends LabelProvider {
|
||||
public static class ViewerLabelProvider extends LabelProvider {
|
||||
/** Returns null by default */
|
||||
@Override
|
||||
public Image getImage(Object element) {
|
||||
@@ -82,9 +72,9 @@ class RepoSources {
|
||||
|
||||
// ------------
|
||||
|
||||
private class TreeContentProvider implements ITreeContentProvider {
|
||||
private static class TreeContentProvider implements ITreeContentProvider {
|
||||
|
||||
private Object mInput;
|
||||
private RepoSourcesAdapter mInput;
|
||||
|
||||
// Called when the viewer is disposed
|
||||
public void dispose() {
|
||||
@@ -93,13 +83,14 @@ class RepoSources {
|
||||
|
||||
// Called when the input is set or changed on the provider
|
||||
public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
|
||||
mInput = newInput;
|
||||
assert newInput == null || newInput instanceof RepoSourcesAdapter;
|
||||
mInput = (RepoSourcesAdapter) newInput;
|
||||
// pass
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to collect the root elements for the given input.
|
||||
* The input here is a {@link RepoSources} object, this returns an array
|
||||
* The input here is a {@link RepoSourcesAdapter} object, this returns an array
|
||||
* of {@link RepoSource}.
|
||||
*/
|
||||
public Object[] getElements(Object inputElement) {
|
||||
@@ -110,20 +101,20 @@ class RepoSources {
|
||||
* Get the children of the given parent. This is requested on-demand as
|
||||
* nodes are expanded.
|
||||
*
|
||||
* For a {@link RepoSources} object, returns an array of {@link RepoSource}s.
|
||||
* For a {@link RepoSourcesAdapter} object, returns an array of {@link RepoSource}s.
|
||||
* For a {@link RepoSource}, returns an array of {@link Package}s.
|
||||
* For a {@link Package}, returns an array of {@link Archive}s.
|
||||
*/
|
||||
public Object[] getChildren(Object parentElement) {
|
||||
if (parentElement instanceof RepoSources) {
|
||||
return ((RepoSources) parentElement).mSources.toArray();
|
||||
if (parentElement instanceof RepoSourcesAdapter) {
|
||||
return ((RepoSourcesAdapter) parentElement).mRepoSources.getSources().toArray();
|
||||
|
||||
} else if (parentElement instanceof RepoSource) {
|
||||
RepoSource source = (RepoSource) parentElement;
|
||||
Package[] packages = source.getPackages();
|
||||
|
||||
if (packages == null) {
|
||||
source.load(mTaskFactory);
|
||||
source.load(mInput.mRepoSources.getTaskFactory());
|
||||
packages = source.getPackages();
|
||||
}
|
||||
if (packages != null) {
|
||||
@@ -134,17 +125,20 @@ class RepoSources {
|
||||
return ((Package) parentElement).getArchives();
|
||||
}
|
||||
|
||||
|
||||
return new Object[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the parent of a given element.
|
||||
* The input {@link RepoSources} is the parent of all {@link RepoSource} elements.
|
||||
* The input {@link RepoSourcesAdapter} is the parent of all {@link RepoSource} elements.
|
||||
*/
|
||||
public Object getParent(Object element) {
|
||||
|
||||
if (element instanceof RepoSource) {
|
||||
return mInput;
|
||||
|
||||
} else if (element instanceof Package) {
|
||||
return ((Package) element).getParentSource();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -152,7 +146,8 @@ class RepoSources {
|
||||
/**
|
||||
* Returns true if a given element has children, which is used to display a
|
||||
* "+/expand" box next to the tree node.
|
||||
* All {@link RepoSource} are expandable, whether they actually have any childre or not.
|
||||
* All {@link RepoSource} and {@link Package} are expandable, whether they actually
|
||||
* have any children or not.
|
||||
*/
|
||||
public boolean hasChildren(Object element) {
|
||||
return element instanceof RepoSource || element instanceof Package;
|
||||
@@ -16,13 +16,21 @@
|
||||
|
||||
package com.android.sdkuilib.internal.repository;
|
||||
|
||||
import com.android.sdklib.internal.repository.LocalSdkParser;
|
||||
import com.android.sdklib.internal.repository.RepoSources;
|
||||
|
||||
/**
|
||||
* Data shared between {@link UpdaterWindowImpl} and its pages.
|
||||
*/
|
||||
class UpdaterData {
|
||||
private String mOsSdkRoot;
|
||||
private boolean mUserCanChangeSdkRoot;
|
||||
private RepoSources mSources = new RepoSources();
|
||||
|
||||
private final LocalSdkParser mLocalSdkParser = new LocalSdkParser();
|
||||
private final RepoSources mSources = new RepoSources();
|
||||
|
||||
private final LocalSdkAdapter mLocalSdkAdapter = new LocalSdkAdapter(mLocalSdkParser);
|
||||
private final RepoSourcesAdapter mSourcesAdapter = new RepoSourcesAdapter(mSources);
|
||||
|
||||
public void setOsSdkRoot(String osSdkRoot) {
|
||||
mOsSdkRoot = osSdkRoot;
|
||||
@@ -40,12 +48,20 @@ class UpdaterData {
|
||||
return mUserCanChangeSdkRoot;
|
||||
}
|
||||
|
||||
public void setSources(RepoSources sources) {
|
||||
mSources = sources;
|
||||
}
|
||||
|
||||
public RepoSources getSources() {
|
||||
return mSources;
|
||||
}
|
||||
|
||||
public RepoSourcesAdapter getSourcesAdapter() {
|
||||
return mSourcesAdapter;
|
||||
}
|
||||
|
||||
public LocalSdkParser getLocalSdkParser() {
|
||||
return mLocalSdkParser;
|
||||
}
|
||||
|
||||
public LocalSdkAdapter getLocalSdkAdapter() {
|
||||
return mLocalSdkAdapter;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -17,7 +17,12 @@
|
||||
package com.android.sdkuilib.internal.repository;
|
||||
|
||||
|
||||
import com.android.sdklib.internal.repository.Archive;
|
||||
import com.android.sdklib.internal.repository.ITask;
|
||||
import com.android.sdklib.internal.repository.ITaskMonitor;
|
||||
import com.android.sdklib.internal.repository.Package;
|
||||
import com.android.sdklib.internal.repository.RepoSource;
|
||||
import com.android.sdklib.repository.SdkRepository;
|
||||
|
||||
import org.eclipse.swt.SWT;
|
||||
import org.eclipse.swt.SWTException;
|
||||
@@ -36,14 +41,22 @@ import org.eclipse.swt.widgets.Display;
|
||||
import org.eclipse.swt.widgets.List;
|
||||
import org.eclipse.swt.widgets.Shell;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.security.MessageDigest;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* This is the private implementation of the UpdateWindow.
|
||||
*/
|
||||
public class UpdaterWindowImpl {
|
||||
|
||||
private static final int NUM_FETCH_URL_MONITOR_INC = 10;
|
||||
|
||||
private final UpdaterData mUpdaterData = new UpdaterData();
|
||||
private ArrayList<Composite> mPages = new ArrayList<Composite>();
|
||||
private boolean mInternalPageChange;
|
||||
@@ -54,10 +67,11 @@ public class UpdaterWindowImpl {
|
||||
private SashForm mSashForm;
|
||||
private List mPageList;
|
||||
private Composite mPagesRootComposite;
|
||||
private InstalledPackagesPage mInstalledPackagePage;
|
||||
private AvailablePackagesPage mAvailablePackagesPage;
|
||||
private LocalPackagesPage mLocalPackagePage;
|
||||
private RemotePackagesPage mRemotePackagesPage;
|
||||
private StackLayout mStackLayout;
|
||||
private Image mIconImage;
|
||||
private ProgressTaskFactory mTaskFactory;
|
||||
|
||||
public UpdaterWindowImpl(String osSdkRoot, boolean userCanChangeSdkRoot) {
|
||||
mUpdaterData.setOsSdkRoot(osSdkRoot);
|
||||
@@ -114,8 +128,8 @@ public class UpdaterWindowImpl {
|
||||
mStackLayout = new StackLayout();
|
||||
mPagesRootComposite.setLayout(mStackLayout);
|
||||
|
||||
mInstalledPackagePage = new InstalledPackagesPage(mPagesRootComposite, mUpdaterData);
|
||||
mAvailablePackagesPage = new AvailablePackagesPage(mPagesRootComposite, mUpdaterData);
|
||||
mLocalPackagePage = new LocalPackagesPage(mPagesRootComposite, mUpdaterData);
|
||||
mRemotePackagesPage = new RemotePackagesPage(this, mPagesRootComposite, mUpdaterData);
|
||||
mSashForm.setWeights(new int[] {150, 576});
|
||||
}
|
||||
|
||||
@@ -158,8 +172,10 @@ public class UpdaterWindowImpl {
|
||||
* Once the UI has been created, initialize the content
|
||||
*/
|
||||
private void firstInit() {
|
||||
addPage(mInstalledPackagePage, "Installed Packages");
|
||||
addPage(mAvailablePackagesPage, "Available Packages");
|
||||
mTaskFactory = new ProgressTaskFactory(getShell());
|
||||
|
||||
addPage(mLocalPackagePage, "Installed Packages");
|
||||
addPage(mRemotePackagesPage, "Available Packages");
|
||||
displayPage(0);
|
||||
mPageList.setSelection(0);
|
||||
|
||||
@@ -199,23 +215,214 @@ public class UpdaterWindowImpl {
|
||||
}
|
||||
|
||||
private void setupSources() {
|
||||
mUpdaterData.getSources().setTaskFactory(new ProgressTaskFactory(getShell()));
|
||||
mUpdaterData.getSources().setTaskFactory(mTaskFactory);
|
||||
|
||||
mUpdaterData.getSources().add(new RepoSource(
|
||||
"https://dl.google.com/android/eclipse/repository/index.xml", //$NON-NLS-1$
|
||||
false /* addonOnly */));
|
||||
mUpdaterData.getSources().add(
|
||||
new RepoSource(SdkRepository.URL_GOOGLE_SDK_REPO_SITE, false /* addonOnly */));
|
||||
|
||||
String url = System.getenv("TEMP_SDK_URL"); // TODO STOPSHIP temporary remove before shipping
|
||||
if (url != null) {
|
||||
mUpdaterData.getSources().add(new RepoSource(url, false /* addonOnly */));
|
||||
}
|
||||
|
||||
mAvailablePackagesPage.setInput(mUpdaterData.getSources());
|
||||
mRemotePackagesPage.setInput(mUpdaterData.getSourcesAdapter());
|
||||
}
|
||||
|
||||
private void scanLocalSdkFolders() {
|
||||
// TODO Auto-generated method stub
|
||||
mUpdaterData.getLocalSdkAdapter().setSdkRoot(mUpdaterData.getOsSdkRoot());
|
||||
|
||||
mLocalPackagePage.setInput(mUpdaterData.getLocalSdkAdapter());
|
||||
}
|
||||
|
||||
public void installArchives(final Collection<Archive> archives) {
|
||||
// TODO move most parts to SdkLib, maybe as part of Archive, making archives self-installing.
|
||||
mTaskFactory.start("Installing Archives", new ITask() {
|
||||
public void run(ITaskMonitor monitor) {
|
||||
|
||||
monitor.setProgressMax(archives.size() * (NUM_FETCH_URL_MONITOR_INC + 3));
|
||||
monitor.setDescription("Preparing to install archives");
|
||||
|
||||
int num_installed = 0;
|
||||
for (Archive archive : archives) {
|
||||
|
||||
if (!archive.isCompatible()) {
|
||||
monitor.setResult("Skipping incompatible archive: %1$s",
|
||||
archive.getShortDescription());
|
||||
monitor.incProgress(3);
|
||||
continue;
|
||||
}
|
||||
|
||||
File archiveFile = null;
|
||||
try {
|
||||
archiveFile = downloadArchive(archive, monitor);
|
||||
monitor.incProgress(1);
|
||||
if (archiveFile != null) {
|
||||
if (installArchive(archive, archiveFile, monitor)) {
|
||||
num_installed++;
|
||||
}
|
||||
}
|
||||
monitor.incProgress(1);
|
||||
} finally {
|
||||
if (archiveFile != null) {
|
||||
if (!archiveFile.delete()) {
|
||||
archiveFile.deleteOnExit();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (num_installed == 0) {
|
||||
monitor.setResult("Nothing was installed.");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Downloads an archive and returns the temp file with it.
|
||||
* Caller is responsible with deleting the temp file when done.
|
||||
*/
|
||||
private File downloadArchive(Archive archive, ITaskMonitor monitor) {
|
||||
|
||||
try {
|
||||
File tmpFile = File.createTempFile("sdkupload", "bin"); //$NON-NLS-1$ //$NON-NLS-2$
|
||||
|
||||
monitor.setDescription("Downloading %1$s", archive.getShortDescription());
|
||||
|
||||
String link = archive.getUrl();
|
||||
if (!link.startsWith("http://") //$NON-NLS-1$
|
||||
&& !link.startsWith("https://") //$NON-NLS-1$
|
||||
&& !link.startsWith("ftp://")) { //$NON-NLS-1$
|
||||
// Make the URL absolute by prepending the source
|
||||
Package pkg = archive.getParentPackage();
|
||||
RepoSource src = pkg.getParentSource();
|
||||
if (src == null) {
|
||||
monitor.setResult("Internal error: no source for archive %1$s",
|
||||
archive.getShortDescription());
|
||||
return null;
|
||||
}
|
||||
|
||||
String base = src.getUrl();
|
||||
if (!base.endsWith("/") && !link.startsWith("/")) { //$NON-NLS-1$ //$NON-NLS-2$
|
||||
base += "/"; //$NON-NLS-1$
|
||||
}
|
||||
|
||||
link = base + link;
|
||||
}
|
||||
|
||||
fetchUrl(tmpFile, archive, link, monitor);
|
||||
|
||||
} catch (IOException e) {
|
||||
monitor.setResult(e.getMessage());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Actually performs the download.
|
||||
* Also computes the SHA1 of the file on the fly.
|
||||
* <p/>
|
||||
* Success is defined as downloading as many bytes as was expected and having the same
|
||||
* SHA1 as expected. Returns true on success or false if any of those checks fail.
|
||||
* <p/>
|
||||
* Increments the monitor by {@link #NUM_FETCH_URL_MONITOR_INC} (which is 10).
|
||||
*/
|
||||
private boolean fetchUrl(File tmpFile, Archive archive, String urlString, ITaskMonitor monitor) {
|
||||
URL url;
|
||||
|
||||
FileOutputStream os = null;
|
||||
InputStream is = null;
|
||||
try {
|
||||
url = new URL(urlString);
|
||||
is = url.openStream();
|
||||
os = new FileOutputStream(tmpFile);
|
||||
|
||||
MessageDigest digester = archive.getChecksumType().getMessageDigest();
|
||||
|
||||
byte[] buf = new byte[65536];
|
||||
int n;
|
||||
|
||||
long total = 0;
|
||||
long size = archive.getSize();
|
||||
long inc = size / NUM_FETCH_URL_MONITOR_INC;
|
||||
long next_inc = inc;
|
||||
|
||||
while ((n = is.read(buf)) >= 0) {
|
||||
if (n > 0) {
|
||||
os.write(buf, 0, n);
|
||||
digester.update(buf, 0, n);
|
||||
}
|
||||
|
||||
total += n;
|
||||
if (total >= next_inc) {
|
||||
monitor.incProgress(1);
|
||||
next_inc += inc;
|
||||
}
|
||||
|
||||
if (monitor.cancelRequested()) {
|
||||
monitor.setResult("Download aborted by user at %1$d bytes.", total);
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (total != size) {
|
||||
monitor.setResult("Download finished with wrong size. Expected %1$d bytes, got %2$d bytes.",
|
||||
size, total);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 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++) {
|
||||
byte b = digest[i];
|
||||
hexDigest[i*2 + 0] = hex.charAt(b >>> 4);
|
||||
hexDigest[i*2 + 1] = hex.charAt(b & 0x0f);
|
||||
}
|
||||
|
||||
String expected = archive.getChecksum();
|
||||
String actual = new String(hexDigest);
|
||||
if (!actual.equalsIgnoreCase(expected)) {
|
||||
monitor.setResult("Download finished with wrong checksum. Expected %1$s, got %2$s.",
|
||||
expected, actual);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
} catch (Exception e) {
|
||||
monitor.setResult(e.getMessage());
|
||||
|
||||
} finally {
|
||||
if (os != null) {
|
||||
try {
|
||||
os.close();
|
||||
} catch (IOException e) {
|
||||
// pass
|
||||
}
|
||||
}
|
||||
|
||||
if (is != null) {
|
||||
try {
|
||||
is.close();
|
||||
} catch (IOException e) {
|
||||
// pass
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean installArchive(Archive archive, File archiveFile, ITaskMonitor monitor) {
|
||||
monitor.setDescription("Installing %1$s", archive.getShortDescription());
|
||||
|
||||
File destFolder = archive.getParentPackage().getInstallFolder(mUpdaterData.getOsSdkRoot());
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// End of hiding from SWT Designer
|
||||
|
||||
Reference in New Issue
Block a user