diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6a44e89 --- /dev/null +++ b/.gitignore @@ -0,0 +1,38 @@ +# built application files +*.apk +*.ap_ + +# files for the dex VM +*.dex + +# Java class files +*.class + +# generated files +bin/ +gen/ +build/ +target/ + +# Local configuration file (sdk path, etc) +local.properties + +# Eclipse project files +.classpath +.project +.settings + +# Proguard folder generated by Eclipse +proguard/ + +# Intellij project files +*.iml +*.ipr +*.iws +.idea/ + +# OSX +.DS_Store + +# Gradle +.gradle diff --git a/MODULE_LICENSE_APACHE2 b/MODULE_LICENSE_APACHE2 new file mode 100644 index 0000000..e69de29 diff --git a/NOTICE b/NOTICE new file mode 100644 index 0000000..b383510 --- /dev/null +++ b/NOTICE @@ -0,0 +1,191 @@ + + Copyright (c) 2009-2013, adakoda + Copyright (c) 2009-2013, Poly's Factory + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + + 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. + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + diff --git a/libs/bluecove-2.1.2.jar b/libs/bluecove-2.1.2.jar new file mode 100644 index 0000000..8b8d504 Binary files /dev/null and b/libs/bluecove-2.1.2.jar differ diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..008af3a --- /dev/null +++ b/pom.xml @@ -0,0 +1,120 @@ + + + + 4.0.0 + + com.polysfactory.myglazz + myglazz-awt + 0.1.0-SNAPSHOT + + MyGlazz + https://github.com/thorikawa/MyGlazz + + + UTF-8 + UTF-8 + + + + https://github.com/thorikawa/MyGlazz + scm:git:git://github.com/thorikawa/MyGlazz.git + scm:git:ssh://git@github.com/thorikawa/MyGlazz.git + + + + GitHub Issues + http://github.com/thorikawa/MyGlazz/issues + + + + + Apache 2.0 + http://www.apache.org/licenses/LICENSE-2.0.txt + + + + + Poly's Factory + http://polysfactory.com + + + + + net.sf.bluecove + bluecove + 2.1.2 + system + ${project.basedir}/libs/bluecove-2.1.2.jar + + + com.polysfactory.lib.glass.bluetooth + glass-bluetooth + 0.1.0 + + + + + + + + org.sonatype.plugins + jarjar-maven-plugin + 1.7 + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.0 + + 1.6 + 1.6 + + + + + + + + org.sonatype.plugins + jarjar-maven-plugin + 1.7 + + + package + + jarjar + + + + net.sf.bluecove:bluecove + com.polysfactory.lib.glass.bluetooth:glass-bluetooth + + + + + + + + + org.apache.maven.plugins + maven-jar-plugin + + + + true + com.polysfactory.myglazz.awt.MyGlazz + + + + + + + diff --git a/src/main/java/com/polysfactory/myglazz/awt/AboutDialog.java b/src/main/java/com/polysfactory/myglazz/awt/AboutDialog.java new file mode 100644 index 0000000..8667bff --- /dev/null +++ b/src/main/java/com/polysfactory/myglazz/awt/AboutDialog.java @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2009-2013 adakoda + * Copyright (C) 2009-2013 Poly's Factory + * + * 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.polysfactory.myglazz.awt; + +import java.awt.BorderLayout; +import java.awt.Container; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.KeyEvent; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; + +import javax.swing.AbstractAction; +import javax.swing.InputMap; +import javax.swing.JButton; +import javax.swing.JComponent; +import javax.swing.JDialog; +import javax.swing.JLabel; +import javax.swing.JTextField; +import javax.swing.KeyStroke; +import javax.swing.border.EmptyBorder; + +@SuppressWarnings("serial") +public class AboutDialog extends JDialog { + + public AboutDialog(Frame owner, boolean modal) { + super(owner, modal); + + // Frame + setTitle("About MyGlazz"); + setBounds(0, 0, 320, 140); + setResizable(false); + + // Label + JLabel labelApp = new JLabel("MyGlazz Version 1.0.0"); + JLabel labelCopyright = new JLabel("Copyright (C) 2013 Poly's Factory All rights reserved."); + JTextField labelUrl = new JTextField("http://polysfactory.com/"); + labelUrl.setEditable(false); + labelUrl.setBorder(new EmptyBorder(0, 0, 0, 0)); + labelUrl.addMouseListener(new MouseListener() { + public void mouseReleased(MouseEvent arg0) { + } + + public void mousePressed(MouseEvent arg0) { + } + + public void mouseExited(MouseEvent arg0) { + } + + public void mouseEntered(MouseEvent arg0) { + + } + + public void mouseClicked(MouseEvent arg0) { + JTextField textField = (JTextField) arg0.getSource(); + textField.selectAll(); + } + }); + + // OK + JButton buttonOK = new JButton("OK"); + buttonOK.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + onOK(); + } + }); + + // Container + Container container1 = new Container(); + FlowLayout flowLayout = new FlowLayout(FlowLayout.CENTER, 5, 5); + container1.setLayout(flowLayout); + container1.add(labelApp); + container1.add(labelCopyright); + container1.add(labelUrl); + container1.add(buttonOK); + + Container containger = getContentPane(); + containger.add(container1, BorderLayout.CENTER); + containger.add(buttonOK, BorderLayout.SOUTH); + + // Key + { + AbstractAction actionOK = new AbstractAction() { + public void actionPerformed(ActionEvent e) { + onOK(); + } + }; + AbstractAction actionCancel = new AbstractAction() { + public void actionPerformed(ActionEvent e) { + onCancel(); + } + }; + + JComponent targetComponent = getRootPane(); + InputMap inputMap = targetComponent.getInputMap(); + inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), "OK"); + inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "Cancel"); + targetComponent.setInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, inputMap); + targetComponent.getActionMap().put("OK", actionOK); + targetComponent.getActionMap().put("Cancel", actionCancel); + } + } + + private void onOK() { + dispose(); + } + + private void onCancel() { + dispose(); + } +} diff --git a/src/main/java/com/polysfactory/myglazz/awt/GlassConnection.java b/src/main/java/com/polysfactory/myglazz/awt/GlassConnection.java new file mode 100644 index 0000000..dcabfef --- /dev/null +++ b/src/main/java/com/polysfactory/myglazz/awt/GlassConnection.java @@ -0,0 +1,188 @@ +/* + * Copyright (C) 2013 Poly's Factory + * + * 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.polysfactory.myglazz.awt; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InterruptedIOException; +import java.io.OutputStream; +import java.util.TimeZone; + +import javax.bluetooth.BluetoothStateException; +import javax.bluetooth.DeviceClass; +import javax.bluetooth.DiscoveryAgent; +import javax.bluetooth.DiscoveryListener; +import javax.bluetooth.LocalDevice; +import javax.bluetooth.RemoteDevice; +import javax.bluetooth.ServiceRecord; +import javax.bluetooth.UUID; +import javax.microedition.io.Connector; +import javax.microedition.io.StreamConnection; + +import com.google.glass.companion.CompanionMessagingUtil; +import com.google.glass.companion.GlassProtocol; +import com.google.glass.companion.Proto.Envelope; +import com.google.glass.companion.Proto.ScreenShot; + +public class GlassConnection { + + private static final String SECURE_UUID = "F15CC914E4BC45CE9930CB7695385850"; + private DiscoveryAgent discoveryAgent; + private String connectionURL; + private StreamConnection streamConnection; + private OutputStream outStream; + private InputStream inStream; + private GlassConnectionListener listener; + private GlassReaderThread mGlassReaderThread; + + public GlassConnection() { + LocalDevice localDevice; + try { + localDevice = LocalDevice.getLocalDevice(); + discoveryAgent = localDevice.getDiscoveryAgent(); + } catch (BluetoothStateException e) { + e.printStackTrace(); + } + } + + public RemoteDevice[] getBondedDevices() { + return discoveryAgent.retrieveDevices(DiscoveryAgent.PREKNOWN); + } + + public void connect(RemoteDevice device) { + UUID[] uuidSet = new UUID[1]; + uuidSet[0] = new UUID(SECURE_UUID, false); + try { + discoveryAgent.searchServices(null, uuidSet, device, new Client()); + } catch (BluetoothStateException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + public void close() { + if (mGlassReaderThread != null) { + mGlassReaderThread.interrupt(); + try { + mGlassReaderThread.join(10000); + } catch (InterruptedException e1) { + e1.printStackTrace(); + } + } + if (streamConnection != null) { + try { + streamConnection.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + public void setListener(GlassConnectionListener listener) { + this.listener = listener; + } + + private class Client implements DiscoveryListener { + + @Override + public void deviceDiscovered(RemoteDevice arg0, DeviceClass arg1) { + // TODO Auto-generated method stub + System.out.println("deviceDiscovered"); + } + + @Override + public void inquiryCompleted(int arg0) { + // TODO Auto-generated method stub + System.out.println("inquiryCompleted"); + } + + @Override + public void serviceSearchCompleted(int transID, int respCode) { + // TODO Auto-generated method stub + System.out.println("serviceSearchCompleted"); + } + + @Override + public void servicesDiscovered(int transID, ServiceRecord[] servRecord) { + connectionURL = null; + if (servRecord != null && servRecord.length > 0) { + ServiceRecord service = servRecord[0]; + connectionURL = service.getConnectionURL(ServiceRecord.AUTHENTICATE_NOENCRYPT, false); + System.out.println(connectionURL); + } + + try { + streamConnection = (StreamConnection) Connector.open(connectionURL); + outStream = streamConnection.openOutputStream(); + inStream = streamConnection.openInputStream(); + mGlassReaderThread = new GlassReaderThread(); + mGlassReaderThread.start(); + + Envelope envelope = CompanionMessagingUtil.newEnvelope(); + envelope.timezoneC2G = TimeZone.getDefault().getID(); + write(envelope); + + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + Envelope envelope2 = CompanionMessagingUtil.newEnvelope(); + ScreenShot screenShot = new ScreenShot(); + screenShot.startScreenshotRequestC2G = true; + envelope2.screenshot = screenShot; + write(envelope2); + + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + public void write(Envelope envelope) { + try { + GlassProtocol.writeMessage(envelope, outStream); + } catch (IOException e) { + e.printStackTrace(); + } + } + + public interface GlassConnectionListener { + public abstract void onReceivedEnvelope(Envelope envelope); + } + + private class GlassReaderThread extends Thread { + public void run() { + try { + while (!Thread.currentThread().isInterrupted()) { + try { + Envelope envelope = (Envelope) GlassProtocol.readMessage(new Envelope(), inStream); + if (listener != null && envelope != null) { + listener.onReceivedEnvelope(envelope); + } + } catch (InterruptedIOException ie) { + Thread.currentThread().interrupt(); + } + } + System.out.println("Reader thread finished."); + } catch (IOException e) { + e.printStackTrace(); + } + }; + }; + +} diff --git a/src/main/java/com/polysfactory/myglazz/awt/MainFrame.java b/src/main/java/com/polysfactory/myglazz/awt/MainFrame.java new file mode 100644 index 0000000..be78399 --- /dev/null +++ b/src/main/java/com/polysfactory/myglazz/awt/MainFrame.java @@ -0,0 +1,584 @@ +/* + * Copyright (C) 2009-2013 adakoda + * Copyright (C) 2013 Poly's Factory + * + * 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.polysfactory.myglazz.awt; + +import java.awt.Color; +import java.awt.Graphics; +import java.awt.Image; +import java.awt.Insets; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +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.ByteArrayInputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.prefs.Preferences; + +import javax.bluetooth.BluetoothStateException; +import javax.bluetooth.RemoteDevice; +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.google.glass.companion.Proto.Envelope; +import com.polysfactory.myglazz.awt.GlassConnection.GlassConnectionListener; + +@SuppressWarnings("serial") +public class MainFrame extends JFrame implements GlassConnectionListener { + + private static final String TITLE = "MyGlazz"; + + private static final int DEFAULT_WIDTH = 640; + private static final int DEFAULT_HEIGHT = 360; + + private static final String EXT_PNG = "png"; + + 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 RemoteDevice mDevice; + + private GlassConnection mGlassConnection; + + public MainFrame(String[] args) { + initialize(args); + mGlassConnection = new GlassConnection(); + } + + public void selectDevice() { + mGlassConnection.setListener(null); + mGlassConnection.close(); + + RemoteDevice[] mDevices = null; + try { + mDevices = mGlassConnection.getBondedDevices(); + + if (mDevices != null) { + ArrayList list = new ArrayList(); + for (int i = 0; i < mDevices.length; i++) { + String name = mDevices[i].getFriendlyName(false) + ":" + mDevices[i].getBluetoothAddress(); + list.add(name); + } + 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]; + mGlassConnection.setListener(this); + mGlassConnection.connect(mDevice); + setImage(null); + } + } + } + } catch (BluetoothStateException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + 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 saveImage() { + BufferedImage inImage = mPanel.getImage(); + 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(BufferedImage fbImage) { + if (fbImage != null) { + mRawImageWidth = fbImage.getWidth(); + mRawImageHeight = fbImage.getHeight(); + } + mPanel.setFBImage(fbImage); + updateSize(); + } + + private void initialize(String[] args) { + + initializePrefs(); + initializeFrame(); + initializePanel(); + initializeMenu(); + initializeActionMap(); + + addMouseListener(mMouseListener); + addWindowListener(mWindowListener); + + pack(); + setImage(null); + } + + private void savePrefs() { + if (mPrefs != null) { + mPrefs.putInt("PrefVer", 1); + mPrefs.putBoolean("Portrait", mPortrait); + mPrefs.putDouble("Zoom", mZoom); + } + } + + 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); + } + } + } + + private void initializeFrame() { + setTitle(TITLE); + // 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(); + 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 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 MyGlazz"); + + 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 MyGlazz", actionAbout); + } + + private void initializeAbout() { + JMenuItem menuItemAbout = new JMenuItem("About MyGlazz"); + menuItemAbout.setMnemonic(KeyEvent.VK_A); + menuItemAbout.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + about(); + } + }); + mPopupMenu.add(menuItemAbout); + } + + private MouseListener mMouseListener = new MouseListener() { + public void mouseReleased(MouseEvent e) { + } + + public void mousePressed(MouseEvent e) { + } + + 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) { + // TODO + // if (mADB != null) { + // mADB.terminate(); + // } + } + + public void windowClosed(WindowEvent arg0) { + } + + public void windowActivated(WindowEvent arg0) { + } + }; + + public class MainPanel extends JPanel { + private BufferedImage mImage; + + public MainPanel() { + setBackground(Color.BLACK); + } + + @Override + protected void paintComponent(Graphics g) { + super.paintComponent(g); + if (mImage != 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(mImage, 0, 0, dstWidth, dstHeight, 0, 0, srcWidth, srcHeight, null); + } else { + Image image = mImage.getScaledInstance(dstWidth, dstHeight, Image.SCALE_SMOOTH); + if (image != null) { + g.drawImage(image, 0, 0, dstWidth, dstHeight, 0, 0, dstWidth, dstHeight, null); + } + } + } + } + + public void setFBImage(BufferedImage image) { + mImage = image; + repaint(); + } + + public BufferedImage getImage() { + return mImage; + } + } + + @Override + public void onReceivedEnvelope(Envelope envelope) { + if (envelope.screenshot != null) { + if (envelope.screenshot.screenshotBytesG2C != null) { + InputStream in = new ByteArrayInputStream(envelope.screenshot.screenshotBytesG2C); + try { + final BufferedImage image = ImageIO.read(in); + SwingUtilities.invokeLater(new Runnable() { + public void run() { + setImage(image); + } + }); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + } +} diff --git a/src/main/java/com/polysfactory/myglazz/awt/MyGlazz.java b/src/main/java/com/polysfactory/myglazz/awt/MyGlazz.java new file mode 100644 index 0000000..ca59078 --- /dev/null +++ b/src/main/java/com/polysfactory/myglazz/awt/MyGlazz.java @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2009-2013 adakoda + * Copyright (C) 2009-2013 Poly's Factory + * + * 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.polysfactory.myglazz.awt; + +import javax.swing.SwingUtilities; + +public class MyGlazz { + + private MainFrame mMainFrame; + private static String[] mArgs; + + public MyGlazz() { + } + + public void initialize() { + mMainFrame = new MainFrame(mArgs); + mMainFrame.setLocationRelativeTo(null); + mMainFrame.setVisible(true); + mMainFrame.setFocusable(true); + mMainFrame.selectDevice(); + } + + public static void main(String[] args) { + mArgs = args; + SwingUtilities.invokeLater(new Runnable() { + public void run() { + new MyGlazz().initialize(); + } + }); + } +} diff --git a/src/main/java/com/polysfactory/myglazz/awt/SelectDeviceDialog.java b/src/main/java/com/polysfactory/myglazz/awt/SelectDeviceDialog.java new file mode 100644 index 0000000..d76f2de --- /dev/null +++ b/src/main/java/com/polysfactory/myglazz/awt/SelectDeviceDialog.java @@ -0,0 +1,176 @@ +/* + * 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.polysfactory.myglazz.awt; + +import java.awt.BorderLayout; +import java.awt.Container; +import java.awt.Frame; +import java.awt.GridLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.KeyEvent; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.util.ArrayList; + +import javax.swing.AbstractAction; +import javax.swing.DefaultListModel; +import javax.swing.InputMap; +import javax.swing.JButton; +import javax.swing.JComponent; +import javax.swing.JDialog; +import javax.swing.JList; +import javax.swing.JScrollPane; +import javax.swing.KeyStroke; + +@SuppressWarnings("serial") +public class SelectDeviceDialog extends JDialog { + private JList mList; + private JScrollPane mScrollPane; + private JButton mOK; + private JButton mCancel; + + private DefaultListModel mModel; + private boolean mIsOK = false; + private int mSelectedIndex = -1; + + public SelectDeviceDialog(Frame owner, boolean modal, ArrayList initialList) { + super(owner, modal); + + // Frame + { + setTitle("Select your Glass"); + setBounds(0, 0, 240, 164); + setResizable(false); + } + + // List + { + // Model + { + mModel = new DefaultListModel(); + for (int i = 0; i < initialList.size(); i++) { + mModel.addElement(initialList.get(i)); + } + } + + mList = new JList(mModel); + if (mModel.getSize() > 0) { + mSelectedIndex = 0; + mList.setSelectedIndex(mSelectedIndex); + } + mList.addMouseListener(new MouseListener() { + public void mouseReleased(MouseEvent e) { + } + + public void mousePressed(MouseEvent e) { + } + + public void mouseExited(MouseEvent e) { + } + + public void mouseEntered(MouseEvent e) { + } + + public void mouseClicked(MouseEvent e) { + if (e.getClickCount() > 1) { + onOK(); + } + } + }); + + // Scroll pane + { + mScrollPane = new JScrollPane(mList); + mScrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED); + } + } + + // OK + { + mOK = new JButton("OK"); + mOK.setEnabled((mModel.getSize() > 0)); + mOK.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + onOK(); + } + }); + } + + // Cancel + { + mCancel = new JButton("Cancel"); + mCancel.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + onCancel(); + } + }); + } + + // Container + { + Container container1 = new Container(); + GridLayout gridLayout = new GridLayout(1, 2, 0, 0); + container1.setLayout(gridLayout); + container1.add(mOK); + container1.add(mCancel); + + Container containger = getContentPane(); + containger.add(mScrollPane, BorderLayout.CENTER); + containger.add(container1, BorderLayout.SOUTH); + } + + // Key + { + AbstractAction actionOK = new AbstractAction() { + public void actionPerformed(ActionEvent e) { + onOK(); + } + }; + AbstractAction actionCancel = new AbstractAction() { + public void actionPerformed(ActionEvent e) { + onCancel(); + } + }; + + JComponent targetComponent = getRootPane(); + InputMap inputMap = targetComponent.getInputMap(); + inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), "OK"); + inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "Cancel"); + targetComponent.setInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, inputMap); + targetComponent.getActionMap().put("OK", actionOK); + targetComponent.getActionMap().put("Cancel", actionCancel); + } + } + + public int getSelectedIndex() { + return mSelectedIndex; + } + + public boolean isOK() { + return mIsOK; + } + + private void onOK() { + mSelectedIndex = mList.getSelectedIndex(); + mIsOK = true; + dispose(); + } + + private void onCancel() { + dispose(); + } +}