OSDN Git Service

SDK Manager: remember window positions.
authorRaphael Moll <ralf@android.com>
Tue, 30 Aug 2011 21:26:22 +0000 (14:26 -0700)
committerRaphael <raphael@google.com>
Thu, 1 Sep 2011 07:41:32 +0000 (00:41 -0700)
Remember window positions & sizes for the
SDK Manager 2 and the AVD Manager 1.
When restoring the windows, make sure they
fit on the visible monitor(s) area.

Also fixed a race condition where closing the
AvdManager1 window would dispose some bitmaps
but it turns out the AVD Manager can be launched
from the SDK Manager and the latter still need
these bitmaps... Hilarity ensues.

Change-Id: I2312e760029bc32561355f557a621d23ec1b6916

sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/sdkman2/AvdManagerWindowImpl1.java
sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/sdkman2/SdkUpdaterWindowImpl2.java
sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/sdkman2/ShellSizeAndPos.java [new file with mode: 0755]

index b4f4fd7..12ddf6e 100755 (executable)
@@ -62,7 +62,7 @@ public class AvdManagerWindowImpl1 {
 \r
     private static final String APP_NAME = "Android Virtual Device Manager";\r
     private static final String APP_NAME_MAC_MENU = "AVD Manager";\r
-\r
+    private static final String SIZE_POS_PREFIX = "avdman1"; //$NON-NLS-1$\r
 \r
     private final Shell mParentShell;\r
     private final AvdInvocationContext mContext;\r
@@ -135,23 +135,31 @@ public class AvdManagerWindowImpl1 {
         mShell.open();\r
         mShell.layout();\r
 \r
-        if (postCreateContent()) {    //$hide$ (hide from SWT designer)\r
+        boolean ok = postCreateContent();\r
+\r
+        if (ok && mContext == AvdInvocationContext.STANDALONE) {\r
             Display display = Display.getDefault();\r
             while (!mShell.isDisposed()) {\r
                 if (!display.readAndDispatch()) {\r
                     display.sleep();\r
                 }\r
             }\r
-        }\r
 \r
-        dispose();  //$hide$\r
+            dispose();  //$hide$\r
+        }\r
     }\r
 \r
     private void createShell() {\r
         mShell = new Shell(mParentShell, SWT.SHELL_TRIM | SWT.APPLICATION_MODAL);\r
         mShell.addDisposeListener(new DisposeListener() {\r
             public void widgetDisposed(DisposeEvent e) {\r
-                onAndroidSdkUpdaterDispose();    //$hide$ (hide from SWT designer)\r
+                ShellSizeAndPos.saveSizeAndPos(mShell, SIZE_POS_PREFIX);\r
+\r
+                if (mContext != AvdInvocationContext.SDK_MANAGER) {\r
+                    // When invoked from the sdk manager, don't dispose\r
+                    // resources that the sdk manager still needs.\r
+                    onAndroidSdkUpdaterDispose();    //$hide$ (hide from SWT designer)\r
+                }\r
             }\r
         });\r
 \r
@@ -165,6 +173,8 @@ public class AvdManagerWindowImpl1 {
         mShell.setMinimumSize(new Point(500, 300));\r
         mShell.setSize(700, 500);\r
         mShell.setText(APP_NAME);\r
+\r
+        ShellSizeAndPos.loadSizeAndPos(mShell, SIZE_POS_PREFIX);\r
     }\r
 \r
     private void createContents() {\r
index 6ae6653..1d0c484 100755 (executable)
@@ -73,6 +73,8 @@ import java.util.ArrayList;
 public class SdkUpdaterWindowImpl2 implements ISdkUpdaterWindow {\r
 \r
     private static final String APP_NAME = "Android SDK Manager";\r
+    private static final String SIZE_POS_PREFIX = "sdkman2"; //$NON-NLS-1$\r
+\r
     private final Shell mParentShell;\r
     private final SdkInvocationContext mContext;\r
     /** Internal data shared between the window and its pages. */\r
@@ -166,6 +168,7 @@ public class SdkUpdaterWindowImpl2 implements ISdkUpdaterWindow {
         mShell = new Shell(mParentShell, SWT.SHELL_TRIM | SWT.APPLICATION_MODAL);\r
         mShell.addDisposeListener(new DisposeListener() {\r
             public void widgetDisposed(DisposeEvent e) {\r
+                ShellSizeAndPos.saveSizeAndPos(mShell, SIZE_POS_PREFIX);\r
                 onAndroidSdkUpdaterDispose();    //$hide$ (hide from SWT designer)\r
             }\r
         });\r
@@ -180,6 +183,8 @@ public class SdkUpdaterWindowImpl2 implements ISdkUpdaterWindow {
         mShell.setMinimumSize(new Point(500, 300));\r
         mShell.setSize(700, 500);\r
         mShell.setText(APP_NAME);\r
+\r
+        ShellSizeAndPos.loadSizeAndPos(mShell, SIZE_POS_PREFIX);\r
     }\r
 \r
     private void createContents() {\r
@@ -671,7 +676,7 @@ public class SdkUpdaterWindowImpl2 implements ISdkUpdaterWindow {
             }\r
 \r
             getShell().setText(\r
-                    String.format("%1$s - %2$s", APP_NAME, content.getPageTitle()));  //$NON-NLS-1$\r
+                    String.format("%1$s - %2$s", APP_NAME, content.getPageTitle()));\r
 \r
             Label filler = new Label(shell, SWT.NONE);\r
             GridDataBuilder.create(filler).hFill().hGrab();\r
diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/sdkman2/ShellSizeAndPos.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/sdkman2/ShellSizeAndPos.java
new file mode 100755 (executable)
index 0000000..e53c8ae
--- /dev/null
@@ -0,0 +1,166 @@
+/*\r
+ * Copyright (C) 2011 The Android Open Source Project\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ *      http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+\r
+package com.android.sdkuilib.internal.repository.sdkman2;\r
+\r
+\r
+import com.android.prefs.AndroidLocation;\r
+\r
+import org.eclipse.swt.graphics.Point;\r
+import org.eclipse.swt.graphics.Rectangle;\r
+import org.eclipse.swt.widgets.Monitor;\r
+import org.eclipse.swt.widgets.Shell;\r
+\r
+import java.io.File;\r
+import java.io.FileInputStream;\r
+import java.io.FileOutputStream;\r
+import java.io.IOException;\r
+import java.util.Properties;\r
+\r
+/**\r
+ * Utility to save & restore the size and position on a window\r
+ * using a common config file.\r
+ */\r
+public class ShellSizeAndPos {\r
+\r
+    private static final String SETTINGS_FILENAME = "androidwin.cfg";   //$NON-NLS-1$\r
+    private static final String PX = "_px";                             //$NON-NLS-1$\r
+    private static final String PY = "_py";                             //$NON-NLS-1$\r
+    private static final String SX = "_sx";                             //$NON-NLS-1$\r
+    private static final String SY = "_sy";                             //$NON-NLS-1$\r
+\r
+    public static void loadSizeAndPos(Shell shell, String prefix) {\r
+        Properties props = loadProperties();\r
+\r
+        try {\r
+            int px = Integer.parseInt(props.getProperty(prefix + PX));\r
+            int py = Integer.parseInt(props.getProperty(prefix + PY));\r
+            int sx = Integer.parseInt(props.getProperty(prefix + SX));\r
+            int sy = Integer.parseInt(props.getProperty(prefix + SY));\r
+\r
+            Point p1 = new Point(px, py);\r
+            Point p2 = new Point(px + sx, py + sy);\r
+            Rectangle r = new Rectangle(px, py, sy, sy);\r
+\r
+            Monitor bestMatch = null;\r
+            int bestSurface = -1;\r
+            for (Monitor monitor : shell.getDisplay().getMonitors()) {\r
+                Rectangle area = monitor.getClientArea();\r
+                if (area.contains(p1) && area.contains(p2)) {\r
+                    // The shell is fully visible on this monitor. Just use that.\r
+                    bestMatch = monitor;\r
+                    bestSurface = Integer.MAX_VALUE;\r
+                    break;\r
+                } else {\r
+                    // Find which monitor displays the largest surface of the window.\r
+                    // We'll use this one to center the window there, to make sure we're not\r
+                    // starting split between several monitors.\r
+                    Rectangle i = area.intersection(r);\r
+                    int surface = i.width * i.height;\r
+                    if (surface > bestSurface) {\r
+                        bestSurface = surface;\r
+                        bestMatch = monitor;\r
+                    }\r
+                }\r
+            }\r
+\r
+            if (bestMatch != null && bestSurface != Integer.MAX_VALUE) {\r
+                // Recenter the window on this monitor and make sure it fits\r
+                Rectangle area = bestMatch.getClientArea();\r
+\r
+                sx = Math.min(sx, area.width);\r
+                sy = Math.min(sy, area.height);\r
+                px = area.x + (area.width - sx) / 2;\r
+                py = area.y + (area.height - sy) / 2;\r
+            }\r
+\r
+            shell.setLocation(px, py);\r
+            shell.setSize(sx, sy);\r
+\r
+        } catch ( Exception e) {\r
+            // Ignore exception. We could typically get NPE from the getProperty\r
+            // or NumberFormatException from parseInt calls. Either way, do\r
+            // nothing if anything goes wrong.\r
+        }\r
+    }\r
+\r
+    public static void saveSizeAndPos(Shell shell, String prefix) {\r
+        Properties props = loadProperties();\r
+\r
+        Point loc = shell.getLocation();\r
+        Point size = shell.getSize();\r
+\r
+        props.setProperty(prefix + PX, Integer.toString(loc.x));\r
+        props.setProperty(prefix + PY, Integer.toString(loc.y));\r
+        props.setProperty(prefix + SX, Integer.toString(size.x));\r
+        props.setProperty(prefix + SY, Integer.toString(size.y));\r
+\r
+        saveProperties(props);\r
+    }\r
+\r
+    /**\r
+     * Load properties saved in {@link #SETTINGS_FILENAME}.\r
+     * If the file does not exists or doesn't load properly, just return an\r
+     * empty set of properties.\r
+     */\r
+    private static Properties loadProperties() {\r
+        Properties props = new Properties();\r
+        FileInputStream fis = null;\r
+\r
+        try {\r
+            String folder = AndroidLocation.getFolder();\r
+            File f = new File(folder, SETTINGS_FILENAME);\r
+            if (f.exists()) {\r
+                fis = new FileInputStream(f);\r
+\r
+                props.load(fis);\r
+            }\r
+        } catch (Exception e) {\r
+            // Ignore\r
+        } finally {\r
+            if (fis != null) {\r
+                try {\r
+                    fis.close();\r
+                } catch (IOException e) {\r
+                }\r
+            }\r
+        }\r
+\r
+        return props;\r
+    }\r
+\r
+    private static void saveProperties(Properties props) {\r
+        FileOutputStream fos = null;\r
+\r
+        try {\r
+            String folder = AndroidLocation.getFolder();\r
+            File f = new File(folder, SETTINGS_FILENAME);\r
+            fos = new FileOutputStream(f);\r
+\r
+            props.store(fos, "## Size and Pos for SDK Manager Windows");  //$NON-NLS-1$\r
+\r
+        } catch (Exception e) {\r
+            // ignore\r
+        } finally {\r
+            if (fos != null) {\r
+                try {\r
+                    fos.close();\r
+                } catch (IOException e) {\r
+                }\r
+            }\r
+        }\r
+    }\r
+}\r