content: /*
   * Copyright (C) 2009-2013 adakoda
   *
   * 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.adakoda.android.asm;
  
  import java.awt.Color;
  import java.awt.Graphics;
  import java.awt.Image;
  import java.awt.Insets;
  import java.awt.Point;
  import java.awt.Toolkit;
  import java.awt.event.ActionEvent;
  import java.awt.event.ActionListener;
  import java.awt.event.InputEvent;
  import java.awt.event.KeyEvent;
  import java.awt.event.KeyListener;
  import java.awt.event.MouseEvent;
  import java.awt.event.MouseListener;
  import java.awt.event.MouseMotionListener;
  import java.awt.event.WindowEvent;
  import java.awt.event.WindowListener;
  import java.awt.geom.AffineTransform;
  import java.awt.image.AffineTransformOp;
  import java.awt.image.BufferedImage;
  import java.io.File;
  import java.io.IOException;
  import java.util.ArrayList;
  import java.util.Collection;
  import java.util.prefs.Preferences;
  
  import javax.imageio.ImageIO;
  import javax.swing.AbstractAction;
  import javax.swing.ButtonGroup;
  import javax.swing.InputMap;
  import javax.swing.JComponent;
  import javax.swing.JFileChooser;
  import javax.swing.JFrame;
  import javax.swing.JMenu;
  import javax.swing.JMenuItem;
  import javax.swing.JOptionPane;
  import javax.swing.JPanel;
  import javax.swing.JPopupMenu;
  import javax.swing.JRadioButtonMenuItem;
  import javax.swing.KeyStroke;
  import javax.swing.SwingUtilities;
  import javax.swing.event.PopupMenuEvent;
  import javax.swing.event.PopupMenuListener;
  import javax.swing.filechooser.FileFilter;
  
  import com.android.chimpchat.adb.AdbChimpDevice;
  import com.android.ddmlib.AdbCommandRejectedException;
  import com.android.ddmlib.IDevice;
  import com.android.ddmlib.RawImage;
  import com.android.ddmlib.TimeoutException;
  
  @SuppressWarnings("serial")
  public class MainFrame extends JFrame {
  	private static final int DEFAULT_WIDTH = 320;
  	private static final int DEFAULT_HEIGHT = 480;
  	
  	private static final String EXT_PNG = "png";
  	
  	private static final int FB_TYPE_XBGR = 0;
  	private static final int FB_TYPE_RGBX = 1;
  	private static final int FB_TYPE_XRGB = 2;
  
  	private static int[][] FB_OFFSET_LIST = {
  			{0, 1, 2, 3}, // XBGR : Normal
  			{3, 2, 1, 0}, // RGBX : Xperia Arc
  			{2, 1, 0, 3}  // XRGB : FireFox OS(B2G)
  	};
  	
  	private MainPanel mPanel;
  	private JPopupMenu mPopupMenu;
  
  	private Preferences mPrefs;
  	private int mRawImageWidth = DEFAULT_WIDTH;
  	private int mRawImageHeight = DEFAULT_HEIGHT;
  	private boolean mPortrait = true;
  	private double mZoom = 1.0;
  	private int mFbType = FB_TYPE_XBGR;
  
  	private ADB mADB;
  	private IDevice[] mDevices;
  	private IDevice mDevice;
  	private AdbChimpDevice mChimpDevice;
  
  	private MonitorThread mMonitorThread;
  
  	private String mBuildDevice = "";
  
  	public MainFrame(String[] args) {
  		initialize(args);
  	}
  
  	public void startMonitor() {
  		mMonitorThread = new MonitorThread();
  		mMonitorThread.start();
  	}
  
  	public void stopMonitor() {
  		mMonitorThread = null;
  	}
  
  	public void selectDevice() {
  		stopMonitor();
  
  		mDevices = mADB.getDevices();
  		if (mDevices != null) {
  			ArrayList<String> list = new ArrayList<String>();
  			for (int i = 0; i < mDevices.length; i++) {
  				list.add(mDevices[i].toString());
  			}
  			SelectDeviceDialog dialog = new SelectDeviceDialog(this, true, list);
  			dialog.setLocationRelativeTo(this);
  			dialog.setVisible(true);
  			if (dialog.isOK()) {
  				int selectedIndex = dialog.getSelectedIndex();
  				if (selectedIndex >= 0) {
  					mDevice = mDevices[selectedIndex];
  					setImage(null);
  				}
  			}
  		}
  
  		mChimpDevice = new AdbChimpDevice(mDevice);
  		mBuildDevice = mChimpDevice.getProperty("build.device");
  
  		startMonitor();
  	}
  
  	public void setOrientation(boolean portrait) {
  		if (mPortrait != portrait) {
  			mPortrait = portrait;
  			savePrefs();
  			updateSize();
  		}
  	}
  
  	public void setZoom(double zoom) {
  		if (mZoom != zoom) {
  			mZoom = zoom;
  			savePrefs();
  			updateSize();
  		}
  	}
  
  	public void setFrameBuffer(int fbType) {
  		if (mFbType != fbType) {
  			mFbType = fbType;
  			savePrefs();
  		}
  	}
  	
  	public void saveImage() {
  		FBImage inImage = mPanel.getFBImage();
  		if (inImage != null) {
  			BufferedImage outImage = new BufferedImage((int) (inImage
  					.getWidth() * mZoom), (int) (inImage.getHeight() * mZoom),
  					inImage.getType());
  			if (outImage != null) {
  				AffineTransformOp op = new AffineTransformOp(AffineTransform
  						.getScaleInstance(mZoom, mZoom),
  						AffineTransformOp.TYPE_BILINEAR);
  				op.filter(inImage, outImage);
  				JFileChooser fileChooser = new JFileChooser();
  				fileChooser.setFileFilter(new FileFilter() {
  					@Override
  					public String getDescription() {
  						return "*." + EXT_PNG;
  					}
  
  					@Override
  					public boolean accept(File f) {
  						String ext = f.getName().toLowerCase();
  						return (ext.endsWith("." + EXT_PNG));
  					}
  				});
  				if (fileChooser.showSaveDialog(this) == JFileChooser.APPROVE_OPTION) {
  					try {
  						File file = fileChooser.getSelectedFile();
  						String path = file.getAbsolutePath();
  						if (!path.endsWith("." + EXT_PNG)) {
  							file = new File(path + "." + EXT_PNG);
  						}
  						ImageIO.write(outImage, EXT_PNG, file);
  					} catch (Exception ex) {
  						JOptionPane.showMessageDialog(this,
  								"Failed to save a image.", "Save Image",
  								JOptionPane.ERROR_MESSAGE);
  					}
  				}
  			}
  		}
  	}
  
  	public void about() {
  		AboutDialog dialog = new AboutDialog(this, true);
  		dialog.setLocationRelativeTo(this);
  		dialog.setVisible(true);
  	}
  
  	public void updateSize() {
  		int width;
  		int height;
  		if (mPortrait) {
  			width = mRawImageWidth;
  			height = mRawImageHeight;
  		} else {
  			width = mRawImageHeight;
  			height = mRawImageWidth;
  		}
  		Insets insets = getInsets();
  		int newWidth = (int) (width * mZoom) + insets.left + insets.right;
  		int newHeight = (int) (height * mZoom) + insets.top + insets.bottom;
  
  		// Known bug
  		// If new window size is over physical window size, cannot update window
  		// size...
  		// FIXME
  		if ((getWidth() != newWidth) || (getHeight() != newHeight)) {
  			setSize(newWidth, newHeight);
  		}
  	}
  
  	public void setImage(FBImage fbImage) {
  		if (fbImage != null) {
  			mRawImageWidth = fbImage.getRawWidth();
  			mRawImageHeight = fbImage.getRawHeight();
  		}
  		mPanel.setFBImage(fbImage);
  		updateSize();
  	}
  
  	private void initialize(String[] args) {
  		mADB = new ADB();
  		if (!mADB.initialize(args)) {
  			JOptionPane.showMessageDialog(this,
  				"Could not find adb, please install Android SDK and set path to adb.",
  				"Error", JOptionPane.ERROR_MESSAGE);
  		}
  
  		parseArgs(args);
  		
  		initializePrefs();
  		initializeFrame();
  		initializePanel();
  		initializeMenu();
  		initializeActionMap();
  
  		addMouseListener(mMouseListener);
  		addWindowListener(mWindowListener);
  		addMouseMotionListener(mMouseMotionListener);
  		addKeyListener(mKeyListener);
  
  		pack();
  		setImage(null);
  	}
  
  	private void parseArgs(String[] args) {
  		if (args != null) {
  			for (int i = 0; i < args.length; i++) {
  				String arg = args[i];
  				if (arg.equals("-f0")) {
  					mFbType = FB_TYPE_XBGR;
  				} else if (arg.equals("-f1")) {
  					mFbType = FB_TYPE_RGBX;
  				} else if (arg.equals("-f2")) {
  					mFbType = FB_TYPE_XRGB;
  				}
  			}
  		}
  	}
  	
  	private void savePrefs() {
  		if (mPrefs != null) {
  			mPrefs.putInt("PrefVer", 1);
  			mPrefs.putBoolean("Portrait", mPortrait);
  			mPrefs.putDouble("Zoom", mZoom);
  			mPrefs.putInt("FbType", mFbType);
  		}
  	}
  	
  	private void initializePrefs() {
  		mPrefs = Preferences.userNodeForPackage(this.getClass());
  		if (mPrefs != null) {
  			int prefVer = mPrefs.getInt("PrefVer", 1);
  			if (prefVer == 1) {
  				mPortrait = mPrefs.getBoolean("Portrait", true);
  				mZoom = mPrefs.getDouble("Zoom", 1.0);
  				mFbType = mPrefs.getInt("FbType", FB_TYPE_XBGR);
  			}
  		}
  	}
  	
  	private void initializeFrame() {
  		setTitle("Android Screen Monitor");
  		setIconImage(Toolkit.getDefaultToolkit().getImage(
  				getClass().getResource("icon.png")));
  		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  		setResizable(false);
  	}
  
  	private void initializePanel() {
  		mPanel = new MainPanel();
  		add(mPanel);
  	}
  
  	private void initializeMenu() {
  		mPopupMenu = new JPopupMenu();
  
  		initializeSelectDeviceMenu();
  		mPopupMenu.addSeparator();
  		initializeOrientationMenu();
  		initializeZoomMenu();
  		initializeFrameBufferMenu();
  		mPopupMenu.addSeparator();
  		initializeSaveImageMenu();
  		mPopupMenu.addSeparator();
  		initializeAbout();
  
  		mPopupMenu.addPopupMenuListener(new PopupMenuListener() {
  			public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
  			}
  
  			public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
  			}
  
  			public void popupMenuCanceled(PopupMenuEvent e) {
  			}
  		});
  	}
  
  	private void initializeSelectDeviceMenu() {
  		JMenuItem menuItemSelectDevice = new JMenuItem("Select Device...");
  		menuItemSelectDevice.setMnemonic(KeyEvent.VK_D);
  		menuItemSelectDevice.addActionListener(new ActionListener() {
  			public void actionPerformed(ActionEvent e) {
  				selectDevice();
  			}
  		});
  		mPopupMenu.add(menuItemSelectDevice);
  	}
  
  	private void initializeOrientationMenu() {
  		JMenu menuOrientation = new JMenu("Orientation");
  		menuOrientation.setMnemonic(KeyEvent.VK_O);
  		mPopupMenu.add(menuOrientation);
  
  		ButtonGroup buttonGroup = new ButtonGroup();
  
  		// Portrait
  		JRadioButtonMenuItem radioButtonMenuItemPortrait = new JRadioButtonMenuItem(
  				"Portrait");
  		radioButtonMenuItemPortrait.setMnemonic(KeyEvent.VK_P);
  		radioButtonMenuItemPortrait.addActionListener(new ActionListener() {
  			public void actionPerformed(ActionEvent e) {
  				setOrientation(true);
  			}
  		});
  		if (mPortrait) {
  			radioButtonMenuItemPortrait.setSelected(true);
  		}
  		buttonGroup.add(radioButtonMenuItemPortrait);
  		menuOrientation.add(radioButtonMenuItemPortrait);
  
  		// Landscape
  		JRadioButtonMenuItem radioButtonMenuItemLandscape = new JRadioButtonMenuItem(
  				"Landscape");
  		radioButtonMenuItemLandscape.setMnemonic(KeyEvent.VK_L);
  		radioButtonMenuItemLandscape.addActionListener(new ActionListener() {
  			public void actionPerformed(ActionEvent e) {
  				setOrientation(false);
  			}
  		});
  		if (!mPortrait) {
  			radioButtonMenuItemLandscape.setSelected(true);
  		}
  		buttonGroup.add(radioButtonMenuItemLandscape);
  		menuOrientation.add(radioButtonMenuItemLandscape);
  	}
  
  	private void initializeZoomMenu() {
  		JMenu menuZoom = new JMenu("Zoom");
  		menuZoom.setMnemonic(KeyEvent.VK_Z);
  		mPopupMenu.add(menuZoom);
  
  		ButtonGroup buttonGroup = new ButtonGroup();
  
  		addRadioButtonMenuItemZoom(menuZoom, buttonGroup, 0.1, "10%", -1, mZoom);
  		addRadioButtonMenuItemZoom(menuZoom, buttonGroup, 0.25, "25%", -1, mZoom);
  		addRadioButtonMenuItemZoom(menuZoom, buttonGroup, 0.5, "50%", KeyEvent.VK_5, mZoom);
  		addRadioButtonMenuItemZoom(menuZoom, buttonGroup, 0.75, "75%", KeyEvent.VK_7, mZoom);
  		addRadioButtonMenuItemZoom(menuZoom, buttonGroup, 1.0, "100%", KeyEvent.VK_1, mZoom);
  		addRadioButtonMenuItemZoom(menuZoom, buttonGroup, 1.5, "150%", KeyEvent.VK_0, mZoom);
  		addRadioButtonMenuItemZoom(menuZoom, buttonGroup, 2.0, "200%", KeyEvent.VK_2, mZoom);
  	}
  
  	private void addRadioButtonMenuItemZoom(
  			JMenu menuZoom, ButtonGroup buttonGroup,
  			final double zoom, String caption, int nemonic,
  			double currentZoom) {
  		JRadioButtonMenuItem radioButtonMenuItemZoom = new JRadioButtonMenuItem(caption);
  		if (nemonic != -1) {
  			radioButtonMenuItemZoom.setMnemonic(nemonic);
  		}
  		radioButtonMenuItemZoom.addActionListener(new ActionListener() {
  			public void actionPerformed(ActionEvent e) {
  				setZoom(zoom);
  			}
  		});
  		if (currentZoom == zoom) {
  			radioButtonMenuItemZoom.setSelected(true);
  		}
  		buttonGroup.add(radioButtonMenuItemZoom);
  		menuZoom.add(radioButtonMenuItemZoom);
  	}
  	
  	private void initializeFrameBufferMenu() {
  		JMenu menuZoom = new JMenu("FrameBuffer");
  		menuZoom.setMnemonic(KeyEvent.VK_F);
  		mPopupMenu.add(menuZoom);
  
  		ButtonGroup buttonGroup = new ButtonGroup();
  
  		// XBGR
  		JRadioButtonMenuItem radioButtonMenuItemFbXBGR = new JRadioButtonMenuItem(
  				"XBGR");
  		radioButtonMenuItemFbXBGR.addActionListener(new ActionListener() {
  			public void actionPerformed(ActionEvent e) {
  				setFrameBuffer(FB_TYPE_XBGR);
  			}
  		});
  		if (mFbType == FB_TYPE_XBGR) {
  			radioButtonMenuItemFbXBGR.setSelected(true);
  		}
  		buttonGroup.add(radioButtonMenuItemFbXBGR);
  		menuZoom.add(radioButtonMenuItemFbXBGR);
  
  		// RGBX
  		JRadioButtonMenuItem radioButtonMenuItemFbRGBX = new JRadioButtonMenuItem(
  				"RGBX");
  		radioButtonMenuItemFbRGBX.addActionListener(new ActionListener() {
  			public void actionPerformed(ActionEvent e) {
  				setFrameBuffer(FB_TYPE_RGBX);
  			}
  		});
  		if (mFbType == FB_TYPE_RGBX) {
  			radioButtonMenuItemFbRGBX.setSelected(true);
  		}
  		buttonGroup.add(radioButtonMenuItemFbRGBX);
  		menuZoom.add(radioButtonMenuItemFbRGBX);
  
  		// XRGB
  		JRadioButtonMenuItem radioButtonMenuItemFbXRGB = new JRadioButtonMenuItem(
  				"XRGB");
  		radioButtonMenuItemFbXRGB.addActionListener(new ActionListener() {
  			public void actionPerformed(ActionEvent e) {
  				setFrameBuffer(FB_TYPE_XRGB);
  			}
  		});
  		if (mFbType == FB_TYPE_XRGB) {
  			radioButtonMenuItemFbXRGB.setSelected(true);
  		}
  		buttonGroup.add(radioButtonMenuItemFbXRGB);
  		menuZoom.add(radioButtonMenuItemFbXRGB);
  	}
  
  	private void initializeSaveImageMenu() {
  		JMenuItem menuItemSaveImage = new JMenuItem("Save Image...");
  		menuItemSaveImage.setMnemonic(KeyEvent.VK_S);
  		menuItemSaveImage.addActionListener(new ActionListener() {
  			public void actionPerformed(ActionEvent e) {
  				saveImage();
  			}
  		});
  		mPopupMenu.add(menuItemSaveImage);
  	}
  
  	private void initializeActionMap() {
  		AbstractAction actionSelectDevice = new AbstractAction() {
  			public void actionPerformed(ActionEvent e) {
  				selectDevice();
  			}
  		};
  		AbstractAction actionPortrait = new AbstractAction() {
  			public void actionPerformed(ActionEvent e) {
  				setOrientation(true);
  			}
  		};
  		AbstractAction actionLandscape = new AbstractAction() {
  			public void actionPerformed(ActionEvent e) {
  				setOrientation(false);
  			}
  		};
  		AbstractAction actionZoom50 = new AbstractAction() {
  			public void actionPerformed(ActionEvent e) {
  				setZoom(0.5);
  			}
  		};
  		AbstractAction actionZoom75 = new AbstractAction() {
  			public void actionPerformed(ActionEvent e) {
  				setZoom(0.75);
  			}
  		};
  		AbstractAction actionZoom100 = new AbstractAction() {
  			public void actionPerformed(ActionEvent e) {
  				setZoom(1.0);
  			}
  		};
  		AbstractAction actionZoom150 = new AbstractAction() {
  			public void actionPerformed(ActionEvent e) {
  				setZoom(1.5);
  			}
  		};
  		AbstractAction actionZoom200 = new AbstractAction() {
  			public void actionPerformed(ActionEvent e) {
  				setZoom(2.0);
  			}
  		};
  		AbstractAction actionSaveImage = new AbstractAction() {
  			public void actionPerformed(ActionEvent e) {
  				saveImage();
  			}
  		};
  		AbstractAction actionAbout = new AbstractAction() {
  			public void actionPerformed(ActionEvent e) {
  				about();
  			}
  		};
  
  		JComponent targetComponent = getRootPane();
  		InputMap inputMap = targetComponent.getInputMap();
  
  		inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_D,
  				InputEvent.CTRL_DOWN_MASK), "Select Device");
  		inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_P,
  				InputEvent.CTRL_DOWN_MASK), "Portrait");
  		inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_L,
  				InputEvent.CTRL_DOWN_MASK), "Landscape");
  		inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_5,
  				InputEvent.CTRL_DOWN_MASK), "50%");
  		inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_7,
  				InputEvent.CTRL_DOWN_MASK), "75%");
  		inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_1,
  				InputEvent.CTRL_DOWN_MASK), "100%");
  		inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_0,
  				InputEvent.CTRL_DOWN_MASK), "150%");
  		inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_2,
  				InputEvent.CTRL_DOWN_MASK), "200%");
  		inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_S,
  				InputEvent.CTRL_DOWN_MASK), "Save Image");
  		inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_A,
  				InputEvent.CTRL_DOWN_MASK), "About ASM");
  
  		targetComponent.setInputMap(
  				JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, inputMap);
  
  		targetComponent.getActionMap().put("Select Device", actionSelectDevice);
  		targetComponent.getActionMap().put("Portrait", actionPortrait);
  		targetComponent.getActionMap().put("Landscape", actionLandscape);
  		targetComponent.getActionMap().put("Select Device", actionSelectDevice);
  		targetComponent.getActionMap().put("50%", actionZoom50);
  		targetComponent.getActionMap().put("75%", actionZoom75);
  		targetComponent.getActionMap().put("100%", actionZoom100);
  		targetComponent.getActionMap().put("150%", actionZoom150);
  		targetComponent.getActionMap().put("200%", actionZoom200);
  		targetComponent.getActionMap().put("Save Image", actionSaveImage);
  		targetComponent.getActionMap().put("About ASM", actionAbout);
  	}
  
  	private void initializeAbout() {
  		JMenuItem menuItemAbout = new JMenuItem("About ASM");
  		menuItemAbout.setMnemonic(KeyEvent.VK_A);
  		menuItemAbout.addActionListener(new ActionListener() {
  			public void actionPerformed(ActionEvent e) {
  				about();
  			}
  		});
  		mPopupMenu.add(menuItemAbout);
  	}
  
  	private Point getRealPoint(Point p) {
  		int x = p.x;
  		int y = p.y;
  		Insets insets = getInsets();
  		int realX = (int) ((x - insets.left) / mZoom);
  		int realY = (int) ((y - insets.top) / mZoom);
  		return new Point(realX, realY);
  	}
  
  	private boolean isGlass() {
  		return mBuildDevice.startsWith("glass-");
  	}
  
  	private MouseListener mMouseListener = new MouseListener() {
  		public void mouseReleased(MouseEvent e) {
  			try {
  				if (isGlass()) {
  					// TODO
  				} else {
  					Point p = new Point(e.getX(), e.getY());
  					Point real = getRealPoint(p);
  					mChimpDevice.getManager().touchUp(real.x, real.y);
  				}
  			} catch (IOException ex) {
  				ex.printStackTrace();
  			}
  		}
  
  		public void mousePressed(MouseEvent e) {
  			try {
  				if (isGlass()) {
  					mChimpDevice.getManager().press("KEYCODE_DPAD_CENTER");
  				} else {
  					Point p = new Point(e.getX(), e.getY());
  					Point real = getRealPoint(p);
  					mChimpDevice.getManager().touchDown(real.x, real.y);
  				}
  			} catch (IOException ex) {
  				ex.printStackTrace();
  			}
  		}
  
  		public void mouseExited(MouseEvent e) {
  		}
  
  		public void mouseEntered(MouseEvent e) {
  		}
  
  		public void mouseClicked(MouseEvent e) {
  			if (SwingUtilities.isRightMouseButton(e)) {
  				mPopupMenu.show(e.getComponent(), e.getX(), e.getY());
  			}
  		}
  	};
  
  	private WindowListener mWindowListener = new WindowListener() {
  		public void windowOpened(WindowEvent arg0) {
  		}
  
  		public void windowIconified(WindowEvent arg0) {
  		}
  
  		public void windowDeiconified(WindowEvent arg0) {
  		}
  
  		public void windowDeactivated(WindowEvent arg0) {
  		}
  
  		public void windowClosing(WindowEvent arg0) {
  			if (mChimpDevice != null) {
  				mChimpDevice.dispose();
  			}
  			if (mADB != null) {
  				mADB.terminate();
  			}
  		}
  
  		public void windowClosed(WindowEvent arg0) {
  		}
  
  		public void windowActivated(WindowEvent arg0) {
  		}
  	};
  
  	private MouseMotionListener mMouseMotionListener = new MouseMotionListener() {
  
  		@Override
  		public void mouseMoved(MouseEvent e) {
  		}
  
  		@Override
  		public void mouseDragged(MouseEvent e) {
  			try {
  				Point p = new Point(e.getX(), e.getY());
  				Point real = getRealPoint(p);
  				mChimpDevice.getManager().touchMove(real.x, real.y);
  			} catch (IOException ex) {
  				ex.printStackTrace();
  			}
  		}
  	};
  
  	private KeyListener mKeyListener = new KeyListener() {
  
  		@Override
  		public void keyTyped(KeyEvent keyEvent) {
  		}
  
  		@Override
  		public void keyReleased(KeyEvent keyEvent) {
  		}
  
  		@Override
  		public void keyPressed(KeyEvent keyEvent) {
  			int code = keyEvent.getKeyCode();
  			char c = keyEvent.getKeyChar();
  			System.out.println(c + ":" + code + ":" + (int) c);
  			try {
  				switch (code) {
  				case KeyEvent.VK_BACK_SPACE:
  				case KeyEvent.VK_DELETE:
  					mChimpDevice.getManager().press("KEYCODE_DEL");
  					break;
  				case KeyEvent.VK_SPACE:
  					if (isGlass()) {
  						mChimpDevice.getManager().press("KEYCODE_DPAD_CENTER");
  					} else {
  						mChimpDevice.getManager().press("KEYCODE_SPACE");
  					}
  					break;
  				case KeyEvent.VK_ESCAPE:
  					mChimpDevice.getManager().press("KEYCODE_HOME");
  					break;
  				case KeyEvent.VK_LEFT:
  					mChimpDevice.getManager().press("KEYCODE_DPAD_LEFT");
  					break;
  				case KeyEvent.VK_RIGHT:
  					mChimpDevice.getManager().press("KEYCODE_DPAD_RIGHT");
  					break;
  				case KeyEvent.VK_UP:
  					mChimpDevice.getManager().press("KEYCODE_DPAD_UP");
  					break;
  				case KeyEvent.VK_DOWN:
  					mChimpDevice.getManager().press("KEYCODE_DPAD_DOWN");
  					break;
  				default:
  					mChimpDevice.getManager().type(c);
  					break;
  				}
  			} catch (IOException e) {
  				e.printStackTrace();
  			}
  		}
  	};
  
  	public class MainPanel extends JPanel {
  		private FBImage mFBImage;
  
  		public MainPanel() {
  			setBackground(Color.BLACK);
  		}
  
  		@Override
  		protected void paintComponent(Graphics g) {
  			super.paintComponent(g);
  			if (mFBImage != null) {
  				int srcWidth;
  				int srcHeight;
  				int dstWidth;
  				int dstHeight;
  				if (mPortrait) {
  					srcWidth = mRawImageWidth;
  					srcHeight = mRawImageHeight;
  				} else {
  					srcWidth = mRawImageHeight;
  					srcHeight = mRawImageWidth;
  				}
  				dstWidth = (int) (srcWidth * mZoom);
  				dstHeight = (int) (srcHeight * mZoom);
  				if (mZoom == 1.0) {
  					g.drawImage(mFBImage, 0, 0, dstWidth, dstHeight, 0, 0,
  							srcWidth, srcHeight, null);
  				} else {
  					Image image = mFBImage.getScaledInstance(dstWidth,
  							dstHeight, Image.SCALE_SMOOTH);
  					if (image != null) {
  						g.drawImage(image, 0, 0, dstWidth, dstHeight, 0, 0,
  								dstWidth, dstHeight, null);
  					}
  				}
  			}
  		}
  
  		public void setFBImage(FBImage fbImage) {
  			mFBImage = fbImage;
  			repaint();
  		}
  
  		public FBImage getFBImage() {
  			return mFBImage;
  		}
  	}
  	
  	public class MonitorThread extends Thread {
  
  		@Override
  		public void run() {
  			Thread thread = Thread.currentThread();
  			if (mDevice != null) {
  				try {
  					while (mMonitorThread == thread) {
  						final FBImage fbImage = getDeviceImage();
  						SwingUtilities.invokeLater(new Runnable() {
  							public void run() {
  								setImage(fbImage);
  							}
  						});
  					}
  				} catch (IOException e) {
  				}
  			}
  		}
  
  		private FBImage getDeviceImage() throws IOException {
  			boolean success = true;
  			boolean debug = false;
  			FBImage fbImage = null;
  			RawImage tmpRawImage = null;
  			RawImage rawImage = null;
  
  			if (success) {
  				try {
  					tmpRawImage = mDevice.getScreenshot();
  
  					if (tmpRawImage == null) {
  						success = false;
  					} else {
  						if (debug == false) {
  							rawImage = tmpRawImage;
  						} else {
  							rawImage = new RawImage();
  							rawImage.version = 1;
  							rawImage.bpp = 32;
  							rawImage.size = tmpRawImage.width
  									* tmpRawImage.height * 4;
  							rawImage.width = tmpRawImage.width;
  							rawImage.height = tmpRawImage.height;
  							rawImage.red_offset = 0;
  							rawImage.red_length = 8;
  							rawImage.blue_offset = 16;
  							rawImage.blue_length = 8;
  							rawImage.green_offset = 8;
  							rawImage.green_length = 8;
  							rawImage.alpha_offset = 0;
  							rawImage.alpha_length = 0;
  							rawImage.data = new byte[rawImage.size];
  
  							int index = 0;
  							int dst = 0;
  							for (int y = 0; y < rawImage.height; y++) {
  								for (int x = 0; x < rawImage.width; x++) {
  									int value = tmpRawImage.data[index++] & 0x00FF;
  									value |= (tmpRawImage.data[index++] << 8) & 0xFF00;
  									int r = ((value >> 11) & 0x01F) << 3;
  									int g = ((value >> 5) & 0x03F) << 2;
  									int b = ((value >> 0) & 0x01F) << 3;
  									// little endian
  									rawImage.data[dst++] = (byte) r;
  									rawImage.data[dst++] = (byte) g;
  									rawImage.data[dst++] = (byte) b;
  									rawImage.data[dst++] = (byte) 0xFF;
  //									// big endian
  //									rawImage.data[dst++] = (byte) 0xFF;
  //									rawImage.data[dst++] = (byte) b;
  //									rawImage.data[dst++] = (byte) g;
  //									rawImage.data[dst++] = (byte) r;
  								}
  							}
  						}
  					}
  				} catch (IOException ioe) {
  				} catch (TimeoutException e) {
  					e.printStackTrace();
  				} catch (AdbCommandRejectedException e) {
  					e.printStackTrace();
  				} finally {
  					if ((rawImage == null)
  							|| ((rawImage.bpp != 16) && (rawImage.bpp != 32))) {
  						success = false;
  					}
  				}
  			}
  
  			if (success) {
  				final int imageWidth;
  				final int imageHeight;
  
  				if (mPortrait) {
  					imageWidth = rawImage.width;
  					imageHeight = rawImage.height;
  				} else {
  					imageWidth = rawImage.height;
  					imageHeight = rawImage.width;
  				}
  
  				fbImage = new FBImage(imageWidth, imageHeight,
  						BufferedImage.TYPE_INT_RGB, // BufferedImage.TYPE_INT_ARGB
  						rawImage.width, rawImage.height);
  
  				final byte[] buffer = rawImage.data;
  				final int redOffset = rawImage.red_offset;
  				final int greenOffset = rawImage.green_offset;
  				final int blueOffset = rawImage.blue_offset;
  				final int alphaOffset = rawImage.alpha_offset;
  				final int redMask = getMask(rawImage.red_length);
  				final int greenMask = getMask(rawImage.green_length);
  				final int blueMask = getMask(rawImage.blue_length);
  				final int alphaMask = getMask(rawImage.alpha_length);
  				final int redShift = (8 - rawImage.red_length);
  				final int greenShift = (8 - rawImage.green_length);
  				final int blueShift = (8 - rawImage.blue_length);
  				final int alphaShift = (8 - rawImage.alpha_length);
  
  				int index = 0;
  
  				final int offset0;
  				final int offset1;
  				final int offset2;
  				final int offset3;
  				
  				if (rawImage.bpp == 16) {
  					offset0 = 0;
  					offset1 = 1;
  					if (mPortrait) {
  						int[] lineBuf = new int[rawImage.width];
  						for (int y = 0; y < rawImage.height; y++) {
  							for (int x = 0; x < rawImage.width; x++, index += 2) {
  								int value = buffer[index + offset0] & 0x00FF;
  								value |= (buffer[index + offset1] << 8) & 0xFF00;
  								int r = ((value >>> redOffset) & redMask) << redShift;
  								int g = ((value >>> greenOffset) & greenMask) << greenShift;
  								int b = ((value >>> blueOffset) & blueMask) << blueShift;
  								lineBuf[x] = 255 << 24 | r << 16 | g << 8 | b;
  							}
  							fbImage.setRGB(0, y, rawImage.width, 1, lineBuf, 0,
  									rawImage.width);
  						}
  					} else {
  						for (int y = 0; y < rawImage.height; y++) {
  							for (int x = 0; x < rawImage.width; x++) {
  								int value = buffer[index + offset0] & 0x00FF;
  								value |= (buffer[index + offset1] << 8) & 0xFF00;
  								int r = ((value >>> redOffset) & redMask) << redShift;
  								int g = ((value >>> greenOffset) & greenMask) << greenShift;
  								int b = ((value >>> blueOffset) & blueMask) << blueShift;
  								value = 255 << 24 | r << 16 | g << 8 | b;
  								index += 2;
  								fbImage
  										.setRGB(y, rawImage.width - x - 1,
  												value);
  							}
  						}
  					}
  				} else if (rawImage.bpp == 32) {
  					offset0 = FB_OFFSET_LIST[mFbType][0];
  					offset1 = FB_OFFSET_LIST[mFbType][1];
  					offset2 = FB_OFFSET_LIST[mFbType][2];
  					offset3 = FB_OFFSET_LIST[mFbType][3];					
  					if (mPortrait) {
  						int[] lineBuf = new int[rawImage.width];
  						for (int y = 0; y < rawImage.height; y++) {
  							for (int x = 0; x < rawImage.width; x++, index += 4) {
  								int value = buffer[index + offset0] & 0x00FF;
  								value |= (buffer[index + offset1] & 0x00FF) << 8;
  								value |= (buffer[index + offset2] & 0x00FF) << 16;
  								value |= (buffer[index + offset3] & 0x00FF) << 24;
  								final int r = ((value >>> redOffset) & redMask) << redShift;
  								final int g = ((value >>> greenOffset) & greenMask) << greenShift;
  								final int b = ((value >>> blueOffset) & blueMask) << blueShift;
  								final int a;
  								if (rawImage.alpha_length == 0) {
  									a = 0xFF;
  								} else {
  									a = ((value >>> alphaOffset) & alphaMask) << alphaShift;
  								}
  								lineBuf[x] = a << 24 | r << 16 | g << 8 | b;
  							}
  							fbImage.setRGB(0, y, rawImage.width, 1, lineBuf, 0,
  									rawImage.width);
  						}
  					} else {
  						for (int y = 0; y < rawImage.height; y++) {
  							for (int x = 0; x < rawImage.width; x++) {
  								int value;
  								value = buffer[index + offset0] & 0x00FF;
  								value |= (buffer[index + offset1] & 0x00FF) << 8;
  								value |= (buffer[index + offset2] & 0x00FF) << 16;
  								value |= (buffer[index + offset3] & 0x00FF) << 24;
  								final int r = ((value >>> redOffset) & redMask) << redShift;
  								final int g = ((value >>> greenOffset) & greenMask) << greenShift;
  								final int b = ((value >>> blueOffset) & blueMask) << blueShift;
  								final int a;
  								if (rawImage.alpha_length == 0) {
  									a = 0xFF;
  								} else {
  									a = ((value >>> alphaOffset) & alphaMask) << alphaShift;
  								}
  								value = a << 24 | r << 16 | g << 8 | b;
  								index += 4;
  								fbImage
  										.setRGB(y, rawImage.width - x - 1,
  												value);
  							}
  						}
  					}
  				}
  			}
  
  			return fbImage;
  		}
  		
  		public int getMask(int length) {
  	        int res = 0;
  	        for (int i = 0 ; i < length ; i++) {
  	            res = (res << 1) + 1;
  	        }
  
  	        return res;
  	    }
  	}
  }
  