Android Open Source - AndroidDesignPreview Proofer






From Project

Back to project page AndroidDesignPreview.

License

The source code is released under:

Apache License

If you think the Android project AndroidDesignPreview listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

/*
 * Copyright 2012 Google Inc.//  w w  w  .  j a v a 2  s  . co  m
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.android.desktop.proofer;

import com.google.android.desktop.proofer.os.OSBinder;

import java.awt.*;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.File;
import java.io.IOException;
import java.net.Socket;

import javax.imageio.ImageIO;

public class Proofer {
    public static final String SOURCE_TYPE_FILE = "file";
    public static final String SOURCE_TYPE_SCREEN = "screen";

    private boolean debug = Util.isDebug();

    private AdbRunner adbRunner;
    private ProoferClient client;

    private String sourceType = SOURCE_TYPE_SCREEN;
    private File file;
    private State state = State.Unknown;
    private ProoferCallbacks prooferCallbacks;

    public static interface ProoferCallbacks {
        public void onStateChange(State newState);
        public void onDeviceSizeChanged(Dimension size);
    }

    public static enum State {
        ConnectedActive,
        ConnectedIdle,
        Disconnected,
        Unknown,
    }

    public Proofer(ProoferCallbacks prooferCallbacks) {
        this.adbRunner = new AdbRunner();
        this.client = new ProoferClient();
        this.prooferCallbacks = prooferCallbacks;
    }

    public void startConnectionLoop() {
        new Thread(new Runnable() {
            public void run() {
                while (true) {
                    try {
                        client.connectAndWaitForRequests();
                    } catch (CannotConnectException e) {
                        // Can't connect to device, try re-setting up port forwarding.
                        // If no devices are connected, this will fail.
                        try {
                            setupPortForwarding();
                        } catch (ProoferException e2) {
                            // If we get an error here, we're disconnected.
                            updateState(State.Disconnected);
                        }
                    }

                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        break;
                    }
                }
            }
        }).start();
    }

    public void runAndroidApp() throws ProoferException {
        adbRunner.adb(new String[]{
                "shell", "am", "start",
                "-a", "android.intent.action.MAIN",
                "-c", "android.intent.category.LAUNCHER",
                "-n", Config.ANDROID_APP_PACKAGE_NAME + "/.DesktopViewerActivity"
        });
    }

    public void killAndroidApp() throws ProoferException {
        adbRunner.adb(new String[]{
                "shell", "am", "force-stop",
                Config.ANDROID_APP_PACKAGE_NAME
        });
    }

    public void uninstallAndroidApp() throws ProoferException {
        adbRunner.adb(new String[]{
                "uninstall", Config.ANDROID_APP_PACKAGE_NAME
        });
    }

    public void installAndroidApp(boolean force) throws ProoferException {
        if (force || !isAndroidAppInstalled()) {
            File apkPath = new File(Util.getCacheDirectory(), "Proofer.apk");
            if (Util.extractResource("assets/Proofer.apk", apkPath)) {
                adbRunner.adb(new String[]{
                        "install", "-r", apkPath.toString()
                });
            } else {
                throw new ProoferException("Error extracting Android APK.");
            }
        }
    }

    public void setupPortForwarding() throws ProoferException {
        try {
            adbRunner.adb(new String[]{
                    "forward", "tcp:" + Config.PORT_LOCAL, "tcp:" + Config.PORT_DEVICE
            });
        } catch (ProoferException e) {
            throw new ProoferException("Couldn't automatically setup port forwarding. "
                    + "You'll need to "
                    + "manually run "
                    + "\"adb forward tcp:" + Config.PORT_LOCAL + " "
                    + "tcp:" + Config.PORT_DEVICE + "\" "
                    + "on the command line.", e);
        }
    }

    public boolean isAndroidAppInstalled() throws ProoferException {
        String out = adbRunner.adb(new String[]{
                "shell", "pm", "list", "packages"
        });
        return out.contains(Config.ANDROID_APP_PACKAGE_NAME);
    }

    public void setSourceType(String sourceType) {
        this.sourceType = sourceType;
    }

    public String getSourceType() {
        return sourceType;
    }

    public File getFile() {
        return file;
    }

    public void setRequestedSourceRegion(Rectangle region) {
        client.setRequestedSourceRegion(region);
    }

    public void setImage(File file, BufferedImage image) {
        this.file = file;
        client.setImage(image);
    }

    private void updateState(State newState) {
        if (this.state != newState && debug) {
            switch (newState) {
                case ConnectedActive:
                    System.out.println("State: Connected and active");
                    break;
                case ConnectedIdle:
                    System.out.println("State: Connected and idle");
                    break;
                case Disconnected:
                    System.out.println("State: Disconnected");
                    break;
            }
        }

        if (this.state != newState && prooferCallbacks != null) {
            prooferCallbacks.onStateChange(newState);
        }

        this.state = newState;
    }

    public static class CannotConnectException extends ProoferException {
        public CannotConnectException(Throwable throwable) {
            super(throwable);
        }
    }

    private class ProoferClient {
        private Rectangle requestedSourceRegion = new Rectangle(0, 0, 0, 0);
        private BufferedImage forcedImage;
        private Robot robot;
        private Rectangle screenBounds;
        private Dimension currentDeviceSize = new Dimension();

        public ProoferClient() {
            try {
                this.robot = new Robot();
            } catch (AWTException e) {
                System.err.println("Error getting robot.");
                e.printStackTrace();
                System.exit(1);
            }

            GraphicsEnvironment environment = GraphicsEnvironment.getLocalGraphicsEnvironment();
            GraphicsDevice[] screenDevices = environment.getScreenDevices();

            Rectangle2D tempBounds = new Rectangle();
            for (GraphicsDevice screenDevice : screenDevices) {
                tempBounds = tempBounds.createUnion(
                        screenDevice.getDefaultConfiguration().getBounds());
            }
            screenBounds = tempBounds.getBounds();
        }

        public void setRequestedSourceRegion(Rectangle region) {
            requestedSourceRegion = region;
        }

        public void setImage(BufferedImage image) {
            this.forcedImage = image;
        }

        public void connectAndWaitForRequests() throws CannotConnectException {
            Socket socket;

            // Establish the connection.
            try {
                socket = new Socket("localhost", Config.PORT_LOCAL);
            } catch (IOException e) {
                throw new CannotConnectException(e);
            }

            if (debug) {
                System.out.println(
                        "Local socket established " + socket.getRemoteSocketAddress().toString());
            }

            // Wait for requests.
            try {
                DataInputStream dis = new DataInputStream(socket.getInputStream());
                BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());
                Dimension deviceSize = new Dimension();

                while (true) {
                    // Try processing a request.
                    dis.readInt(); // unused x
                    dis.readInt(); // unused y

                    deviceSize.width = dis.readInt();
                    deviceSize.height = dis.readInt();

                    // If we reach this point, we didn't hit an IOException and we've received
                    // a request from the device.

                    if (!deviceSize.equals(currentDeviceSize) && prooferCallbacks != null) {
                        prooferCallbacks.onDeviceSizeChanged(deviceSize);
                        if (debug) {
                            System.out.println("Got device size: " + currentDeviceSize.width
                                    + "x" + currentDeviceSize.height);
                        }
                        currentDeviceSize = new Dimension(deviceSize);
                    }

                    updateState(State.ConnectedActive);

                    if (deviceSize.width > 1 && deviceSize.height > 1) {
                        BufferedImage bi;
                        if (SOURCE_TYPE_FILE.equals(sourceType)) {
                            bi = forcedImage;
                        } else {
                            bi = capture();
                        }

                        ByteArrayOutputStream baos = new ByteArrayOutputStream();

                        if (bi != null) {
                            if (bi.getWidth() != currentDeviceSize.width ||
                                    bi.getHeight() != currentDeviceSize.height) {
                                // Scale the bitmap
                                BufferedImage resized = new BufferedImage(
                                        currentDeviceSize.width,
                                        currentDeviceSize.height,
                                        bi.getType());
                                Graphics2D g2d = resized.createGraphics();
                                g2d.setRenderingHint(
                                        RenderingHints.KEY_INTERPOLATION,
                                        RenderingHints.VALUE_INTERPOLATION_BILINEAR);
                                g2d.drawImage(
                                        bi,
                                        0, 0, currentDeviceSize.width, currentDeviceSize.height,
                                        0, 0, bi.getWidth(), bi.getHeight(),
                                        null);
                                g2d.dispose();
                                bi = resized;
                            }

                            ImageIO.write(bi, "PNG", baos);
                        } else {
                            baos.write(new byte[]{0});
                        }

                        byte[] out = baos.toByteArray();
                        int len = out.length;
                        byte[] outlen = new byte[4];
                        outlen[0] = (byte) ((len >> 24) & 0xFF);
                        outlen[1] = (byte) ((len >> 16) & 0xFF);
                        outlen[2] = (byte) ((len >> 8) & 0xFF);
                        outlen[3] = (byte) (len & 0xFF);

                        if (debug) {
                            System.out.println("Writing " + len + " bytes.");
                        }

                        bos.write(outlen, 0, 4);
                        bos.write(out, 0, len);
                        bos.flush();
                    }

                    // This loop will exit only when an IOException is thrown, indicating there's
                    // nothing further to read.
                }
            } catch (IOException e) {
                // If we're not "connected", this just means we haven't received any requests yet
                // on the socket, so there's no error to log.
                if (debug) {
                    System.out.println("No activity.");
                }
            }

            // No (or no more) requests.
            updateState(State.ConnectedIdle);
        }

        private BufferedImage capture() {
            Rectangle captureRect = new Rectangle(
                    Math.max(screenBounds.x, requestedSourceRegion.x),
                    Math.max(screenBounds.y, requestedSourceRegion.y),
                    requestedSourceRegion.width,
                    requestedSourceRegion.height);

            if (captureRect.x + captureRect.width > screenBounds.x + screenBounds.width) {
                captureRect.x = screenBounds.x + screenBounds.width - captureRect.width;
            }

            if (captureRect.y + captureRect.height > screenBounds.y + screenBounds.height) {
                captureRect.y = screenBounds.y + screenBounds.height - captureRect.height;
            }

            long before = System.currentTimeMillis();
            BufferedImage bi = robot.createScreenCapture(captureRect);
            long after = System.currentTimeMillis();

            if (debug) {
                System.out.println("Capture time: " + (after - before) + " msec");
            }

            return bi;
        }
    }
}




Java Source Code List

com.google.android.apps.proofer.DesktopViewerActivity.java
com.google.android.apps.proofer.SystemUiHider.java
com.google.android.desktop.proofer.AdbRunner.java
com.google.android.desktop.proofer.Config.java
com.google.android.desktop.proofer.ControllerForm.java
com.google.android.desktop.proofer.ProoferException.java
com.google.android.desktop.proofer.Proofer.java
com.google.android.desktop.proofer.RegionSelector.java
com.google.android.desktop.proofer.Util.java
com.google.android.desktop.proofer.os.DefaultBinder.java
com.google.android.desktop.proofer.os.MacBinder.java
com.google.android.desktop.proofer.os.OSBinder.java