· 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.
     */


ID
Password
Join
The person you rejected yesterday could make you happy, if you say yes.


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.0158 sec