/src/main/java/com/ribomation/droidAtScreen/gui/DeviceFrame.java * DroidAtScreenApplication.java * DeviceFrame frame = new DeviceFrame(DroidAtScreenApplication.this, dev); {{{ public void connected(final AndroidDevice dev) {^M log.debug("connected: dev=" + dev);^M ^M SwingUtilities.invokeLater(new Runnable() {^M @Override^M public void run() {^M getAppFrame().getStatusBar().message("Connected to " + dev.getName());^M ^M DeviceFrame frame = new DeviceFrame(DroidAtScreenApplication.this, dev);^M deviceTableModel.add(frame);^M fireDeviceConnected(dev);^M ^M frame.setVisible(!getSettings().isHideEmulators() || !dev.isEmulator());^M ^M updateDeviceFramePositionsOnScreen(frame);^M }^M });^M }^M }}} * deviceTableModel.add(frame); * updateDeviceFramePositionsOnScreen(frame); * ÀÌ ºÎºÐÀ» È®½ÇÈ÷ ¾Ë¾Æº¼ °Í * table ¿¡´Â µî·Ï * µî·ÏÀ» À§ÇÑ class ¿Í µð½ºÇ÷¹À̸¦ À§ÇÑ class ·Î ºÐ¸®ÇÏ´Â °ÍÀº ¾î¶³±î? * select device ¸¦ ÇÏ°í, ¼±ÅÃµÈ µð¹ÙÀ̽º Çϳª¿¡ ´ëÇØ deviceframe »ý¼ºÇÒ °Í * design ÀÇ ¹®Á¦ÀÓ. ¿ì¼± Çϳª¸¸ »ç¿ëÇÏ´Â °ÍÀ¸·Î Á¤Çϸé, deviceFrame ÀÇ ±â´ÉÀ» applicationFrame À¸·Î ¿Å±æ ¼ö ÀÖÀ» °Í °°À½. === Gui files === {{{ tcheun@tcheun-system:~/my-app/src/main/java/com/ribomation/droidAtScreen/gui$ ls ApplicationFrame.java GuiUtil.java StatusBar.java DeviceFrame.java RecordingListener.java DeviceTableModel.java SkinUtil.java }}} * ApplicationFrame.java * public class ApplicationFrame extends JFrame * public ApplicationFrame(Application app) * public StatusBar getStatusBar() * public void initGUI() * protected JMenuBar createMenubar() * private JComponent createDevicesTable() === DeviceTableModel.java === * DroidAtScreenApplication.java * private final List deviceListeners = new ArrayList(); * private final DeviceTableModel deviceTableModel = new DeviceTableModel(); * public void updateDeviceFramePositionsOnScreen(DeviceFrame newFrame) * synchronized (deviceTableModel) * List devices = deviceTableModel.getDevices(); * public List getDevices() * return getDeviceTableModel().getDevices(); * public DeviceTableModel getDeviceTableModel() * return deviceTableModel; * public void connected(final AndroidDevice dev) * SwingUtilities.invokeLater(new Runnable() * public void run() * DeviceFrame frame = new DeviceFrame(DroidAtScreenApplication.this, dev); * deviceTableModel.add(frame); * DroidAtScreenApplication.java * private void initGUI() * appFrame = new ApplicationFrame(this); * appFrame.initGUI(); * ApplicationFrame.java * public void initGUI() * add(createDevicesTable(), BorderLayout.CENTER); * private JComponent createDevicesTable() {{{ private JComponent createDevicesTable() {^M JTable tbl = new JTable(app.getDeviceTableModel());^M tbl.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);^M tbl.setRowSelectionAllowed(true);^M tbl.setShowHorizontalLines(true);^M tbl.setFillsViewportHeight(true);^M tbl.setPreferredScrollableViewportSize(new Dimension(450, 150));^M ^M JScrollPane pane = new JScrollPane(tbl);^M pane.setBorder(BorderFactory.createTitledBorder("Devices"));^M ^M return pane;^M }^M }}} * DeviceTable °ú °ü·ÃµÈ DeviceFrame ¸¦ ã¾Æ¼­ ´Ù¸¥ Ŭ·¡½º·Î ´ëüÇÑ´Ù. * connected µÈ ¼ø°£, table ÀÌ update µÈ ÈÄ, select device ¸¦ ¼öÇàÇÑ´Ù. * select µÈ device ¿¡ ´ëÇؼ­¸¸, DeviceFrame ¸¦ »ý¼ºÇÑ´Ù. === DeviceFrame.java === {{{ 2016-09-07 08:58:42,748 INFO AndroidDeviceManager - ADB changed 2016-09-07 08:58:42,748 INFO AndroidDeviceManager - Connected to ADB via /127.0.0.1:5037 2016-09-07 08:58:42,761 INFO AndroidDeviceManager - Device connected: 5d027563 2016-09-07 08:58:42,762 DEBUG DroidAtScreenApplication - connected: dev=5d027563 (device) 2016-09-07 08:58:42,792 DEBUG DeviceFrame:5d027563 - DeviceFrame(device=5d027563 (device)) 2016-09-07 08:58:42,867 DEBUG DeviceFrame:5d027563 - getPreferredSize: screen=java.awt.Dimension[width=1855,height=1056], canvas=java.awt.Dimension[width=200,height=300], frame=java.awt.Dimension[width=251,height=348] 2016-09-07 08:58:42,867 DEBUG DeviceFrame:5d027563 - getPreferredSize: frame2=java.awt.Dimension[width=210,height=330] 2016-09-07 08:58:42,869 DEBUG DroidAtScreenApplication - devices size: count=1 2016-09-07 08:58:42,993 DEBUG AndroidDeviceManager - Device changed: 5d027563, mask=CHANGE_BUILD_INFO 2016-09-07 08:58:43,945 DEBUG DeviceFrame:SHV-E330K - getPreferredSize: screen=java.awt.Dimension[width=1855,height=1056], canvas=java.awt.Dimension[width=1080,height=1920], frame=java.awt.Dimension[width=1121,height=1938] 2016-09-07 08:58:43,945 DEBUG DeviceFrame:SHV-E330K - getPreferredSize: frame2=java.awt.Dimension[width=1121,height=950] 2016-09-07 08:58:43,947 DEBUG DroidAtScreenApplication - devices size: count=1 2016-09-07 08:58:56,972 DEBUG DeviceFrame:SHV-E330K - windowClosing 2016-09-07 08:59:01,250 INFO AndroidDeviceManager - ADB changed }}} * public class DeviceFrame extends JFrame * private final class AnimationActionListener implements ActionListener * private final class AnimationTimer extends Timer * public DeviceFrame(Application app, final AndroidDevice device) * setting * scale ´Â 100À¸·Î, landscapemode ´Â false ·Î ¼³Á¤ * canvas = new ImageCanvas(); * canvasScrollable = new JScrollPane(canvas); * add(canvasScrollable, BorderLayout.CENTER); * add(toolBar = createToolBar(), BorderLayout.WEST); * add(infoPane = new InfoPane(), BorderLayout.SOUTH);^ * canvas.setBorder(BorderFactory.createEmptyBorder());^ * canvas.addMouseListener(new MouseAdapter() * startRetriever();^ * pack(); attachment:DeviceFrame.png * * public Dimension getPreferredSize() * private void applySkin() * skin = SkinUtil.loadSkin(device.getName().toLowerCase()) {{{ if (skin != null) {^M // Apply skin^M canvas.setSkin(skin);^M pack();^M boolean wasVisible = isVisible();^M dispose();^M setUndecorated(true);^M setBackground(new Color(1.0f, 1.0f, 1.0f, 0.0f));^M toolBar.setVisible(false);^M infoPane.setVisible(false);^M setVisible(wasVisible);^M forceRepaint();^M }^M }}} * * public void startRetriever() * retriever = new Retriever(); * app.getTimer().schedule(retriever, 0, 500); * public void stopRetriever() * class Retriever extends TimerTask * public void run() * ScreenImage image = device.getScreenImage(); device ÀÇ image ¸¦ °¡Á®¿È. * boolean fresh = canvas.getScreenshot() == null; ÀÌ¹Ì °¡Áö°í ¿Â À̹ÌÁö°¡ canvas ¿¡ Àִ°¡? canvas ÀÇ image °¡ null À̸é óÀ½ image ¸¦ °¡Áö°í ¿Â °ÍÀÓ. * canvas.setScreenshot(image); °¡Áö°í ¿Â image ¸¦ canvas ÀÇ image ¿¡ ¼³Á¤ * infoPane.setSizeInfo(canvas); {{{ if (fresh) {^M log = Logger.getLogger(DeviceFrame.class.getName() + ":" + device.getName());^M setTitle(device.getName());^M pack();^M applySkin();^M app.getDeviceTableModel().refresh();^M app.updateDeviceFramePositionsOnScreen(null);^M }^M }}} * * protected JComponent createToolBar() * public class InfoPane extends JPanel * class Unit * class ImageCanvas extends JComponent * public ImageCanvas() * public void setSkin(Skin skin) * public void setScreenshot(ScreenImage image) * public ScreenImage getScreenshot() * protected void paintComponent(Graphics g) * Graphics2D g2 = (Graphics2D) g; * g2.clearRect(0, 0, canvas.getWidth(), canvas.getHeight()); * AffineTransform TX = new AffineTransform(); * BufferedImage bufImg = image.toBufferedImage();^ * g2.drawImage(bufImg, TX, null); * BufferedImage toLandscape(BufferedImage img) * BufferedImage rotate(int quadrants, BufferedImage img) * public Dimension getPreferredSize() * public Dimension getMinimumSize() * public void setLandscapeMode(boolean landscape) * public void setScale(int scalePercentage) * public void updateScale(int scalePercentage) * public void setUpsideDown(boolean upsideDown) * public void setRecordingListener(RecordingListener recordingListener) * public ScreenImage getLastScreenshot() * public InfoPane getInfoPane() * public AndroidDevice getDevice() * public String getName() * public boolean isLandscapeMode() * public int getScale() * public boolean isUpsideDown() * private int scale(int value) * getScaleFactor() ÀÇ ¸®ÅÏ°ª factor À» 0¿¡¼­ 100 »çÀÌÀÇ °ªÀ¸·Î º¯È¯ * private double getScaleFactor() * ¸ð´ÏÅÍÀÇ Å©±â¸¦ Àд´Ù. {{{ final Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();^M final double screenHeight = screen.getHeight();^M final double screenWidth = screen.getWidth();^M }}} * * * * DeviceFrame ÀÇ getHeight() ¿Í ºñ±³ÇÏ¿© scale factor À» 0.75, 0.5, 0.25 Áß Çϳª·Î Á¤ÇÑ´Ù. * private void forceRepaint() * public boolean equals(Object o) * public int hashCode() * public int compareTo(DeviceFrame that) * public void setLocation(int x, int y, boolean animate) * GuiUtil.java * DeviceTableModel.java * SkinUtil.java === point === * DeviceFrame.java * public class DeviceFrame extends JFrame implements Comparable * private ImageCanvas canvas; * public DeviceFrame(Application app, final AndroidDevice device) * canvas = new ImageCanvas(); * canvasScrollable = new JScrollPane(canvas); * add(canvasScrollable, BorderLayout.CENTER); * canvas.setBorder(BorderFactory.createEmptyBorder()); {{{ canvas.addMouseListener(new MouseAdapter() {^M @Override^M public void mouseClicked(MouseEvent e) {^M Point p = e.getPoint();^M log.debug(String.format("mouse: %s", p));^M p = new Point(^M (int) (p.getX() * 100) / getScale(),^M (int) (p.getY() * 100) / getScale()^M );^M log.debug(String.format("scaled: %s", p));^M device.tap(p);^M }^M });^M }}} * class ImageCanvas extends JComponent {{{ public void setScreenshot(ScreenImage image) {^M this.image = image;^M repaint();^M }^M }}} === scale === * DroidAtScreenApplication.java ¿¡¼­ connected ¿¡¼­ DeviceFrame Ŭ·¡½º¸¦ »ý¼ºÇÑ´Ù. * public class DeviceFrame extends JFrame * private int scalePercentage = 100; * private ImageCanvas canvas;^ * private JScrollPane canvasScrollable; * private JComponent toolBar;^ * private InfoPane infoPane;^ * public DeviceFrame(Application app, final AndroidDevice device) {{{ canvas = new ImageCanvas();^M canvasScrollable = new JScrollPane(canvas);^M ^M add(canvasScrollable, BorderLayout.CENTER);^M add(toolBar = createToolBar(), BorderLayout.WEST);^M add(infoPane = new InfoPane(), BorderLayout.SOUTH);^M startRetriever();^M pack();^ }}} * * class ImageCanvas extends JComponent * private ScreenImage image; * public ImageCanvas() * setBorder(BorderFactory.createLoweredBevelBorder()); * public Dimension getPreferredSize() * return new Dimension(200, 300); * return new Dimension(scale(image.getWidth()), scale(image.getHeight())); {{{ private int scale(int value) {^M double factor = getScaleFactor();^M if (factor != 1) {^M scalePercentage = (int) (factor * 100);^M }^M if (scalePercentage == 100) {^M return value;^M }^M return (int) Math.round(value * scalePercentage / 100.0);^M }^M }}} * private double getScaleFactor() * screenHeight ¿Í DeviceFrame ÀÇ height ¸¦ ºñ±³ÇÏ¿© 1, 0.75, 0.5, 0.25 ÀÇ °ªÀ» ¸®ÅÏ === misc === * JFrame ÀÇ pack() ¿¡ ÀÇÇØ layout manager of the content pane ÀÌ getPreferredSize method ¸¦ call ÇÑ´Ù. * void deviceChanged(IDevice device, int changeMask) * Sent when a device data changed, or when clients are started/terminated on the device. This is sent from a non UI thread. {{{ public void deviceChanged(IDevice device, int changeMask) { 119 // Only fire events if the phone properties are available 120 if (IDevice.CHANGE_BUILD_INFO == changeMask && device.isEmulator() == false) { 121 for (HardwareDeviceListener listener : deviceListeners) { 122 listener.onDeviceConnected(connectedDevices.get(device)); 123 } 124 } 125 } }}} * app.getTimer().schedule(retriever, 0, 500); 0.5ÃÊ¿¡ Çѹø¾¿ * Application.java * java.util.Timer getTimer(); * schedule(TimerTask task, long delay, long period) * device.getScreenImage(); * private final AndroidDevice device; * AndroidDevice.java {{{ public ScreenImage getScreenImage() {^M try {^M RawImage rawImage = target.getScreenshot();^M if (rawImage == null)^M return null;^M setState(target.getState());^M return new ScreenImage(rawImage);^M } catch (IOException e) {^M setState(ConnectionState.error);^M log.error("Failed to get screenshot: " + e);^M } catch (TimeoutException e) {^M setState(ConnectionState.timeout);^M log.warn("Got timeout");^M } catch (AdbCommandRejectedException e) {^M setState(ConnectionState.rejected);^M log.error("ADB command rejected: OFFLINE=" + e.isDeviceOffline());^M }^M return null;^M }^M }}} * private final IDevice target;^ {{{ public IDevice getDevice() {^M return target;^M }^M }}} * package com.android.ddmlib; * public interface IDevice extends IShellEnabledDevice * public RawImage getScreenshot() {{{ /** * Takes a screen shot of the device and returns it as a {@link RawImage}. * * @return the screenshot as a RawImage or null if something * went wrong. * @throws TimeoutException in case of timeout on the connection. * @throws AdbCommandRejectedException if adb rejects the command * @throws IOException in case of I/O error on the connection. */ }}} * /GetPreferredSize