├── LICENSE
├── app-debug.apk
├── app-release.apk
├── app
├── app.iml
├── build.gradle
├── lint.xml
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── mobi
│ │ └── omegacentauri
│ │ └── vectordisplay
│ │ ├── BluetoothService.java
│ │ ├── ConnectionService.java
│ │ ├── Coords.java
│ │ ├── DisplayState.java
│ │ ├── IntCoords.java
│ │ ├── MainActivity.java
│ │ ├── Options.java
│ │ ├── RecordAndPlay.java
│ │ ├── Transformation.java
│ │ ├── UsbService.java
│ │ ├── VectorAPI.java
│ │ ├── VectorView.java
│ │ ├── WifiService.java
│ │ └── commands
│ │ ├── AddButton.java
│ │ ├── Arc.java
│ │ ├── Attribute16.java
│ │ ├── Attribute32.java
│ │ ├── Attribute8.java
│ │ ├── Circle.java
│ │ ├── Clear.java
│ │ ├── Command.java
│ │ ├── DeleteButton.java
│ │ ├── DrawBitmap.java
│ │ ├── FillCircle.java
│ │ ├── FillPoly.java
│ │ ├── FillRectangle.java
│ │ ├── FillTriangle.java
│ │ ├── Initialize.java
│ │ ├── InitializeWithResolution.java
│ │ ├── Line.java
│ │ ├── Point.java
│ │ ├── PolyLine.java
│ │ ├── PopupMessage.java
│ │ ├── Reset.java
│ │ ├── RoundedRectangle.java
│ │ ├── Text.java
│ │ └── Update.java
│ └── res
│ ├── layout
│ └── activity_main.xml
│ ├── menu
│ └── menu.xml
│ ├── mipmap
│ └── icon.png
│ └── values
│ ├── arrays.xml
│ ├── colors.xml
│ └── strings.xml
├── build.gradle
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── icon.svg
├── import-summary.txt
├── local.properties
├── settings.gradle
├── test
├── arc.py
├── bitmap.py
├── bitmap_data.py
├── helper.py
├── nettest.py
├── poly.py
├── rects.py
├── roundedrects.py
├── serialtest.py
├── text.py
└── vectordisplay.py
├── usbserial-4.5.2-release
├── build.gradle
├── usbserial-4.5.2-release.aar
└── usbserial-4.5.2-release.iml
└── vectordisplay.iml
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | VectorDisplay Copyright (c) 2018 Omega Centauri Software
4 | UsbSerial library Copyright (c) 2014 Felipe Herranz
5 |
6 | Permission is hereby granted, free of charge, to any person obtaining a copy
7 | of this software and associated documentation files (the "Software"), to deal
8 | in the Software without restriction, including without limitation the rights
9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | copies of the Software, and to permit persons to whom the Software is
11 | furnished to do so, subject to the following conditions:
12 |
13 | The above copyright notice and this permission notice shall be included in all
14 | copies or substantial portions of the Software.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | SOFTWARE.
--------------------------------------------------------------------------------
/app-debug.apk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arpruss/VectorDisplay/286bf571c13b825e34b3911e5875d997b8519454/app-debug.apk
--------------------------------------------------------------------------------
/app-release.apk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arpruss/VectorDisplay/286bf571c13b825e34b3911e5875d997b8519454/app-release.apk
--------------------------------------------------------------------------------
/app/app.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | generateDebugSources
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 | android {
3 | compileSdkVersion 23
4 | buildToolsVersion "26.0.2"
5 | defaultConfig {
6 | applicationId "mobi.omegacentauri.vectordisplay"
7 | minSdkVersion 12
8 | targetSdkVersion 23
9 | versionCode 18
10 | versionName "0.38.0"
11 | }
12 | buildTypes {
13 | release {
14 | minifyEnabled false
15 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
16 | }
17 | }
18 | lintOptions {
19 | checkReleaseBuilds false
20 | // Or, if you prefer, you can continue to check for errors in release builds,
21 | // but continue the build even when errors are found:
22 | abortOnError false
23 | }
24 | productFlavors {
25 | }
26 | }
27 |
28 | dependencies {
29 | compile project(':usbserial-4.5.2-release')
30 | compile 'com.android.support:appcompat-v7:23.3.0'
31 | }
--------------------------------------------------------------------------------
/app/lint.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
6 |
7 |
8 |
9 |
10 |
17 |
18 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
38 |
39 |
42 |
43 |
44 |
47 |
48 |
49 |
52 |
53 |
54 |
55 |
56 |
57 |
--------------------------------------------------------------------------------
/app/src/main/java/mobi/omegacentauri/vectordisplay/BluetoothService.java:
--------------------------------------------------------------------------------
1 | package mobi.omegacentauri.vectordisplay;
2 |
3 | import android.bluetooth.BluetoothAdapter;
4 | import android.bluetooth.BluetoothDevice;
5 | import android.bluetooth.BluetoothSocket;
6 | import android.content.BroadcastReceiver;
7 | import android.content.Context;
8 | import android.content.Intent;
9 | import android.content.IntentFilter;
10 | import android.os.Build;
11 | import android.util.Log;
12 |
13 | import com.felhr.usbserial.UsbSerialInterface;
14 |
15 | import java.io.IOException;
16 | import java.io.InputStream;
17 | import java.io.OutputStream;
18 | import java.util.UUID;
19 |
20 | public class BluetoothService extends ConnectionService {
21 | public static final String EXTRA_DEVICE_ADDRESS = "deviceAddress";
22 | public static boolean SERVICE_CONNECTED = false;
23 | String btAddress = null;
24 | BluetoothDevice device = null;
25 | BluetoothSocket mSock = null;
26 | public static final String ACTION_BLUETOOTH_DEVICE_SELECTED="mobi.omegacentauri.vectordisplay.BLUETOOTH_DEVICE_SELECTED";
27 |
28 | private final BroadcastReceiver usbReceiver = new BroadcastReceiver() {
29 | @Override
30 | public void onReceive(Context arg0, Intent arg1) {
31 | if (arg1.getAction().equals(ACTION_BLUETOOTH_DEVICE_SELECTED)) {
32 | btAddress = arg1.getExtras().getString(EXTRA_DEVICE_ADDRESS);
33 | new ConnectionThread().start();
34 | }
35 | }
36 | };
37 | /*
38 | * Data received from serial port will be received here.
39 | */
40 | private UsbSerialInterface.UsbReadCallback mCallback = new UsbSerialInterface.UsbReadCallback() {
41 | @Override
42 | public void onReceivedData(byte[] data) {
43 | feed(data);
44 | }
45 | };
46 | private OutputStream out;
47 | private InputStream in;
48 |
49 | private void setFilter() {
50 | IntentFilter filter = new IntentFilter();
51 | filter.addAction(ACTION_BLUETOOTH_DEVICE_SELECTED);
52 | registerReceiver(usbReceiver, filter);
53 | }
54 | @Override
55 | synchronized public void onDestroy() {
56 | super.onDestroy();
57 | stopSocket();
58 | unregisterReceiver(usbReceiver);
59 | BluetoothService.SERVICE_CONNECTED = false;
60 | }
61 |
62 |
63 | @Override
64 | public void onCreate() {
65 | super.onCreate();
66 | btAddress = null;
67 | device = null;
68 | mSock = null;
69 | Log.v("VectorDisplay", "on create BluetoothService");
70 | BluetoothService.SERVICE_CONNECTED = true;
71 | setFilter();
72 | }
73 |
74 | @Override
75 | public void disconnectDevice() {
76 | stopSocket();
77 | }
78 |
79 | @Override
80 | synchronized public void close() {
81 | stopSocket();
82 | stopSelf();
83 | }
84 |
85 | @Override
86 | public void setRecord(RecordAndPlay r) {
87 | super.setRecord(r);
88 | }
89 |
90 | public void stopSocket() {
91 | if (mSock != null)
92 | broadcast(ACTION_DEVICE_DISCONNECTED);
93 | if (mSock != null) {
94 | try {
95 | mSock.close();
96 | } catch (IOException e1) {
97 | }
98 | mSock = null;
99 | }
100 | }
101 |
102 | @Override
103 | synchronized public void write(byte[] data) {
104 | if (out != null) {
105 | try {
106 | out.write(data);
107 | } catch (IOException e) {
108 | }
109 | }
110 | }
111 |
112 | /*
113 | * A simple thread to open a serial port.
114 | * Although it should be a fast operation. moving usb operations away from UI thread is a good thing.
115 | */
116 | private class ConnectionThread extends Thread {
117 | @Override
118 | public void run() {
119 | byte[] byteBuffer = new byte[256];
120 |
121 | BluetoothAdapter btAdapter = BluetoothAdapter.getDefaultAdapter();
122 | device = null;
123 | for (BluetoothDevice d : btAdapter.getBondedDevices())
124 | if (d.getAddress().equals(btAddress)) {
125 | device = d;
126 | break;
127 | }
128 |
129 | if (device == null) {
130 | broadcast(ACTION_DEVICE_UNSUPPORTED);
131 | return;
132 | }
133 |
134 | mSock = null;
135 | try {
136 | mSock = device.createInsecureRfcommSocketToServiceRecord(UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"));
137 | } catch (IOException e) {
138 | broadcast(ACTION_DEVICE_UNSUPPORTED);
139 | return;
140 | }
141 |
142 | try {
143 | BluetoothSocket sock = mSock; // local copy
144 | sock.connect();
145 | out = sock.getOutputStream();
146 | in = sock.getInputStream();
147 | broadcast(ConnectionService.ACTION_DEVICE_CONNECTED);
148 |
149 | while(Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH || sock.isConnected()) {
150 | int n = in.read(byteBuffer);
151 | record.feed(byteBuffer, n);
152 | }
153 | } catch (IOException e) {
154 | stopSocket();
155 | }
156 | }
157 | }
158 | }
159 |
--------------------------------------------------------------------------------
/app/src/main/java/mobi/omegacentauri/vectordisplay/ConnectionService.java:
--------------------------------------------------------------------------------
1 | package mobi.omegacentauri.vectordisplay;
2 |
3 | import android.app.Service;
4 | import android.content.Context;
5 | import android.content.Intent;
6 | import android.hardware.usb.UsbManager;
7 | import android.os.Binder;
8 | import android.os.IBinder;
9 | import android.util.Log;
10 |
11 | abstract class ConnectionService extends Service {
12 | public static final String ACTION_DEVICE_CONNECTED = "mobi.omegacentauri.vectordisplay.CONNECTED";
13 | public static final String ACTION_DEVICE_DISCONNECTED = "mobi.omegacentauri.vectordisplay.DISCONNECTED";
14 | public static final String ACTION_DEVICE_UNSUPPORTED = "mobi.omegacentauri.vectordisplay.UNSUPPORTED";
15 | protected IBinder binder = new ConnectionBinder();
16 | protected RecordAndPlay record;
17 | protected Context context;
18 |
19 | @Override
20 | public IBinder onBind(Intent intent) {
21 | return binder;
22 | }
23 |
24 | @Override
25 | public int onStartCommand(Intent intent, int flags, int startId) {
26 | return Service.START_NOT_STICKY;
27 | }
28 |
29 | protected void feed(byte[] data) {
30 | if (record != null)
31 | record.feed(data);
32 | }
33 |
34 | public class ConnectionBinder extends Binder {
35 | public ConnectionService getService() {
36 | return ConnectionService.this;
37 | }
38 | }
39 |
40 | public void broadcast(String msg) {
41 | Intent intent = new Intent(msg);
42 | Log.v("VectorDisplay", "sending "+intent.getAction());
43 | sendBroadcast(intent);
44 | }
45 |
46 | public void setRecord(RecordAndPlay r) {
47 | this.record = r;
48 | }
49 |
50 | abstract public void write(byte[] data);
51 | /*
52 | * onCreate will be executed when service is started. It configures an IntentFilter to listen for
53 | * incoming Intents (USB ATTACHED, USB DETACHED...) and it tries to open a serial port.
54 | */
55 |
56 | public void disconnectDevice() {
57 | broadcast(ACTION_DEVICE_DISCONNECTED);
58 | }
59 |
60 | abstract public void close();
61 |
62 | @Override
63 | public void onCreate() {
64 | this.context = this;
65 | MainActivity.log("service created "+this.getClass());
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/app/src/main/java/mobi/omegacentauri/vectordisplay/Coords.java:
--------------------------------------------------------------------------------
1 | package mobi.omegacentauri.vectordisplay;
2 |
3 | public class Coords {
4 | public float x;
5 | public float y;
6 |
7 | public Coords(float x, float y) {
8 | this.x = x;
9 | this.y = y;
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/app/src/main/java/mobi/omegacentauri/vectordisplay/DisplayState.java:
--------------------------------------------------------------------------------
1 | package mobi.omegacentauri.vectordisplay;
2 |
3 | import android.graphics.Canvas;
4 | import android.graphics.Color;
5 | import android.graphics.Paint;
6 | import android.graphics.Typeface;
7 | import android.text.TextPaint;
8 |
9 | public class DisplayState implements Cloneable {
10 | public float cursorX;
11 | public float cursorY;
12 | public boolean wrap;
13 | public int width;
14 | public int height;
15 | public float pixelAspectRatio;
16 | public int foreColor;
17 | public int backColor;
18 | public float thickness;
19 | public float textSize;
20 | public char hAlignText;
21 | public char vAlignText;
22 | public boolean opaqueTextBackground;
23 | public boolean continuousUpdate;
24 | public static final char ALIGN_LEFT = 'l';
25 | public static final char ALIGN_RIGHT = 'r';
26 | public static final char ALIGN_CENTER = 'c';
27 | public static final char ALIGN_TOP = 't';
28 | public static final char ALIGN_BOTTOM = 'b';
29 | public static final char ALIGN_BASELINE = 'l';
30 | public int textBackColor;
31 | public int textForeColor;
32 | public byte rotate;
33 | public float monoFontScaleX;
34 | public float monoFontScale;
35 | public boolean cp437;
36 | public boolean rounded;
37 | public byte fontInfo;
38 | public static final byte FONT_TYPEFACE_MASK = 0x7;
39 | public static final byte FONT_SERIF = 0;
40 | public static final byte FONT_SANS = 1;
41 | public static final byte FONT_MONO = 2;
42 | public static final byte FONT_ITALIC = 8;
43 | public static final byte FONT_BOLD = 16;
44 | public static final byte FONTINFO_MASK = 0x1F;
45 | public static TextPaint[] fontPaints = new TextPaint[FONTINFO_MASK+1];
46 |
47 | public Object clone() throws
48 | CloneNotSupportedException
49 | {
50 | return super.clone();
51 | }
52 |
53 | public void reset() {
54 | width = 240;
55 | height = 320;
56 | pixelAspectRatio = 1.0f;
57 | foreColor = Color.WHITE;
58 | backColor = Color.BLACK;
59 | textBackColor = Color.BLACK;
60 | textForeColor = Color.WHITE;
61 | thickness = 1f;
62 | textSize = 8;
63 | hAlignText = 'l';
64 | vAlignText = 't';
65 | fontInfo = FONT_MONO;
66 | rotate = 0;
67 | cp437 = false;
68 | opaqueTextBackground = true;
69 | continuousUpdate = true;
70 | rounded = true;
71 | measureMonoFont();
72 | wrap = true;
73 | cursorX = 0f;
74 | cursorY = 0f;
75 | }
76 |
77 | static public TextPaint getTextPaint(byte fontInfo) {
78 | int f = fontInfo & FONTINFO_MASK;
79 |
80 | TextPaint p = fontPaints[f];
81 |
82 | if (p == null) {
83 | p = new TextPaint();
84 | p.setStyle(Paint.Style.FILL);
85 | Typeface family;
86 | switch (f & FONT_TYPEFACE_MASK) {
87 | case FONT_SANS:
88 | family = Typeface.SANS_SERIF;
89 | break;
90 | case FONT_MONO:
91 | family = Typeface.MONOSPACE;
92 | break;
93 | default:
94 | family = Typeface.SERIF;
95 | break;
96 | }
97 |
98 | int style;
99 |
100 | switch (f & (FONT_BOLD | FONT_ITALIC)) {
101 | case FONT_BOLD:
102 | style = Typeface.BOLD;
103 | break;
104 | case FONT_ITALIC:
105 | style = Typeface.ITALIC;
106 | break;
107 | case FONT_BOLD | FONT_ITALIC:
108 | style = Typeface.BOLD_ITALIC;
109 | break;
110 | default:
111 | style = Typeface.NORMAL;
112 | break;
113 | }
114 |
115 | p.setTypeface(Typeface.create(family, style));
116 | fontPaints[f] = p;
117 | }
118 |
119 | return p;
120 | }
121 |
122 | public TextPaint getTextPaint() {
123 | return getTextPaint(fontInfo);
124 | }
125 |
126 | private void measureMonoFont() {
127 | TextPaint p = new TextPaint();
128 | p.setTypeface(Typeface.MONOSPACE);
129 | p.setTextSize(8f);
130 | float h = p.getFontMetrics().bottom - p.getFontMetrics().top;
131 | monoFontScale = 8f / h;
132 | p.setTextSize(monoFontScale * 8f);
133 | float w = p.measureText("0123456789")/10f;
134 | monoFontScaleX = 5f / w;
135 | }
136 |
137 | public DisplayState() {
138 | reset();
139 | }
140 |
141 | public float getAspectRatio() {
142 | return width*pixelAspectRatio/height;
143 | }
144 |
145 | public int rotatedWidth() {
146 | return rotate % 2 == 0 ? width : height;
147 | }
148 |
149 | public int rotatedHeight() {
150 | return rotate % 2 == 0 ? height : width;
151 | }
152 | }
153 |
--------------------------------------------------------------------------------
/app/src/main/java/mobi/omegacentauri/vectordisplay/IntCoords.java:
--------------------------------------------------------------------------------
1 | package mobi.omegacentauri.vectordisplay;
2 |
3 | public class IntCoords {
4 | public int x;
5 | public int y;
6 |
7 | public IntCoords(int x, int y) {
8 | this.x = x;
9 | this.y = y;
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/app/src/main/java/mobi/omegacentauri/vectordisplay/MainActivity.java:
--------------------------------------------------------------------------------
1 | package mobi.omegacentauri.vectordisplay;
2 |
3 | import android.bluetooth.BluetoothAdapter;
4 | import android.bluetooth.BluetoothDevice;
5 | import android.content.BroadcastReceiver;
6 | import android.content.ComponentName;
7 | import android.content.Context;
8 | import android.content.DialogInterface;
9 | import android.content.Intent;
10 | import android.content.IntentFilter;
11 | import android.content.ServiceConnection;
12 | import android.content.SharedPreferences;
13 | import android.content.pm.ActivityInfo;
14 | import android.content.res.Configuration;
15 | import android.net.Uri;
16 | import android.os.Bundle;
17 | import android.os.Handler;
18 | import android.os.IBinder;
19 | import android.os.Message;
20 | import android.preference.PreferenceManager;
21 | import android.support.v7.app.AlertDialog;
22 | import android.support.v7.app.AppCompatActivity;
23 | import android.text.Html;
24 | import android.util.Log;
25 | import android.view.Menu;
26 | import android.view.MenuInflater;
27 | import android.view.MenuItem;
28 | import android.view.View;
29 | import android.widget.AdapterView;
30 | import android.widget.ArrayAdapter;
31 | import android.widget.EditText;
32 | import android.widget.ListView;
33 | import android.widget.TextView;
34 | import android.widget.Toast;
35 |
36 | import java.lang.ref.WeakReference;
37 | import java.util.ArrayList;
38 | import java.util.Collections;
39 | import java.util.Comparator;
40 | import java.util.List;
41 | import java.util.Set;
42 |
43 | import mobi.omegacentauri.vectordisplay.commands.Clear;
44 | import mobi.omegacentauri.vectordisplay.commands.Reset;
45 |
46 | public class MainActivity extends AppCompatActivity {
47 | public static final String KEY_COMMAND = "cmd";
48 | public static final String KEY_LABEL = "label";
49 | public static final String KEY_ASPECT = "aspect";
50 | public int physicalWidth = -1;
51 | public int physicalHeight = -1;
52 | public RecordAndPlay record;
53 | SharedPreferences prefs;
54 | List userCommands;
55 | List userLabels;
56 | ArrayAdapter commandListAdapter = null;
57 | static final boolean DEBUG = false;
58 | ListView commandList;
59 | MyHandler commandHandler;
60 | public static final int ADD_COMMAND = 1;
61 | public static final int DELETE_COMMAND = 2;
62 | public static final int DELETE_ALL_COMMANDS = 3;
63 | public static final int ACK = 4;
64 | public static final int RESET_VIEW = 5;
65 | public static final int INVALIDATE_VIEW = 6;
66 | public static final int TOAST = 7;
67 | boolean attached;
68 | byte[] outBuf = new byte[8];
69 |
70 | static public void log(String s) {
71 | if (DEBUG)
72 | Log.v("VectorDisplay", s);
73 | }
74 |
75 | /*
76 | * Notifications from UsbService will be received here.
77 | */
78 | private final BroadcastReceiver mConnectionReceiver = new BroadcastReceiver() {
79 | @Override
80 | public void onReceive(Context context, Intent intent) {
81 | switch (intent.getAction()) {
82 | case ConnectionService.ACTION_DEVICE_CONNECTED:
83 | Toast.makeText(context, "Device ready", Toast.LENGTH_SHORT).show();
84 | // if (prefs.getBoolean(Options.PREF_RESET_ON_CONNECT, true))
85 | // record.feed(new Reset(record.parser.state));
86 | attached = true;
87 | break;
88 | case UsbService.ACTION_DEVICE_PERMISSION_NOT_GRANTED: // USB PERMISSION NOT GRANTED
89 | Toast.makeText(context, "Device permission not granted", Toast.LENGTH_SHORT).show();
90 | attached = false;
91 | break;
92 | case UsbService.ACTION_NO_USB: // NO USB CONNECTED
93 | Toast.makeText(context, "No USB device connected", Toast.LENGTH_SHORT).show();
94 | attached = false;
95 | break;
96 | case ConnectionService.ACTION_DEVICE_UNSUPPORTED: // USB DISCONNECTED
97 | Toast.makeText(context, "Device not supported", Toast.LENGTH_SHORT).show();
98 | attached = false;
99 | break;
100 | case ConnectionService.ACTION_DEVICE_DISCONNECTED: // USB DISCONNECTED
101 | Toast.makeText(context, "Device disconnected", Toast.LENGTH_SHORT).show();
102 | attached = false;
103 | break;
104 | case UsbService.ACTION_USB_NOT_SUPPORTED: // USB NOT SUPPORTED
105 | Toast.makeText(context, "USB device not supported", Toast.LENGTH_SHORT).show();
106 | attached = false;
107 | break;
108 | default:
109 | return;
110 | }
111 | if (record != null) {
112 | record.setConnected(attached);
113 | record.forceUpdate();
114 | }
115 | supportInvalidateOptionsMenu();
116 | }
117 | };
118 | public ConnectionService connectionService;
119 | private TextView display;
120 | private EditText editText;
121 | private final ServiceConnection connection = new ServiceConnection() {
122 | @Override
123 | public void onServiceConnected(ComponentName arg0, IBinder arg1) {
124 | synchronized (MainActivity.this) {
125 | connectionService = ((ConnectionService.ConnectionBinder) arg1).getService();
126 | Log.v("VectorDisplay", "setRecord");
127 | connectionService.setRecord(record);
128 | }
129 | }
130 |
131 | @Override
132 | public void onServiceDisconnected(ComponentName arg0) {
133 | synchronized (MainActivity.this) {
134 | connectionService = null;
135 | }
136 | }
137 | };
138 |
139 | private int connectionMode() {
140 | return prefs.getInt(Options.PREF_CONNECTION, Options.OPT_USB);
141 | }
142 |
143 | private void chooseConnection() {
144 | AlertDialog.Builder builder = new AlertDialog.Builder(this);
145 | CharSequence[] array = getResources().getStringArray(R.array.modes);
146 | final int oldConn = connectionMode();
147 | builder.setTitle("Select Connection")
148 | .setSingleChoiceItems(array, oldConn, new DialogInterface.OnClickListener() {
149 |
150 | @Override
151 | public void onClick(DialogInterface dialog, int which) {
152 | if (which == oldConn)
153 | return;
154 | prefs.edit().putInt(Options.PREF_CONNECTION, which).commit();
155 | if (connectionService != null)
156 | connectionService.close();
157 | disconnectService();
158 | connectService();
159 | supportInvalidateOptionsMenu();
160 | dialog.dismiss();
161 | }
162 | });
163 | builder.create().show();
164 | }
165 |
166 | private void chooseFPS() {
167 | AlertDialog.Builder builder = new AlertDialog.Builder(this);
168 | final CharSequence[] array = getResources().getStringArray(R.array.fpss);
169 | final int fps = prefs.getInt(Options.PREF_FPS, 30);
170 | int selected = -1;
171 | for (int i=0;i devs = new ArrayList();
197 | devs.addAll(btAdapter.getBondedDevices());
198 | if (devs == null) {
199 | Toast.makeText(this, "No paired Bluetooth devices", Toast.LENGTH_LONG).show();
200 | return;
201 | }
202 | Collections.sort(devs, new Comparator(){
203 | @Override
204 | public int compare(BluetoothDevice lhs, BluetoothDevice rhs) {
205 | return String.CASE_INSENSITIVE_ORDER.compare(lhs.getName(), rhs.getName());
206 | }});
207 | int selected = -1;
208 | String lastAddress = prefs.getString(Options.PREF_LAST_BLUETOOTH_ADDRESS, "");
209 | CharSequence[] devLabels = new CharSequence[devs.size()];
210 | for (int i=0; i();
251 | userLabels = new ArrayList();
252 |
253 | commandHandler = new MyHandler(this);
254 | record = new RecordAndPlay(this, commandHandler);
255 |
256 | setContentView(R.layout.activity_main);
257 |
258 | commandList = (ListView) findViewById(R.id.commands);
259 | commandListAdapter = new ArrayAdapter(this,
260 | android.R.layout.simple_list_item_1,
261 | android.R.id.text1,
262 | userLabels);
263 | commandList.setAdapter(commandListAdapter);
264 | commandList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
265 | @Override
266 | public void onItemClick(AdapterView> parent, View view, int position, long id) {
267 | outBuf[0] = 'B';
268 | outBuf[1] = 'T';
269 | outBuf[2] = userCommands.get(position);
270 | for (int i = 3; i < 8; i++)
271 | outBuf[i] = 0;
272 | synchronized (MainActivity.this) {
273 | if (connectionService != null)
274 | connectionService.write(outBuf);
275 | }
276 | }
277 | });
278 |
279 | setOrientation();
280 | // record = new RecordAndPlay(this, this);
281 | resetVectorView(this, record.parser.state.getAspectRatio());
282 |
283 | MainActivity.log("OnCreate");
284 | }
285 |
286 | private void stopServices() {
287 | stopService(new Intent(this, UsbService.class));
288 | stopService(new Intent(this, WifiService.class));
289 | stopService(new Intent(this, BluetoothService.class));
290 | }
291 |
292 | static void resetVectorView(MainActivity main, float aspectRatio) {
293 | MainActivity.log("reset to aspect "+aspectRatio);
294 | VectorView v = (VectorView) main.findViewById(R.id.vector);
295 | if (v != null) {
296 | v.aspectRatio = aspectRatio;
297 | v.getParent().requestLayout();
298 | v.forceLayout();
299 | }
300 | }
301 |
302 | void updateFPS() {
303 | record.updateTimeMillis = 1000 / (long) prefs.getInt(Options.PREF_FPS, 30);
304 | }
305 |
306 | @Override
307 | public void onResume() {
308 | super.onResume();
309 | updateFPS();
310 | connectService();
311 | }
312 |
313 | private void connectService() {
314 | Log.v("VectorDisplay", "connectService()");
315 | setFilters(); // Start listening notifications from UsbService
316 | switch (connectionMode()) {
317 | case Options.OPT_IP:
318 | Log.v("VectorDisplay", "starting wifi service");
319 | startService(WifiService.class, connection, null); // Start WifiService (if it was not started before) and Bind it
320 | record.setDisconnectedStatus(new String[]{"WiFi device not connected"});
321 | break;
322 | case Options.OPT_BLUETOOTH:
323 | Log.v("VectorDisplay", "starting bluetooth service");
324 | startService(BluetoothService.class, connection, null); // Start WifiService (if it was not started before) and Bind it
325 | record.setDisconnectedStatus(new String[]{"Start Bluetooth device and press CONNECT"});
326 | break;
327 | default:
328 | startService(UsbService.class, connection, null); // Start UsbService(if it was not started before) and Bind it
329 | record.setDisconnectedStatus(new String[]{"USB Disconnected"});
330 | break;
331 | }
332 | VectorView view = (VectorView)findViewById(R.id.vector);
333 | /* if (view != null) {
334 | view.invalidate();
335 | } */
336 | }
337 |
338 | @Override
339 | public void onPause() {
340 | super.onPause();
341 | disconnectService();
342 | }
343 |
344 | @Override
345 | public void onStop() {
346 | super.onStop();
347 | stopServices();
348 | }
349 |
350 | void disconnectService() {
351 | unregisterReceiver(mConnectionReceiver);
352 | try {
353 | unbindService(connection);
354 | } catch(Exception e) {}
355 | }
356 |
357 | private void startService(Class> service, ServiceConnection serviceConnection, Bundle extras) {
358 | if (!UsbService.SERVICE_CONNECTED) {
359 | Intent startService = new Intent(this, service);
360 | if (extras != null && !extras.isEmpty()) {
361 | Set keys = extras.keySet();
362 | for (String key : keys) {
363 | String extra = extras.getString(key);
364 | startService.putExtra(key, extra);
365 | }
366 | }
367 | startService(startService);
368 | }
369 | Intent bindingIntent = new Intent(this, service);
370 | bindService(bindingIntent, serviceConnection, Context.BIND_AUTO_CREATE);
371 | }
372 |
373 | private void setFilters() {
374 | IntentFilter filter = new IntentFilter();
375 | filter.addAction(ConnectionService.ACTION_DEVICE_CONNECTED);
376 | filter.addAction(UsbService.ACTION_NO_USB);
377 | filter.addAction(ConnectionService.ACTION_DEVICE_DISCONNECTED);
378 | filter.addAction(ConnectionService.ACTION_DEVICE_UNSUPPORTED);
379 | filter.addAction(UsbService.ACTION_USB_NOT_SUPPORTED);
380 | filter.addAction(UsbService.ACTION_DEVICE_PERMISSION_NOT_GRANTED);
381 | registerReceiver(mConnectionReceiver, filter);
382 | }
383 |
384 | @Override
385 | public boolean onCreateOptionsMenu(Menu menu) {
386 | int conn = connectionMode();
387 | MenuInflater inflater = getMenuInflater();
388 | inflater.inflate(R.menu.menu, menu);
389 | MenuItem item = menu.findItem(R.id.mode);
390 | item.setTitle(getResources().getStringArray(R.array.modes)[conn]);
391 | item = menu.findItem(R.id.disconnect);
392 | item.setVisible(attached);
393 | item = menu.findItem(R.id.connect);
394 | item.setVisible(!attached && conn == Options.OPT_BLUETOOTH);
395 |
396 | return super.onCreateOptionsMenu(menu);
397 | }
398 |
399 | @Override
400 | public boolean onOptionsItemSelected(MenuItem item) {
401 | int id = item.getItemId();
402 | if (id == R.id.clear) {
403 | record.feed(new Clear(record.parser.state));
404 | }
405 | else if (id == R.id.reset) {
406 | record.feed(new Reset(record.parser.state));
407 | }
408 | else if (id == R.id.rotate) {
409 | boolean landscape = ! prefs.getBoolean(Options.PREF_LANDSCAPE, false);
410 | prefs.edit().putBoolean(Options.PREF_LANDSCAPE, landscape).commit();
411 | setOrientation();
412 | }
413 | else if (id == R.id.fps) {
414 | chooseFPS();
415 | }
416 | else if (id == R.id.license) {
417 | message("The MIT License",
418 | "VectorDisplay Copyright (c) 2018 Omega Centauri Software
" +
419 | "UsbSerial library Copyright (c) 2014 Felipe Herranz
" +
420 | "Permission is hereby granted, free of charge, to any person obtaining a copy\n" +
421 | "of this software and associated documentation files (the \"Software\"), to deal\n" +
422 | "in the Software without restriction, including without limitation the rights\n" +
423 | "to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n" +
424 | "copies of the Software, and to permit persons to whom the Software is\n" +
425 | "furnished to do so, subject to the following conditions:
\n" +
426 | "\n" +
427 | "The above copyright notice and this permission notice shall be included in all\n" +
428 | "copies or substantial portions of the Software.
\n" +
429 | "\n" +
430 | "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n" +
431 | "IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n" +
432 | "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n" +
433 | "AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n" +
434 | "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n" +
435 | "OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n" +
436 | "SOFTWARE.", false);
437 | }
438 | else if (id == R.id.help) {
439 | Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://www.instructables.com/id/TabletPhone-As-Arduino-Screen-and-a-2-Oscilloscope/"));
440 | startActivity(browserIntent);
441 | }
442 | else if (id == R.id.mode) {
443 | chooseConnection();
444 | }
445 | else if (id == R.id.disconnect) {
446 | if (connectionService != null)
447 | connectionService.disconnectDevice();
448 | }
449 | else if (id == R.id.connect && connectionMode() == Options.OPT_BLUETOOTH) {
450 | chooseBluetoothDevice();
451 | }
452 | return super.onOptionsItemSelected(item);
453 | }
454 |
455 | private void message(String title, String msg, final boolean exit) {
456 | AlertDialog alertDialog = new AlertDialog.Builder(this).create();
457 |
458 | alertDialog.setTitle(title);
459 | alertDialog.setMessage(Html.fromHtml(msg));
460 | alertDialog.setButton(DialogInterface.BUTTON_POSITIVE,
461 | "OK",
462 | new DialogInterface.OnClickListener() {
463 | public void onClick(DialogInterface dialog, int which) {
464 | if(exit) finish();
465 | } });
466 | alertDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
467 | public void onCancel(DialogInterface dialog) {if(exit) finish();} });
468 | alertDialog.show();
469 | }
470 |
471 | public static void sendResetViewMessage(Handler h, DisplayState state) {
472 | Message msg = h.obtainMessage(RESET_VIEW);
473 | Bundle b = new Bundle();
474 | b.putFloat(KEY_ASPECT, state.getAspectRatio());
475 | msg.setData(b);
476 | h.sendMessage(msg);
477 | }
478 |
479 | private static class MyHandler extends Handler {
480 | private final WeakReference mActivity;
481 |
482 | public MyHandler(MainActivity activity) {
483 | super();
484 | mActivity = new WeakReference<>(activity);
485 | }
486 |
487 | @Override
488 | public void handleMessage(Message msg) {
489 | final MainActivity main = mActivity.get();
490 |
491 | if (main == null || main.commandListAdapter == null)
492 | return;
493 |
494 | if (msg.what == MainActivity.DELETE_ALL_COMMANDS) {
495 | main.userCommands.clear();
496 | main.commandListAdapter.clear();
497 | main.commandList.setVisibility(main.userCommands.size() > 0 ? View.VISIBLE : View.GONE);
498 | }
499 | else if (msg.what == MainActivity.ADD_COMMAND || msg.what == MainActivity.DELETE_COMMAND) {
500 | byte cmd = msg.getData().getByte(MainActivity.KEY_COMMAND);
501 | for (int i=0; i 0 ? View.VISIBLE : View.GONE);
512 | MainActivity.resetVectorView(main, main.record.parser.state.getAspectRatio());
513 | }
514 | else if (msg.what == MainActivity.ACK) {
515 | byte[] out = "Acknwld_".getBytes();
516 | out[7] = (byte)msg.arg1;
517 | synchronized(main) {
518 | if (main.connectionService != null)
519 | main.connectionService.write(out);
520 | }
521 | }
522 | else if (msg.what == MainActivity.RESET_VIEW) {
523 | MainActivity.log("resetting view");
524 | MainActivity.resetVectorView(main, msg.getData().getFloat(MainActivity.KEY_ASPECT));
525 | }
526 | else if (msg.what == MainActivity.INVALIDATE_VIEW) {
527 | main.record.forceUpdate();
528 | }
529 | else if (msg.what == MainActivity.TOAST) {
530 | String text = msg.getData().getString(MainActivity.KEY_LABEL);
531 | Log.v("VectorDisplay", "toast "+text);
532 | Toast.makeText(main, text, Toast.LENGTH_LONG).show();
533 | }
534 | }
535 | }
536 | }
537 |
538 |
--------------------------------------------------------------------------------
/app/src/main/java/mobi/omegacentauri/vectordisplay/Options.java:
--------------------------------------------------------------------------------
1 | package mobi.omegacentauri.vectordisplay;
2 |
3 | import android.content.SharedPreferences;
4 | import android.content.pm.ActivityInfo;
5 | import android.os.Bundle;
6 | import android.preference.PreferenceActivity;
7 | import android.preference.PreferenceManager;
8 |
9 | public class Options {
10 | public static final String PREF_LANDSCAPE = "landscape";
11 | public static final String PREF_RESET_ON_CONNECT = "resetOnConnect";
12 | public static final String PREF_FPS = "fps_int";
13 | public static final String PREF_CONNECTION = "connection";
14 | public static final int OPT_USB = 0;
15 | public static final int OPT_IP = 1;
16 | public static final int OPT_BLUETOOTH = 2;
17 | public static final int NUM_CONNECTION_OPTS = 3;
18 | public static final String PREF_LAST_BLUETOOTH_ADDRESS = "lastBT";
19 | /*
20 | @Override
21 | public void onCreate(Bundle icicle) {
22 | super.onCreate(icicle);
23 |
24 | addPreferencesFromResource(R.xml.options);
25 | //
26 | // Preference p = getPreferenceScreen().findPreference(PREF_VOLUME);
27 | // if (p != null) {
28 | // SpeakerBoost.log("Setting PREF_VOLUME to "+defaultShowVolume());
29 | // p.setDefaultValue(defaultShowVolume());
30 | // }
31 | }
32 |
33 | @Override
34 | public void onResume() {
35 | super.onResume();
36 |
37 | setRequestedOrientation(
38 | PreferenceManager.getDefaultSharedPreferences(this).getBoolean(Options.PREF_LANDSCAPE, false) ?
39 | ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE : ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
40 |
41 | }
42 |
43 | @Override
44 | public void onStop() {
45 | super.onStop();
46 | }
47 | */
48 | }
--------------------------------------------------------------------------------
/app/src/main/java/mobi/omegacentauri/vectordisplay/RecordAndPlay.java:
--------------------------------------------------------------------------------
1 | package mobi.omegacentauri.vectordisplay;
2 |
3 | import android.app.Activity;
4 | import android.graphics.Canvas;
5 | import android.graphics.Matrix;
6 | import android.os.Handler;
7 |
8 | import mobi.omegacentauri.vectordisplay.commands.Command;
9 | import mobi.omegacentauri.vectordisplay.commands.Update;
10 |
11 | /**
12 | * Created by Alexander_Pruss on 10/13/2017.
13 | */
14 |
15 | public class RecordAndPlay {
16 | private static final int MAX_ITEMS = 5000;
17 | private Command commands[] = new Command[MAX_ITEMS];
18 | public VectorAPI parser;
19 | private int head;
20 | private int tail;
21 | Activity context;
22 | boolean continuous = false;
23 | volatile long waitTime = -1;
24 | public static final int LAYOUT_DELAY = 250;
25 | Handler commandHandler;
26 | Transformation curMatrix = new Transformation();
27 | boolean connected = false;
28 | volatile boolean forcedUpdate = false;
29 | String[] disconnectedStatus = new String[] { "Disconnected" };
30 | volatile public long updateTimeMillis;
31 | // TODO: control update speed
32 | // TODO: delay rendering after view change
33 |
34 | public RecordAndPlay(Activity c, Handler h) {
35 | head = 0;
36 | tail = 0;
37 | parser = new VectorAPI(c);
38 | context = c;
39 | commandHandler = h;
40 | }
41 |
42 | synchronized public void feed(Command c) {
43 | if (MainActivity.DEBUG)
44 | MainActivity.log("Feeding "+c.getClass());
45 | if (c.handleCommand(commandHandler)) {
46 | waitTime = System.currentTimeMillis() + LAYOUT_DELAY; // TODO: fix this hack
47 | // so we correctly wait for the screen layout
48 | }
49 |
50 | continuous = c.state.continuousUpdate;
51 |
52 | if (c.needToClearHistory()) {
53 | head = tail;
54 | }
55 |
56 | if (c instanceof Update) {
57 | forceUpdate();
58 | }
59 |
60 | if (!c.doesDraw())
61 | return;
62 |
63 | commands[tail] = c;
64 | tail = (tail + 1) % MAX_ITEMS;
65 | if (tail == head)
66 | head = (head + 1) % MAX_ITEMS;
67 | }
68 |
69 | synchronized public void setConnected(boolean c) {
70 | connected = c;
71 | }
72 |
73 | public void setDisconnectedStatus(String[] lines) {
74 | disconnectedStatus = lines;
75 | }
76 |
77 | public void feed(byte[] data) {
78 | feed(data, data.length);
79 | }
80 |
81 | synchronized public void feed(byte[] data, int n) {
82 | for(int i=0; i= System.currentTimeMillis() || head == tail)
91 | return -1;
92 | if (continuous)
93 | return tail;
94 | int t0 = tail - 1;
95 | if (t0 < 0)
96 | t0 += MAX_ITEMS;
97 | while (t0 != head) {
98 | if (commands[t0] instanceof Update) {
99 | return (t0 + 1) % MAX_ITEMS;
100 | }
101 | t0--;
102 | if (t0<0)
103 | t0 = MAX_ITEMS - 1;
104 | }
105 | return -1;
106 | }
107 |
108 | synchronized public void draw(Canvas canvas) {
109 | MainActivity.log("drawing");
110 | forcedUpdate = false;
111 |
112 | int endPos = -1;
113 |
114 | endPos = findAdjustedTail();
115 |
116 | if (endPos < 0)
117 | return;
118 |
119 | while (head != endPos) {
120 | try {
121 | Command c = commands[head];
122 | synchronized (curMatrix) {
123 | curMatrix.update(canvas, c.state);
124 | canvas.setMatrix(curMatrix);
125 | }
126 | if (MainActivity.DEBUG)
127 | MainActivity.log("" + c.getClass());
128 | c.draw(canvas);
129 | }
130 | catch (Exception e) {} // don't crash all of the rendering thread in case of bug
131 | commands[head] = null;
132 | head = (head + 1) % MAX_ITEMS;
133 | }
134 | }
135 |
136 | synchronized public String[] getStatus() {
137 | if (connected)
138 | return null;
139 | else
140 | return disconnectedStatus;
141 | }
142 |
143 | public void forceUpdate() {
144 | forcedUpdate = true;
145 | }
146 |
147 | synchronized public boolean haveStuffToDraw() {
148 | return forcedUpdate || 0 <= findAdjustedTail();
149 | }
150 | }
151 |
--------------------------------------------------------------------------------
/app/src/main/java/mobi/omegacentauri/vectordisplay/Transformation.java:
--------------------------------------------------------------------------------
1 | package mobi.omegacentauri.vectordisplay;
2 |
3 | import android.graphics.Canvas;
4 | import android.graphics.Matrix;
5 |
6 | public class Transformation extends Matrix {
7 | int prevCanvasW=-1;
8 | int prevEmulatedW=-1;
9 | int prevCanvasH=-1;
10 | int prevEmulatedH=-1;
11 | byte prevRotate=-1;
12 |
13 | public Transformation() {
14 | super();
15 | }
16 |
17 | public void update(Canvas c, DisplayState state) {
18 | int width = c.getWidth();
19 | int height = c.getHeight();
20 | if (state.width == prevEmulatedW && state.height == prevEmulatedH &&
21 | state.rotate == prevRotate && width == prevCanvasW && height == prevCanvasH)
22 | return;
23 |
24 | if (state.width <= 1 || state.height <=1 || c.getWidth() <=1 || c.getHeight() <=1)
25 | return;
26 |
27 | setScale((float)(width)/(state.width), (float)(height)/(state.height));
28 | preTranslate(0.5f,0.5f);
29 | postRotate(state.rotate * 90, width / 2f, height / 2f);
30 | if ((state.rotate & 3) == 1) {
31 | float delta = (height-width)/2f;
32 | postTranslate(-delta, -delta);
33 | }
34 | else if ((state.rotate & 3) == 3) {
35 | float delta = (height-width)/2f;
36 | postTranslate(delta, delta);
37 | }
38 | prevEmulatedW = state.width;
39 | prevEmulatedH = state.height;
40 | prevRotate = state.rotate;
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/app/src/main/java/mobi/omegacentauri/vectordisplay/UsbService.java:
--------------------------------------------------------------------------------
1 | package mobi.omegacentauri.vectordisplay;
2 |
3 | import android.app.PendingIntent;
4 | import android.content.BroadcastReceiver;
5 | import android.content.Context;
6 | import android.content.Intent;
7 | import android.content.IntentFilter;
8 | import android.hardware.usb.UsbDevice;
9 | import android.hardware.usb.UsbDeviceConnection;
10 | import android.hardware.usb.UsbManager;
11 |
12 | import com.felhr.usbserial.CDCSerialDevice;
13 | import com.felhr.usbserial.UsbSerialDevice;
14 | import com.felhr.usbserial.UsbSerialInterface;
15 |
16 | import java.util.HashMap;
17 | import java.util.Map;
18 |
19 | public class UsbService extends ConnectionService {
20 | public static final String ACTION_DEVICE_ATTACHED = "android.hardware.usb.action.USB_DEVICE_ATTACHED";
21 | public static final String ACTION_DEVICE_DETACHED = "android.hardware.usb.action.USB_DEVICE_DETACHED";
22 | public static final String ACTION_USB_READY = "mobi.omegacentauri.vectordisplay.USB_READY";
23 | public static final String ACTION_DEVICE_PERMISSION_NOT_GRANTED = "mobi.omegacentauri.vectordisplay.USB_PERMISSION_NOT_GRANTED";
24 | public static final String ACTION_USB_NOT_SUPPORTED = "mobi.omegacentauri.vectordisplay.USB_NOT_SUPPORTED";
25 | public static final String ACTION_NO_USB = "mobi.omegacentauri.vectordisplay.NO_USB";
26 | public static final String ACTION_CDC_DRIVER_NOT_WORKING = "mobi.omegacentauri.vectordisplay.ACTION_CDC_DRIVER_NOT_WORKING";
27 | public static final String ACTION_USB_DEVICE_NOT_WORKING = "mobi.omegacentauri.vectordisplay.ACTION_USB_DEVICE_NOT_WORKING";
28 | public static final int MESSAGE_FROM_SERIAL_PORT = 0;
29 | public static final int CTS_CHANGE = 1;
30 | public static final int DSR_CHANGE = 2;
31 | public static final int SYNC_READ = 3;
32 | private static final String ACTION_USB_PERMISSION = "com.android.example.USB_PERMISSION";
33 | private static final int BAUD_RATE = 115200; // 115200*4; // 115200; // BaudRate. Change this value if you need
34 | public static boolean SERVICE_CONNECTED = false;
35 |
36 | private UsbManager usbManager;
37 | private UsbDevice device;
38 | private UsbDeviceConnection connection;
39 | private UsbSerialDevice serialPort;
40 |
41 | public boolean serialPortConnected;
42 | /*
43 | * Data received from serial port will be received here.
44 | */
45 | private UsbSerialInterface.UsbReadCallback mCallback = new UsbSerialInterface.UsbReadCallback() {
46 | @Override
47 | public void onReceivedData(byte[] data) {
48 | feed(data);
49 | }
50 | };
51 |
52 | @Override
53 | public void disconnectDevice() {
54 | super.disconnectDevice();
55 |
56 | if (serialPort != null)
57 | serialPort.close();
58 | }
59 |
60 | @Override
61 | public void onDestroy() {
62 | super.onDestroy();
63 | if (serialPort != null)
64 | serialPort.close();
65 | unregisterReceiver(usbReceiver);
66 | UsbService.SERVICE_CONNECTED = false;
67 | }
68 |
69 | /*
70 | * Different notifications from OS will be received here (USB attached, detached, permission responses...)
71 | * About BroadcastReceiver: http://developer.android.com/reference/android/content/BroadcastReceiver.html
72 | */
73 | private final BroadcastReceiver usbReceiver = new BroadcastReceiver() {
74 | @Override
75 | public void onReceive(Context arg0, Intent arg1) {
76 | if (arg1.getAction().equals(ACTION_USB_PERMISSION)) {
77 | boolean granted = arg1.getExtras().getBoolean(UsbManager.EXTRA_PERMISSION_GRANTED);
78 | if (granted) // User accepted our USB connection. Try to open the device as a serial port
79 | {
80 | Intent intent = new Intent(ACTION_DEVICE_CONNECTED);
81 | arg0.sendBroadcast(intent);
82 | connection = usbManager.openDevice(device);
83 | new ConnectionThread().start();
84 | } else // User not accepted our USB connection. Send an Intent to the Main Activity
85 | {
86 | Intent intent = new Intent(ACTION_DEVICE_PERMISSION_NOT_GRANTED);
87 | arg0.sendBroadcast(intent);
88 | }
89 | } else if (arg1.getAction().equals(ACTION_DEVICE_ATTACHED)) {
90 | if (!serialPortConnected)
91 | findSerialPortDevice(); // A USB device has been attached. Try to open it as a Serial port
92 | } else if (arg1.getAction().equals(ACTION_DEVICE_DETACHED)) {
93 | // Usb device was disconnected. send an intent to the Main Activity
94 | Intent intent = new Intent(ACTION_DEVICE_DISCONNECTED);
95 | arg0.sendBroadcast(intent);
96 | synchronized (this) {
97 | if (serialPortConnected) {
98 | serialPort.close(); // syncClose();
99 | }
100 | serialPortConnected = false;
101 | }
102 | }
103 | }
104 | };
105 |
106 | /*
107 | * onCreate will be executed when service is started. It configures an IntentFilter to listen for
108 | * incoming Intents (USB ATTACHED, USB DETACHED...) and it tries to open a serial port.
109 | */
110 | @Override
111 | public void onCreate() {
112 | super.onCreate();
113 | serialPortConnected = false;
114 | UsbService.SERVICE_CONNECTED = true;
115 | setFilter();
116 | usbManager = (UsbManager) getSystemService(Context.USB_SERVICE);
117 | findSerialPortDevice();
118 | }
119 |
120 | /* MUST READ about services
121 | * http://developer.android.com/guide/components/services.html
122 | * http://developer.android.com/guide/components/bound-services.html
123 | */
124 | /*
125 | * This function will be called from MainActivity to write data through Serial Port
126 | */
127 | @Override
128 | public void write(byte[] data) {
129 | if (serialPort != null)
130 | serialPort.write(data);
131 | }
132 |
133 | /*
134 | * This function will be called from MainActivity to change baud rate
135 | */
136 |
137 | public void changeBaudRate(int baudRate){
138 | if(serialPort != null)
139 | serialPort.setBaudRate(baudRate);
140 | }
141 |
142 | public void close() {
143 | if (serialPort != null)
144 | serialPort.close();
145 | stopSelf();
146 | }
147 |
148 | private void findSerialPortDevice() {
149 | // This snippet will try to open the first encountered usb device connected, excluding usb root hubs
150 | HashMap usbDevices = usbManager.getDeviceList();
151 | if (!usbDevices.isEmpty()) {
152 | boolean keep = true;
153 | for (Map.Entry entry : usbDevices.entrySet()) {
154 | device = entry.getValue();
155 | int deviceVID = device.getVendorId();
156 | int devicePID = device.getProductId();
157 |
158 | if (deviceVID != 0x1d6b && (devicePID != 0x0001 && devicePID != 0x0002 && devicePID != 0x0003)) {
159 | // There is a device connected to our Android device. Try to open it as a Serial Port.
160 | requestUserPermission();
161 | keep = false;
162 | } else {
163 | connection = null;
164 | device = null;
165 | }
166 |
167 | if (!keep)
168 | break;
169 | }
170 | if (!keep) {
171 | // There is no USB devices connected (but usb host were listed). Send an intent to MainActivity.
172 | broadcast(ACTION_NO_USB);
173 | }
174 | } else {
175 | // There is no USB devices connected. Send an intent to MainActivity
176 | broadcast(ACTION_NO_USB);
177 | }
178 | }
179 |
180 | private void setFilter() {
181 | IntentFilter filter = new IntentFilter();
182 | filter.addAction(ACTION_USB_PERMISSION);
183 | filter.addAction(ACTION_DEVICE_DETACHED);
184 | filter.addAction(ACTION_DEVICE_ATTACHED);
185 | registerReceiver(usbReceiver, filter);
186 | }
187 |
188 | /*
189 | * Request user permission. The response will be received in the BroadcastReceiver
190 | */
191 | private void requestUserPermission() {
192 | PendingIntent mPendingIntent = PendingIntent.getBroadcast(this, 0, new Intent(ACTION_USB_PERMISSION), 0);
193 | usbManager.requestPermission(device, mPendingIntent);
194 | }
195 |
196 | /*
197 | * A simple thread to open a serial port.
198 | * Although it should be a fast operation. moving usb operations away from UI thread is a good thing.
199 | */
200 | private class ConnectionThread extends Thread {
201 | @Override
202 | public void run() {
203 | serialPort = UsbSerialDevice.createUsbSerialDevice(device, connection);
204 | if (serialPort != null) {
205 | if (serialPort.open()) {
206 | serialPortConnected = true;
207 | serialPort.setBaudRate(BAUD_RATE);
208 | serialPort.setDataBits(UsbSerialInterface.DATA_BITS_8);
209 | serialPort.setStopBits(UsbSerialInterface.STOP_BITS_1);
210 | serialPort.setParity(UsbSerialInterface.PARITY_NONE);
211 | /**
212 | * Current flow control Options:
213 | * UsbSerialInterface.FLOW_CONTROL_OFF
214 | * UsbSerialInterface.FLOW_CONTROL_RTS_CTS only for CP2102 and FT232
215 | * UsbSerialInterface.FLOW_CONTROL_DSR_DTR only for CP2102 and FT232
216 | */
217 | serialPort.setFlowControl(UsbSerialInterface.FLOW_CONTROL_OFF);
218 | serialPort.read(mCallback);
219 | //serialPort.getCTS(ctsCallback);
220 | //serialPort.getDSR(dsrCallback);
221 |
222 | // Some Arduinos would need some sleep because firmware wait some time to know whether a new sketch is going
223 | // to be uploaded or not
224 | //Thread.sleep(2000); // sleep some. YMMV with different chips.
225 |
226 | // Everything went as expected. Send an intent to MainActivity
227 | broadcast(ACTION_USB_READY);
228 | } else {
229 | // Serial port could not be opened, maybe an I/O error or if CDC driver was chosen, it does not really fit
230 | // Send an Intent to Main Activity
231 | if (serialPort instanceof CDCSerialDevice) {
232 | broadcast(ACTION_CDC_DRIVER_NOT_WORKING);
233 | } else {
234 | broadcast(ACTION_USB_DEVICE_NOT_WORKING);
235 | }
236 | }
237 | } else {
238 | // No driver for given device, even generic CDC driver could not be loaded
239 | broadcast(ACTION_USB_NOT_SUPPORTED);
240 | }
241 | }
242 | }
243 | }
244 |
245 |
--------------------------------------------------------------------------------
/app/src/main/java/mobi/omegacentauri/vectordisplay/VectorAPI.java:
--------------------------------------------------------------------------------
1 | package mobi.omegacentauri.vectordisplay;
2 |
3 | import android.app.Activity;
4 | import android.util.Log;
5 |
6 | import mobi.omegacentauri.vectordisplay.commands.*;
7 |
8 | public class VectorAPI {
9 | public DisplayState state;
10 | private Class extends Command>[] map = new Class[256];
11 | private Command currentCommand = null;
12 | public MyBuffer buffer = new MyBuffer();
13 | private static final int TIMEOUT = 5000;
14 | private long commandStartTime;
15 | private Activity context;
16 | private byte lastChar = 0;
17 | public static final byte RESET_COMMAND = 'E';
18 | public static final byte INITIALIZE_COMMAND = 'H';
19 | public static final byte ATTRIBUTE32_COMMAND = 'B';
20 | public static final byte INITIALIZE_WITH_RESOLUTION_COMMAND = 'Z';
21 |
22 | public VectorAPI(Activity context) {
23 | this.context = context;
24 | this.state = new DisplayState();
25 | // need to have capital letters
26 | for(int i=0;i cl = map[lastChar & 0xFF];
78 | lastChar = 0;
79 | if (cl != null) {
80 | Command c;
81 | try {
82 | c = (Command) cl.getConstructor(DisplayState.class).newInstance(state);
83 | } catch (Exception e) {
84 | Log.e("VectorDisplay", e.toString());
85 | c = null;
86 | }
87 | if (c != null) {
88 | currentCommand = c;
89 | commandStartTime = System.currentTimeMillis();
90 | buffer.clear();
91 | }
92 | }
93 | }
94 | else {
95 | if (map[ch & 0xFF] != null) {
96 | lastChar = ch;
97 | }
98 | else {
99 | // MainActivity.log("Unparsed char "+Integer.toHexString(ch&0xFF));
100 | lastChar = 0;
101 | }
102 | }
103 | }
104 | return null;
105 | }
106 |
107 | public static class MyBuffer {
108 | static final int MAX_BUFFER = 1024*256;
109 | public byte[] data = new byte[MAX_BUFFER];
110 | public int length = 0;
111 | public boolean lowEndian = true;
112 | public static final char[] CP437 = {
113 | 0x0000,0x0001,0x0002,0x0003,0x0004,0x0005,0x0006,0x0007,0x0008,0x0009,0x000a,0x000b,0x000c,0x000d,0x000e,0x000f,
114 | 0x0010,0x0011,0x0012,0x0013,0x0014,0x0015,0x0016,0x0017,0x0018,0x0019,0x001a,0x001b,0x001c,0x001d,0x001e,0x001f,
115 | 0x0020,0x0021,0x0022,0x0023,0x0024,0x0025,0x0026,0x0027,0x0028,0x0029,0x002a,0x002b,0x002c,0x002d,0x002e,0x002f,
116 | 0x0030,0x0031,0x0032,0x0033,0x0034,0x0035,0x0036,0x0037,0x0038,0x0039,0x003a,0x003b,0x003c,0x003d,0x003e,0x003f,
117 | 0x0040,0x0041,0x0042,0x0043,0x0044,0x0045,0x0046,0x0047,0x0048,0x0049,0x004a,0x004b,0x004c,0x004d,0x004e,0x004f,
118 | 0x0050,0x0051,0x0052,0x0053,0x0054,0x0055,0x0056,0x0057,0x0058,0x0059,0x005a,0x005b,0x005c,0x005d,0x005e,0x005f,
119 | 0x0060,0x0061,0x0062,0x0063,0x0064,0x0065,0x0066,0x0067,0x0068,0x0069,0x006a,0x006b,0x006c,0x006d,0x006e,0x006f,
120 | 0x0070,0x0071,0x0072,0x0073,0x0074,0x0075,0x0076,0x0077,0x0078,0x0079,0x007a,0x007b,0x007c,0x007d,0x007e,0x007f,
121 | 0x00c7,0x00fc,0x00e9,0x00e2,0x00e4,0x00e0,0x00e5,0x00e7,0x00ea,0x00eb,0x00e8,0x00ef,0x00ee,0x00ec,0x00c4,0x00c5,
122 | 0x00c9,0x00e6,0x00c6,0x00f4,0x00f6,0x00f2,0x00fb,0x00f9,0x00ff,0x00d6,0x00dc,0x00a2,0x00a3,0x00a5,0x20a7,0x0192,
123 | 0x00e1,0x00ed,0x00f3,0x00fa,0x00f1,0x00d1,0x00aa,0x00ba,0x00bf,0x2310,0x00ac,0x00bd,0x00bc,0x00a1,0x00ab,0x00bb,
124 | 0x2591,0x2592,0x2593,0x2502,0x2524,0x2561,0x2562,0x2556,0x2555,0x2563,0x2551,0x2557,0x255d,0x255c,0x255b,0x2510,
125 | 0x2514,0x2534,0x252c,0x251c,0x2500,0x253c,0x255e,0x255f,0x255a,0x2554,0x2569,0x2566,0x2560,0x2550,0x256c,0x2567,
126 | 0x2568,0x2564,0x2565,0x2559,0x2558,0x2552,0x2553,0x256b,0x256a,0x2518,0x250c,0x2588,0x2584,0x258c,0x2590,0x2580,
127 | 0x03b1,0x00df,0x0393,0x03c0,0x03a3,0x03c3,0x00b5,0x03c4,0x03a6,0x0398,0x03a9,0x03b4,0x221e,0x03c6,0x03b5,0x2229,
128 | 0x2261,0x00b1,0x2265,0x2264,0x2320,0x2321,0x00f7,0x2248,0x00b0,0x2219,0x00b7,0x221a,0x207f,0x00b2,0x25a0,0x00a0,
129 | };
130 |
131 | void clear() {
132 | length = 0;
133 | }
134 |
135 | boolean put(byte b) {
136 | if (length < MAX_BUFFER) {
137 | data[length++] = b;
138 | return true;
139 | }
140 | else {
141 | return false;
142 | }
143 | }
144 |
145 | public boolean checksum() {
146 | int sum = 0;
147 | for (int i = 0; i< length -1; i++) {
148 | sum = ((data[i] & 0xFF) + sum) & 0xFF;
149 | }
150 | sum ^= 0xFF;
151 | // Log.v("VectorDisplay", "checksum "+data[length-1]+" vs "+sum);
152 | return data[length -1] == (byte)sum;
153 | }
154 |
155 | public short getShort(int start) {
156 | if (lowEndian)
157 | return (short) ((data[start] & 0xFF) | (data[start+1] << 8));
158 | else
159 | return (short) ((data[start+1] & 0xFF) | (data[start] << 8));
160 | }
161 |
162 | public int getInt(int start) {
163 | if (lowEndian)
164 | return (data[start] & 0xFF) | ((data[start+1] & 0xFF) << 8) |
165 | ((data[start+2] & 0xFF) << 16) | ((data[start+3] & 0xFF) << 24);
166 | else
167 | return (data[start+3] & 0xFF) | ((data[start+2] & 0xFF) << 8) |
168 | ((data[start+1] & 0xFF) << 16) | ((data[start] & 0xFF) << 24);
169 | }
170 |
171 | String getString(int start, DisplayState state) {
172 | return getString(start, length -start, state);
173 | }
174 |
175 | public String getString(int start, int length, DisplayState state) {
176 | if (state.cp437) {
177 | char[] out = new char[length];
178 | for (int i=0; i>5) & ((1<<6)-1)) * 255 + (1<<5)) / ((1<<6)-1);
204 | int b = ((c>>11) * 255 + (1<<4)) / ((1<<5)-1);
205 | return (0xFF<<24)|(r<<16)|(g<<8)|b;
206 | }
207 |
208 | public float getFixed32(int i) {
209 | return getInt(i) / 65536f;
210 | }
211 | }
212 | }
213 |
--------------------------------------------------------------------------------
/app/src/main/java/mobi/omegacentauri/vectordisplay/VectorView.java:
--------------------------------------------------------------------------------
1 | package mobi.omegacentauri.vectordisplay;
2 |
3 | import android.content.Context;
4 | import android.graphics.Bitmap;
5 | import android.graphics.Canvas;
6 | import android.graphics.Color;
7 | import android.graphics.Matrix;
8 | import android.graphics.Paint;
9 | import android.graphics.Rect;
10 | import android.graphics.RectF;
11 | import android.graphics.Typeface;
12 | import android.os.Build;
13 | import android.text.TextPaint;
14 | import android.util.AttributeSet;
15 | import android.util.Log;
16 | import android.util.TypedValue;
17 | import android.view.MotionEvent;
18 | import android.view.Surface;
19 | import android.view.SurfaceHolder;
20 | import android.view.SurfaceView;
21 | import android.view.View;
22 |
23 | import static android.util.TypedValue.*;
24 |
25 | /**
26 | * Created by Alexander_Pruss on 10/13/2017.
27 | */
28 |
29 | public class VectorView extends SurfaceView implements SurfaceHolder.Callback {
30 | RecordAndPlay record;
31 | float aspectRatio = 4f/3f;
32 | MainActivity main;
33 | static final int MOTION_TIMING = 30;
34 | static final byte[] UP = new byte[] { 'U', 'P'};
35 | static final byte[] DOWN = new byte[] { 'D', 'N'};
36 | static final byte[] MOVE = new byte[] { 'M', 'V'};
37 | long lastEvent = -MOTION_TIMING;
38 | byte[] outBuf = new byte[8];
39 | float[] coords = new float[2];
40 | TextPaint statusPaint;
41 | MySurfaceThread thread;
42 | private Canvas savedCanvas;
43 | Paint paint = new Paint();
44 | volatile long waitForSurfaceChanged = -1;
45 |
46 | @Override
47 | protected void onMeasure(int wspec, int hspec) {
48 | int w = View.MeasureSpec.getSize(wspec);
49 | int h = View.MeasureSpec.getSize(hspec);
50 | MainActivity.log( "onmeasure "+w+" "+h+" want "+aspectRatio );
51 | if (h != 0 && aspectRatio != 0f) {
52 | if ((float) w / h >= aspectRatio) {
53 | w = (int) (h * aspectRatio);
54 | } else {
55 | h = (int) (w / aspectRatio);
56 | }
57 | }
58 | if (w==0)
59 | w=1;
60 | if (h==0)
61 | h=1;
62 | setMeasuredDimension(w, h);
63 | }
64 |
65 | /* @Override
66 | public void onLayout(boolean changed, int l, int t, int r, int b) {
67 | super.onLayout(changed, l, t, r, b);
68 | MainActivity.log("onLayout1");
69 | } */
70 |
71 | public VectorView(Context c, AttributeSet set) {
72 | super(c,set);
73 |
74 | Log.v("VectorDisplay", "creating vector view");
75 |
76 | getHolder().addCallback(this);
77 | setZOrderOnTop(true);
78 |
79 | main = (MainActivity)c;
80 |
81 | statusPaint = new TextPaint();
82 | statusPaint.setStyle(Paint.Style.FILL);
83 | statusPaint.setTypeface(Typeface.MONOSPACE);
84 | statusPaint.setColor(Color.YELLOW);
85 | statusPaint.setTextAlign(Paint.Align.CENTER);
86 |
87 | //this.redraw = true;
88 | this.record = main.record;
89 |
90 | setOnTouchListener(new OnTouchListener() {
91 | @Override
92 | public boolean onTouch(View v, MotionEvent event) {
93 | if (record == null || record.parser == null || record.parser.state == null)
94 | return true;
95 | if (event.getAction() == MotionEvent.ACTION_UP) {
96 | outBuf[0] = 'U';
97 | outBuf[1] = 'P';
98 | } else if (event.getAction() == MotionEvent.ACTION_DOWN) {
99 | outBuf[0] = 'D';
100 | outBuf[1] = 'N';
101 | } else if (event.getAction() == MotionEvent.ACTION_MOVE &&
102 | System.currentTimeMillis() >= lastEvent + MOTION_TIMING) {
103 | outBuf[0] = 'M';
104 | outBuf[1] = 'V';
105 | } else {
106 | return true;
107 | }
108 | lastEvent = System.currentTimeMillis();
109 | Matrix inv = new Matrix();
110 | synchronized(record.curMatrix) {
111 | if (!record.curMatrix.invert(inv))
112 | inv.reset();
113 | }
114 | coords[0] = event.getX();
115 | coords[1] = event.getY();
116 | inv.mapPoints(coords);
117 | int x = (int)(coords[0]+0.5f);
118 | int y = (int)(coords[1]+0.5f);
119 |
120 | if (record.parser.buffer.lowEndian) {
121 | outBuf[2] = (byte) (x & 0xFF);
122 | outBuf[3] = (byte) (x >> 8);
123 | outBuf[4] = (byte) (y & 0xFF);
124 | outBuf[5] = (byte) (y >> 8);
125 | }
126 | else {
127 | outBuf[3] = (byte) (x & 0xFF);
128 | outBuf[2] = (byte) (x >> 8);
129 | outBuf[5] = (byte) (y & 0xFF);
130 | outBuf[4] = (byte) (y >> 8);
131 | }
132 | outBuf[6] = 0;
133 | outBuf[7] = 0;
134 | synchronized(main) {
135 | if (main.connectionService != null)
136 | main.connectionService.write(outBuf);
137 | }
138 | return true;
139 | }
140 | });
141 | }
142 |
143 | public void updateStatus(Canvas canvas) {
144 | String[] status = record.getStatus();
145 | if (status != null) {
146 | int w = canvas.getWidth();
147 | statusPaint.setTextSize(w / 15f);
148 | float lineHeight = -(statusPaint.getFontMetrics().top - statusPaint.getFontMetrics().bottom) * 1.1f;
149 | float y = canvas.getHeight() - lineHeight * (status.length - 1) - statusPaint.descent();
150 | for (String line : status) {
151 | statusPaint.setTextSize(w / 15f);
152 | float m = statusPaint.measureText(line);
153 | if (m > w)
154 | statusPaint.setTextSize(w / 15f * w / m);
155 | canvas.drawText(line, canvas.getWidth() / 2, y, statusPaint);
156 | y += lineHeight;
157 | }
158 | }
159 | }
160 |
161 | @Override
162 | public void surfaceCreated(SurfaceHolder holder) {
163 | Log.v("VectorDisplay", "surfaceCreated");
164 | thread = new MySurfaceThread(holder);
165 | thread.start();
166 | }
167 |
168 | @Override
169 | public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
170 | Log.v("VectorDisplay", "surfaceChanged");
171 | record.forceUpdate();
172 | waitForSurfaceChanged = -1;
173 | }
174 |
175 | @Override
176 | public void surfaceDestroyed(SurfaceHolder holder) {
177 | if (thread != null) {
178 | thread.close();
179 | thread = null;
180 | }
181 | }
182 |
183 | public class MySurfaceThread extends Thread {
184 | private SurfaceHolder holder;
185 | volatile private boolean active;
186 | private Bitmap bitmap;
187 |
188 | public MySurfaceThread(SurfaceHolder h) {
189 | holder = h;
190 | active = true;
191 | }
192 |
193 | synchronized public void close() {
194 | active = false;
195 | }
196 |
197 | private void prepareBitmap(Canvas c) {
198 | int w = c.getWidth();
199 | int h = c.getHeight();
200 | if (bitmap == null || bitmap.getWidth() != w || bitmap.getHeight() != h) {
201 | Bitmap old = bitmap;
202 | bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
203 | savedCanvas = new Canvas();
204 | savedCanvas.setBitmap(bitmap);
205 | if (old != null) {
206 | Rect newR = new Rect(0, 0, w, h);
207 | savedCanvas.drawBitmap(old, null, newR, null);
208 | old.recycle();
209 | }
210 | }
211 | }
212 |
213 | private void deleteBitmap() {
214 | if (bitmap != null) {
215 | bitmap.recycle();
216 | bitmap = null;
217 | }
218 | }
219 |
220 | @Override
221 | public void run() {
222 | while(active) {
223 | boolean drew = false;
224 | long t0 = System.currentTimeMillis();
225 | synchronized (record) {
226 | if (record.haveStuffToDraw() && active) { // check we're still active
227 | try {
228 | Canvas c;
229 | if (Build.VERSION.SDK_INT >= 23)
230 | c = holder.getSurface().lockHardwareCanvas();
231 | else
232 | c = holder.lockCanvas();
233 | prepareBitmap(c);
234 | record.draw(savedCanvas);
235 | c.drawBitmap(bitmap, 0f, 0f, paint);
236 | updateStatus(c);
237 | holder.unlockCanvasAndPost(c);
238 | drew = true;
239 | } catch (Exception e) {
240 | // in case surface gets invalidated mid-way
241 | }
242 | }
243 | }
244 | long t = System.currentTimeMillis()-t0;
245 | long pauseTime = drew ? record.updateTimeMillis : 2;
246 | if (0 <= t && t < pauseTime) {
247 | try {
248 | sleep(pauseTime-t);
249 | } catch (InterruptedException e) {
250 | }
251 | }
252 | }
253 | deleteBitmap();
254 | }
255 | }
256 | }
257 |
--------------------------------------------------------------------------------
/app/src/main/java/mobi/omegacentauri/vectordisplay/WifiService.java:
--------------------------------------------------------------------------------
1 | package mobi.omegacentauri.vectordisplay;
2 |
3 | import android.app.PendingIntent;
4 | import android.content.BroadcastReceiver;
5 | import android.content.Context;
6 | import android.content.Intent;
7 | import android.content.IntentFilter;
8 | import android.hardware.usb.UsbDevice;
9 | import android.hardware.usb.UsbDeviceConnection;
10 | import android.hardware.usb.UsbManager;
11 | import android.net.wifi.WifiInfo;
12 | import android.net.wifi.WifiManager;
13 | import android.util.Log;
14 |
15 | import java.io.IOException;
16 | import java.io.InputStream;
17 | import java.io.OutputStream;
18 | import java.net.ServerSocket;
19 | import java.net.Socket;
20 | import java.util.HashMap;
21 | import java.util.Map;
22 |
23 | public class WifiService extends ConnectionService {
24 | public static final int PORT = 7788;
25 | public static boolean SERVICE_CONNECTED = false;
26 | public boolean stop = false;
27 | public String ipAddress = "";
28 |
29 | private WifiServer server;
30 | private WifiInfo wifiInfo;
31 |
32 | @Override
33 | synchronized public void onDestroy() {
34 | super.onDestroy();
35 | if (server != null)
36 | server.close();
37 | stop = true;
38 | WifiService.SERVICE_CONNECTED = false;
39 | }
40 |
41 |
42 | @Override
43 | public void onCreate() {
44 | super.onCreate();
45 | Log.v("VectorDisplay", "on create WiFiService");
46 | WifiService.SERVICE_CONNECTED = true;
47 | new WifiService.ConnectionThread().start();
48 | }
49 |
50 | @Override
51 | synchronized public void write(byte[] data) {
52 | if (server != null) { // TODO: fix synchronization
53 | server.write(data);
54 | }
55 | }
56 |
57 | /*
58 | * This function will be called from MainActivity to change baud rate
59 | */
60 |
61 | @Override
62 | public void disconnectDevice() {
63 | if (server != null && server.mClient != null) {
64 | try {
65 | server.mClient.close();
66 | server.mClient = null;
67 | } catch (IOException e) {
68 | }
69 | }
70 | }
71 |
72 | @Override
73 | synchronized public void close() {
74 | if (server != null)
75 | server.close();
76 | stop = true;
77 | stopSelf();
78 | }
79 |
80 | @Override
81 | public void setRecord(RecordAndPlay r) {
82 | super.setRecord(r);
83 | if (wifiInfo != null && wifiInfo.getNetworkId() != -1) {
84 | int ip = wifiInfo.getIpAddress();
85 | if (record != null) {
86 | record.setDisconnectedStatus(new String[]{"WiFi device not connected", "Connect to " + ipString(ip)});
87 | record.forceUpdate();
88 | }
89 | }
90 | }
91 |
92 | private static String ipString(int ip) {
93 | return "" + (0xFF & ip) + "." + (0xFF & (ip >> 8)) + "." + (0xFF & (ip >> 16)) + "." + (0xFF & (ip >> 24));
94 | }
95 |
96 | private class ConnectionThread extends Thread {
97 | @Override
98 | public void run() {
99 | boolean done = false;
100 |
101 | do {
102 | server = null;
103 |
104 | WifiManager wifiManager = (WifiManager) getApplicationContext().getSystemService(WIFI_SERVICE);
105 | int net = -1;
106 | int ip = 0;
107 | if (wifiManager != null) {
108 | wifiInfo = wifiManager.getConnectionInfo();
109 | net = wifiInfo.getNetworkId();
110 | if (net != -1)
111 | ip = wifiInfo.getIpAddress();
112 | }
113 | else
114 | wifiInfo = null;
115 |
116 | if (ip != 0) {
117 | if (record != null) {
118 | record.setDisconnectedStatus(new String[]{"WiFi device not connected", "Connect to " + ipString(ip)});
119 | MainActivity.log("ip "+ipString(ip));
120 | record.forceUpdate();
121 | }
122 |
123 | try {
124 | synchronized (WifiService.this) {
125 | done = true;
126 | server = new WifiServer(WifiService.this, PORT);
127 | }
128 | server.start();
129 | } catch (Exception e) {
130 | if (server != null) {
131 | server.stop();
132 | server = null;
133 | }
134 | }
135 |
136 | if (!done) {
137 | synchronized (WifiService.this) {
138 | if (WifiService.this.stop)
139 | break;
140 | }
141 | }
142 | }
143 | else {
144 | if (record != null)
145 | record.setDisconnectedStatus(new String[]{"WiFi not connected"});
146 | }
147 |
148 | try {
149 | Thread.sleep(1000);
150 | } catch (InterruptedException e) {
151 | }
152 | } while(!done);
153 |
154 | Intent intent = new Intent(ACTION_DEVICE_DISCONNECTED); // TODO: more informative
155 | context.sendBroadcast(intent);
156 | }
157 | }
158 | public class WifiServer extends Thread {
159 | private ServerSocket listener;
160 | WifiService wifiService;
161 | Socket mClient = null;
162 | InputStream in = null;
163 | OutputStream out = null;
164 | byte[] byteBuffer = new byte[256];
165 | Boolean stop;
166 |
167 | public WifiServer(WifiService service, int port) throws IOException {
168 | listener = new ServerSocket(port);
169 | wifiService = service;
170 | }
171 |
172 | @Override
173 | public void run() {
174 | stop = false;
175 | while(! stop) {
176 | synchronized(wifiService) {
177 | if (wifiService.stop)
178 | break;
179 | }
180 | try {
181 | WifiServer.this.mClient = listener.accept();
182 | Socket client = WifiServer.this.mClient; // local copy
183 | Log.v("VectorDisplay", "Accepted");
184 | in = client.getInputStream();
185 | synchronized(WifiServer.this) {
186 | out = client.getOutputStream();
187 | }
188 | wifiService.broadcast(ConnectionService.ACTION_DEVICE_CONNECTED);
189 | while(client.isConnected()) {
190 | int n = in.read(byteBuffer);
191 | wifiService.record.feed(byteBuffer, n);
192 | }
193 | } catch (IOException e) {
194 | Log.v("VectorDisplay", "disconnected client");
195 | }
196 | stopClient();
197 | }
198 | }
199 |
200 | public void close() {
201 | stopClient();
202 | stop = true;
203 | if (listener != null) {
204 | try {
205 | listener.close();
206 | } catch (IOException e) {
207 | listener = null;
208 | }
209 | }
210 | }
211 |
212 | public void stopClient() {
213 | wifiService.broadcast(ACTION_DEVICE_DISCONNECTED);
214 | if (mClient != null) {
215 | try {
216 | mClient.close();
217 | } catch (IOException e1) {
218 | }
219 | mClient = null;
220 | }
221 | }
222 |
223 | synchronized public void write(byte[] data) {
224 | if (out != null) {
225 | try {
226 | out.write(data);
227 | } catch (IOException e) {
228 | }
229 | }
230 | }
231 | }
232 | }
233 |
--------------------------------------------------------------------------------
/app/src/main/java/mobi/omegacentauri/vectordisplay/commands/AddButton.java:
--------------------------------------------------------------------------------
1 | package mobi.omegacentauri.vectordisplay.commands;
2 |
3 | import android.app.Activity;
4 | import android.os.Bundle;
5 | import android.os.Handler;
6 | import android.os.Message;
7 |
8 | import mobi.omegacentauri.vectordisplay.DisplayState;
9 | import mobi.omegacentauri.vectordisplay.MainActivity;
10 | import mobi.omegacentauri.vectordisplay.VectorAPI.MyBuffer;
11 |
12 | public class AddButton extends Command {
13 | byte cmd;
14 | String label;
15 |
16 | public AddButton(DisplayState state) {
17 | super(state);
18 | }
19 |
20 | @Override
21 | public int fixedArgumentsLength() {
22 | return 1;
23 | }
24 |
25 | @Override
26 | public boolean haveStringArgument() {
27 | return true;
28 | }
29 |
30 | @Override
31 | public DisplayState parseArguments(Activity context, MyBuffer buffer) {
32 | cmd = buffer.data[0];
33 | label = buffer.getString(1, buffer.length-1-1, state);
34 | return state;
35 | }
36 |
37 | @Override
38 | public boolean handleCommand(Handler h) {
39 | Message msg = h.obtainMessage(MainActivity.ADD_COMMAND);
40 | Bundle b = new Bundle();
41 | b.putString(MainActivity.KEY_LABEL, label);
42 | b.putByte(MainActivity.KEY_COMMAND, cmd);
43 | msg.setData(b);
44 | h.sendMessage(msg);
45 | return true;
46 | }
47 |
48 | @Override
49 | public boolean doesDraw() { // does this type actually do anything when show() is called?
50 | return false;
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/app/src/main/java/mobi/omegacentauri/vectordisplay/commands/Arc.java:
--------------------------------------------------------------------------------
1 | package mobi.omegacentauri.vectordisplay.commands;
2 |
3 | import android.app.Activity;
4 | import android.graphics.Canvas;
5 | import android.graphics.Paint;
6 | import android.graphics.RectF;
7 | import android.util.Log;
8 |
9 | import mobi.omegacentauri.vectordisplay.DisplayState;
10 | import mobi.omegacentauri.vectordisplay.VectorAPI.MyBuffer;
11 |
12 | import static mobi.omegacentauri.vectordisplay.commands.RoundedRectangle.rect;
13 |
14 | public class Arc extends Command {
15 | short x, y, r;
16 | float startAngle;
17 | float sweepAngle;
18 | RectF oval = new RectF();
19 | boolean filled;
20 | static Paint filledPaint = DefaultFilledPaint();
21 | static Paint strokePaint = DefaultStrokePaint();
22 |
23 | private static Paint DefaultFilledPaint() {
24 | Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
25 | p.setStyle(Paint.Style.FILL);
26 | p.setStrokeWidth(0);
27 | return p;
28 | }
29 |
30 | private static Paint DefaultStrokePaint() {
31 | Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
32 | p.setStyle(Paint.Style.STROKE);
33 | return p;
34 | }
35 |
36 | public Arc(DisplayState state) {
37 | super(state);
38 | }
39 |
40 | @Override
41 | public int fixedArgumentsLength() {
42 | return 15;
43 | }
44 |
45 | @Override
46 | public DisplayState parseArguments(Activity context, MyBuffer buffer) {
47 | x = buffer.getShort(0);
48 | y = buffer.getShort(2);
49 | r = buffer.getShort(4);
50 | startAngle = buffer.getFixed32(6);
51 | sweepAngle = buffer.getFixed32(10);
52 | filled = buffer.data[14] != 0;
53 |
54 | return state;
55 | }
56 |
57 | @Override
58 | public void draw(Canvas c) {
59 | oval.left = x-r;
60 | oval.right = x+r;
61 | oval.top = y-r;
62 | oval.bottom = y+r;
63 |
64 | if (filled) {
65 | filledPaint.setColor(state.foreColor);
66 | c.drawArc(oval, startAngle, sweepAngle, true, filledPaint);
67 | }
68 | else {
69 | strokePaint.setColor(state.foreColor);
70 | strokePaint.setStrokeWidth(state.thickness);
71 | strokePaint.setStrokeCap(state.rounded ? Paint.Cap.ROUND : Paint.Cap.SQUARE);
72 | strokePaint.setStrokeJoin(state.rounded ? Paint.Join.ROUND : Paint.Join.MITER);
73 | filledPaint.setColor(state.foreColor);
74 | c.drawArc(oval, startAngle, sweepAngle, false, strokePaint);
75 | }
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/app/src/main/java/mobi/omegacentauri/vectordisplay/commands/Attribute16.java:
--------------------------------------------------------------------------------
1 | package mobi.omegacentauri.vectordisplay.commands;
2 |
3 | import mobi.omegacentauri.vectordisplay.DisplayState;
4 | import mobi.omegacentauri.vectordisplay.VectorAPI.MyBuffer;
5 |
6 | import android.app.Activity;
7 | import android.graphics.Canvas;
8 |
9 | public class Attribute16 extends Command {
10 | boolean resetView = false;
11 |
12 | public Attribute16(DisplayState state) {
13 | super(state);
14 | }
15 |
16 | @Override
17 | public int fixedArgumentsLength() {
18 | return 3;
19 | }
20 |
21 | @Override
22 | public DisplayState parseArguments(Activity context, MyBuffer buffer) {
23 | switch((char)buffer.data[0]) {
24 | case 'b':
25 | state.backColor = buffer.getColor565(1);
26 | break;
27 | case 'k':
28 | state.textBackColor = buffer.getColor565(1);
29 | break;
30 | case 'F':
31 | state.textForeColor = buffer.getColor565(1);
32 | break;
33 | case 'f':
34 | state.foreColor = buffer.getColor565(1);
35 | break;
36 | }
37 | return state;
38 | }
39 |
40 | @Override
41 | public boolean doesDraw() {
42 | return resetView;
43 | }
44 |
45 | @Override
46 | public void draw(Canvas c) {
47 | Clear.clearCanvas(c, state);
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/app/src/main/java/mobi/omegacentauri/vectordisplay/commands/Attribute32.java:
--------------------------------------------------------------------------------
1 | package mobi.omegacentauri.vectordisplay.commands;
2 |
3 | import android.app.Activity;
4 | import android.graphics.Canvas;
5 | import android.os.Handler;
6 |
7 | import mobi.omegacentauri.vectordisplay.DisplayState;
8 | import mobi.omegacentauri.vectordisplay.MainActivity;
9 | import mobi.omegacentauri.vectordisplay.RecordAndPlay;
10 | import mobi.omegacentauri.vectordisplay.VectorAPI;
11 | import mobi.omegacentauri.vectordisplay.VectorAPI.MyBuffer;
12 |
13 | public class Attribute32 extends Command {
14 | boolean resetView = false;
15 | boolean ack = false;
16 |
17 | public Attribute32(DisplayState state) {
18 | super(state);
19 | }
20 |
21 | @Override
22 | public int fixedArgumentsLength() {
23 | return 5;
24 | }
25 |
26 | @Override
27 | public DisplayState parseArguments(Activity context, MyBuffer buffer) {
28 | switch((char)buffer.data[0]) {
29 | case 't':
30 | state.thickness = buffer.getFixed32(1);
31 | break;
32 | case 'c':
33 | state.width = buffer.getShort(1);
34 | state.height = buffer.getShort(3);
35 | ack = true;
36 | resetView = true;
37 | break;
38 | case 'a':
39 | state.pixelAspectRatio = buffer.getFixed32(1);
40 | resetView = true;
41 | break;
42 | case 's':
43 | state.textSize = buffer.getFixed32(1);
44 | break;
45 | case 'b':
46 | state.backColor = buffer.getInt(1);
47 | break;
48 | case 'k':
49 | state.textBackColor = buffer.getInt(1);
50 | break;
51 | case 'F':
52 | state.textForeColor = buffer.getInt(1);
53 | break;
54 | case 'f':
55 | state.foreColor = buffer.getInt(1);
56 | break;
57 | case 'x':
58 | state.cursorX = buffer.getFixed32(1);
59 | break;
60 | case 'y':
61 | state.cursorY = buffer.getFixed32(1);
62 | break;
63 | }
64 | return state;
65 | }
66 |
67 | @Override
68 | public boolean doesDraw() {
69 | return resetView;
70 | }
71 |
72 | // @Override
73 | // public boolean needToResetView() { return resetView; }
74 |
75 | @Override
76 | public void draw(Canvas c) {
77 | Clear.clearCanvas(c, state);
78 | }
79 |
80 | @Override
81 | public boolean handleCommand(Handler h) {
82 | if (resetView)
83 | MainActivity.sendResetViewMessage(h, state);
84 | if (ack)
85 | sendAck(h, VectorAPI.ATTRIBUTE32_COMMAND, RecordAndPlay.LAYOUT_DELAY);
86 | return resetView;
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/app/src/main/java/mobi/omegacentauri/vectordisplay/commands/Attribute8.java:
--------------------------------------------------------------------------------
1 | package mobi.omegacentauri.vectordisplay.commands;
2 |
3 | import android.app.Activity;
4 |
5 | import mobi.omegacentauri.vectordisplay.DisplayState;
6 | import mobi.omegacentauri.vectordisplay.VectorAPI.MyBuffer;
7 |
8 | public class Attribute8 extends Command {
9 | public Attribute8(DisplayState state) {
10 | super(state);
11 | }
12 |
13 | @Override
14 | public int fixedArgumentsLength() {
15 | return 2;
16 | }
17 |
18 | @Override
19 | public DisplayState parseArguments(Activity context, MyBuffer buffer) {
20 | switch((char)buffer.data[0]) {
21 | case 'h':
22 | state.hAlignText = (char)buffer.data[1];
23 | break;
24 | case 'v':
25 | state.vAlignText = (char)buffer.data[1];
26 | break;
27 | case 'b':
28 | if (buffer.data[1] != 0)
29 | state.fontInfo |= DisplayState.FONT_BOLD;
30 | else
31 | state.fontInfo &= ~DisplayState.FONT_BOLD;
32 | break;
33 | case 'I':
34 | if (buffer.data[1] != 0)
35 | state.fontInfo |= DisplayState.FONT_ITALIC;
36 | else
37 | state.fontInfo &= ~DisplayState.FONT_ITALIC;
38 | break;
39 | case 'f':
40 | state.fontInfo = buffer.data[1];
41 | break;
42 | case 'F':
43 | state.fontInfo = (byte) ((state.fontInfo & DisplayState.FONT_TYPEFACE_MASK) | (buffer.data[1] & DisplayState.FONT_TYPEFACE_MASK));
44 | break;
45 | case 'n':
46 | state.rounded = buffer.data[1] != 0;
47 | break;
48 | case 'o':
49 | state.opaqueTextBackground = buffer.data[1] != 0;
50 | break;
51 | case 'r':
52 | state.rotate = (byte) buffer.data[1];
53 | break;
54 | case 'i':
55 | state.cp437 = buffer.data[1] != 0;
56 | break;
57 | case 'c':
58 | state.continuousUpdate = buffer.data[1] != 0;
59 | // Log.v("VectorDisplay", "cupdate "+state.continuousUpdate);
60 | break;
61 | case 'w':
62 | state.wrap = buffer.data[1] != 0;
63 | }
64 | return state;
65 | }
66 |
67 | @Override
68 | public boolean doesDraw() {
69 | return false;
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/app/src/main/java/mobi/omegacentauri/vectordisplay/commands/Circle.java:
--------------------------------------------------------------------------------
1 | package mobi.omegacentauri.vectordisplay.commands;
2 |
3 | import android.app.Activity;
4 | import android.graphics.Canvas;
5 | import android.graphics.Paint;
6 |
7 | import mobi.omegacentauri.vectordisplay.DisplayState;
8 | import mobi.omegacentauri.vectordisplay.VectorAPI.MyBuffer;
9 |
10 | public class Circle extends Command {
11 | short x,y,r;
12 | static Paint p = DefaultPaint();
13 |
14 | private static Paint DefaultPaint() {
15 | Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
16 | p.setStyle(Paint.Style.STROKE);
17 | return p;
18 | }
19 |
20 | public Circle(DisplayState state) {
21 | super(state);
22 | }
23 |
24 | @Override
25 | public int fixedArgumentsLength() {
26 | return 6;
27 | }
28 |
29 | @Override
30 | public DisplayState parseArguments(Activity context, MyBuffer buffer) {
31 | x = buffer.getShort(0);
32 | y = buffer.getShort(2);
33 | r = buffer.getShort(4);
34 | return state;
35 | }
36 |
37 | @Override
38 | public void draw(Canvas c) {
39 | p.setColor(state.foreColor);
40 | p.setStrokeWidth(state.thickness);
41 |
42 | c.drawCircle(x, y, r, p);
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/app/src/main/java/mobi/omegacentauri/vectordisplay/commands/Clear.java:
--------------------------------------------------------------------------------
1 | package mobi.omegacentauri.vectordisplay.commands;
2 |
3 | import mobi.omegacentauri.vectordisplay.DisplayState;
4 | import mobi.omegacentauri.vectordisplay.MainActivity;
5 | import mobi.omegacentauri.vectordisplay.commands.Command;
6 |
7 | import android.graphics.Canvas;
8 | import android.graphics.Paint;
9 |
10 | import static java.lang.Math.max;
11 |
12 | public class Clear extends Command {
13 | public Clear(DisplayState state) {
14 | super(state);
15 | }
16 |
17 | @Override
18 | public boolean needToClearHistory() { return true; }
19 |
20 | @Override
21 | public int fixedArgumentsLength() { return 0; }
22 |
23 | public static void clearCanvas(Canvas c, DisplayState state) {
24 | Paint paint = new Paint();
25 | paint.setColor(state.backColor);
26 | MainActivity.log( "clearing to "+state.backColor+ " "+c.getWidth()+" "+c.getHeight());
27 | c.drawRect(-0.5f,-0.5f,max(state.width,state.height),max(state.width,state.height), paint);
28 | }
29 | @Override
30 | public void draw(Canvas c) {
31 | clearCanvas(c, state);
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/app/src/main/java/mobi/omegacentauri/vectordisplay/commands/Command.java:
--------------------------------------------------------------------------------
1 | package mobi.omegacentauri.vectordisplay.commands;
2 |
3 | import mobi.omegacentauri.vectordisplay.DisplayState;
4 | import mobi.omegacentauri.vectordisplay.MainActivity;
5 | import mobi.omegacentauri.vectordisplay.VectorAPI.MyBuffer;
6 |
7 | import android.app.Activity;
8 | import android.graphics.Canvas;
9 | import android.os.Handler;
10 | import android.os.Message;
11 | import android.util.Log;
12 |
13 | public class Command {
14 | public DisplayState state;
15 |
16 | public boolean errorState;
17 |
18 | public Command(DisplayState state) {
19 | this.errorState = false;
20 | try {
21 | this.state = (DisplayState)state.clone();
22 | } catch (CloneNotSupportedException e) {
23 | }
24 | }
25 |
26 | public boolean haveStringArgument() { return false; }
27 |
28 | public int fixedArgumentsLength() { return 0; }
29 |
30 | public DisplayState parseArguments(Activity context, MyBuffer buffer) {
31 | return state;
32 | }
33 |
34 | public DisplayState parse(Activity context, MyBuffer buffer) {
35 | if (!haveFullData(buffer))
36 | return null;
37 | // DrawBitmap doesn't get checksummed, as small errors in bitmap transmission can
38 | // be ignored, hopefully.
39 | if (this.getClass()==DrawBitmap.class || buffer.checksum()) {
40 | return parseArguments(context, buffer);
41 | }
42 | else {
43 | Log.e( "VectorDisplay","bad checksum "+this.getClass());
44 | errorState = true;
45 | return null;
46 | }
47 | }
48 |
49 | public boolean haveFullData(MyBuffer buffer) {
50 | if (! haveStringArgument()) {
51 | if (buffer.length < fixedArgumentsLength()+1)
52 | return false;
53 | }
54 | else {
55 | if (buffer.length <= fixedArgumentsLength()+1 ||
56 | buffer.getByte(buffer.length-2) != 0)
57 | return false;
58 | }
59 | return true;
60 | }
61 |
62 | public void draw(Canvas c) {
63 | }
64 |
65 | public boolean doesDraw() { // does this type actually do anything when show() is called?
66 | return true;
67 | }
68 |
69 | public boolean needToClearHistory() { return false; }
70 |
71 | // returns true if we need to wait for onLayout() before proceeding with rendering
72 | public boolean handleCommand(Handler h) { return false; }
73 |
74 | static void sendAck(Handler h, byte command, long delay) {
75 | Message msg = h.obtainMessage(MainActivity.ACK);
76 | msg.arg1 = command;
77 | if (delay > 0)
78 | h.sendMessageDelayed(msg, delay);
79 | else
80 | h.sendMessage(msg);
81 | }
82 |
83 | static void sendAck(Handler h, byte command) {
84 | sendAck(h, command, 0);
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/app/src/main/java/mobi/omegacentauri/vectordisplay/commands/DeleteButton.java:
--------------------------------------------------------------------------------
1 | package mobi.omegacentauri.vectordisplay.commands;
2 |
3 | import android.app.Activity;
4 | import android.os.Bundle;
5 | import android.os.Handler;
6 | import android.os.Message;
7 |
8 | import mobi.omegacentauri.vectordisplay.DisplayState;
9 | import mobi.omegacentauri.vectordisplay.MainActivity;
10 | import mobi.omegacentauri.vectordisplay.VectorAPI.MyBuffer;
11 |
12 | public class DeleteButton extends Command {
13 | byte cmd;
14 |
15 | public DeleteButton(DisplayState state) {
16 | super(state);
17 | }
18 |
19 | @Override
20 | public int fixedArgumentsLength() {
21 | return 1;
22 | }
23 |
24 | @Override
25 | public boolean haveStringArgument() {
26 | return false;
27 | }
28 |
29 | @Override
30 | public DisplayState parseArguments(Activity context, MyBuffer buffer) {
31 | cmd = buffer.data[0];
32 | return state;
33 | }
34 |
35 | @Override
36 | public boolean doesDraw() { // does this type actually do anything when show() is called?
37 | return false;
38 | }
39 |
40 | @Override
41 | public boolean handleCommand(Handler h) {
42 | Message msg = h.obtainMessage(MainActivity.DELETE_COMMAND);
43 | Bundle b = new Bundle();
44 | b.putByte(MainActivity.KEY_COMMAND, cmd);
45 | msg.setData(b);
46 | h.sendMessage(msg);
47 | return true;
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/app/src/main/java/mobi/omegacentauri/vectordisplay/commands/DrawBitmap.java:
--------------------------------------------------------------------------------
1 | package mobi.omegacentauri.vectordisplay.commands;
2 |
3 | import android.app.Activity;
4 | import android.graphics.Bitmap;
5 | import android.graphics.BitmapFactory;
6 | import android.graphics.Canvas;
7 | import android.graphics.Paint;
8 |
9 | import java.nio.IntBuffer;
10 | import java.nio.ShortBuffer;
11 | import java.util.Arrays;
12 |
13 | import mobi.omegacentauri.vectordisplay.DisplayState;
14 | import mobi.omegacentauri.vectordisplay.MainActivity;
15 | import mobi.omegacentauri.vectordisplay.VectorAPI.MyBuffer;
16 |
17 | public class DrawBitmap extends Command {
18 | short x,y,w,h;
19 | int length;
20 | byte[] data;
21 | byte[] mask;
22 | static final byte MONOCHROME=1;
23 | static final byte GRAYSCALE=8;
24 | static final byte RGB565=16;
25 | static final byte RGB888=24;
26 | static final byte RGBA8888=32;
27 | static final int headerSize = 14;
28 | int foreColor;
29 | int backColor;
30 | byte depth;
31 | byte flags;
32 | static final byte FLAG_LOW_ENDIAN_BITS = 1; // lsbit on left
33 | static final byte FLAG_HAVE_MASK = 2;
34 | static final byte FLAG_PAD_BYTE = 4;
35 | static final byte FLAG_LOW_ENDIAN_BYTES = 8; // lsbyte first
36 | static final byte FLAG_IMAGE_FILE=16; // standard Android supported image file
37 | int neededLength = 4;
38 |
39 | private static Paint DefaultPaint() {
40 | Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
41 | p.setStyle(Paint.Style.STROKE);
42 | return p;
43 | }
44 |
45 | @Override
46 | public boolean haveFullData(MyBuffer buffer) {
47 | if (buffer.length < neededLength)
48 | return false;
49 |
50 | if (neededLength <= 4) {
51 | neededLength = buffer.getInt(0)+1+headerSize;
52 | }
53 |
54 | return buffer.length >= neededLength;
55 | }
56 |
57 | public DrawBitmap(DisplayState state) {
58 | super(state);
59 | }
60 |
61 | @Override
62 | public int fixedArgumentsLength() {
63 | return 6;
64 | }
65 |
66 | @Override
67 | public DisplayState parseArguments(Activity context, MyBuffer buffer) {
68 | depth = buffer.data[4];
69 | flags = buffer.data[5];
70 | x = buffer.getShort(6);
71 | y = buffer.getShort(8);
72 |
73 | int bitmapLength;
74 | int maskLength;
75 |
76 | MainActivity.log("flags = "+flags+" depth = "+depth);
77 | if ((flags & FLAG_IMAGE_FILE)==0) {
78 | w = buffer.getShort(10);
79 | h = buffer.getShort(12);
80 | MainActivity.log("width = "+w+" height = "+h);
81 | if (depth>=8) {
82 | bitmapLength = (depth/8) * w * h;
83 | }
84 | else {
85 | bitmapLength = ((flags & FLAG_PAD_BYTE) != 0) ? (w+7)/8*h : (w*h + 7) / 8;
86 | }
87 |
88 | if ((flags & FLAG_HAVE_MASK) != 0) {
89 | maskLength = (w*h + 7) / 8;
90 | }
91 | else {
92 | maskLength = 0;
93 | }
94 |
95 | }
96 | else {
97 | bitmapLength = buffer.getInt(10);
98 | maskLength = 0;
99 | }
100 |
101 | int bitmapOffset;
102 | if (depth == MONOCHROME) {
103 | foreColor = buffer.getInt(14);
104 | backColor = buffer.getInt(18);
105 | bitmapOffset = 22;
106 | }
107 | else {
108 | bitmapOffset = 14;
109 | }
110 |
111 | if (bitmapLength > 0) {
112 | data = Arrays.copyOfRange(buffer.data, bitmapOffset, bitmapOffset+bitmapLength);
113 | }
114 | else {
115 | data = null;
116 | }
117 |
118 | if (maskLength > 0) {
119 | mask = Arrays.copyOfRange(buffer.data, bitmapOffset + bitmapLength, bitmapOffset + bitmapLength+maskLength);
120 | }
121 | else {
122 | mask = null;
123 | }
124 |
125 | return state;
126 | }
127 |
128 | public Bitmap getArduinoBitmap() {
129 | Bitmap bitmap;
130 |
131 | boolean leBits = (flags & FLAG_LOW_ENDIAN_BITS) != 0;
132 | boolean leBytes = (flags & FLAG_LOW_ENDIAN_BYTES) != 0;
133 |
134 | if (mask == null && depth == RGB565) {
135 | short[] pixels = new short[ w*h ];
136 | if (leBytes)
137 | for (int i = 0; i < pixels.length; i++) {
138 | pixels[i] = (short) ( (data[2*i] & 0xFF) | ((data[2*i+1] & 0xFF) << 8));
139 | }
140 | else
141 | for (int i = 0; i < pixels.length; i++) {
142 | pixels[i] = (short) ((data[2*i] & 0xFF) | ((data[2*i+1] & 0xFF) << 8));
143 | }
144 | bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.RGB_565);
145 | bitmap.copyPixelsFromBuffer(ShortBuffer.wrap(pixels));
146 | }
147 | else {
148 | int[] pixels = new int[ w*h ];
149 | if (depth == MONOCHROME) {
150 | if ((flags & FLAG_PAD_BYTE) == 0){
151 | int offset = 0;
152 | if (leBits)
153 | for (int y = 0; y < h; y++) {
154 | int yw = y * w;
155 | for (int x = 0; x < w; x++) {
156 | if ((data[offset / 8] & (1 << (offset % 8))) != 0)
157 | pixels[yw + x] = foreColor;
158 | else
159 | pixels[yw + x] = backColor;
160 | offset++;
161 | }
162 | }
163 | else {
164 | for (int y = 0; y < h; y++) {
165 | int yw = y * w;
166 | for (int x = 0; x < w; x++) {
167 | if ((data[offset / 8] & (1 << (7 - offset % 8))) != 0)
168 | pixels[yw + x] = foreColor;
169 | else
170 | pixels[yw + x] = backColor;
171 | offset++;
172 | }
173 | }
174 | }
175 | }
176 | else {
177 | if (leBits)
178 | for (int y = 0; y < h; y++) {
179 | int yw = y * w;
180 | int yw1 = (w+7)/8 * w;
181 | for (int x = 0; x < w; x++) {
182 | if ((data[yw1 + x/8] & (1 << (x% 8))) != 0)
183 | pixels[yw + x] = foreColor;
184 | else
185 | pixels[yw + x] = backColor;
186 | }
187 | }
188 | else {
189 | for (int y = 0; y < h; y++) {
190 | int yw = y * w;
191 | int yw1 = (w+7)/8 * w;
192 | for (int x = 0; x < w; x++) {
193 | if ((data[yw1 + x/8] & (1 << (7 - x% 8))) != 0)
194 | pixels[yw + x] = foreColor;
195 | else
196 | pixels[yw + x] = backColor;
197 | }
198 | }
199 | }
200 | }
201 | }
202 | else if (depth == GRAYSCALE) {
203 | for (int i = 0; i < pixels.length; i++) {
204 | int g = data[i] & 0xFF;
205 | pixels[i] = 0xFF000000 | (g<<16) | (g<<8) | g;
206 | }
207 | }
208 | else if (depth == RGB888) {
209 | if (leBytes)
210 | for (int i = 0; i < pixels.length; i++) {
211 | int i3 = i*3;
212 | pixels[i] = 0xFF000000 | ((data[i3+2] & 0xFF) << 16) | ((data[i3+1] & 0xFF) << 8) | (data[i3] & 0xFF);
213 | }
214 | else
215 | for (int i = 0; i < pixels.length; i++) {
216 | int i3 = i*3;
217 | pixels[i] = 0xFF000000 | ((data[i3] & 0xFF) << 16) | ((data[i3+1] & 0xFF) << 8) | (data[i3+2] & 0xFF);
218 | }
219 | }
220 | else if (depth == RGB565) {
221 | if (leBytes)
222 | for (int i = 0; i < pixels.length; i++) {
223 | pixels[i] = rgb565to8888(data[2*i],data[2*i+1]);
224 | }
225 | else
226 | for (int i = 0; i < pixels.length; i++) {
227 | pixels[i] = rgb565to8888(data[2*i+1],data[2*i]);
228 | }
229 | }
230 | else if (depth == RGBA8888) {
231 | if (leBytes)
232 | for (int i = 0; i < pixels.length; i++) {
233 | int i4 = i*4;
234 | pixels[i] = ((data[i4+3] & 0xFF) << 24) | ((data[i4+2] & 0xFF) << 16) | ((data[i4+1] & 0xFF) << 8) | (data[i4] & 0xFF);
235 | }
236 | else
237 | for (int i = 0; i < pixels.length; i++) {
238 | int i4 = i*4;
239 | pixels[i] = ((data[i4] & 0xFF) << 24) | ((data[i4+1] & 0xFF) << 16) | ((data[i4+2] & 0xFF) << 8) | (data[i4+3] & 0xFF);
240 | }
241 | }
242 | else {
243 | return null;
244 | }
245 |
246 | if (mask != null) {
247 | if ((flags & FLAG_PAD_BYTE) == 0){
248 | int offset = 0;
249 | if (leBits)
250 | for (int y = 0; y < h; y++) {
251 | int yw = y * w;
252 | for (int x = 0; x < w; x++) {
253 | if ((mask[offset / 8] & (1 << (offset % 8))) == 0)
254 | pixels[yw + x] = 0;
255 | offset++;
256 | }
257 | }
258 | else {
259 | for (int y = 0; y < h; y++) {
260 | int yw = y * w;
261 | for (int x = 0; x < w; x++) {
262 | if ((mask[offset / 8] & (1 << (7 - offset % 8))) == 0)
263 | pixels[yw + x] = 0;
264 | offset++;
265 | }
266 | }
267 | }
268 | }
269 | else {
270 | if (leBits)
271 | for (int y = 0; y < h; y++) {
272 | int yw = y * w;
273 | int yw1 = (w+7)/8 * w;
274 | for (int x = 0; x < w; x++) {
275 | if ((mask[yw1 + x/8] & (1 << (x% 8))) == 0)
276 | pixels[yw + x] = foreColor;
277 | }
278 | }
279 | else {
280 | for (int y = 0; y < h; y++) {
281 | int yw = y * w;
282 | int yw1 = (w+7)/8 * w;
283 | for (int x = 0; x < w; x++) {
284 | if ((mask[yw1 + x/8] & (1 << (7 - x% 8))) == 0)
285 | pixels[yw + x] = foreColor;
286 | }
287 | }
288 | }
289 | }
290 | }
291 |
292 | bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
293 | bitmap.setPixels(pixels,0,w,0,0,w,h);
294 | MainActivity.log(" pixel 0 0 = "+pixels[0]);
295 | }
296 |
297 | return bitmap;
298 | }
299 |
300 | @Override
301 | public void draw(Canvas c) {
302 | if (data == null)
303 | return;
304 |
305 | Bitmap bitmap;
306 | if ((flags & FLAG_IMAGE_FILE) == 0)
307 | bitmap = getArduinoBitmap();
308 | else
309 | bitmap = decodeBitmap();
310 |
311 | if (bitmap != null) {
312 | MainActivity.log("drawing bitmap of size "+bitmap.getWidth()+" "+bitmap.getHeight()+" at "+x+" "+y);
313 | c.drawBitmap(bitmap, x, y, null);
314 | bitmap.recycle();
315 | }
316 | }
317 |
318 | private Bitmap decodeBitmap() {
319 | try {
320 | return BitmapFactory.decodeByteArray(data, 0, data.length);
321 | }
322 | catch (Exception e) {
323 | return null;
324 | }
325 | }
326 |
327 | static private int rgb565to8888(byte low, byte high) {
328 | int c = (low&0xFF) | ((high&0xFF)<<8);
329 | return 0xFF000000 | ((((c>>11) & 0x1F) * 255 / 0x1F) << 16) | ((((c>>5) & 0x3F) * 255 / 0x3F) << 8) | ((c & 0x1F) * 255 / 0x1F);
330 | }
331 | }
332 |
--------------------------------------------------------------------------------
/app/src/main/java/mobi/omegacentauri/vectordisplay/commands/FillCircle.java:
--------------------------------------------------------------------------------
1 | package mobi.omegacentauri.vectordisplay.commands;
2 |
3 | import android.app.Activity;
4 | import android.graphics.Canvas;
5 | import android.graphics.Paint;
6 |
7 | import mobi.omegacentauri.vectordisplay.DisplayState;
8 | import mobi.omegacentauri.vectordisplay.VectorAPI.MyBuffer;
9 |
10 | public class FillCircle extends Command {
11 | short x,y,r;
12 | static Paint p = DefaultPaint();
13 |
14 | private static Paint DefaultPaint() {
15 | Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
16 | p.setStyle(Paint.Style.FILL);
17 | p.setStrokeWidth(0);
18 | return p;
19 | }
20 |
21 | public FillCircle(DisplayState state) {
22 | super(state);
23 | }
24 |
25 | @Override
26 | public int fixedArgumentsLength() {
27 | return 6;
28 | }
29 |
30 | @Override
31 | public DisplayState parseArguments(Activity context, MyBuffer buffer) {
32 | x = buffer.getShort(0);
33 | y = buffer.getShort(2);
34 | r = buffer.getShort(4);
35 | return state;
36 | }
37 |
38 | @Override
39 | public void draw(Canvas c) {
40 | p.setColor(state.foreColor);
41 |
42 | c.drawCircle(x, y, r, p);
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/app/src/main/java/mobi/omegacentauri/vectordisplay/commands/FillPoly.java:
--------------------------------------------------------------------------------
1 | package mobi.omegacentauri.vectordisplay.commands;
2 |
3 | import android.app.Activity;
4 | import android.graphics.Canvas;
5 | import android.graphics.Paint;
6 | import android.graphics.Path;
7 |
8 | import mobi.omegacentauri.vectordisplay.DisplayState;
9 | import mobi.omegacentauri.vectordisplay.VectorAPI.MyBuffer;
10 |
11 | public class FillPoly extends Command {
12 | int n;
13 | short[] x;
14 | short[] y;
15 | int pos;
16 | static Paint p = DefaultPaint();
17 | static Path poly = new Path();
18 | int neededLength = 2;
19 |
20 | private static Paint DefaultPaint() {
21 | Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
22 | p.setStyle(Paint.Style.FILL);
23 | return p;
24 | }
25 |
26 | public FillPoly(DisplayState state) {
27 | super(state);
28 | }
29 |
30 | @Override
31 | public boolean haveFullData(MyBuffer buffer) {
32 | if (buffer.length < neededLength)
33 | return false;
34 | int n = 0xFFFF & buffer.getShort(0);
35 | neededLength = 2+n*4+1;
36 | return buffer.length >= neededLength;
37 | }
38 |
39 | @Override
40 | public DisplayState parseArguments(Activity context, MyBuffer buffer) {
41 | n = buffer.getShort(0);
42 | x = new short[n];
43 | y = new short[n];
44 | for (int i=0;i= neededLength;
36 | }
37 |
38 | @Override
39 | public DisplayState parseArguments(Activity context, MyBuffer buffer) {
40 | n = buffer.getShort(0);
41 | x = new short[n];
42 | y = new short[n];
43 | for (int i=0;i
8 |
9 |
16 |
21 |
22 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/menu.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arpruss/VectorDisplay/286bf571c13b825e34b3911e5875d997b8519454/app/src/main/res/mipmap/icon.png
--------------------------------------------------------------------------------
/app/src/main/res/values/arrays.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | - 4
5 | - 10
6 | - 20
7 | - 25
8 | - 30
9 | - 40
10 | - 50
11 | - 60
12 |
13 |
14 | - USB
15 | - WiFi
16 | - Bluetooth
17 |
18 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | VectorDisplay
3 | Settings
4 | Synchronous Serial Port
5 |
6 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 | buildscript {
3 | repositories {
4 | jcenter()
5 | maven {
6 | url 'https://maven.google.com/'
7 | name 'Google'
8 | }
9 | }
10 | dependencies {
11 | classpath 'com.android.tools.build:gradle:2.3.3'
12 | }
13 | }
14 |
15 | allprojects {
16 | repositories {
17 | jcenter()
18 | maven {
19 | url 'https://maven.google.com/'
20 | name 'Google'
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arpruss/VectorDisplay/286bf571c13b825e34b3911e5875d997b8519454/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Sun May 20 10:15:54 CDT 2018
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/icon.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
172 |
--------------------------------------------------------------------------------
/import-summary.txt:
--------------------------------------------------------------------------------
1 | ECLIPSE ANDROID PROJECT IMPORT SUMMARY
2 | ======================================
3 |
4 | Ignored Files:
5 | --------------
6 | The following files were *not* copied into the new Gradle project; you
7 | should evaluate whether these are still needed in your project and if
8 | so manually move them:
9 |
10 | * License.txt
11 | * proguard.cfg
12 |
13 | Moved Files:
14 | ------------
15 | Android Gradle projects use a different directory structure than ADT
16 | Eclipse projects. Here's how the projects were restructured:
17 |
18 | * AndroidManifest.xml => app\src\main\AndroidManifest.xml
19 | * assets\ => app\src\main\assets
20 | * lint.xml => app\lint.xml
21 | * res\ => app\src\main\res\
22 | * src\ => app\src\main\java\
23 |
24 | Next Steps:
25 | -----------
26 | You can now build the project. The Gradle project needs network
27 | connectivity to download dependencies.
28 |
29 | Bugs:
30 | -----
31 | If for some reason your project does not build, and you determine that
32 | it is due to a bug or limitation of the Eclipse to Gradle importer,
33 | please file a bug at http://b.android.com with category
34 | Component-Tools.
35 |
36 | (This import summary is for your information only, and can be deleted
37 | after import once you are satisfied with the results.)
38 |
--------------------------------------------------------------------------------
/local.properties:
--------------------------------------------------------------------------------
1 | ## This file must *NOT* be checked into Version Control Systems,
2 | # as it contains information specific to your local configuration.
3 | #
4 | # Location of the SDK. This is only used by Gradle.
5 | # For customization when using a Version Control System, please read the
6 | # header note.
7 | #Sat May 26 20:51:46 CDT 2018
8 | ndk.dir=C\:\\Users\\alexander_pruss\\AppData\\Local\\Android\\Sdk\\ndk-bundle
9 | sdk.dir=C\:\\Users\\alexander_pruss\\AppData\\Local\\Android\\Sdk
10 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app', ':usbserial-4.5.2-release'
2 |
--------------------------------------------------------------------------------
/test/arc.py:
--------------------------------------------------------------------------------
1 | import socket
2 | import sys
3 | import os
4 | import math
5 | from vectordisplay import VectorDisplay
6 | from random import randint
7 | from time import sleep
8 |
9 | if len(sys.argv)>1:
10 | addr = sys.argv[1]
11 | else:
12 | addr = '192.168.1.110'
13 |
14 | port = 7788
15 |
16 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
17 | s.connect((addr, port))
18 | print("connected")
19 |
20 | w = 300
21 | h = 300
22 | v = VectorDisplay(s.sendall,lowendian=False)
23 | v.initialize(w,h)
24 | v.arc(100,100,50,45,135)
25 | v.arc(100,100,50,180+45,180+135,fill=True)
26 | sleep(0.5) # needed on Windows
27 | s.close()
28 |
--------------------------------------------------------------------------------
/test/bitmap.py:
--------------------------------------------------------------------------------
1 | import socket
2 | import sys
3 | import os
4 | import math
5 | from vectordisplay import VectorDisplay
6 | from random import randint
7 | from time import sleep
8 | from bitmap_data import *
9 |
10 | if len(sys.argv)>1:
11 | addr = sys.argv[1]
12 | else:
13 | addr = '192.168.1.110'
14 |
15 | port = 7788
16 |
17 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
18 | s.connect((addr, port))
19 | print("connected")
20 |
21 | w = 420
22 | h = 420
23 | v = VectorDisplay(s.sendall,lowendian=False)
24 | v.initialize(w,h)
25 | v.bitmap(10,10,dimensions[0],dimensions[1],bw,foreColor=0x7f007F00,backColor=0x7f1f1f1f,depth=1)
26 | v.bitmap(80,80,dimensions[0],dimensions[1],gray,depth=8)
27 | v.bitmap(160,160,dimensions[0],dimensions[1],rgb888,depth=24)
28 | v.bitmap(220,220,dimensions[0],dimensions[1],rgb8888,depth=32)
29 | v.bitmap(300,300,dimensions[0],dimensions[1],rgb565,depth=16)
30 | sleep(0.5) # needed on Windows
31 | s.close()
--------------------------------------------------------------------------------
/test/helper.py:
--------------------------------------------------------------------------------
1 | import socket
2 | import sys
3 | import os
4 | import math
5 | from vectordisplay import VectorDisplay
6 | from random import randint
7 | from time import sleep
8 |
9 | if len(sys.argv)>1:
10 | addr = sys.argv[1]
11 | else:
12 | addr = '192.168.1.110'
13 |
14 | port = 7788
15 |
16 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
17 | s.connect((addr, port))
18 | print("connected")
19 |
20 | w = 160
21 | h = 160
22 | v = VectorDisplay(s.sendall,lowendian=False)
23 | v.initialize(w,h)
24 | v.rounded(True)
25 | v.foreColor(0xFFFF0000)
26 | v.fillCircleHelper(60,55,50,2,50)
27 | v.foreColor(0xFF00FF00)
28 | v.fillCircleHelper(80,55,50,1,50)
29 | sleep(0.5) # needed on Windows
30 | s.close()
--------------------------------------------------------------------------------
/test/nettest.py:
--------------------------------------------------------------------------------
1 | import socket
2 | import sys
3 | import os
4 | from vectordisplay import VectorDisplay
5 | from threading import Thread
6 | from random import randint
7 | from time import sleep
8 |
9 | if len(sys.argv)>1:
10 | addr = sys.argv[1]
11 | else:
12 | addr = '192.168.1.110'
13 |
14 | port = 7788
15 |
16 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
17 | s.connect((addr, port))
18 | print("connected")
19 |
20 | def dec16(a,b):
21 | return (a<<8) | (b&0xFF);
22 |
23 | def reader():
24 | while 1:
25 | try:
26 | data = s.recv(8)
27 | print(" ".join("%02X" % b for b in data))
28 | c = chr(data[0])
29 | if c=='U' or c=='D' or c=='M':
30 | print ( chr(data[0])+":%d,%d" % (dec16(data[2],data[3]),dec16(data[4],data[5])) )
31 | except:
32 | pass
33 |
34 | thread = Thread(target = reader, args = ())
35 | thread.start()
36 |
37 | out = VectorDisplay(s.sendall,lowendian=False)
38 |
39 | out.initialize(240,320)
40 |
41 | sleep(1)
42 | while 1:
43 | x1 = randint(0,239)
44 | y1 = randint(0,319)
45 | x2 = randint(0,239)
46 | y2 = randint(0,319)
47 | out.line(x1,y1,x2,y2)
48 | sleep(1)
49 |
--------------------------------------------------------------------------------
/test/poly.py:
--------------------------------------------------------------------------------
1 | import socket
2 | import sys
3 | import os
4 | import math
5 | from vectordisplay import VectorDisplay
6 | from random import randint
7 | from time import sleep
8 |
9 | if len(sys.argv)>1:
10 | addr = sys.argv[1]
11 | else:
12 | addr = '192.168.1.110'
13 |
14 | port = 7788
15 |
16 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
17 | s.connect((addr, port))
18 | print("connected")
19 |
20 | w = 300
21 | h = 400
22 | v = VectorDisplay(s.sendall,lowendian=False)
23 | v.initialize(w,h)
24 | v.rounded(True)
25 | v.poly([(100,100),(200,100),(150,50)], fill=True)
26 | sleep(0.5) # needed on Windows
27 | s.close()
28 |
--------------------------------------------------------------------------------
/test/rects.py:
--------------------------------------------------------------------------------
1 | import socket
2 | import sys
3 | import os
4 | from vectordisplay import VectorDisplay
5 | from random import randint
6 | from time import sleep
7 |
8 | if len(sys.argv)>1:
9 | addr = sys.argv[1]
10 | else:
11 | addr = '192.168.1.110'
12 |
13 | port = 7788
14 |
15 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
16 | s.connect((addr, port))
17 | print("connected")
18 |
19 | w = 3
20 | h = 4
21 | v = VectorDisplay(s.sendall,lowendian=False)
22 | v.initialize(w,h)
23 | v.rounded(True)
24 | v.foreColor(0xFFFF0000)
25 | v.rectangle(0,0,w-1,h-1)
26 | v.foreColor(0x3F00FF00)
27 | v.fillRectangle(0,0,w-1,h-1)
28 | v.foreColor(0x7F0000FF)
29 | v.fillRectangle(0,0,0,0)
30 | v.foreColor(0x7f00007F)
31 | v.fillRectangle(1,0,1,0)
32 | sleep(0.5) # needed on Windows
33 | s.close()
--------------------------------------------------------------------------------
/test/roundedrects.py:
--------------------------------------------------------------------------------
1 | import socket
2 | import sys
3 | import os
4 | from vectordisplay import VectorDisplay
5 | from random import randint
6 | from time import sleep
7 |
8 | if len(sys.argv)>1:
9 | addr = sys.argv[1]
10 | else:
11 | addr = '192.168.1.110'
12 |
13 | port = 7788
14 |
15 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
16 | s.connect((addr, port))
17 | print("connected")
18 |
19 | v = VectorDisplay(s.sendall,lowendian=True)
20 | v.initialize() # 200,200)
21 | v.coordinates(200,200);
22 | v.foreColor(0xFFFF0000)
23 | v.roundedRectangle(10,10,100,100,30,fill=False)
24 | v.foreColor(0x3F00FF00)
25 | v.roundedRectangle(80,80,180,180,30,fill=True)
26 | sleep(0.5) # needed on Windows
27 | s.close()
--------------------------------------------------------------------------------
/test/serialtest.py:
--------------------------------------------------------------------------------
1 | import serial
2 | from random import randint
3 | from time import sleep
4 | import sys
5 |
6 | if len(sys.argv)>1:
7 | addr = sys.argv[1]
8 | else:
9 | addr = 'COM29'
10 |
11 | s = serial.Serial(addr,baudrate=115200)
12 | print("connected")
13 |
14 | def sendCommand(c, args):
15 | command = [ ord(c), ord(c)^0xff ] + args + [ 0xff^sum(bytearray(args)) ]
16 | s.write(bytearray(c&0xFF for c in command))
17 | s.flush()
18 |
19 | ack = False
20 |
21 | while not ack:
22 | print("Init")
23 | sendCommand('H', [0x12, 0x34, 0, 0]) # big endian
24 | while s.inWaiting()>0:
25 | r = s.read()
26 | if b'A' == r:
27 | ack = True
28 | break
29 | if not ack:
30 | sleep(1)
31 | print("Ready")
32 | while 1:
33 | x1 = randint(0,239)
34 | y1 = randint(0,319)
35 | x2 = randint(0,239)
36 | y2 = randint(0,319)
37 | sendCommand('L', [x1>>8,x1&0xFF, y1>>8,y1&0xFF, x2>>8,x2&0xff, y2>>8,y2&0xFF ])
38 | sleep(0.1)
39 |
--------------------------------------------------------------------------------
/test/text.py:
--------------------------------------------------------------------------------
1 | import socket
2 | import sys
3 | import os
4 | import math
5 | from vectordisplay import VectorDisplay
6 | from random import randint
7 | from time import sleep
8 | from bitmap_data import *
9 |
10 | if len(sys.argv)>1:
11 | addr = sys.argv[1]
12 | else:
13 | addr = '192.168.1.143'
14 |
15 | port = 7788
16 |
17 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
18 | s.connect((addr, port))
19 | print("connected")
20 |
21 | w = 100
22 | h = 100
23 | v = VectorDisplay(s.sendall,lowendian=False)
24 | v.initialize(w,h)
25 | v.text(0,0,"Hello, world!")
26 | v.font(8|16); # bold italic serif
27 | v.text(0,8,"Hello, world!")
28 | sleep(0.5) # needed on Windows
29 | s.close()
30 |
--------------------------------------------------------------------------------
/test/vectordisplay.py:
--------------------------------------------------------------------------------
1 | from math import floor
2 |
3 | class VectorDisplay(object):
4 | def __init__(self,writer,lowendian=True):
5 | self.write = writer
6 | if lowendian:
7 | self.e16 = self.e16le
8 | self.e32 = self.e32le
9 | else:
10 | self.e16 = self.e16be
11 | self.e32 = self.e32be
12 |
13 | def e16le(self, x):
14 | x = int(floor(x))
15 | return (x&0xFF,(x>>8)&0xFF)
16 | def e16be(self, x):
17 | x = int(floor(x))
18 | return ((x>>8)&0xFF,x&0xFF)
19 | def e32le(self, x):
20 | x = int(floor(x))
21 | return (x&0xFF,(x>>8)&0xFF,(x>>16)&0xFF,(x>>24)&0xFF)
22 | def e32be(self, x):
23 | x = int(floor(x))
24 | return ((x>>24)&0xFF, (x>>16)&0xFF, (x>>8)&0xFF, x&0xFF)
25 |
26 | def command(self,c,data):
27 | bc = ord(c)
28 | self.write(bytearray((bc,bc^0xFF)+data+((0xFF&sum(data))^0xFF,)))
29 |
30 | def initialize(self,width=240,height=320):
31 | self.command('Z',self.e16(0x1234)+self.e16(width)+self.e16(height)+self.e32(65536)+self.e16(0)*3)
32 |
33 | def line(self,x1,y1,x2,y2):
34 | self.command('L',self.e16(x1)+self.e16(y1)+self.e16(x2)+self.e16(y2))
35 |
36 | def attr8(self,attr,value):
37 | self.command('Y',(ord(attr),value))
38 |
39 | def attr16(self,attr,data):
40 | self.command('A',(ord(attr),)+data)
41 |
42 | def attr32(self,attr,data):
43 | self.command('B',(ord(attr),)+data)
44 |
45 | def fp32(self,x):
46 | return int(x*65536.+0.5)
47 |
48 | def thickness(self,x):
49 | self.attr32('t',self.e32(self.fp32(x)))
50 |
51 | def fillRectangle(self,x1,y1,x2,y2):
52 | self.command('R', self.e16(x1)+self.e16(y1)+self.e16(x2)+self.e16(y2))
53 |
54 | def coordinates(self,w,h):
55 | self.attr32('c',self.e16(w)+self.e16(h))
56 |
57 | def rounded(self,r):
58 | self.attr8('n',r)
59 |
60 | def font(self,f):
61 | self.attr8('f',f)
62 |
63 | def rectangle(self,x1,y1,x2,y2,fill=False):
64 | if fill:
65 | self.command('R', self.e16(x1)+self.e16(y1)+self.e16(x2)+self.e16(y2))
66 | else:
67 | self.line(x1,y1,x2,y1)
68 | self.line(x2,y1,x2,y2)
69 | self.line(x2,y2,x1,y2)
70 | self.line(x1,y2,x1,y1)
71 |
72 | def text(self,x,y,s):
73 | self.command('T', self.e16(x)+self.e16(y)+tuple(ord(x) for x in s)+(0,))
74 |
75 | def roundedRectangle(self,x1,y1,x2,y2,r,fill=False):
76 | self.command('Q', self.e16(x1)+self.e16(y1)+self.e16(x2)+self.e16(y2)+self.e16(r)+(1 if fill else 0,))
77 |
78 | def foreColor(self,c):
79 | self.attr32('f',self.e32(c))
80 |
81 | def point(self,x,y):
82 | self.command('P',self.e16(x)+self.e16(y))
83 |
84 | def arc(self,cx,cy,r,angle1,angle2,fill=False):
85 | self.command('S',self.e16(cx)+self.e16(cy)+self.e16(r)+self.e32(angle1*65536)+self.e32(angle2*65536)+(1 if fill else 0,))
86 |
87 | def poly(self,points,fill=True):
88 | path = self.e16(len(points))
89 | for p in points:
90 | path += self.e16(p[0])+self.e16(p[1])
91 | self.command('N' if fill else 'O',path)
92 |
93 | def bitmap(self,x,y,width,height,data,depth=1,foreColor=0xFFFFFFFF,backColor=0xFF000000):
94 | if depth==1:
95 | dataLen = (width*height+7)//8
96 | outData = ( self.e32(8+dataLen)+(depth,0)+self.e16(x)+self.e16(y)+self.e16(width)+self.e16(height)+
97 | self.e32(foreColor)+self.e32(backColor)+data[:dataLen] )
98 | elif depth>=8:
99 | dataLen = width*height*(depth//8)
100 | outData = ( self.e32(dataLen)+(depth,8)+self.e16(x)+self.e16(y)+self.e16(width)+self.e16(height)+
101 | data[:dataLen] )
102 | else:
103 | return
104 |
105 | self.command('K',outData)
106 |
107 | def fillCircleHelper(self,cx,cy,r,corners,delta):
108 | # this will be ugly if alpha < 1.0 in the color
109 | if corners & 3:
110 | self.line(cx,cy-r,cx,cy+r+delta)
111 |
112 | if corners & 2:
113 | self.arc(cx,cy,r,90,180,fill=False)
114 | self.arc(cx,cy,r,90,180,fill=True)
115 | self.arc(cx,cy+delta,r,90,180,fill=False)
116 | self.arc(cx,cy+delta,r,90,180,fill=True)
117 | if delta>0:
118 | self.rectangle(cx-r,cy,cx,cy+delta,fill=False)
119 | self.rectangle(cx-r,cy,cx,cy+delta,fill=True)
120 | if corners & 1:
121 | self.arc(cx,cy,r,270,180,fill=False)
122 | self.arc(cx,cy,r,270,180,fill=True)
123 | self.arc(cx,cy+delta,r,270,180,fill=False)
124 | self.arc(cx,cy+delta,r,270,180,fill=True)
125 | if delta>0:
126 | self.rectangle(cx,cy,cx+r,cy+delta,fill=False)
127 | self.rectangle(cx,cy,cx+r,cy+delta,fill=True)
128 |
--------------------------------------------------------------------------------
/usbserial-4.5.2-release/build.gradle:
--------------------------------------------------------------------------------
1 | configurations.maybeCreate("default")
2 | artifacts.add("default", file('usbserial-4.5.2-release.aar'))
--------------------------------------------------------------------------------
/usbserial-4.5.2-release/usbserial-4.5.2-release.aar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arpruss/VectorDisplay/286bf571c13b825e34b3911e5875d997b8519454/usbserial-4.5.2-release/usbserial-4.5.2-release.aar
--------------------------------------------------------------------------------
/usbserial-4.5.2-release/usbserial-4.5.2-release.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/vectordisplay.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------