OSDN Git Service

SDK Updater: Revamp ITaskMonitor to be able to create sub monitors.
authorRaphael <raphael@google.com>
Thu, 11 Jun 2009 01:33:45 +0000 (18:33 -0700)
committerRaphael <raphael@google.com>
Thu, 11 Jun 2009 01:33:45 +0000 (18:33 -0700)
This allows us to nest tasks that share the same
progress task dialog..

sdkmanager/app/src/com/android/sdkmanager/internal/repository/SettingsPage.java
sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ITaskMonitor.java
sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/RepoSource.java
sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/ProgressDialog.java
sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/ProgressTask.java
sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/RemotePackagesPage.java
sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/RepoSourcesAdapter.java
sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterWindowImpl.java

index 77e6a54..b07617f 100755 (executable)
@@ -21,6 +21,8 @@ import com.android.prefs.AndroidLocation.AndroidLocationException;
 import com.android.sdkuilib.internal.repository.ISettingsPage;\r
 \r
 import org.eclipse.swt.SWT;\r
+import org.eclipse.swt.events.SelectionAdapter;\r
+import org.eclipse.swt.events.SelectionEvent;\r
 import org.eclipse.swt.layout.GridData;\r
 import org.eclipse.swt.layout.GridLayout;\r
 import org.eclipse.swt.widgets.Button;\r
@@ -28,16 +30,11 @@ import org.eclipse.swt.widgets.Composite;
 import org.eclipse.swt.widgets.Group;\r
 import org.eclipse.swt.widgets.Label;\r
 import org.eclipse.swt.widgets.Text;\r
-import org.eclipse.swt.events.SelectionAdapter;\r
-import org.eclipse.swt.events.SelectionEvent;\r
 \r
 import java.io.File;\r
 import java.io.FileInputStream;\r
 import java.io.FileOutputStream;\r
-import java.io.FileReader;\r
-import java.io.FileWriter;\r
 import java.io.IOException;\r
-import java.io.InputStream;\r
 import java.net.URL;\r
 import java.util.Properties;\r
 \r
index c8f7c06..85596ba 100755 (executable)
@@ -45,18 +45,27 @@ public interface ITaskMonitor {
     /**\r
      * Sets the max value of the progress bar.\r
      * This method can be invoked from a non-UI thread.\r
+     *\r
+     * This method MUST be invoked once before using {@link #incProgress(int)} or\r
+     * {@link #getProgress()} or {@link #createSubMonitor(int)}. Callers are\r
+     * discouraged from using more than once -- implementations can either discard\r
+     * the next calls or behave incoherently.\r
      */\r
     public void setProgressMax(int max);\r
 \r
     /**\r
      * Increments the current value of the progress bar.\r
      * This method can be invoked from a non-UI thread.\r
+     *\r
+     * Callers MUST use setProgressMax before using this method.\r
      */\r
     public void incProgress(int delta);\r
 \r
     /**\r
      * Returns the current value of the progress bar,\r
      * between 0 and up to {@link #setProgressMax(int)} - 1.\r
+     *\r
+     * Callers MUST use setProgressMax before using this method.\r
      */\r
     public int getProgress();\r
 \r
@@ -66,4 +75,10 @@ public interface ITaskMonitor {
      * as possible.\r
      */\r
     public boolean isCancelRequested();\r
+\r
+    /**\r
+     * Creates a sub-monitor that will use up to tickCount on the progress bar.\r
+     * tickCount must be 1 or more.\r
+     */\r
+    public ITaskMonitor createSubMonitor(int tickCount);\r
 }\r
index d8ab806..c58a214 100755 (executable)
@@ -68,7 +68,7 @@ public class RepoSource implements IDescription {
     }\r
 \r
     /**\r
-     * Returns the list of known packages found by the last call to {@link #load(ITaskFactory)}.\r
+     * Returns the list of known packages found by the last call to {@link #load(ITaskMonitor)}.\r
      * This is null when the source hasn't been loaded yet.\r
      */\r
     public Package[] getPackages() {\r
@@ -77,7 +77,7 @@ public class RepoSource implements IDescription {
 \r
     /**\r
      * Clear the internal packages list. After this call, {@link #getPackages()} will return\r
-     * null till {@link #load(ITaskFactory)} is called.\r
+     * null till {@link #load(ITaskMonitor)} is called.\r
      */\r
     public void clearPackages() {\r
         mPackages = null;\r
@@ -94,47 +94,43 @@ public class RepoSource implements IDescription {
     /**\r
      * Tries to fetch the repository index for the given URL.\r
      */\r
-    public void load(ITaskFactory taskFactory) {\r
+    public void load(ITaskMonitor monitor) {\r
 \r
-        taskFactory.start("Init SDK Updater", new ITask() {\r
-            public void run(ITaskMonitor monitor) {\r
-                monitor.setProgressMax(4);\r
+        monitor.setProgressMax(4);\r
 \r
-                setDefaultDescription();\r
+        setDefaultDescription();\r
 \r
-                monitor.setDescription("Fetching %1$s", mUrl);\r
-                monitor.incProgress(1);\r
+        monitor.setDescription("Fetching %1$s", mUrl);\r
+        monitor.incProgress(1);\r
 \r
-                String xml = fetchUrl(mUrl, monitor);\r
+        String xml = fetchUrl(mUrl, monitor);\r
 \r
-                if (xml == null) {\r
-                    mDescription += String.format("\nFailed to fetch URL %1$s", mUrl);\r
-                    return;\r
-                }\r
+        if (xml == null) {\r
+            mDescription += String.format("\nFailed to fetch URL %1$s", mUrl);\r
+            return;\r
+        }\r
 \r
-                monitor.setDescription("Validate XML");\r
-                monitor.incProgress(1);\r
+        monitor.setDescription("Validate XML");\r
+        monitor.incProgress(1);\r
 \r
-                if (!validateXml(xml, monitor)) {\r
-                    mDescription += String.format("\nFailed to validate XML at %1$s", mUrl);\r
-                    return;\r
-                }\r
+        if (!validateXml(xml, monitor)) {\r
+            mDescription += String.format("\nFailed to validate XML at %1$s", mUrl);\r
+            return;\r
+        }\r
 \r
-                monitor.setDescription("Parse XML");\r
-                monitor.incProgress(1);\r
-                parsePackages(xml, monitor);\r
-                if (mPackages.length == 0) {\r
-                    mDescription += "\nNo packages found.";\r
-                } else if (mPackages.length == 1) {\r
-                    mDescription += "\nOne package found.";\r
-                } else {\r
-                    mDescription += String.format("\n%1$d packages found.", mPackages.length);\r
-                }\r
+        monitor.setDescription("Parse XML");\r
+        monitor.incProgress(1);\r
+        parsePackages(xml, monitor);\r
+        if (mPackages.length == 0) {\r
+            mDescription += "\nNo packages found.";\r
+        } else if (mPackages.length == 1) {\r
+            mDescription += "\nOne package found.";\r
+        } else {\r
+            mDescription += String.format("\n%1$d packages found.", mPackages.length);\r
+        }\r
 \r
-                // done\r
-                monitor.incProgress(1);\r
-            }\r
-        });\r
+        // done\r
+        monitor.incProgress(1);\r
     }\r
 \r
     private void setDefaultDescription() {\r
index a5cb86f..ca847c3 100755 (executable)
@@ -320,15 +320,15 @@ final class ProgressDialog extends Dialog {
     }\r
 \r
     /**\r
-     * Increments the current value of the progress bar.\r
+     * Sets the current value of the progress bar.\r
      * This method can be invoked from a non-UI thread.\r
      */\r
-    public void incProgress(final int delta) {\r
+    public void setProgress(final int value) {\r
         if (!mDialogShell.isDisposed()) {\r
             mDialogShell.getDisplay().syncExec(new Runnable() {\r
                 public void run() {\r
                     if (!mProgressBar.isDisposed()) {\r
-                        mProgressBar.setSelection(mProgressBar.getSelection() + delta);\r
+                        mProgressBar.setSelection(value);\r
                     }\r
                 }\r
             });\r
index 709cce0..1ec7287 100755 (executable)
@@ -28,8 +28,12 @@ import org.eclipse.swt.widgets.Shell;
  */\r
 class ProgressTask implements ITaskMonitor {\r
 \r
-    private ProgressDialog mDialog;\r
+    private static final double MAX_COUNT = 10000.0;\r
+\r
+    private final ProgressDialog mDialog;\r
     private boolean mAutomaticallyCloseOnTaskCompletion = true;\r
+    private double mIncCoef = 0;\r
+    private double mValue = 0;\r
 \r
 \r
     /**\r
@@ -46,29 +50,39 @@ class ProgressTask implements ITaskMonitor {
 \r
     /**\r
      * Sets the description in the current task dialog.\r
-     * This method can be invoke from a non-UI thread.\r
+     * This method can be invoked from a non-UI thread.\r
      */\r
-    public void setDescription(final String descriptionFormat, final Object...args) {\r
+    public void setDescription(String descriptionFormat, Object...args) {\r
         mDialog.setDescription(descriptionFormat, args);\r
     }\r
 \r
     /**\r
      * Sets the description in the current task dialog.\r
-     * This method can be invoke from a non-UI thread.\r
+     * This method can be invoked from a non-UI thread.\r
      */\r
-    public void setResult(final String resultFormat, final Object...args) {\r
+    public void setResult(String resultFormat, Object...args) {\r
         mAutomaticallyCloseOnTaskCompletion = false;\r
         mDialog.setResult(resultFormat, args);\r
     }\r
 \r
     /**\r
      * Sets the max value of the progress bar.\r
-     * This method can be invoke from a non-UI thread.\r
+     * This method can be invoked from a non-UI thread.\r
+     *\r
+     * Weird things will happen if setProgressMax is called multiple times\r
+     * *after* {@link #incProgress(int)}: we don't try to adjust it on the\r
+     * fly.\r
      *\r
      * @see ProgressBar#setMaximum(int)\r
      */\r
-    public void setProgressMax(final int max) {\r
-        mDialog.setProgressMax(max);\r
+    public void setProgressMax(int max) {\r
+        assert max > 0;\r
+        // Always set the dialog's progress max to 10k since it only handles\r
+        // integers and we want to have a better inner granularity. Instead\r
+        // we use the max to compute a coefficient for inc deltas.\r
+        mDialog.setProgressMax((int) MAX_COUNT);\r
+        mIncCoef = max > 0 ? MAX_COUNT / max : 0;\r
+        assert mIncCoef > 0;\r
     }\r
 \r
     /**\r
@@ -76,8 +90,15 @@ class ProgressTask implements ITaskMonitor {
      *\r
      * This method can be invoked from a non-UI thread.\r
      */\r
-    public void incProgress(final int delta) {\r
-        mDialog.incProgress(delta);\r
+    public void incProgress(int delta) {\r
+        assert mIncCoef > 0;\r
+        assert delta > 0;\r
+        internalIncProgress(delta * mIncCoef);\r
+    }\r
+\r
+    private void internalIncProgress(double realDelta) {\r
+        mValue += realDelta;\r
+        mDialog.setProgress((int)mValue);\r
     }\r
 \r
     /**\r
@@ -87,7 +108,8 @@ class ProgressTask implements ITaskMonitor {
      * This method can be invoked from a non-UI thread.\r
      */\r
     public int getProgress() {\r
-        return mDialog.getProgress();\r
+        assert mIncCoef > 0;\r
+        return mIncCoef > 0 ? (int)(mDialog.getProgress() / mIncCoef) : 0;\r
     }\r
 \r
     /**\r
@@ -119,4 +141,95 @@ class ProgressTask implements ITaskMonitor {
         }\r
         return null;\r
     }\r
+\r
+    /**\r
+     * Creates a sub-monitor that will use up to tickCount on the progress bar.\r
+     * tickCount must be 1 or more.\r
+     */\r
+    public ITaskMonitor createSubMonitor(int tickCount) {\r
+        assert mIncCoef > 0;\r
+        assert tickCount > 0;\r
+        return new SubTaskMonitor(this, null, mValue, tickCount * mIncCoef);\r
+    }\r
+\r
+    private interface ISubTaskMonitor extends ITaskMonitor {\r
+        public void subIncProgress(double realDelta);\r
+    }\r
+\r
+    private static class SubTaskMonitor implements ISubTaskMonitor {\r
+\r
+        private final ProgressTask mRoot;\r
+        private final ISubTaskMonitor mParent;\r
+        private final double mStart;\r
+        private final double mSpan;\r
+        private double mSubValue;\r
+        private double mSubCoef;\r
+\r
+        /**\r
+         * Creates a new sub task monitor which will work for the given range [start, start+span]\r
+         * in its parent.\r
+         *\r
+         * @param root The ProgressTask root\r
+         * @param parent The immediate parent. Can be the null or another sub task monitor.\r
+         * @param start The start value in the root's coordinates\r
+         * @param span The span value in the root's coordinates\r
+         */\r
+        public SubTaskMonitor(ProgressTask root,\r
+                ISubTaskMonitor parent,\r
+                double start,\r
+                double span) {\r
+            mRoot = root;\r
+            mParent = parent;\r
+            mStart = start;\r
+            mSpan = span;\r
+            mSubValue = start;\r
+        }\r
+\r
+        public boolean isCancelRequested() {\r
+            return mRoot.isCancelRequested();\r
+        }\r
+\r
+        public void setDescription(String descriptionFormat, Object... args) {\r
+            mRoot.setDescription(descriptionFormat, args);\r
+        }\r
+\r
+        public void setResult(String resultFormat, Object... args) {\r
+            mRoot.setResult(resultFormat, args);\r
+        }\r
+\r
+        public void setProgressMax(int max) {\r
+            assert max > 0;\r
+            mSubCoef = max > 0 ? mSpan / max : 0;\r
+            assert mSubCoef > 0;\r
+        }\r
+\r
+        public int getProgress() {\r
+            assert mSubCoef > 0;\r
+            return mSubCoef > 0 ? (int)((mSubValue - mStart) / mSubCoef) : 0;\r
+        }\r
+\r
+        public void incProgress(int delta) {\r
+            assert mSubCoef > 0;\r
+            subIncProgress(delta * mSubCoef);\r
+        }\r
+\r
+        public void subIncProgress(double realDelta) {\r
+            mSubValue += realDelta;\r
+            if (mParent != null) {\r
+                mParent.subIncProgress(realDelta);\r
+            } else {\r
+                mRoot.internalIncProgress(realDelta);\r
+            }\r
+        }\r
+\r
+        public ITaskMonitor createSubMonitor(int tickCount) {\r
+            assert mSubCoef > 0;\r
+            assert tickCount > 0;\r
+            return new SubTaskMonitor(mRoot,\r
+                    this,\r
+                    mSubValue,\r
+                    tickCount * mSubCoef);\r
+        }\r
+    }\r
+\r
 }\r
index 6cc11a3..fb0c504 100755 (executable)
@@ -19,6 +19,8 @@ package com.android.sdkuilib.internal.repository;
 \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
 \r
 import org.eclipse.jface.viewers.CheckStateChangedEvent;\r
 import org.eclipse.jface.viewers.CheckboxTreeViewer;\r
@@ -265,7 +267,7 @@ public class RemotePackagesPage extends Composite {
 \r
     private void onRefreshSelected() {\r
         if (mUpdaterWindow != null) {\r
-            mUpdaterWindow.refreshSources(false /*forceFetching*/);\r
+            mUpdaterWindow.refreshSources(false /*forceFetching*/, null /*monitor*/);\r
         }\r
     }\r
 \r
index 3f020d1..a170775 100755 (executable)
@@ -18,6 +18,8 @@ package com.android.sdkuilib.internal.repository;
 \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
 import com.android.sdklib.internal.repository.RepoSource;\r
 import com.android.sdklib.internal.repository.RepoSources;\r
@@ -110,11 +112,17 @@ class RepoSourcesAdapter {
                 return ((RepoSourcesAdapter) parentElement).mRepoSources.getSources().toArray();\r
 \r
             } else if (parentElement instanceof RepoSource) {\r
-                RepoSource source = (RepoSource) parentElement;\r
+                final RepoSource source = (RepoSource) parentElement;\r
                 Package[] packages = source.getPackages();\r
 \r
                 if (packages == null) {\r
-                    source.load(mInput.mRepoSources.getTaskFactory());\r
+\r
+                    mInput.mRepoSources.getTaskFactory().start("Loading Source", new ITask() {\r
+                        public void run(ITaskMonitor monitor) {\r
+                            source.load(monitor);\r
+                        }\r
+                    });\r
+\r
                     packages = source.getPackages();\r
                 }\r
                 if (packages != null) {\r
index cf679c7..3e98fae 100755 (executable)
@@ -407,17 +407,14 @@ public class UpdaterWindowImpl {
     }\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
+        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, monitor.createSubMonitor(1));\r
+            }\r
+        });\r
     }\r
 \r
     /**\r
@@ -426,32 +423,26 @@ public class UpdaterWindowImpl {
      * @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
+    public void refreshSources(final boolean forceFetching, ITaskMonitor monitor) {\r
+        ITask task = new ITask() {\r
+            public void run(ITaskMonitor monitor) {\r
+                ArrayList<RepoSource> sources = mUpdaterData.getSources().getSources();\r
+                monitor.setProgressMax(sources.size());\r
+                for (RepoSource source : sources) {\r
+                    if (forceFetching || source.getPackages() != null) {\r
+                        source.load(monitor.createSubMonitor(1));\r
+                    }\r
+                    monitor.incProgress(1);\r
+\r
+                }\r
             }\r
+        };\r
 \r
+        if (monitor != null) {\r
+            task.run(monitor);\r
+        } else {\r
+            mTaskFactory.start("Refresh Sources", task);\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