1/*
2 * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23
24import java.awt.*;
25import java.awt.event.*;
26import java.awt.geom.Point2D;
27import java.awt.image.BufferedImage;
28import java.lang.reflect.Field;
29import java.lang.reflect.Method;
30import java.security.AccessController;
31import java.security.PrivilegedActionException;
32import java.security.PrivilegedExceptionAction;
33
34/*
35 * @summary This is a helper class to find the location of a system tray icon,
36 *          and skip some OS specific cases in tests.
37 * @library ../../../../../lib/testlibrary
38 * @build ExtendedRobot SystemTrayIconHelper
39 */
40public class SystemTrayIconHelper {
41
42    static Frame frame;
43
44    /**
45     * Call this method if the tray icon need to be followed in an automated manner
46     * This method will be called by automated testcases
47     */
48    static Point getTrayIconLocation(TrayIcon icon) throws Exception {
49        if (icon == null) {
50            return null;
51        }
52
53        //This is added just in case the tray's native menu is visible.
54        //It has to be hidden if visible. For that, we are showing a Frame
55        //and clicking on it - the assumption is, the menu will
56        //be closed if another window is clicked
57        ExtendedRobot robot = new ExtendedRobot();
58        try {
59           EventQueue.invokeAndWait(() -> {
60               frame = new Frame();
61               frame.setSize(100, 100);
62               frame.setVisible(true);
63           });
64            robot.mouseMove(frame.getLocationOnScreen().x + frame.getWidth() / 2,
65                    frame.getLocationOnScreen().y + frame.getHeight() / 2);
66            robot.waitForIdle();
67            robot.click();
68            EventQueue.invokeAndWait(frame::dispose);
69        } catch (Exception e) {
70            return null;
71        }
72
73        if (System.getProperty("os.name").startsWith("Win")) {
74            try {
75                // sun.awt.windows.WTrayIconPeer
76                Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
77                Dimension iconSize = icon.getSize();
78
79                int width = (int) iconSize.getWidth();
80                int height = (int) iconSize.getHeight();
81
82                // Some previously created icons may not be removed
83                // from tray until mouse move on it. So we glide
84                // through the whole tray bar.
85                robot.glide((int) screenSize.getWidth(), (int) (screenSize.getHeight()-15), 0, (int) (screenSize.getHeight() - 15), 1, 2);
86
87                BufferedImage screen = robot.createScreenCapture(new Rectangle(screenSize));
88
89                for (int x = (int) (screenSize.getWidth()-width); x > 0; x--) {
90                    for (int y = (int) (screenSize.getHeight()-height); y > (screenSize.getHeight()-50); y--) {
91                        if (imagesEquals(((BufferedImage)icon.getImage()).getSubimage(0, 0, width, height), screen.getSubimage(x, y, width, height))) {
92                            Point point = new Point(x + 5, y + 5);
93                            System.out.println("Icon location " + point);
94                            return point;
95                        }
96                    }
97                }
98            } catch (Exception e) {
99                e.printStackTrace();
100                return null;
101            }
102        } else if (System.getProperty("os.name").startsWith("Mac")) {
103            Point2D point2d;
104            try {
105                // sun.lwawt.macosx.CTrayIcon
106                Field f_peer = getField( java.awt.TrayIcon.class, "peer");
107                Method m_addExports = Class.forName("java.awt.Helper").getDeclaredMethod("addExports", String.class, java.lang.Module.class);
108                m_addExports.invoke(null, "sun.lwawt.macosx", robot.getClass().getModule());
109
110
111                Object peer = f_peer.get(icon);
112                Class<?> superclass = peer.getClass().getSuperclass();
113                System.out.println("superclass = " + superclass);
114                Field m_getModel = superclass.getDeclaredField("ptr");
115                m_getModel.setAccessible(true);
116                long model = (Long) m_getModel.get(peer);
117                Method m_getLocation = peer.getClass().getDeclaredMethod(
118                        "nativeGetIconLocation", new Class[]{Long.TYPE});
119                m_getLocation.setAccessible(true);
120                point2d = (Point2D)m_getLocation.invoke(peer, new Object[]{model});
121                Point po = new Point((int)(point2d.getX()), (int)(point2d.getY()));
122                po.translate(10, -5);
123                System.out.println("Icon location " + po);
124                return po;
125            }catch(Exception e) {
126                e.printStackTrace();
127                return null;
128            }
129        } else {
130            try {
131                // sun.awt.X11.XTrayIconPeer
132                Method m_addExports = Class.forName("java.awt.Helper").getDeclaredMethod("addExports", String.class, java.lang.Module.class);
133                m_addExports.invoke(null, "sun.awt.X11", robot.getClass().getModule());
134
135                Field f_peer = getField(java.awt.TrayIcon.class, "peer");
136
137                SystemTrayIconHelper.openTrayIfNeeded(robot);
138
139                Object peer = f_peer.get(icon);
140                Method m_getLOS = peer.getClass().getDeclaredMethod(
141                        "getLocationOnScreen", new Class[]{});
142                m_getLOS.setAccessible(true);
143                Point point = (Point)m_getLOS.invoke(peer, new Object[]{});
144                point.translate(5, 5);
145                System.out.println("Icon location " + point);
146                return point;
147            } catch (Exception e) {
148                e.printStackTrace();
149                return null;
150            }
151        }
152        return null;
153    }
154
155    static Field getField(final Class clz, final String fieldName) {
156        Field res = null;
157        try {
158            res = (Field)AccessController.doPrivileged((PrivilegedExceptionAction) () -> {
159                Field f = clz.getDeclaredField(fieldName);
160                f.setAccessible(true);
161                return f;
162            });
163        } catch (PrivilegedActionException ex) {
164            ex.printStackTrace();
165        }
166        return res;
167    }
168
169    static boolean imagesEquals(BufferedImage img1, BufferedImage img2) {
170        for (int x = 0; x < img1.getWidth(); x++) {
171            for (int y = 0; y < img1.getHeight(); y++) {
172                if (img1.getRGB(x, y) != img2.getRGB(x, y))
173                    return false;
174            }
175        }
176        return true;
177    }
178
179    static void doubleClick(Robot robot) {
180        if (System.getProperty("os.name").startsWith("Mac")) {
181            robot.mousePress(InputEvent.BUTTON3_MASK);
182            robot.delay(50);
183            robot.mouseRelease(InputEvent.BUTTON3_MASK);
184        } else {
185            robot.mousePress(InputEvent.BUTTON1_MASK);
186            robot.delay(50);
187            robot.mouseRelease(InputEvent.BUTTON1_MASK);
188            robot.delay(50);
189            robot.mousePress(InputEvent.BUTTON1_MASK);
190            robot.delay(50);
191            robot.mouseRelease(InputEvent.BUTTON1_MASK);
192        }
193    }
194
195    // Method for skipping some OS specific cases
196    static boolean skip(int button) {
197        if (System.getProperty("os.name").toLowerCase().startsWith("win")){
198            if (button == InputEvent.BUTTON1_MASK){
199                // See JDK-6827035
200                return true;
201            }
202        } else if (System.getProperty("os.name").toLowerCase().contains("os x")){
203            // See JDK-7153700
204            return true;
205        }
206        return false;
207    }
208
209    public static boolean openTrayIfNeeded(Robot robot) {
210        String sysv = System.getProperty("os.version");
211        System.out.println("System version is " + sysv);
212        //Additional step to raise the system try in Gnome 3 in OEL 7
213        if(isOel7()) {
214            System.out.println("OEL 7 detected");
215            GraphicsConfiguration gc = GraphicsEnvironment.
216                    getLocalGraphicsEnvironment().getDefaultScreenDevice().
217                    getDefaultConfiguration();
218            Insets insets = Toolkit.getDefaultToolkit().getScreenInsets(gc);
219            if(insets.bottom > 0) {
220                Dimension screenSize = Toolkit.getDefaultToolkit()
221                        .getScreenSize();
222                robot.mouseMove(screenSize.width - insets.bottom / 2,
223                        screenSize.height - insets.bottom / 2);
224                robot.delay(50);
225                robot.mousePress(InputEvent.BUTTON1_MASK);
226                robot.delay(50);
227                robot.mouseRelease(InputEvent.BUTTON1_MASK);
228                robot.waitForIdle();
229                robot.delay(1000);
230                System.out.println("Tray is opened");
231                return true;
232            }
233        }
234        return false;
235    }
236
237    public static boolean isOel7() {
238        return System.getProperty("os.name").toLowerCase()
239                .contains("linux") && System.getProperty("os.version")
240                .toLowerCase().contains("el7");
241    }
242}
243