· KLDP.org · KLDP.net · KLDP Wiki · KLDP BBS ·
Android Mirroring/Device Frame

/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<AndroidDeviceListener> deviceListeners = new ArrayList<AndroidDeviceListener>();
    • private final DeviceTableModel deviceTableModel = new DeviceTableModel();

    • public void updateDeviceFramePositionsOnScreen(DeviceFrame newFrame)
      • synchronized (deviceTableModel)
        • List<DeviceFrame> devices = deviceTableModel.getDevices();

    • public List<DeviceFrame> 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();

DeviceFrame.png
[PNG image (13.09 KB)]
    • 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<DeviceFrame>
    • 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 <code>RawImage</code> or <code>null</code> 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.
     */




sponsored by andamiro
sponsored by cdnetworks
sponsored by HP

Valid XHTML 1.0! Valid CSS! powered by MoniWiki
last modified 2016-10-22 16:21:14
Processing time 0.0264 sec