Compare commits
46 Commits
experiment
...
plaza-inte
Author | SHA1 | Date | |
---|---|---|---|
|
fc4c9139c5 | ||
|
13fa966557 | ||
|
d07d73b56a | ||
|
f82edf041c | ||
|
f8786abc14 | ||
|
486c342f86 | ||
|
b0fc1df080 | ||
|
7cea4a293c | ||
|
75421d4c95 | ||
|
4ecd4936bd | ||
|
af9352de42 | ||
|
8b3bbfee78 | ||
|
a2bcf79309 | ||
|
948a04c78f | ||
|
ef7e173caf | ||
|
6c2ced0685 | ||
|
1cbb712eee | ||
|
f456b75030 | ||
|
ceee0db011 | ||
|
047a13120e | ||
|
a4aff44818 | ||
|
af28ef0a3c | ||
|
d0f4a82043 | ||
|
c60ec32e05 | ||
|
e1895caa9d | ||
|
ac26c0d721 | ||
|
ca0fd6a529 | ||
|
41da29f669 | ||
|
3f24489138 | ||
|
188f3290cf | ||
|
da3dfd5d2e | ||
|
a9b5c8f02b | ||
|
acb0dbec05 | ||
|
7f2686166e | ||
|
2c88dd4b03 | ||
|
de2d474294 | ||
|
4d477cfa5d | ||
|
b6c712860d | ||
|
8507e868d8 | ||
|
cca4b70b90 | ||
|
7f5aea94ec | ||
|
033b79cba1 | ||
|
1cdc679c70 | ||
|
9fb10281cb | ||
|
a02d372b90 | ||
|
ac76c7d369 |
45
Makefile
Normal file
45
Makefile
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
|
||||||
|
ROUND_ICONS=app/src/main/res/mipmap-mdpi/ic_launcher_round.png \
|
||||||
|
app/src/main/res/mipmap-hdpi/ic_launcher_round.png \
|
||||||
|
app/src/main/res/mipmap-xhdpi/ic_launcher_round.png \
|
||||||
|
app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
|
||||||
|
|
||||||
|
RECT_ICONS=app/src/main/res/mipmap-mdpi/ic_launcher.png \
|
||||||
|
app/src/main/res/mipmap-hdpi/ic_launcher.png \
|
||||||
|
app/src/main/res/mipmap-xhdpi/ic_launcher.png \
|
||||||
|
app/src/main/res/mipmap-xxhdpi/ic_launcher.png
|
||||||
|
|
||||||
|
ICONS=$(ROUND_ICONS) $(RECT_ICONS)
|
||||||
|
RECT_BASE=app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
|
||||||
|
ROUND_BASE=app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
|
||||||
|
BASE_ICONS=$(RECT_BASE) $(ROUND_BASE)
|
||||||
|
|
||||||
|
.PHONY: all
|
||||||
|
|
||||||
|
all: $(ICONS)
|
||||||
|
|
||||||
|
app/src/main/res/mipmap-mdpi/ic_launcher_round.png: $(ROUND_BASE)
|
||||||
|
convert $< -resize 48x48 $@
|
||||||
|
|
||||||
|
app/src/main/res/mipmap-hdpi/ic_launcher_round.png: $(ROUND_BASE)
|
||||||
|
convert $< -resize 72x72 $@
|
||||||
|
|
||||||
|
app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: $(ROUND_BASE)
|
||||||
|
convert $< -resize 96x96 $@
|
||||||
|
|
||||||
|
app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: $(ROUND_BASE)
|
||||||
|
convert $< -resize 144x144 $@
|
||||||
|
|
||||||
|
app/src/main/res/mipmap-mdpi/ic_launcher.png: $(RECT_BASE)
|
||||||
|
convert $< -resize 48x48 $@
|
||||||
|
|
||||||
|
app/src/main/res/mipmap-hdpi/ic_launcher.png: $(RECT_BASE)
|
||||||
|
convert $< -resize 72x72 $@
|
||||||
|
|
||||||
|
app/src/main/res/mipmap-xhdpi/ic_launcher.png: $(RECT_BASE)
|
||||||
|
convert $< -resize 96x96 $@
|
||||||
|
|
||||||
|
app/src/main/res/mipmap-xxhdpi/ic_launcher.png: $(RECT_BASE)
|
||||||
|
convert $< -resize 144x144 $@
|
||||||
|
|
||||||
|
|
@ -1,15 +1,17 @@
|
|||||||
apply plugin: 'com.android.application'
|
apply plugin: 'com.android.application'
|
||||||
|
apply plugin: 'kotlin-android-extensions'
|
||||||
|
apply plugin: 'kotlin-android'
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 25
|
compileSdkVersion 29
|
||||||
buildToolsVersion "27.0.1"
|
buildToolsVersion '29.0.2'
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId "com.codigoparallevar.minicards"
|
applicationId "com.codigoparallevar.minicards"
|
||||||
minSdkVersion 15
|
minSdkVersion 15
|
||||||
targetSdkVersion 25
|
targetSdkVersion 29
|
||||||
versionCode 1
|
versionCode 1
|
||||||
versionName "1.0"
|
versionName "1.0"
|
||||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
}
|
}
|
||||||
buildTypes {
|
buildTypes {
|
||||||
release {
|
release {
|
||||||
@ -17,18 +19,31 @@ android {
|
|||||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
compileOptions {
|
||||||
|
sourceCompatibility = 1.8
|
||||||
|
targetCompatibility = 1.8
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||||
androidTestImplementation('com.android.support.test.espresso:espresso-core:2.2.2', {
|
implementation 'androidx.annotation:annotation:1.0.0'
|
||||||
|
implementation 'androidx.lifecycle:lifecycle-extensions:2.0.0'
|
||||||
|
androidTestImplementation('androidx.test.espresso:espresso-core:3.1.0', {
|
||||||
exclude group: 'com.android.support', module: 'support-annotations'
|
exclude group: 'com.android.support', module: 'support-annotations'
|
||||||
})
|
})
|
||||||
implementation 'com.android.support:appcompat-v7:25.4.0'
|
implementation 'androidx.appcompat:appcompat:1.0.0'
|
||||||
testImplementation 'junit:junit:4.12'
|
testImplementation 'junit:junit:4.12'
|
||||||
implementation 'com.android.support.constraint:constraint-layout:1.0.2'
|
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
|
||||||
implementation 'com.android.support:design:25.4.0'
|
implementation 'com.google.android.material:material:1.0.0'
|
||||||
compile 'com.getbase:floatingactionbutton:1.10.1'
|
implementation 'com.getbase:floatingactionbutton:1.10.1'
|
||||||
compile 'com.larswerkman:HoloColorPicker:1.5'
|
implementation 'com.larswerkman:HoloColorPicker:1.5'
|
||||||
implementation 'com.android.support:cardview-v7:25.4.0'
|
implementation 'androidx.cardview:cardview:1.0.0'
|
||||||
|
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||||
|
implementation 'com.google.code.gson:gson:2.8.6'
|
||||||
|
|
||||||
|
implementation group: 'com.squareup.okhttp3', name: 'okhttp', version: '4.7.2'
|
||||||
|
}
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
package com.codigoparallevar.minicards;
|
package com.codigoparallevar.minicards;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.support.test.InstrumentationRegistry;
|
import androidx.test.platform.app.InstrumentationRegistry;
|
||||||
import android.support.test.runner.AndroidJUnit4;
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
@ -2,11 +2,25 @@
|
|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
package="com.codigoparallevar.minicards">
|
package="com.codigoparallevar.minicards">
|
||||||
|
|
||||||
<!-- OpenGL ES 2.0 -->
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
|
<!-- Network state on programaker bridge -->
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" android:required="false" />
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" android:required="false" />
|
||||||
|
<!-- For Wifi SSID -->
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" android:required="false" />
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" android:required="false" />
|
||||||
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" android:required="false" />
|
||||||
|
<!-- For vibration block on bridge -->
|
||||||
|
<uses-permission android:name="android.permission.VIBRATE" android:required="false" />
|
||||||
|
<!-- For Camera block -->
|
||||||
|
<uses-permission android:name="android.permission.CAMERA" android:required="false" />
|
||||||
|
|
||||||
|
<!-- OpenGL ES 2.0 -->
|
||||||
<uses-feature
|
<uses-feature
|
||||||
android:glEsVersion="0x00020000"
|
android:glEsVersion="0x00020000"
|
||||||
android:required="true" />
|
android:required="true" />
|
||||||
|
<uses-feature android:name="android.hardware.camera" />
|
||||||
|
<uses-feature android:name="android.hardware.camera.autofocus" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
@ -15,9 +29,15 @@
|
|||||||
android:roundIcon="@mipmap/ic_launcher_round"
|
android:roundIcon="@mipmap/ic_launcher_round"
|
||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:theme="@style/AppTheme">
|
android:theme="@style/AppTheme">
|
||||||
|
<service
|
||||||
|
android:name=".bridge.ProgramakerBridgeService"
|
||||||
|
android:enabled="true"
|
||||||
|
android:exported="true" />
|
||||||
|
|
||||||
<activity android:name=".CardActivity">
|
<activity android:name=".CardActivity">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="com.codigoparallevar.minicards.CARD" />
|
<action android:name="com.codigoparallevar.minicards.CARD" />
|
||||||
|
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
@ -26,10 +46,12 @@
|
|||||||
android:theme="@style/AppTheme.NoActionBar">
|
android:theme="@style/AppTheme.NoActionBar">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="com.codigoparallevar.minicards.DECK" />
|
<action android:name="com.codigoparallevar.minicards.DECK" />
|
||||||
|
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
@ -4,27 +4,33 @@ import android.content.Context;
|
|||||||
import android.graphics.Canvas;
|
import android.graphics.Canvas;
|
||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
import android.graphics.Paint;
|
import android.graphics.Paint;
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
import android.support.annotation.Nullable;
|
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import com.codigoparallevar.minicards.motion.MotionMode;
|
import com.codigoparallevar.minicards.motion.MotionMode;
|
||||||
|
import com.codigoparallevar.minicards.parts.connectors.ProgramakerCustomBlockInputConnector;
|
||||||
import com.codigoparallevar.minicards.types.Part;
|
import com.codigoparallevar.minicards.types.Part;
|
||||||
import com.codigoparallevar.minicards.types.PartConnection;
|
import com.codigoparallevar.minicards.types.PartConnection;
|
||||||
import com.codigoparallevar.minicards.types.PartGrid;
|
import com.codigoparallevar.minicards.types.PartGrid;
|
||||||
import com.codigoparallevar.minicards.types.Position;
|
import com.codigoparallevar.minicards.types.Position;
|
||||||
import com.codigoparallevar.minicards.types.Selectable;
|
import com.codigoparallevar.minicards.types.Selectable;
|
||||||
import com.codigoparallevar.minicards.types.Tuple2;
|
import com.codigoparallevar.minicards.types.connectors.Wiring.SignalWire;
|
||||||
import com.codigoparallevar.minicards.types.Tuple4;
|
import com.codigoparallevar.minicards.types.connectors.Wiring.Wire;
|
||||||
import com.codigoparallevar.minicards.types.connectors.input.AnyInputConnector;
|
import com.codigoparallevar.minicards.types.connectors.input.AnyInputConnector;
|
||||||
import com.codigoparallevar.minicards.types.connectors.input.BooleanInputConnector;
|
import com.codigoparallevar.minicards.types.connectors.input.BooleanInputConnector;
|
||||||
|
import com.codigoparallevar.minicards.types.connectors.input.ImageInputConnector;
|
||||||
import com.codigoparallevar.minicards.types.connectors.input.InputConnector;
|
import com.codigoparallevar.minicards.types.connectors.input.InputConnector;
|
||||||
import com.codigoparallevar.minicards.types.connectors.input.SignalInputConnector;
|
import com.codigoparallevar.minicards.types.connectors.input.SignalInputConnector;
|
||||||
import com.codigoparallevar.minicards.types.connectors.input.StringInputConnector;
|
import com.codigoparallevar.minicards.types.connectors.input.StringInputConnector;
|
||||||
import com.codigoparallevar.minicards.types.connectors.output.OutputConnector;
|
import com.codigoparallevar.minicards.types.connectors.output.OutputConnector;
|
||||||
|
import com.codigoparallevar.minicards.types.functional.Tuple2;
|
||||||
|
import com.codigoparallevar.minicards.types.functional.Tuple4;
|
||||||
|
import com.programaker.api.ProgramakerApi;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@ -32,7 +38,8 @@ import java.util.HashMap;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
class CanvasView extends View implements PartGrid {
|
public class CanvasView extends View implements PartGrid {
|
||||||
|
private static final String LogTag = "Canvas view";
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
List<Part> parts = new ArrayList<>();
|
List<Part> parts = new ArrayList<>();
|
||||||
@ -51,6 +58,7 @@ class CanvasView extends View implements PartGrid {
|
|||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
private String name = "default";
|
private String name = "default";
|
||||||
|
private ProgramakerApi api = null;
|
||||||
private final static float touchTimeForLongTouchInMillis = 500;
|
private final static float touchTimeForLongTouchInMillis = 500;
|
||||||
private boolean _isDragging = false;
|
private boolean _isDragging = false;
|
||||||
private CardActivity parentActivity = null;
|
private CardActivity parentActivity = null;
|
||||||
@ -63,6 +71,7 @@ class CanvasView extends View implements PartGrid {
|
|||||||
@Nullable
|
@Nullable
|
||||||
private Tuple2<Integer, Integer> _mouseDownPoint = null;
|
private Tuple2<Integer, Integer> _mouseDownPoint = null;
|
||||||
private int cardBackgroundColor;
|
private int cardBackgroundColor;
|
||||||
|
private SignalListenerManager listenerManager = null;
|
||||||
|
|
||||||
public CanvasView(Context context) {
|
public CanvasView(Context context) {
|
||||||
super(context);
|
super(context);
|
||||||
@ -88,7 +97,7 @@ class CanvasView extends View implements PartGrid {
|
|||||||
Map<String, Part> partsById = buildPartsById();
|
Map<String, Part> partsById = buildPartsById();
|
||||||
for (PartConnection connection : connections){
|
for (PartConnection connection : connections){
|
||||||
if (!partsById.containsKey(connection.inputPartId)){
|
if (!partsById.containsKey(connection.inputPartId)){
|
||||||
Log.e("Canvas view", "Key '" + connection.inputPartId
|
Log.e(LogTag, "Key '" + connection.inputPartId
|
||||||
+ "' not found on deserialization");
|
+ "' not found on deserialization");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -97,14 +106,14 @@ class CanvasView extends View implements PartGrid {
|
|||||||
InputConnector inputConnector = inputPart.getConnectorWithId(connection.inputConnectorId);
|
InputConnector inputConnector = inputPart.getConnectorWithId(connection.inputConnectorId);
|
||||||
|
|
||||||
if (inputConnector == null){
|
if (inputConnector == null){
|
||||||
Log.e("Canvas view", "Connector ID '" + connection.inputConnectorId
|
Log.e(LogTag, "Connector ID '" + connection.inputConnectorId
|
||||||
+ "' not found on deserialization");
|
+ "' not found on deserialization");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
OutputConnector outputConnector = connection.outputConnector;
|
OutputConnector outputConnector = connection.outputConnector;
|
||||||
if (inputConnector == null){
|
if (inputConnector == null){
|
||||||
Log.e("Canvas view", "Connector not found on connection");
|
Log.e(LogTag, "Connector not found on connection");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -116,7 +125,7 @@ class CanvasView extends View implements PartGrid {
|
|||||||
outputConnector.connectTo(inputConnector);
|
outputConnector.connectTo(inputConnector);
|
||||||
}
|
}
|
||||||
catch (ClassCastException e) {
|
catch (ClassCastException e) {
|
||||||
Log.e("Minicards - Canvas view", "Malformed connection", e);
|
Log.e(LogTag, "Malformed connection", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -125,7 +134,7 @@ class CanvasView extends View implements PartGrid {
|
|||||||
Map<String, Part> partsById = new HashMap<>(parts.size());
|
Map<String, Part> partsById = new HashMap<>(parts.size());
|
||||||
for (Part part : parts) {
|
for (Part part : parts) {
|
||||||
partsById.put(part.get_id(), part);
|
partsById.put(part.get_id(), part);
|
||||||
Log.w("CanvasView", "Added part ID: " + part.get_id() + " - " + part);
|
Log.w(LogTag, "Added part ID: " + part.get_id() + " - " + part);
|
||||||
}
|
}
|
||||||
|
|
||||||
return partsById;
|
return partsById;
|
||||||
@ -142,7 +151,7 @@ class CanvasView extends View implements PartGrid {
|
|||||||
part.draw(scrolledCanvas, _devMode);
|
part.draw(scrolledCanvas, _devMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.d("Render time", System.currentTimeMillis() - renderStartTime + "ms");
|
Log.v(LogTag, "Render time: " + (System.currentTimeMillis() - renderStartTime) + "ms");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void drawBackground(ScrolledCanvas canvas) {
|
private void drawBackground(ScrolledCanvas canvas) {
|
||||||
@ -177,7 +186,15 @@ class CanvasView extends View implements PartGrid {
|
|||||||
final int xInCanvas = xInScreen + _viewOrigin.item1;
|
final int xInCanvas = xInScreen + _viewOrigin.item1;
|
||||||
final int yInCanvas = yInScreen + _viewOrigin.item2;
|
final int yInCanvas = yInScreen + _viewOrigin.item2;
|
||||||
|
|
||||||
switch (event.getAction()){
|
int action = event.getAction();
|
||||||
|
if ((action == MotionEvent.ACTION_MOVE)
|
||||||
|
&& _devMode
|
||||||
|
&& (selectedPart instanceof Wire)) {
|
||||||
|
// Wires cannot be moved, so go into "move canvas" mode
|
||||||
|
selectedPart = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (action){
|
||||||
case MotionEvent.ACTION_DOWN:
|
case MotionEvent.ACTION_DOWN:
|
||||||
{
|
{
|
||||||
_mouseDownPoint = new Tuple2<>(xInScreen, yInScreen);
|
_mouseDownPoint = new Tuple2<>(xInScreen, yInScreen);
|
||||||
@ -186,11 +203,10 @@ class CanvasView extends View implements PartGrid {
|
|||||||
lastTouchedPosition.to(xInScreen, yInScreen);
|
lastTouchedPosition.to(xInScreen, yInScreen);
|
||||||
lastTouchedTime = System.currentTimeMillis();
|
lastTouchedTime = System.currentTimeMillis();
|
||||||
if (selectedPart == null) {
|
if (selectedPart == null) {
|
||||||
Log.d("Touched part", "not found");
|
Log.d(LogTag, "Touched part not found");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
Log.d(LogTag, "Touched part FOUND. Part: " + selectedPart);
|
||||||
Log.d("Touched part", "Part: " + selectedPart);
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -204,14 +220,28 @@ class CanvasView extends View implements PartGrid {
|
|||||||
if (selectedPart instanceof Part){
|
if (selectedPart instanceof Part){
|
||||||
((Part) selectedPart).touched();
|
((Part) selectedPart).touched();
|
||||||
}
|
}
|
||||||
|
else if (selectedPart instanceof Wire) {
|
||||||
|
// TODO: No drag or long-touch, just show the "cut" option
|
||||||
|
Log.d(LogTag, "Touched Wire");
|
||||||
|
Wire selectedWire = (Wire) selectedPart;
|
||||||
|
selectedPart = null;
|
||||||
|
|
||||||
|
selectedWire.unlink();
|
||||||
|
}
|
||||||
|
else if (selectedPart instanceof ProgramakerCustomBlockInputConnector) {
|
||||||
|
((ProgramakerCustomBlockInputConnector) selectedPart).touched();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (motionMode == MotionMode.Type.LongTouch && _devMode) {
|
else if (motionMode == MotionMode.Type.LongTouch && _devMode) {
|
||||||
|
if (selectedPart instanceof Wire) {
|
||||||
|
// Wires cannot be moved, so go into the
|
||||||
|
}
|
||||||
if (selectedPart != null) {
|
if (selectedPart != null) {
|
||||||
selectedPart.getMoveable().drop(xInCanvas, yInCanvas);
|
selectedPart.getMoveable().drop(xInCanvas, yInCanvas);
|
||||||
|
|
||||||
if (inDropZone(xInScreen, yInScreen)) {
|
if (inDropZone(xInScreen, yInScreen)) {
|
||||||
Log.d("Canvas", "Deleting element" + selectedPart);
|
Log.d(LogTag, "Deleting element" + selectedPart);
|
||||||
parts.remove(selectedPart);
|
parts.remove(selectedPart);
|
||||||
selectedPart.unlink();
|
selectedPart.unlink();
|
||||||
}
|
}
|
||||||
@ -226,7 +256,7 @@ class CanvasView extends View implements PartGrid {
|
|||||||
try {
|
try {
|
||||||
saveState();
|
saveState();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Log.w("PartCanvasView", e.getMessage());
|
Log.w(LogTag, e.getMessage());
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -236,6 +266,7 @@ class CanvasView extends View implements PartGrid {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Log.v(LogTag, "Moving part="+selectedPart);
|
||||||
if (selectedPart == null){
|
if (selectedPart == null){
|
||||||
int xMovement = _mouseDownPoint.item1 - xInScreen;
|
int xMovement = _mouseDownPoint.item1 - xInScreen;
|
||||||
int yMovement = _mouseDownPoint.item2 - yInScreen;
|
int yMovement = _mouseDownPoint.item2 - yInScreen;
|
||||||
@ -246,7 +277,7 @@ class CanvasView extends View implements PartGrid {
|
|||||||
_mouseDownPoint = new Tuple2(xInScreen, yInScreen);
|
_mouseDownPoint = new Tuple2(xInScreen, yInScreen);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Log.i("Canvas", "X: " + xInScreen + " Y: " + yInScreen
|
Log.d(LogTag, "X: " + xInScreen + " Y: " + yInScreen
|
||||||
+ " in drop zone " + _dropToRemoveZone + " : "
|
+ " in drop zone " + _dropToRemoveZone + " : "
|
||||||
+ inDropZone(xInScreen, yInScreen));
|
+ inDropZone(xInScreen, yInScreen));
|
||||||
|
|
||||||
@ -268,7 +299,7 @@ class CanvasView extends View implements PartGrid {
|
|||||||
|
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
Log.d("PartCanvasView", "Unhandled action: " + event.getAction());
|
Log.d(LogTag, "Unhandled action: " + event.getAction());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -323,22 +354,66 @@ class CanvasView extends View implements PartGrid {
|
|||||||
for (int i = parts.size() - 1; i >= 0; i--){
|
for (int i = parts.size() - 1; i >= 0; i--){
|
||||||
final Part part = parts.get(i);
|
final Part part = parts.get(i);
|
||||||
// First try with output connectors
|
// First try with output connectors
|
||||||
for (OutputConnector outputConnector : part.getOutputConnectors()){
|
List<OutputConnector> outputConnectors = part.getOutputConnectors();
|
||||||
|
if (outputConnectors != null) {
|
||||||
|
for (OutputConnector outputConnector : outputConnectors) {
|
||||||
if (outputConnector.containsPoint(x, y)) {
|
if (outputConnector.containsPoint(x, y)) {
|
||||||
return outputConnector;
|
return outputConnector;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Then with input ones
|
// Then with input ones
|
||||||
for (InputConnector inputConnector : part.getInputConnectors()){
|
List<InputConnector> inputConnectors = part.getInputConnectors();
|
||||||
|
if (inputConnectors != null) {
|
||||||
|
for (InputConnector inputConnector : inputConnectors) {
|
||||||
if (inputConnector.containsPoint(x, y)) {
|
if (inputConnector.containsPoint(x, y)) {
|
||||||
return inputConnector;
|
return inputConnector;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally, try with the wires
|
||||||
|
for (int i = parts.size() - 1; i >= 0; i--) {
|
||||||
|
final Part part = parts.get(i);
|
||||||
|
List<OutputConnector> outputConnectors = part.getOutputConnectors();
|
||||||
|
if (outputConnectors != null) {
|
||||||
|
for (OutputConnector outputConnector : outputConnectors) {
|
||||||
|
List<SignalWire> wires = outputConnector.getWires();
|
||||||
|
if (wires == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (Wire wire : wires) {
|
||||||
|
if (wire.containsPoint(x, y)) {
|
||||||
|
Log.d(LogTag, "Point in wire " + wire);
|
||||||
|
return wire;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ProgramakerApi getApi() {
|
||||||
|
if (api == null) {
|
||||||
|
api = new ProgramakerApi();
|
||||||
|
api.setToken(new ConfigManager(this.getContext()).getToken());
|
||||||
|
}
|
||||||
|
return api;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SignalListenerManager getListenerManager() {
|
||||||
|
if (listenerManager == null) {
|
||||||
|
listenerManager = new SignalListenerManager(getApi());
|
||||||
|
}
|
||||||
|
return listenerManager;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Nullable
|
@Nullable
|
||||||
public SignalInputConnector getSignalInputConnectorOn(int x, int y) {
|
public SignalInputConnector getSignalInputConnectorOn(int x, int y) {
|
||||||
@ -371,11 +446,9 @@ class CanvasView extends View implements PartGrid {
|
|||||||
@Override
|
@Override
|
||||||
@Nullable
|
@Nullable
|
||||||
public BooleanInputConnector getBooleanInputConnectorOn(int x, int y) {
|
public BooleanInputConnector getBooleanInputConnectorOn(int x, int y) {
|
||||||
// If no part was found, do the same for connectors
|
|
||||||
for (int i = parts.size() - 1; i >= 0; i--){
|
for (int i = parts.size() - 1; i >= 0; i--){
|
||||||
final Part part = parts.get(i);
|
final Part part = parts.get(i);
|
||||||
|
|
||||||
// Then with input ones
|
|
||||||
for (InputConnector inputConnector : part.getInputConnectors()){
|
for (InputConnector inputConnector : part.getInputConnectors()){
|
||||||
BooleanInputConnector booleanInputConnector;
|
BooleanInputConnector booleanInputConnector;
|
||||||
if (inputConnector instanceof BooleanInputConnector){
|
if (inputConnector instanceof BooleanInputConnector){
|
||||||
@ -397,6 +470,25 @@ class CanvasView extends View implements PartGrid {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AnyInputConnector getAnyInputConnectorOn(int x, int y) {
|
||||||
|
for (int i = parts.size() - 1; i >= 0; i--){
|
||||||
|
final Part part = parts.get(i);
|
||||||
|
|
||||||
|
for (InputConnector inputConnector : part.getInputConnectors()){
|
||||||
|
if (!(inputConnector instanceof AnyInputConnector)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inputConnector.containsPoint(x, y)){
|
||||||
|
return (AnyInputConnector) inputConnector;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Nullable
|
@Nullable
|
||||||
public StringInputConnector getStringInputConnectorOn(int x, int y) {
|
public StringInputConnector getStringInputConnectorOn(int x, int y) {
|
||||||
@ -426,6 +518,31 @@ class CanvasView extends View implements PartGrid {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ImageInputConnector getImageInputConnectorOn(int x, int y) {
|
||||||
|
// If no part was found, do the same for connectors
|
||||||
|
for (int i = parts.size() - 1; i >= 0; i--){
|
||||||
|
final Part part = parts.get(i);
|
||||||
|
|
||||||
|
// Then with input ones
|
||||||
|
for (InputConnector inputConnector : part.getInputConnectors()){
|
||||||
|
ImageInputConnector imageInputConnector;
|
||||||
|
if (inputConnector instanceof ImageInputConnector){
|
||||||
|
imageInputConnector = (ImageInputConnector) inputConnector;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (imageInputConnector.containsPoint(x, y)){
|
||||||
|
return imageInputConnector;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public void addPart(Part part) {
|
public void addPart(Part part) {
|
||||||
parts.add(part);
|
parts.add(part);
|
||||||
@ -465,11 +582,8 @@ class CanvasView extends View implements PartGrid {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void update() {
|
public void update() {
|
||||||
parentActivity.runOnUiThread(new Runnable() {
|
parentActivity.runOnUiThread(() -> {
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
CanvasView.this.invalidate();
|
CanvasView.this.invalidate();
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -490,7 +604,11 @@ class CanvasView extends View implements PartGrid {
|
|||||||
|
|
||||||
public void pause() {
|
public void pause() {
|
||||||
for (Part part : parts) {
|
for (Part part : parts) {
|
||||||
part.resume();
|
part.pause();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void requestPermission(String permission, Runnable ifAccepted) {
|
||||||
|
this.parentActivity.requestPermissions(permission, ifAccepted);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,15 +2,32 @@ package com.codigoparallevar.minicards;
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.support.design.widget.FloatingActionButton;
|
import android.content.pm.PackageManager;
|
||||||
import android.support.v7.app.ActionBar;
|
import android.os.AsyncTask;
|
||||||
import android.support.v7.app.AppCompatActivity;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.appcompat.app.ActionBar;
|
||||||
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
|
||||||
|
import com.codigoparallevar.minicards.toolbox.PartsHolder;
|
||||||
|
import com.codigoparallevar.minicards.types.functional.Tuple2;
|
||||||
|
import com.codigoparallevar.minicards.types.functional.Tuple3;
|
||||||
|
import com.codigoparallevar.minicards.ui_helpers.GetAsync;
|
||||||
|
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
||||||
|
import com.programaker.api.ProgramakerApi;
|
||||||
|
import com.programaker.api.data.ProgramakerBridgeInfo;
|
||||||
|
import com.programaker.api.data.api_results.ProgramakerBridgeCustomBlockResult;
|
||||||
|
import com.programaker.api.data.api_results.ProgramakerGetCustomBlocksResult;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
public class CardActivity extends AppCompatActivity {
|
public class CardActivity extends AppCompatActivity {
|
||||||
|
|
||||||
@ -18,7 +35,10 @@ public class CardActivity extends AppCompatActivity {
|
|||||||
public static final String CARD_PATH_KEY = "CARD_PATH";
|
public static final String CARD_PATH_KEY = "CARD_PATH";
|
||||||
public static final String VISUALIZATION_MODE_KEY = "VISUALIZATION_MODE";
|
public static final String VISUALIZATION_MODE_KEY = "VISUALIZATION_MODE";
|
||||||
public static final String DEVELOPER_VISUALIZATION_MODE = "DEVELOPER_VISUALIZATION_MODE";
|
public static final String DEVELOPER_VISUALIZATION_MODE = "DEVELOPER_VISUALIZATION_MODE";
|
||||||
|
public static final String USER_VISUALIZATION_MODE = "USER_VISUALIZATION_MODE";
|
||||||
|
|
||||||
|
private int permissionRequestLatest = 1;
|
||||||
|
private Map<Integer, Runnable> permissionRequestCallbacks = new HashMap<>();
|
||||||
|
|
||||||
CanvasView canvasView;
|
CanvasView canvasView;
|
||||||
com.getbase.floatingactionbutton.AddFloatingActionButton AddPartButton;
|
com.getbase.floatingactionbutton.AddFloatingActionButton AddPartButton;
|
||||||
@ -34,11 +54,35 @@ public class CardActivity extends AppCompatActivity {
|
|||||||
boolean devMode = false;
|
boolean devMode = false;
|
||||||
FloatingActionButton removePartFab;
|
FloatingActionButton removePartFab;
|
||||||
private PartsHolder partsHolder;
|
private PartsHolder partsHolder;
|
||||||
|
private ProgramakerApi ProgramakerApi;
|
||||||
|
private ConfigManager Config;
|
||||||
|
private ProgramakerGetCustomBlocksResult CustomBlocks = null;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
this.Config = new ConfigManager(this);
|
||||||
|
this.ProgramakerApi = new ProgramakerApi();
|
||||||
|
String token = this.Config.getToken();
|
||||||
|
if (token != null) {
|
||||||
|
this.ProgramakerApi.setToken(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
partsHolder = new PartsHolder(this);
|
||||||
|
|
||||||
|
new GetAsync<Tuple2<List<ProgramakerBridgeInfo>, List<ProgramakerBridgeCustomBlockResult>>>()
|
||||||
|
.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,
|
||||||
|
new Tuple3<>(() ->
|
||||||
|
new Tuple2<>(
|
||||||
|
CardActivity.this.ProgramakerApi.fetchConnectedBridges(),
|
||||||
|
CardActivity.this.ProgramakerApi.fetchCustomBlocks()),
|
||||||
|
result -> {
|
||||||
|
partsHolder.addCustomBlocks(result.item1, result.item2);
|
||||||
|
Log.d("CARDActivity", "custom blocks: " + result.toString());
|
||||||
|
},
|
||||||
|
exception -> Log.e("CARDActivity", "error retrieving custom blocks: " + exception.toString())));
|
||||||
|
|
||||||
// Hide action bar
|
// Hide action bar
|
||||||
ActionBar actionBar = getSupportActionBar();
|
ActionBar actionBar = getSupportActionBar();
|
||||||
if (actionBar != null) {
|
if (actionBar != null) {
|
||||||
@ -52,7 +96,6 @@ public class CardActivity extends AppCompatActivity {
|
|||||||
canvasView.setParentActivity(this);
|
canvasView.setParentActivity(this);
|
||||||
|
|
||||||
// Initialize auxiliary elements
|
// Initialize auxiliary elements
|
||||||
partsHolder = new PartsHolder(this);
|
|
||||||
|
|
||||||
removePartFab = (FloatingActionButton) findViewById(R.id.remove_part_fab);
|
removePartFab = (FloatingActionButton) findViewById(R.id.remove_part_fab);
|
||||||
canvasView.setDropZone(
|
canvasView.setDropZone(
|
||||||
@ -97,8 +140,7 @@ public class CardActivity extends AppCompatActivity {
|
|||||||
ShowDeckFromDevModeButton.setOnClickListener(new View.OnClickListener() {
|
ShowDeckFromDevModeButton.setOnClickListener(new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
Intent i = new Intent(DeckPreviewActivity.INTENT);
|
finish();
|
||||||
CardActivity.this.startActivity(i);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -107,8 +149,7 @@ public class CardActivity extends AppCompatActivity {
|
|||||||
ShowDeckFromUserModeButton.setOnClickListener(new View.OnClickListener() {
|
ShowDeckFromUserModeButton.setOnClickListener(new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
Intent i = new Intent(DeckPreviewActivity.INTENT);
|
finish();
|
||||||
CardActivity.this.startActivity(i);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -196,13 +237,11 @@ public class CardActivity extends AppCompatActivity {
|
|||||||
if (canvasView.isDragging()){
|
if (canvasView.isDragging()){
|
||||||
devFabMenu.setVisibility(View.GONE);
|
devFabMenu.setVisibility(View.GONE);
|
||||||
userFabMenu.setVisibility(View.GONE);
|
userFabMenu.setVisibility(View.GONE);
|
||||||
removePartFab.setVisibility(View.VISIBLE);
|
((View)removePartFab).setVisibility(View.VISIBLE);
|
||||||
Log.d("Main", "Changing visibility!");
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
this.setDevMode(devMode);
|
this.setDevMode(devMode);
|
||||||
removePartFab.setVisibility(View.GONE);
|
((View)removePartFab).setVisibility(View.GONE);
|
||||||
Log.d("Main", "Now changing visibility!");
|
|
||||||
}
|
}
|
||||||
canvasView.setDropZone(
|
canvasView.setDropZone(
|
||||||
removePartFab.getX(), removePartFab.getX() + removePartFab.getWidth(),
|
removePartFab.getX(), removePartFab.getX() + removePartFab.getWidth(),
|
||||||
@ -211,11 +250,54 @@ public class CardActivity extends AppCompatActivity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void openCard(Context context, CardFile cardfile, String visualizationMode) {
|
public static void openCard(Context context, CardFile cardfile, String visualizationMode) {
|
||||||
|
openCardByPath(context, cardfile.getPath(), visualizationMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void openCard(Context context, PreviewCard cardfile, String visualizationMode) {
|
||||||
|
openCardByPath(context, cardfile.getPath(), visualizationMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void openCardByPath(Context context, String path, String visualizationMode) {
|
||||||
Intent i = new Intent(INTENT);
|
Intent i = new Intent(INTENT);
|
||||||
i.putExtra(CARD_PATH_KEY, cardfile.getPath());
|
i.putExtra(CARD_PATH_KEY, path);
|
||||||
if (visualizationMode != null) {
|
if (visualizationMode != null) {
|
||||||
i.putExtra(VISUALIZATION_MODE_KEY, visualizationMode);
|
i.putExtra(VISUALIZATION_MODE_KEY, visualizationMode);
|
||||||
}
|
}
|
||||||
context.startActivity(i);
|
context.startActivity(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void requestPermissions(String permission, Runnable ifAccepted) {
|
||||||
|
permissionRequestLatest++;
|
||||||
|
int request_code = permissionRequestLatest;
|
||||||
|
permissionRequestCallbacks.put(request_code, ifAccepted);
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||||
|
this.requestPermissions(new String[]{permission}, request_code);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
|
||||||
|
@NonNull int[] grantResults) {
|
||||||
|
// If request is cancelled, the result arrays are empty.
|
||||||
|
if (grantResults.length > 0 &&
|
||||||
|
grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||||
|
// Permission is granted. Continue the action or workflow
|
||||||
|
// in your app.
|
||||||
|
if (permissionRequestCallbacks.containsKey(requestCode)) {
|
||||||
|
Runnable callback = permissionRequestCallbacks.get(requestCode);
|
||||||
|
permissionRequestCallbacks.remove(requestCode);
|
||||||
|
callback.run();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// TODO:
|
||||||
|
// Explain to the user that the feature is unavailable because
|
||||||
|
// the features requires a permission that the user has denied.
|
||||||
|
// At the same time, respect the user's decision. Don't link to
|
||||||
|
// system settings in an effort to convince the user to change
|
||||||
|
// their decision.
|
||||||
|
if (permissionRequestCallbacks.containsKey(requestCode)) {
|
||||||
|
permissionRequestCallbacks.remove(requestCode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,19 +4,23 @@ import android.annotation.TargetApi;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
import com.codigoparallevar.minicards.parts.ProgramakerCustomBlockPart;
|
||||||
|
import com.codigoparallevar.minicards.parts.android.CameraStreamer;
|
||||||
import com.codigoparallevar.minicards.parts.buttons.RoundButton;
|
import com.codigoparallevar.minicards.parts.buttons.RoundButton;
|
||||||
import com.codigoparallevar.minicards.parts.logic.Ticker;
|
import com.codigoparallevar.minicards.parts.logic.Ticker;
|
||||||
import com.codigoparallevar.minicards.parts.logic.Toggle;
|
import com.codigoparallevar.minicards.parts.logic.Toggle;
|
||||||
import com.codigoparallevar.minicards.parts.samples.ColorBox;
|
import com.codigoparallevar.minicards.parts.samples.ColorBox;
|
||||||
import com.codigoparallevar.minicards.parts.samples.Placeholder;
|
|
||||||
import com.codigoparallevar.minicards.parts.strings.ConvertToString;
|
import com.codigoparallevar.minicards.parts.strings.ConvertToString;
|
||||||
|
import com.codigoparallevar.minicards.parts.values.StaticValuePart;
|
||||||
|
import com.codigoparallevar.minicards.parts.viewers.ImageFrame;
|
||||||
import com.codigoparallevar.minicards.types.Part;
|
import com.codigoparallevar.minicards.types.Part;
|
||||||
import com.codigoparallevar.minicards.types.PartConnection;
|
import com.codigoparallevar.minicards.types.PartConnection;
|
||||||
import com.codigoparallevar.minicards.types.PartGrid;
|
import com.codigoparallevar.minicards.types.PartGrid;
|
||||||
import com.codigoparallevar.minicards.types.Tuple2;
|
import com.codigoparallevar.minicards.types.functional.Tuple2;
|
||||||
|
|
||||||
import org.json.JSONArray;
|
import org.json.JSONArray;
|
||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
@ -34,7 +38,7 @@ import java.util.UUID;
|
|||||||
|
|
||||||
public class CardFile {
|
public class CardFile {
|
||||||
|
|
||||||
static final int DEFAULT_BACKGROUND_COLOR = Color.parseColor("#044563");
|
static final int DEFAULT_BACKGROUND_COLOR = Color.parseColor("#4485A3");
|
||||||
static final String PATH_SEPARATOR = "/";
|
static final String PATH_SEPARATOR = "/";
|
||||||
@NonNull
|
@NonNull
|
||||||
private final String cardsDirectory;
|
private final String cardsDirectory;
|
||||||
@ -255,8 +259,8 @@ public class CardFile {
|
|||||||
|
|
||||||
return buttonInfo;
|
return buttonInfo;
|
||||||
}
|
}
|
||||||
else if (type.equals(Placeholder.class.getName())) {
|
else if (type.equals(ImageFrame.class.getName())) {
|
||||||
return new Tuple2<>(Placeholder.deserialize(grid, jsonObject.getJSONObject("_data")),
|
return new Tuple2<>(ImageFrame.deserialize(grid, jsonObject.getJSONObject("_data")),
|
||||||
Collections.<PartConnection>emptyList());
|
Collections.<PartConnection>emptyList());
|
||||||
}
|
}
|
||||||
else if (type.equals(ColorBox.class.getName())){
|
else if (type.equals(ColorBox.class.getName())){
|
||||||
@ -284,6 +288,27 @@ public class CardFile {
|
|||||||
|
|
||||||
return convertToStringInfo;
|
return convertToStringInfo;
|
||||||
}
|
}
|
||||||
|
else if (type.equals(ProgramakerCustomBlockPart.class.getName())){
|
||||||
|
Tuple2<Part, List<PartConnection>> customBlockPartInfo = ProgramakerCustomBlockPart.deserialize(
|
||||||
|
grid,
|
||||||
|
jsonObject.getJSONObject("_data"));
|
||||||
|
|
||||||
|
return customBlockPartInfo;
|
||||||
|
}
|
||||||
|
else if (type.equals(StaticValuePart.class.getName())){
|
||||||
|
Tuple2<Part, List<PartConnection>> staticValuePartInfo = StaticValuePart.deserialize(
|
||||||
|
grid,
|
||||||
|
jsonObject.getJSONObject("_data"));
|
||||||
|
|
||||||
|
return staticValuePartInfo;
|
||||||
|
}
|
||||||
|
else if (type.equals(CameraStreamer.class.getName())){
|
||||||
|
Tuple2<Part, List<PartConnection>> staticValuePartInfo = CameraStreamer.deserialize(
|
||||||
|
grid,
|
||||||
|
jsonObject.getJSONObject("_data"));
|
||||||
|
|
||||||
|
return staticValuePartInfo;
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
throw new JSONException("Expected known class, found " + type);
|
throw new JSONException("Expected known class, found " + type);
|
||||||
}
|
}
|
||||||
|
@ -4,9 +4,9 @@ import android.app.Dialog;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.support.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import android.support.v7.app.AlertDialog;
|
import androidx.appcompat.app.AlertDialog;
|
||||||
import android.support.v7.widget.CardView;
|
import androidx.cardview.widget.CardView;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
@ -17,6 +17,8 @@ import android.widget.ImageView;
|
|||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
class CardPreviewArrayAdapter extends ArrayAdapter<PreviewCard> {
|
class CardPreviewArrayAdapter extends ArrayAdapter<PreviewCard> {
|
||||||
@ -41,16 +43,18 @@ class CardPreviewArrayAdapter extends ArrayAdapter<PreviewCard> {
|
|||||||
final View row = inflater.inflate(R.layout.card_preview, parent, false);
|
final View row = inflater.inflate(R.layout.card_preview, parent, false);
|
||||||
final PreviewCard card = this.cards[position];
|
final PreviewCard card = this.cards[position];
|
||||||
|
|
||||||
row.setOnClickListener(new View.OnClickListener() {
|
TextView nameView = row.findViewById(R.id.card_preview_name);
|
||||||
|
final CardView cardView = row.findViewById(R.id.card_preview_card);
|
||||||
|
final FloatingActionButton settingsButton = row.findViewById(R.id.card_preview_settings_button);
|
||||||
|
|
||||||
|
cardView.setOnClickListener(new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
Intent i = new Intent(CardActivity.INTENT);
|
CardActivity.openCard(getContext(), card, CardActivity.DEVELOPER_VISUALIZATION_MODE);
|
||||||
i.putExtra(CardActivity.CARD_PATH_KEY, card.getPath());
|
|
||||||
CardPreviewArrayAdapter.this.getContext().startActivity(i);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
final ImageView settingsButton = (ImageView) row.findViewById(R.id.card_preview_settings_button);
|
|
||||||
settingsButton.setOnClickListener(new View.OnClickListener() {
|
settingsButton.setOnClickListener(new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
@ -58,19 +62,8 @@ class CardPreviewArrayAdapter extends ArrayAdapter<PreviewCard> {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
CardView cardView = (CardView) row.findViewById(R.id.card_preview_card);
|
|
||||||
TextView nameView = (TextView) row.findViewById(R.id.card_preview_name);
|
|
||||||
|
|
||||||
int backgroundColor = card.getColor();
|
|
||||||
cardView.setBackgroundColor(backgroundColor);
|
|
||||||
nameView.setText(card.getName());
|
nameView.setText(card.getName());
|
||||||
if (backgroundColor == CardFile.DEFAULT_BACKGROUND_COLOR){
|
|
||||||
nameView.setTextColor(0xFFF0F0F0);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
nameView.setTextColor(0xFFFFFF ^ backgroundColor);
|
|
||||||
}
|
|
||||||
|
|
||||||
return row;
|
return row;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -80,7 +73,7 @@ class CardPreviewArrayAdapter extends ArrayAdapter<PreviewCard> {
|
|||||||
final View openCardOptions = (LayoutInflater.from(getContext())
|
final View openCardOptions = (LayoutInflater.from(getContext())
|
||||||
.inflate(R.layout.card_settings_dialog, null));
|
.inflate(R.layout.card_settings_dialog, null));
|
||||||
|
|
||||||
final EditText cardNameEditText = (EditText) openCardOptions.findViewById(R.id.card_setting_name_edit_text);
|
final EditText cardNameEditText = openCardOptions.findViewById(R.id.card_setting_name_edit_text);
|
||||||
cardNameEditText.setText(card.getName());
|
cardNameEditText.setText(card.getName());
|
||||||
|
|
||||||
builder.setTitle("Card settings")
|
builder.setTitle("Card settings")
|
||||||
@ -115,7 +108,7 @@ class CardPreviewArrayAdapter extends ArrayAdapter<PreviewCard> {
|
|||||||
|
|
||||||
final Dialog dialog = builder.create();
|
final Dialog dialog = builder.create();
|
||||||
|
|
||||||
final TextView deleteCardLink = (TextView) openCardOptions.findViewById(R.id.card_setting_delete_card);
|
final TextView deleteCardLink = openCardOptions.findViewById(R.id.card_setting_delete_card);
|
||||||
deleteCardLink.setOnClickListener(new View.OnClickListener() {
|
deleteCardLink.setOnClickListener(new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
|
@ -0,0 +1,80 @@
|
|||||||
|
package com.codigoparallevar.minicards;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
|
||||||
|
import static android.content.Context.MODE_PRIVATE;
|
||||||
|
|
||||||
|
public class ConfigManager {
|
||||||
|
private final Context context;
|
||||||
|
private static final String PREFERENCES_NAME = "MINICARDS_PREFERENCES";
|
||||||
|
private static final String TOKEN_KEY = "PROGRAMAKER_API_TOKEN";
|
||||||
|
private static final String BRIDGE_ID_KEY = "PROGRAMAKER_BRIDGE_ID";
|
||||||
|
private static final String BRIDGE_AUTHENTICATION_TOKEN_KEY = "BRIDGE_AUTHENTICATION_TOKEN";
|
||||||
|
private static final String BRIDGE_CONNECTION_ID_KEY = "PROGRAMAKER_BRIDGE_CONNECTION_ID";
|
||||||
|
|
||||||
|
public ConfigManager(Context ctx) {
|
||||||
|
this.context = ctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setToken(String token) {
|
||||||
|
SharedPreferences preferences = this.context.getSharedPreferences(PREFERENCES_NAME, MODE_PRIVATE);
|
||||||
|
SharedPreferences.Editor edit = preferences.edit();
|
||||||
|
|
||||||
|
edit.putString(TOKEN_KEY, token);
|
||||||
|
edit.apply();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getToken() {
|
||||||
|
SharedPreferences preferences = this.context.getSharedPreferences(PREFERENCES_NAME, MODE_PRIVATE);
|
||||||
|
return preferences.getString(TOKEN_KEY, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeBridgeId() {
|
||||||
|
SharedPreferences preferences = this.context.getSharedPreferences(PREFERENCES_NAME, MODE_PRIVATE);
|
||||||
|
SharedPreferences.Editor edit = preferences.edit();
|
||||||
|
|
||||||
|
if (preferences.contains(BRIDGE_ID_KEY)) {
|
||||||
|
edit.remove(BRIDGE_ID_KEY).apply();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBridgeId(String bridgeId) {
|
||||||
|
SharedPreferences preferences = this.context.getSharedPreferences(PREFERENCES_NAME, MODE_PRIVATE);
|
||||||
|
SharedPreferences.Editor edit = preferences.edit();
|
||||||
|
|
||||||
|
edit.putString(BRIDGE_ID_KEY, bridgeId);
|
||||||
|
edit.commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getBridgeId() {
|
||||||
|
SharedPreferences preferences = this.context.getSharedPreferences(PREFERENCES_NAME, MODE_PRIVATE);
|
||||||
|
return preferences.getString(BRIDGE_ID_KEY, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBridgeConnectionId(String connectionId) {
|
||||||
|
SharedPreferences preferences = this.context.getSharedPreferences(PREFERENCES_NAME, MODE_PRIVATE);
|
||||||
|
SharedPreferences.Editor edit = preferences.edit();
|
||||||
|
|
||||||
|
edit.putString(BRIDGE_CONNECTION_ID_KEY, connectionId);
|
||||||
|
edit.commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getBridgeConnectionId() {
|
||||||
|
SharedPreferences preferences = this.context.getSharedPreferences(PREFERENCES_NAME, MODE_PRIVATE);
|
||||||
|
return preferences.getString(BRIDGE_CONNECTION_ID_KEY, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBridgeAuthenticationToken(String bridgeAuthToken) {
|
||||||
|
SharedPreferences preferences = this.context.getSharedPreferences(PREFERENCES_NAME, MODE_PRIVATE);
|
||||||
|
SharedPreferences.Editor edit = preferences.edit();
|
||||||
|
|
||||||
|
edit.putString(BRIDGE_AUTHENTICATION_TOKEN_KEY, bridgeAuthToken);
|
||||||
|
edit.commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getBridgeAuthenticationToken() {
|
||||||
|
SharedPreferences preferences = this.context.getSharedPreferences(PREFERENCES_NAME, MODE_PRIVATE);
|
||||||
|
return preferences.getString(BRIDGE_AUTHENTICATION_TOKEN_KEY, null);
|
||||||
|
}
|
||||||
|
}
|
@ -2,36 +2,158 @@ package com.codigoparallevar.minicards;
|
|||||||
|
|
||||||
import android.app.Dialog;
|
import android.app.Dialog;
|
||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.AsyncTask;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.design.widget.FloatingActionButton;
|
import android.text.Editable;
|
||||||
import android.support.v7.app.AlertDialog;
|
import android.text.TextWatcher;
|
||||||
import android.support.v7.widget.Toolbar;
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
import android.widget.Button;
|
||||||
import android.widget.EditText;
|
import android.widget.EditText;
|
||||||
import android.widget.ListView;
|
import android.widget.ListView;
|
||||||
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.appcompat.app.AlertDialog;
|
||||||
|
import androidx.appcompat.widget.Toolbar;
|
||||||
|
|
||||||
|
import com.codigoparallevar.minicards.bridge.ProgramakerBridgeService;
|
||||||
|
import com.codigoparallevar.minicards.types.functional.Consumer;
|
||||||
|
import com.codigoparallevar.minicards.types.functional.Tuple2;
|
||||||
|
import com.codigoparallevar.minicards.types.functional.Tuple3;
|
||||||
|
import com.codigoparallevar.minicards.ui_helpers.GetAsync;
|
||||||
|
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
||||||
|
import com.programaker.api.ProgramakerApi;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Vector;
|
import java.util.Vector;
|
||||||
|
|
||||||
public class DeckPreviewActivity extends ReloadableAppCompatActivity {
|
public class DeckPreviewActivity extends ReloadableAppCompatActivity {
|
||||||
|
|
||||||
public static final String INTENT = "com.codigoparallevar.minicards.DECK";
|
public static final String INTENT = "com.codigoparallevar.minicards.DECK";
|
||||||
|
private static final String LogTag = "DeckPreview";
|
||||||
|
|
||||||
private ListView listView;
|
private ListView listView;
|
||||||
private CardPreviewArrayAdapter cardArrayAdapter;
|
private CardPreviewArrayAdapter cardArrayAdapter;
|
||||||
|
private ProgramakerApi ProgramakerApi;
|
||||||
|
private ConfigManager Config;
|
||||||
|
|
||||||
|
protected void openLoginDialog(View view) {
|
||||||
|
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||||
|
|
||||||
|
final View loginDialog = (LayoutInflater.from(this)
|
||||||
|
.inflate(R.layout.login_dialog_view, null));
|
||||||
|
|
||||||
|
final EditText loginUsernameText = loginDialog.findViewById(R.id.login_username_text);
|
||||||
|
final EditText loginPasswordText = loginDialog.findViewById(R.id.login_password_text);
|
||||||
|
final Button loginButton = loginDialog.findViewById(R.id.login_dialog_login_button);
|
||||||
|
final Button cancelButton = loginDialog.findViewById(R.id.login_dialog_cancel_button);
|
||||||
|
final TextView messageLabel = loginDialog.findViewById(R.id.login_message_label);
|
||||||
|
|
||||||
|
builder.setTitle("Login").setView(loginDialog);
|
||||||
|
final Dialog dialog = builder.create();
|
||||||
|
dialog.show();
|
||||||
|
|
||||||
|
cancelButton.setOnClickListener(new View.OnClickListener(){
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
dialog.cancel();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
final TextWatcher watcher = (new TextWatcher() {
|
||||||
|
@Override
|
||||||
|
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTextChanged(CharSequence s, int start, int before, int count) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void afterTextChanged(Editable s) {
|
||||||
|
if ((messageLabel.getVisibility() != View.VISIBLE) &&
|
||||||
|
(loginUsernameText.getText().length() > 0) &&
|
||||||
|
(loginPasswordText.getText().length() > 0)) {
|
||||||
|
loginButton.setEnabled(true);
|
||||||
|
} else {
|
||||||
|
loginButton.setEnabled(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
loginButton.setEnabled(false);
|
||||||
|
loginUsernameText.addTextChangedListener(watcher);
|
||||||
|
loginPasswordText.addTextChangedListener(watcher);
|
||||||
|
|
||||||
|
loginButton.setOnClickListener(new View.OnClickListener(){
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
messageLabel.setVisibility(View.VISIBLE);
|
||||||
|
messageLabel.setText(R.string.loading);
|
||||||
|
new CheckLogin().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,
|
||||||
|
new Tuple3<>(
|
||||||
|
loginUsernameText.getText().toString(),
|
||||||
|
loginPasswordText.getText().toString(),
|
||||||
|
token -> {
|
||||||
|
if (token == null) {
|
||||||
|
messageLabel.setText(R.string.invalid_user_pass);
|
||||||
|
} else {
|
||||||
|
DeckPreviewActivity.this.Config.setToken(token);
|
||||||
|
DeckPreviewActivity.this.ProgramakerApi.setToken(token);
|
||||||
|
final Button loginToProgramakerButton = findViewById(R.id.login_in_programaker_button);
|
||||||
|
|
||||||
|
loginToProgramakerButton.setVisibility(View.GONE);
|
||||||
|
// Re-check... just in case
|
||||||
|
checkNeededLoginButton(
|
||||||
|
DeckPreviewActivity.this.ProgramakerApi,
|
||||||
|
loginToProgramakerButton);
|
||||||
|
dialog.cancel();
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static class CheckLogin extends AsyncTask<Tuple3<String, String, Consumer<String>>,
|
||||||
|
Void, Tuple2<String, Consumer<String>>>{
|
||||||
|
@Override
|
||||||
|
protected Tuple2<String, Consumer<String>> doInBackground(Tuple3<String, String, Consumer<String>>... tuples) {
|
||||||
|
ProgramakerApi api = new ProgramakerApi();
|
||||||
|
String token = null;
|
||||||
|
try {
|
||||||
|
token = api.login(tuples[0]._x, tuples[0]._y);
|
||||||
|
}
|
||||||
|
catch (Exception e) {
|
||||||
|
Log.e("Login to PrograMaker", e.toString(), e);
|
||||||
|
}
|
||||||
|
return new Tuple2<>(token, tuples[0]._z);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPostExecute(Tuple2<String, Consumer<String>> result) {
|
||||||
|
try {
|
||||||
|
result.item2.apply(result.item1);
|
||||||
|
}
|
||||||
|
catch (Throwable ex) {
|
||||||
|
Log.e(LogTag, "Error on login UI update", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
this.Config = new ConfigManager(this);
|
||||||
|
this.ProgramakerApi = new ProgramakerApi();
|
||||||
|
|
||||||
setContentView(R.layout.activity_deck_preview);
|
setContentView(R.layout.activity_deck_preview);
|
||||||
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
|
Toolbar toolbar = findViewById(R.id.toolbar);
|
||||||
setSupportActionBar(toolbar);
|
setSupportActionBar(toolbar);
|
||||||
|
|
||||||
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.create_new_card_fab);
|
FloatingActionButton fab = findViewById(R.id.create_new_card_fab);
|
||||||
fab.setOnClickListener(new View.OnClickListener() {
|
fab.setOnClickListener(new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View view) {
|
public void onClick(View view) {
|
||||||
@ -39,7 +161,43 @@ public class DeckPreviewActivity extends ReloadableAppCompatActivity {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
listView = (ListView) findViewById(R.id.card_deck_list);
|
final Button loginButton = findViewById(R.id.login_in_programaker_button);
|
||||||
|
loginButton.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
DeckPreviewActivity.this.openLoginDialog(v);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
listView = findViewById(R.id.card_deck_list);
|
||||||
|
String token = this.Config.getToken();
|
||||||
|
if (token == null) {
|
||||||
|
loginButton.setVisibility(View.VISIBLE);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
|
||||||
|
this.ProgramakerApi.setToken(token);
|
||||||
|
loginButton.setVisibility(View.GONE);
|
||||||
|
// Double check that is not needed, token might have been deleted
|
||||||
|
checkNeededLoginButton(this.ProgramakerApi, loginButton);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkNeededLoginButton(ProgramakerApi api, Button loginButton) {
|
||||||
|
new GetAsync<Boolean>().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,
|
||||||
|
new Tuple3<>(
|
||||||
|
() -> api.check(),
|
||||||
|
result -> {
|
||||||
|
if (!result) {
|
||||||
|
loginButton.setVisibility(View.VISIBLE);
|
||||||
|
DeckPreviewActivity.this.Config.removeBridgeId();
|
||||||
|
} else {
|
||||||
|
Intent intent = new Intent(this, ProgramakerBridgeService.class);
|
||||||
|
startService(intent);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ex -> Log.e(LogTag, "Error checking API:" + ex, ex)
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -61,7 +219,7 @@ public class DeckPreviewActivity extends ReloadableAppCompatActivity {
|
|||||||
final View openCardOptions = (LayoutInflater.from(this)
|
final View openCardOptions = (LayoutInflater.from(this)
|
||||||
.inflate(R.layout.create_new_card_view, null));
|
.inflate(R.layout.create_new_card_view, null));
|
||||||
|
|
||||||
final EditText cardNameEditText = (EditText) openCardOptions.findViewById(R.id.card_name_edit_text);
|
final EditText cardNameEditText = openCardOptions.findViewById(R.id.card_name_edit_text);
|
||||||
|
|
||||||
builder.setTitle("Create a new card")
|
builder.setTitle("Create a new card")
|
||||||
.setView(openCardOptions)
|
.setView(openCardOptions)
|
||||||
|
@ -2,7 +2,7 @@ package com.codigoparallevar.minicards;
|
|||||||
|
|
||||||
import com.codigoparallevar.minicards.types.Part;
|
import com.codigoparallevar.minicards.types.Part;
|
||||||
import com.codigoparallevar.minicards.types.PartGrid;
|
import com.codigoparallevar.minicards.types.PartGrid;
|
||||||
import com.codigoparallevar.minicards.types.Tuple2;
|
import com.codigoparallevar.minicards.types.functional.Tuple2;
|
||||||
|
|
||||||
public abstract class PartInstantiator {
|
public abstract class PartInstantiator {
|
||||||
protected abstract Part instantiate(PartGrid grid, Tuple2<Integer, Integer> center);
|
protected abstract Part instantiate(PartGrid grid, Tuple2<Integer, Integer> center);
|
||||||
|
@ -1,67 +0,0 @@
|
|||||||
package com.codigoparallevar.minicards;
|
|
||||||
|
|
||||||
import android.app.Dialog;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.DialogInterface;
|
|
||||||
import android.support.v7.app.AlertDialog;
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import com.codigoparallevar.minicards.parts.buttons.RoundButton;
|
|
||||||
import com.codigoparallevar.minicards.parts.logic.Ticker;
|
|
||||||
import com.codigoparallevar.minicards.parts.logic.Toggle;
|
|
||||||
import com.codigoparallevar.minicards.parts.samples.ColorBox;
|
|
||||||
import com.codigoparallevar.minicards.parts.strings.ConvertToString;
|
|
||||||
import com.codigoparallevar.minicards.types.Part;
|
|
||||||
import com.codigoparallevar.minicards.types.Tuple2;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Vector;
|
|
||||||
|
|
||||||
class PartsHolder {
|
|
||||||
private final Context context;
|
|
||||||
|
|
||||||
private final static List<Tuple2<String, PartInstantiator>> BuiltInParts =
|
|
||||||
new Vector<Tuple2<String, PartInstantiator>>(){{
|
|
||||||
add(new Tuple2<>("Round button", RoundButton.getInstantiator()));
|
|
||||||
add(new Tuple2<>("Ticker", Ticker.getInstantiator()));
|
|
||||||
add(new Tuple2<>("Red/Green box", ColorBox.getInstantiator()));
|
|
||||||
add(new Tuple2<>("Toggle", Toggle.getInstantiator()));
|
|
||||||
add(new Tuple2<>("ToString", ConvertToString.getInstantiator()));
|
|
||||||
}};
|
|
||||||
|
|
||||||
public PartsHolder(Context context) {
|
|
||||||
this.context = context;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void openAddPartModal(final CanvasView canvasView) {
|
|
||||||
AlertDialog.Builder builder = new AlertDialog.Builder(context);
|
|
||||||
builder.setTitle("Choose part type")
|
|
||||||
.setItems(getPartTypes(), new DialogInterface.OnClickListener() {
|
|
||||||
public void onClick(DialogInterface dialog, int which) {
|
|
||||||
if ((which >= 0) && (which < BuiltInParts.size())){
|
|
||||||
Log.d("Minicards partsHolder",
|
|
||||||
"Spawning " + BuiltInParts.get(which).item1);
|
|
||||||
PartInstantiator instantiator = BuiltInParts.get(which).item2;
|
|
||||||
PartsHolder.this.runInstantiator(instantiator, canvasView);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Dialog dialog = builder.create();
|
|
||||||
dialog.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void runInstantiator(PartInstantiator instantiator, CanvasView canvasView) {
|
|
||||||
Part part = instantiator.build(canvasView);
|
|
||||||
canvasView.addPart(part);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String[] getPartTypes() {
|
|
||||||
String[] partTypes = new String[BuiltInParts.size()];
|
|
||||||
for (int i = 0; i < BuiltInParts.size(); i++){
|
|
||||||
partTypes[i] = BuiltInParts.get(i).item1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return partTypes;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,6 +1,6 @@
|
|||||||
package com.codigoparallevar.minicards;
|
package com.codigoparallevar.minicards;
|
||||||
|
|
||||||
import android.support.v7.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
|
||||||
abstract class ReloadableAppCompatActivity extends AppCompatActivity {
|
abstract class ReloadableAppCompatActivity extends AppCompatActivity {
|
||||||
public abstract void reload();
|
public abstract void reload();
|
||||||
|
@ -1,12 +1,15 @@
|
|||||||
package com.codigoparallevar.minicards;
|
package com.codigoparallevar.minicards;
|
||||||
|
|
||||||
|
import android.graphics.Bitmap;
|
||||||
import android.graphics.Canvas;
|
import android.graphics.Canvas;
|
||||||
import android.graphics.Paint;
|
import android.graphics.Paint;
|
||||||
import android.graphics.Path;
|
import android.graphics.Path;
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
import android.support.annotation.NonNull;
|
import android.graphics.RectF;
|
||||||
|
|
||||||
import com.codigoparallevar.minicards.types.Tuple2;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
import com.codigoparallevar.minicards.types.functional.Tuple2;
|
||||||
|
|
||||||
public class ScrolledCanvas {
|
public class ScrolledCanvas {
|
||||||
private final Canvas canvas;
|
private final Canvas canvas;
|
||||||
@ -42,7 +45,6 @@ public class ScrolledCanvas {
|
|||||||
canvas.drawPath(offsetPath, paint);
|
canvas.drawPath(offsetPath, paint);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void drawCenteredText(String text, int x, int y, Paint paint) {
|
public void drawCenteredText(String text, int x, int y, Paint paint) {
|
||||||
paint.setTextAlign(Paint.Align.LEFT);
|
paint.setTextAlign(Paint.Align.LEFT);
|
||||||
Rect r = new Rect();
|
Rect r = new Rect();
|
||||||
@ -56,4 +58,17 @@ public class ScrolledCanvas {
|
|||||||
public void drawText(String text, int x, int y, Paint paint) {
|
public void drawText(String text, int x, int y, Paint paint) {
|
||||||
canvas.drawText(text, x - xOrig, y - yOrig, paint);
|
canvas.drawText(text, x - xOrig, y - yOrig, paint);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void drawRoundRect(RectF rect, float rx, float ry, Paint paint) {
|
||||||
|
rect.offset(-xOrig, -yOrig);
|
||||||
|
canvas.drawRoundRect(rect, rx, ry, paint);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void drawBitmap(Bitmap bitmap, Rect rect) {
|
||||||
|
rect.offset(-xOrig, -yOrig);
|
||||||
|
canvas.drawBitmap(bitmap,
|
||||||
|
new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight()),
|
||||||
|
rect,
|
||||||
|
null);
|
||||||
|
}
|
||||||
}
|
}
|
@ -0,0 +1,117 @@
|
|||||||
|
package com.codigoparallevar.minicards;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.codigoparallevar.minicards.types.functional.Tuple2;
|
||||||
|
import com.programaker.api.ProgramakerApi;
|
||||||
|
import com.programaker.api.ProgramakerListeningChannel;
|
||||||
|
import com.programaker.api.ProgramakerSignalListener;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class SignalListenerManager implements ProgramakerSignalListener {
|
||||||
|
private final ProgramakerApi api;
|
||||||
|
private final Map<Tuple2<String, String>, List<ProgramakerSignalListener>> channelToListener = new LinkedHashMap<>();
|
||||||
|
private final Map<ProgramakerSignalListener, List<Tuple2<String, String>>> listenerChannels = new LinkedHashMap<>();
|
||||||
|
private final Map<Tuple2<String, String>, ProgramakerListeningChannel> idToChannel = new LinkedHashMap<>();
|
||||||
|
private long RECONNECT_SLEEP_TIME = 2000; // 2seconds
|
||||||
|
private final static String LogTag = "Signal Listener Manager";
|
||||||
|
|
||||||
|
public SignalListenerManager(ProgramakerApi api) {
|
||||||
|
this.api = api;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void registerSignalListener(String bridgeId, String key, ProgramakerSignalListener listener) {
|
||||||
|
Tuple2<String, String> id = new Tuple2<>(bridgeId, key);
|
||||||
|
if (!idToChannel.containsKey(id)) {
|
||||||
|
// Channel has to be opened
|
||||||
|
idToChannel.put(id, this.api.openChannelTo(bridgeId, key, this,
|
||||||
|
() -> {
|
||||||
|
SignalListenerManager.this.onDisconnect(bridgeId, key);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!channelToListener.containsKey(id)) {
|
||||||
|
channelToListener.put(id, new LinkedList<>());
|
||||||
|
}
|
||||||
|
List<ProgramakerSignalListener> listeners = channelToListener.get(id);
|
||||||
|
listeners.add(listener);
|
||||||
|
|
||||||
|
if (!listenerChannels.containsKey(listener)) {
|
||||||
|
listenerChannels.put(listener, new LinkedList<>());
|
||||||
|
}
|
||||||
|
listenerChannels.get(listener).add(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void unregisterSignalListener(ProgramakerSignalListener listener) {
|
||||||
|
List<Tuple2<String, String>> channels = listenerChannels.get(listener);
|
||||||
|
listenerChannels.remove(listener);
|
||||||
|
for (Tuple2<String, String> id : channels) {
|
||||||
|
List<ProgramakerSignalListener> remainingListeners = channelToListener.get(id);
|
||||||
|
|
||||||
|
remainingListeners.remove(listener);
|
||||||
|
if (remainingListeners.size() == 0) {
|
||||||
|
ProgramakerListeningChannel channel = idToChannel.get(id);
|
||||||
|
idToChannel.remove(id);
|
||||||
|
channelToListener.remove(id);
|
||||||
|
|
||||||
|
channel.stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onDisconnect(String bridgeId, String key) {
|
||||||
|
Tuple2<String, String> id = new Tuple2<>(bridgeId, key);
|
||||||
|
|
||||||
|
// Check that there is still someone listening
|
||||||
|
if (!this.channelToListener.containsKey(id)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this.channelToListener.get(id).size() == 0) {
|
||||||
|
this.channelToListener.remove(id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// On disconnect disable the connection, wait 2 seconds and retry
|
||||||
|
idToChannel.put(id, null);
|
||||||
|
|
||||||
|
new Thread(() -> {
|
||||||
|
try {
|
||||||
|
Thread.sleep(RECONNECT_SLEEP_TIME);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
idToChannel.put(id, this.api.openChannelTo(bridgeId, key, this,
|
||||||
|
() -> {
|
||||||
|
SignalListenerManager.this.onDisconnect(bridgeId, key);
|
||||||
|
}));
|
||||||
|
}).start();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onNewSignal(@NotNull String bridgeId, @NotNull String key, @NotNull HashMap<?, ?> signal) {
|
||||||
|
Tuple2<String, String> id = new Tuple2<>(bridgeId, key);
|
||||||
|
|
||||||
|
if (!channelToListener.containsKey(id)) {
|
||||||
|
Log.e(LogTag, "Got signal to unlistened channel (bridgeId=" + bridgeId + ",key=" + key + ")");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (ProgramakerSignalListener listener : channelToListener.get(id)) {
|
||||||
|
try {
|
||||||
|
listener.onNewSignal(bridgeId, key, signal);
|
||||||
|
}
|
||||||
|
catch (Exception ex) {
|
||||||
|
Log.e(LogTag, "Error passing message (bridge=" + bridgeId + ",key" + key
|
||||||
|
+ ") to " + listener, ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -2,10 +2,13 @@ package com.codigoparallevar.minicards;
|
|||||||
|
|
||||||
import com.codigoparallevar.minicards.types.PartGrid;
|
import com.codigoparallevar.minicards.types.PartGrid;
|
||||||
import com.codigoparallevar.minicards.types.Selectable;
|
import com.codigoparallevar.minicards.types.Selectable;
|
||||||
import com.codigoparallevar.minicards.types.Tuple2;
|
import com.codigoparallevar.minicards.types.connectors.input.AnyInputConnector;
|
||||||
|
import com.codigoparallevar.minicards.types.connectors.input.ImageInputConnector;
|
||||||
|
import com.codigoparallevar.minicards.types.functional.Tuple2;
|
||||||
import com.codigoparallevar.minicards.types.connectors.input.BooleanInputConnector;
|
import com.codigoparallevar.minicards.types.connectors.input.BooleanInputConnector;
|
||||||
import com.codigoparallevar.minicards.types.connectors.input.SignalInputConnector;
|
import com.codigoparallevar.minicards.types.connectors.input.SignalInputConnector;
|
||||||
import com.codigoparallevar.minicards.types.connectors.input.StringInputConnector;
|
import com.codigoparallevar.minicards.types.connectors.input.StringInputConnector;
|
||||||
|
import com.programaker.api.ProgramakerApi;
|
||||||
|
|
||||||
class StubPartGrid implements PartGrid {
|
class StubPartGrid implements PartGrid {
|
||||||
@Override
|
@Override
|
||||||
@ -13,6 +16,16 @@ class StubPartGrid implements PartGrid {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ProgramakerApi getApi() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SignalListenerManager getListenerManager() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SignalInputConnector getSignalInputConnectorOn(int x, int y) {
|
public SignalInputConnector getSignalInputConnectorOn(int x, int y) {
|
||||||
return null;
|
return null;
|
||||||
@ -23,6 +36,21 @@ class StubPartGrid implements PartGrid {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AnyInputConnector getAnyInputConnectorOn(int x, int y) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public StringInputConnector getStringInputConnectorOn(int xEnd, int yEnd) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ImageInputConnector getImageInputConnectorOn(int xEnd, int yEnd) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Tuple2<Integer, Integer> getCenteredOn() {
|
public Tuple2<Integer, Integer> getCenteredOn() {
|
||||||
return null;
|
return null;
|
||||||
@ -32,8 +60,4 @@ class StubPartGrid implements PartGrid {
|
|||||||
public void update() {
|
public void update() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public StringInputConnector getStringInputConnectorOn(int xEnd, int yEnd) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,59 @@
|
|||||||
|
package com.codigoparallevar.minicards.bridge;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.provider.Settings;
|
||||||
|
|
||||||
|
import com.codigoparallevar.minicards.ConfigManager;
|
||||||
|
import com.codigoparallevar.minicards.bridge.blocks.DefaultAndroidBlocks;
|
||||||
|
import com.codigoparallevar.minicards.bridge.helpers.StatusStreamer;
|
||||||
|
import com.programaker.bridge.ProgramakerBridge;
|
||||||
|
import com.programaker.bridge.ProgramakerBridgeConfiguration;
|
||||||
|
|
||||||
|
public class ProgramakerAndroidBridge {
|
||||||
|
private static final String LogTag = "PM Android Bridge";
|
||||||
|
private ProgramakerBridge bridgeRunner = null;
|
||||||
|
private StatusStreamer statusStreamer = null;
|
||||||
|
|
||||||
|
// Static
|
||||||
|
public static ProgramakerAndroidBridge configure(Context ctx, String userId, String bridgeId, String bridgeToken) {
|
||||||
|
return new ProgramakerAndroidBridge(ctx, userId, bridgeId, bridgeToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String GetBridgeName(Context ctx) {
|
||||||
|
String deviceName = Settings.Secure.getString(ctx.getContentResolver(), "bluetooth_name");
|
||||||
|
String serviceName = "MiniCards on " + deviceName;
|
||||||
|
return serviceName;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Builder
|
||||||
|
private final Context ctx;
|
||||||
|
private final String userId;
|
||||||
|
private final String bridgeId;
|
||||||
|
private final String bridgeToken;
|
||||||
|
|
||||||
|
private ProgramakerAndroidBridge(Context ctx, String userId, String bridgeId, String bridgeToken) {
|
||||||
|
this.ctx = ctx;
|
||||||
|
this.userId = userId;
|
||||||
|
this.bridgeId = bridgeId;
|
||||||
|
this.bridgeToken = bridgeToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void start(Runnable onReady, Runnable onComplete) {
|
||||||
|
ProgramakerBridgeConfiguration configuration = new ProgramakerBridgeConfiguration(
|
||||||
|
ProgramakerAndroidBridge.GetBridgeName(this.ctx),
|
||||||
|
new ConfigManager(this.ctx),
|
||||||
|
DefaultAndroidBlocks.GetBuilder(this.ctx).Build()
|
||||||
|
);
|
||||||
|
|
||||||
|
this.bridgeRunner = new ProgramakerBridge(this.bridgeId, this.userId, this.bridgeToken, configuration, onReady, onComplete);
|
||||||
|
this.bridgeRunner.run();
|
||||||
|
|
||||||
|
this.statusStreamer = new StatusStreamer(this.ctx, this.bridgeRunner);
|
||||||
|
this.statusStreamer.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stop() {
|
||||||
|
bridgeRunner.stop();
|
||||||
|
this.statusStreamer.stop();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,247 @@
|
|||||||
|
package com.codigoparallevar.minicards.bridge;
|
||||||
|
|
||||||
|
import android.app.Notification;
|
||||||
|
import android.app.NotificationChannel;
|
||||||
|
import android.app.NotificationManager;
|
||||||
|
import android.app.PendingIntent;
|
||||||
|
import android.app.Service;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.AsyncTask;
|
||||||
|
import android.os.IBinder;
|
||||||
|
import android.os.Process;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.core.app.NotificationCompat;
|
||||||
|
|
||||||
|
import com.codigoparallevar.minicards.ConfigManager;
|
||||||
|
import com.codigoparallevar.minicards.R;
|
||||||
|
import com.codigoparallevar.minicards.types.functional.Tuple2;
|
||||||
|
import com.codigoparallevar.minicards.ui_helpers.DoAsync;
|
||||||
|
import com.programaker.api.ProgramakerApi;
|
||||||
|
|
||||||
|
enum ServiceState {
|
||||||
|
LOADING,
|
||||||
|
RUNNING,
|
||||||
|
STOPPED,
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ProgramakerBridgeService extends Service {
|
||||||
|
public static final String BridgeUserNotificationChannel = "PROGRAMAKER_BRIDGE_USER_NOTIFICATION";
|
||||||
|
public static final CharSequence BridgeUserNotificationChannelName = "User notifications";
|
||||||
|
private static final String AUTOGENERATED_TOKEN_NAME = "Auto-generated";
|
||||||
|
public static String BridgeUserVibrationNotificationChannel = "PROGRAMAKER_BRIDGE_USER_VIBRATION_NOTIFICATIONS";
|
||||||
|
public static CharSequence BridgeUserVibrationNotificationChannelName = "User-triggered vibration";
|
||||||
|
|
||||||
|
private static String BridgeStatusNotificationChannel = "PROGRAMAKER_BRIDGE_STATUS_NOTIFICATION";
|
||||||
|
private static CharSequence BridgeStatusNotificationChannelName = "Programaker bridge status";
|
||||||
|
|
||||||
|
private static final int BridgeStatusNotificationId = 9999;
|
||||||
|
public static final String COMMAND_PROGRAMAKER_BRIDGE_STOP = "com.programaker.bridge.commands.stop";
|
||||||
|
public static final String COMMAND_PROGRAMAKER_BRIDGE_START = "com.programaker.bridge.commands.start";
|
||||||
|
|
||||||
|
private static final long WAIT_TIME_BEFORE_RESTART_MILLIS = 10000; // 10s
|
||||||
|
private ProgramakerAndroidBridge bridge = null;
|
||||||
|
private static final String LogTag = "PM BridgeService";
|
||||||
|
private boolean stopped = false;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate() {
|
||||||
|
setBridgeStatusNotification(getString(R.string.bridge_service_not_started), ServiceState.LOADING, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setBridgeStatusNotification(String title, ServiceState state, String description) {
|
||||||
|
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
|
||||||
|
assert notificationManager != null;
|
||||||
|
|
||||||
|
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
|
||||||
|
NotificationChannel channel = new NotificationChannel(
|
||||||
|
ProgramakerBridgeService.BridgeStatusNotificationChannel,
|
||||||
|
ProgramakerBridgeService.BridgeStatusNotificationChannelName,
|
||||||
|
NotificationManager.IMPORTANCE_DEFAULT);
|
||||||
|
channel.enableVibration(false);
|
||||||
|
notificationManager.createNotificationChannel(channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
Intent stopIntent = new Intent(this, ProgramakerBridgeService.class);
|
||||||
|
stopIntent.setAction(COMMAND_PROGRAMAKER_BRIDGE_STOP);
|
||||||
|
PendingIntent stopPendingIntent =
|
||||||
|
PendingIntent.getService(this, 0, stopIntent, 0);
|
||||||
|
|
||||||
|
Intent startIntent = new Intent(this, ProgramakerBridgeService.class);
|
||||||
|
startIntent.setAction(COMMAND_PROGRAMAKER_BRIDGE_START);
|
||||||
|
PendingIntent startPendingIntent =
|
||||||
|
PendingIntent.getService(this, 0, startIntent, 0);
|
||||||
|
|
||||||
|
NotificationCompat.Builder builder = new NotificationCompat
|
||||||
|
.Builder(this, ProgramakerBridgeService.BridgeStatusNotificationChannel)
|
||||||
|
.setContentTitle(title)
|
||||||
|
.setPriority(NotificationCompat.PRIORITY_DEFAULT);
|
||||||
|
|
||||||
|
|
||||||
|
if (state == ServiceState.STOPPED) {
|
||||||
|
builder.addAction(R.drawable.ic_start, getString(R.string.start_bridge), startPendingIntent)
|
||||||
|
.setSmallIcon(R.drawable.ic_vector_stopped_icon);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
builder.addAction(R.drawable.ic_cancel, getString(R.string.stop_bridge), stopPendingIntent);
|
||||||
|
|
||||||
|
if (state == ServiceState.RUNNING) {
|
||||||
|
builder.setSmallIcon(R.drawable.ic_vector_icon);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
builder.setSmallIcon(R.drawable.ic_vector_icon_loading);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (description != null) {
|
||||||
|
builder.setContentText(description);
|
||||||
|
}
|
||||||
|
|
||||||
|
Notification notification = builder.build();
|
||||||
|
|
||||||
|
if (stopped) {
|
||||||
|
notificationManager.notify(BridgeStatusNotificationId, notification);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.startForeground(BridgeStatusNotificationId, notification);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||||
|
String action = null;
|
||||||
|
if (intent != null) { // Apparently this (intent=null) can happen...
|
||||||
|
action = intent.getAction();
|
||||||
|
}
|
||||||
|
if (action == null) {
|
||||||
|
action = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (action.equals(COMMAND_PROGRAMAKER_BRIDGE_STOP)) {
|
||||||
|
this.stopSelf();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
connectBridge();
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we get killed, after returning from here, restart
|
||||||
|
return START_STICKY;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void connectBridge() {
|
||||||
|
stopped = false;
|
||||||
|
ConfigManager config = new ConfigManager(this);
|
||||||
|
|
||||||
|
String token = config.getToken();
|
||||||
|
if (token == null) {
|
||||||
|
Toast.makeText(this, "Cannot start bridge", Toast.LENGTH_SHORT).show();
|
||||||
|
Log.e(LogTag, "Cannot start bridge: Token is null");
|
||||||
|
this.stopSelf();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ProgramakerBridgeService.this.bridge != null) {
|
||||||
|
// Toast.makeText(this, "Bridge already started", Toast.LENGTH_SHORT).show();
|
||||||
|
Log.w(LogTag, "Bridge already started (not null)");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start up the thread running the service. Note that we create a
|
||||||
|
// separate thread because the service normally runs in the process's
|
||||||
|
// main thread, which we don't want to block. We also make it
|
||||||
|
// background priority so CPU-intensive work doesn't disrupt our UI.
|
||||||
|
Thread thread = new Thread(() -> {
|
||||||
|
try {
|
||||||
|
ProgramakerApi api = new ProgramakerApi();
|
||||||
|
api.setToken(token);
|
||||||
|
String userId = api.getUserId();
|
||||||
|
|
||||||
|
String bridgeIdCheck = config.getBridgeId();
|
||||||
|
if (bridgeIdCheck == null) {
|
||||||
|
bridgeIdCheck = api.createBridge(ProgramakerAndroidBridge.GetBridgeName(this));
|
||||||
|
config.setBridgeId(bridgeIdCheck);
|
||||||
|
}
|
||||||
|
|
||||||
|
String bridgeAuthCheck = config.getBridgeAuthenticationToken();
|
||||||
|
if (bridgeAuthCheck == null) {
|
||||||
|
bridgeAuthCheck = api.createBridgeAuthenticationToken(bridgeIdCheck, ProgramakerBridgeService.AUTOGENERATED_TOKEN_NAME);
|
||||||
|
config.setBridgeAuthenticationToken(bridgeAuthCheck);
|
||||||
|
}
|
||||||
|
|
||||||
|
final String bridgeId = bridgeIdCheck;
|
||||||
|
final String bridgeAuth = bridgeAuthCheck;
|
||||||
|
|
||||||
|
ProgramakerBridgeService.this.bridge = ProgramakerAndroidBridge.configure(
|
||||||
|
this,
|
||||||
|
userId,
|
||||||
|
bridgeId,
|
||||||
|
bridgeAuth);
|
||||||
|
ProgramakerBridgeService.this.bridge.start(
|
||||||
|
() -> { // On ready
|
||||||
|
setBridgeStatusNotification(getString(R.string.bridge_service_online), ServiceState.RUNNING, null);
|
||||||
|
if (config.getBridgeConnectionId() == null) {
|
||||||
|
new DoAsync().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,
|
||||||
|
new Tuple2<>(
|
||||||
|
() -> {
|
||||||
|
boolean established = api.establishConnection(bridgeId);
|
||||||
|
if (!established) {
|
||||||
|
Log.e(LogTag, "Error establishing connection to bridge");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ex -> {
|
||||||
|
Log.e(LogTag, "Error establishing bridge connection: " + ex, ex);
|
||||||
|
ProgramakerBridgeService.this.stopSelf();
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
() -> { // On completed
|
||||||
|
onBridgeFailedAfterConnected();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (Throwable ex) {
|
||||||
|
Log.e(LogTag, "Error on bridge", ex);
|
||||||
|
ProgramakerBridgeService.this.bridge = null;
|
||||||
|
setBridgeStatusNotification(getString(R.string.bridge_service_starting),
|
||||||
|
ServiceState.STOPPED,
|
||||||
|
getString(R.string.error_establishing_connection));
|
||||||
|
}
|
||||||
|
}, "ServiceStartArguments");
|
||||||
|
thread.setPriority(Process.THREAD_PRIORITY_BACKGROUND);
|
||||||
|
|
||||||
|
setBridgeStatusNotification(getString(R.string.bridge_service_starting), ServiceState.LOADING, null);
|
||||||
|
thread.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onBridgeFailedAfterConnected() {
|
||||||
|
ProgramakerBridgeService.this.bridge = null;
|
||||||
|
|
||||||
|
if (!stopped) {
|
||||||
|
Log.e(LogTag, "Bridge stopped after connected. Waiting 10s then restarting");
|
||||||
|
setBridgeStatusNotification(getString(R.string.bridge_service_failed_restarting), ServiceState.LOADING, null);
|
||||||
|
try {
|
||||||
|
Thread.sleep(WAIT_TIME_BEFORE_RESTART_MILLIS);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
connectBridge();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IBinder onBind(Intent intent) {
|
||||||
|
// We don't provide binding, so return null
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroy() {
|
||||||
|
stopped = true;
|
||||||
|
ProgramakerAndroidBridge bridge = this.bridge;
|
||||||
|
if (bridge != null) {
|
||||||
|
bridge.stop();
|
||||||
|
}
|
||||||
|
setBridgeStatusNotification(getString(R.string.bridge_service_failed_stopping), ServiceState.STOPPED, null);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,51 @@
|
|||||||
|
package com.codigoparallevar.minicards.bridge.blocks;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
class BlockArgumentDefinition {
|
||||||
|
private final static String LogTag = "PM BlockArgument";
|
||||||
|
private final Type type;
|
||||||
|
private final String defaultValue;
|
||||||
|
|
||||||
|
public enum Type {
|
||||||
|
STRING,
|
||||||
|
INT,
|
||||||
|
FLOAT,
|
||||||
|
BOOL,
|
||||||
|
}
|
||||||
|
|
||||||
|
BlockArgumentDefinition(BlockArgumentDefinition.Type type, String defaultValue) {
|
||||||
|
this.type = type;
|
||||||
|
this.defaultValue = defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public JSONObject serialize() throws JSONException {
|
||||||
|
JSONObject obj = new JSONObject();
|
||||||
|
|
||||||
|
obj.put("type", BlockArgumentDefinition.TypeToString(type));
|
||||||
|
obj.put("default", this.defaultValue);
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String TypeToString(Type type) {
|
||||||
|
switch (type) {
|
||||||
|
case STRING:
|
||||||
|
return "string";
|
||||||
|
case INT:
|
||||||
|
return "integer";
|
||||||
|
case FLOAT:
|
||||||
|
return "float";
|
||||||
|
case BOOL:
|
||||||
|
return "bool";
|
||||||
|
default:
|
||||||
|
Log.e(LogTag, "Unknown type: " + type);
|
||||||
|
return "string";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,54 @@
|
|||||||
|
package com.codigoparallevar.minicards.bridge.blocks;
|
||||||
|
|
||||||
|
import com.codigoparallevar.minicards.types.functional.Function;
|
||||||
|
import com.programaker.bridge.ProgramakerBridgeConfigurationBlock;
|
||||||
|
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class BridgeBlockListBuilder {
|
||||||
|
private final LinkedList<ProgramakerBridgeConfigurationBlock> blockList;
|
||||||
|
|
||||||
|
public BridgeBlockListBuilder() {
|
||||||
|
this.blockList = new LinkedList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public List<ProgramakerBridgeConfigurationBlock> Build() {
|
||||||
|
return blockList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BridgeBlockListBuilder addOperation(String id, String message,
|
||||||
|
List<BlockArgumentDefinition> arguments,
|
||||||
|
Function<List<?>, Void> operation) {
|
||||||
|
return this.addOperation(new OperationBlockDefinition(id, message, arguments, operation));
|
||||||
|
}
|
||||||
|
|
||||||
|
private BridgeBlockListBuilder addOperation(ProgramakerBridgeConfigurationBlock block) {
|
||||||
|
this.blockList.add(block);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BridgeBlockListBuilder addGetter(String id, String message,
|
||||||
|
List<BlockArgumentDefinition> arguments,
|
||||||
|
Function<List<?>, ?> operation) {
|
||||||
|
return this.addGetter(new GetterBlockDefinition(id, message, arguments, operation));
|
||||||
|
}
|
||||||
|
|
||||||
|
private BridgeBlockListBuilder addGetter(ProgramakerBridgeConfigurationBlock block) {
|
||||||
|
this.blockList.add(block);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BridgeBlockListBuilder addTrigger(String id, String message,
|
||||||
|
List<BlockArgumentDefinition> arguments,
|
||||||
|
String key) {
|
||||||
|
return this.addTrigger(new TriggerBlockDefinition(id, message, arguments, key));
|
||||||
|
}
|
||||||
|
|
||||||
|
private BridgeBlockListBuilder addTrigger(TriggerBlockDefinition block) {
|
||||||
|
this.blockList.add(block);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,216 @@
|
|||||||
|
package com.codigoparallevar.minicards.bridge.blocks;
|
||||||
|
|
||||||
|
import android.app.Notification;
|
||||||
|
import android.app.NotificationChannel;
|
||||||
|
import android.app.NotificationManager;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.net.wifi.SupplicantState;
|
||||||
|
import android.net.wifi.WifiInfo;
|
||||||
|
import android.net.wifi.WifiManager;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.VibrationEffect;
|
||||||
|
import android.os.Vibrator;
|
||||||
|
|
||||||
|
import androidx.core.app.NotificationCompat;
|
||||||
|
|
||||||
|
import com.codigoparallevar.minicards.R;
|
||||||
|
import com.codigoparallevar.minicards.bridge.ProgramakerBridgeService;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
public class DefaultAndroidBlocks {
|
||||||
|
|
||||||
|
private static long VIBRATION_REST_TIME = 1000; // Ms
|
||||||
|
private static long VIBRATION_ACTIVE_TIME = 500; // Ms
|
||||||
|
|
||||||
|
// This means that will take at most ~15 seconds
|
||||||
|
private static int MAX_VIBRATION_CYCLES = 10;
|
||||||
|
|
||||||
|
public static BridgeBlockListBuilder GetBuilder(Context ctx) {
|
||||||
|
// System services
|
||||||
|
NotificationManager notificationManager = (NotificationManager) ctx.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||||
|
Vibrator vibrator = (Vibrator) ctx.getSystemService(Context.VIBRATOR_SERVICE);
|
||||||
|
|
||||||
|
assert notificationManager != null;
|
||||||
|
assert vibrator != null;
|
||||||
|
|
||||||
|
Random notificationRandom = new Random();
|
||||||
|
|
||||||
|
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
|
||||||
|
NotificationChannel channel = new NotificationChannel(
|
||||||
|
ProgramakerBridgeService.BridgeUserNotificationChannel,
|
||||||
|
ProgramakerBridgeService.BridgeUserNotificationChannelName,
|
||||||
|
NotificationManager.IMPORTANCE_DEFAULT);
|
||||||
|
notificationManager.createNotificationChannel(channel);
|
||||||
|
|
||||||
|
NotificationChannel vibrationChannel = new NotificationChannel(
|
||||||
|
ProgramakerBridgeService.BridgeUserVibrationNotificationChannel,
|
||||||
|
ProgramakerBridgeService.BridgeUserVibrationNotificationChannelName,
|
||||||
|
NotificationManager.IMPORTANCE_DEFAULT);
|
||||||
|
|
||||||
|
// Vibration is manually handled for Android O (or superior) devices
|
||||||
|
vibrationChannel.setVibrationPattern(null);
|
||||||
|
notificationManager.createNotificationChannel(vibrationChannel);
|
||||||
|
}
|
||||||
|
|
||||||
|
// OPERATIONS
|
||||||
|
BridgeBlockListBuilder builder = new BridgeBlockListBuilder()
|
||||||
|
// Notifications
|
||||||
|
.addOperation(
|
||||||
|
"notifications_new",
|
||||||
|
"Create notification. Title: %1, text: %2",
|
||||||
|
new LinkedList<BlockArgumentDefinition>() {{
|
||||||
|
add(new BlockArgumentDefinition(BlockArgumentDefinition.Type.STRING, "My notification"));
|
||||||
|
add(new BlockArgumentDefinition(BlockArgumentDefinition.Type.STRING, "Sample description"));
|
||||||
|
}},
|
||||||
|
(List<?> params) -> {
|
||||||
|
if (params.size() != 2) {
|
||||||
|
throw new Exception("Expected two (2) arguments, found " + params.size());
|
||||||
|
}
|
||||||
|
String title = params.get(0) == null ? null : params.get(0).toString();
|
||||||
|
String description = params.get(1) == null ? null : params.get(1).toString();
|
||||||
|
|
||||||
|
NotificationCompat.Builder notifBuilder = new NotificationCompat
|
||||||
|
.Builder(ctx, ProgramakerBridgeService.BridgeUserNotificationChannel)
|
||||||
|
.setSmallIcon(R.drawable.ic_vector_icon_user_triggered)
|
||||||
|
.setPriority(NotificationCompat.PRIORITY_DEFAULT);
|
||||||
|
|
||||||
|
if (title != null) {
|
||||||
|
notifBuilder.setContentTitle(title);
|
||||||
|
}
|
||||||
|
if (description != null) {
|
||||||
|
notifBuilder.setContentText(description);
|
||||||
|
}
|
||||||
|
|
||||||
|
Notification notif = notifBuilder.build();
|
||||||
|
|
||||||
|
int notificationId = notificationRandom.nextInt();
|
||||||
|
notificationManager.notify(notificationId, notif);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.addOperation(
|
||||||
|
"notifications_clear",
|
||||||
|
"Clear notifications",
|
||||||
|
Collections.emptyList(),
|
||||||
|
(List<?> params) -> {
|
||||||
|
notificationManager.cancelAll();
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.addOperation(
|
||||||
|
"vibration_start",
|
||||||
|
"Vibrate %1 times",
|
||||||
|
new LinkedList<BlockArgumentDefinition>() {{
|
||||||
|
add(new BlockArgumentDefinition(BlockArgumentDefinition.Type.INT, "3"));
|
||||||
|
}},
|
||||||
|
(List<?> params) -> {
|
||||||
|
if (params.size() != 1) {
|
||||||
|
throw new Exception("Expected one (1) argument1, found " + params.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
int userTimes = Integer.parseInt(params.get(0).toString());
|
||||||
|
final int times = Math.max(1, Math.min(MAX_VIBRATION_CYCLES, userTimes));
|
||||||
|
|
||||||
|
long[] pattern = new long[times * 2];
|
||||||
|
for (int i = 0; i < times; i++) {
|
||||||
|
pattern[i * 2] = VIBRATION_ACTIVE_TIME;
|
||||||
|
pattern[i * 2 + 1] = VIBRATION_REST_TIME;
|
||||||
|
}
|
||||||
|
|
||||||
|
Notification notif = new NotificationCompat
|
||||||
|
.Builder(ctx, ProgramakerBridgeService.BridgeUserVibrationNotificationChannel)
|
||||||
|
.setContentTitle(ctx.getString(R.string.vibration_activated))
|
||||||
|
.setSmallIcon(R.drawable.ic_vector_icon_user_triggered)
|
||||||
|
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
|
||||||
|
.setAutoCancel(true)
|
||||||
|
.setVibrate(pattern)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
int notificationId = notificationRandom.nextInt();
|
||||||
|
notificationManager.notify(notificationId, notif);
|
||||||
|
|
||||||
|
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
|
// Manually handle vibration if notification channels are in use
|
||||||
|
vibrator.vibrate(VibrationEffect.createWaveform(pattern, -1));
|
||||||
|
}
|
||||||
|
|
||||||
|
new Thread( () -> {
|
||||||
|
// Wait the activation time
|
||||||
|
for (int i = 0; i < times; i++) {
|
||||||
|
try {
|
||||||
|
Thread.sleep(pattern[i * 2]);
|
||||||
|
Thread.sleep(pattern[i * 2 + 1]);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove notification
|
||||||
|
notificationManager.cancel(notificationId);
|
||||||
|
}).start();
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
)
|
||||||
|
;
|
||||||
|
|
||||||
|
// GETTERS
|
||||||
|
builder.addGetter(
|
||||||
|
"wifi_is_connected",
|
||||||
|
"Is WIFI connected?",
|
||||||
|
Collections.emptyList(),
|
||||||
|
(List<?> params) -> {
|
||||||
|
WifiManager wifiManager = (WifiManager) ctx.getApplicationContext().getSystemService(Context.WIFI_SERVICE);
|
||||||
|
assert wifiManager != null;
|
||||||
|
WifiInfo wifiInfo = wifiManager.getConnectionInfo();
|
||||||
|
|
||||||
|
if (wifiInfo == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return wifiInfo.getSupplicantState() == SupplicantState.COMPLETED;
|
||||||
|
})
|
||||||
|
.addGetter(
|
||||||
|
"wifi_get_ssid",
|
||||||
|
"Get WIFI SSID",
|
||||||
|
Collections.emptyList(),
|
||||||
|
(List<?> params) -> {
|
||||||
|
WifiManager wifiManager = (WifiManager) ctx.getApplicationContext().getSystemService(Context.WIFI_SERVICE);
|
||||||
|
assert wifiManager != null;
|
||||||
|
WifiInfo wifiInfo = wifiManager.getConnectionInfo();
|
||||||
|
|
||||||
|
if (wifiInfo == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wifiInfo.getSupplicantState() != SupplicantState.COMPLETED) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return wifiInfo.getSSID();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Signal blocks
|
||||||
|
builder
|
||||||
|
.addTrigger(
|
||||||
|
"on_wifi_connected",
|
||||||
|
"When WIFI connection is ESTABLISHED",
|
||||||
|
Collections.emptyList(),// TODO: Save content to variable
|
||||||
|
"on_wifi_connected"
|
||||||
|
)
|
||||||
|
.addTrigger(
|
||||||
|
"on_wifi_disconnected",
|
||||||
|
"When WIFI connection is LOST",
|
||||||
|
Collections.emptyList(),
|
||||||
|
"on_wifi_disconnected"
|
||||||
|
);
|
||||||
|
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,65 @@
|
|||||||
|
package com.codigoparallevar.minicards.bridge.blocks;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.codigoparallevar.minicards.types.functional.Function;
|
||||||
|
import com.programaker.bridge.ProgramakerBridgeConfigurationBlock;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.json.JSONArray;
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
class GetterBlockDefinition implements ProgramakerBridgeConfigurationBlock {
|
||||||
|
private final static String LogTag = "PM GetterBlockDef";
|
||||||
|
|
||||||
|
private final String id;
|
||||||
|
private final String message;
|
||||||
|
private final List<BlockArgumentDefinition> args;
|
||||||
|
private final Function<List<?>, ?> operation;
|
||||||
|
|
||||||
|
public GetterBlockDefinition(String id, String message, List<BlockArgumentDefinition> args, Function<List<?>, ?> operation) {
|
||||||
|
this.id = id;
|
||||||
|
this.message = message;
|
||||||
|
this.args = args;
|
||||||
|
this.operation = operation;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public JSONObject serialize() {
|
||||||
|
JSONObject obj = new JSONObject();
|
||||||
|
|
||||||
|
try {
|
||||||
|
JSONArray arguments = new JSONArray();
|
||||||
|
for (BlockArgumentDefinition arg : this.args) {
|
||||||
|
arguments.put(arg.serialize());
|
||||||
|
}
|
||||||
|
|
||||||
|
obj.put("id", this.id);
|
||||||
|
obj.put("function_name", this.id);
|
||||||
|
obj.put("message", this.message);
|
||||||
|
obj.put("block_type", "getter");
|
||||||
|
obj.put("block_result_type", JSONObject.NULL); // TODO: Properly declare type
|
||||||
|
obj.put("arguments", arguments);
|
||||||
|
obj.put("save_to", JSONObject.NULL);
|
||||||
|
} catch (JSONException ex) {
|
||||||
|
Log.e(LogTag, "Error serializing block definition: " + ex, ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public String getFunctionName() {
|
||||||
|
return this.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object call(@NotNull List<?> arguments) throws Exception {
|
||||||
|
return this.operation.apply(arguments);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,65 @@
|
|||||||
|
package com.codigoparallevar.minicards.bridge.blocks;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.codigoparallevar.minicards.types.functional.Function;
|
||||||
|
import com.programaker.bridge.ProgramakerBridgeConfigurationBlock;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.json.JSONArray;
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
class OperationBlockDefinition implements ProgramakerBridgeConfigurationBlock {
|
||||||
|
private final static String LogTag = "PM OpBlockDef";
|
||||||
|
|
||||||
|
private final String id;
|
||||||
|
private final String message;
|
||||||
|
private final List<BlockArgumentDefinition> args;
|
||||||
|
private final Function<List<?>, Void> operation;
|
||||||
|
|
||||||
|
public OperationBlockDefinition(String id, String message, List<BlockArgumentDefinition> args, Function<List<?>, Void> operation) {
|
||||||
|
this.id = id;
|
||||||
|
this.message = message;
|
||||||
|
this.args = args;
|
||||||
|
this.operation = operation;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public JSONObject serialize() {
|
||||||
|
JSONObject obj = new JSONObject();
|
||||||
|
|
||||||
|
try {
|
||||||
|
JSONArray arguments = new JSONArray();
|
||||||
|
for (BlockArgumentDefinition arg : this.args) {
|
||||||
|
arguments.put(arg.serialize());
|
||||||
|
}
|
||||||
|
|
||||||
|
obj.put("id", this.id);
|
||||||
|
obj.put("function_name", this.id);
|
||||||
|
obj.put("message", this.message);
|
||||||
|
obj.put("block_type", "operation");
|
||||||
|
obj.put("block_result_type", JSONObject.NULL);
|
||||||
|
obj.put("arguments", arguments);
|
||||||
|
obj.put("save_to", JSONObject.NULL);
|
||||||
|
} catch (JSONException ex) {
|
||||||
|
Log.e(LogTag, "Error serializing block definition: " + ex, ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public String getFunctionName() {
|
||||||
|
return this.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object call(@NotNull List<?> arguments) throws Exception {
|
||||||
|
return this.operation.apply(arguments);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,67 @@
|
|||||||
|
package com.codigoparallevar.minicards.bridge.blocks;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.programaker.bridge.ProgramakerBridgeConfigurationBlock;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.json.JSONArray;
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
class TriggerBlockDefinition implements ProgramakerBridgeConfigurationBlock {
|
||||||
|
private final static String LogTag = "PM TriggerBlockDef";
|
||||||
|
|
||||||
|
private final String id;
|
||||||
|
private final String message;
|
||||||
|
private final List<BlockArgumentDefinition> args;
|
||||||
|
private final String key;
|
||||||
|
|
||||||
|
public TriggerBlockDefinition(String id, String message, List<BlockArgumentDefinition> args,
|
||||||
|
String key) {
|
||||||
|
this.id = id;
|
||||||
|
this.message = message;
|
||||||
|
this.args = args;
|
||||||
|
this.key = key;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public JSONObject serialize() {
|
||||||
|
JSONObject obj = new JSONObject();
|
||||||
|
|
||||||
|
try {
|
||||||
|
JSONArray arguments = new JSONArray();
|
||||||
|
for (BlockArgumentDefinition arg : this.args) {
|
||||||
|
arguments.put(arg.serialize());
|
||||||
|
}
|
||||||
|
|
||||||
|
obj.put("id", this.id);
|
||||||
|
obj.put("function_name", this.id);
|
||||||
|
obj.put("message", this.message);
|
||||||
|
obj.put("arguments", arguments);
|
||||||
|
obj.put("save_to", JSONObject.NULL);
|
||||||
|
obj.put("expected_value", JSONObject.NULL);
|
||||||
|
obj.put("block_type", "trigger");
|
||||||
|
obj.put("key", this.key);
|
||||||
|
obj.put("subkey", JSONObject.NULL);
|
||||||
|
} catch (JSONException ex) {
|
||||||
|
Log.e(LogTag, "Error serializing block definition: " + ex, ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public String getFunctionName() {
|
||||||
|
return this.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object call(@NotNull List<?> arguments) throws Exception {
|
||||||
|
throw new IllegalArgumentException("Non executable block");
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,78 @@
|
|||||||
|
package com.codigoparallevar.minicards.bridge.helpers;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.net.ConnectivityManager;
|
||||||
|
import android.net.Network;
|
||||||
|
import android.net.NetworkCapabilities;
|
||||||
|
import android.net.NetworkRequest;
|
||||||
|
import android.net.wifi.SupplicantState;
|
||||||
|
import android.net.wifi.WifiInfo;
|
||||||
|
import android.net.wifi.WifiManager;
|
||||||
|
import android.os.Build;
|
||||||
|
|
||||||
|
import com.programaker.bridge.ProgramakerBridge;
|
||||||
|
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class StatusStreamer {
|
||||||
|
private final Context ctx;
|
||||||
|
private final ProgramakerBridge bridge;
|
||||||
|
private WifiNetworkCallbackReceiver networkCallback = null;
|
||||||
|
|
||||||
|
public StatusStreamer(Context ctx, ProgramakerBridge bridge) {
|
||||||
|
this.ctx = ctx;
|
||||||
|
this.bridge = bridge;
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||||
|
this.networkCallback = new WifiNetworkCallbackReceiver(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void run() {
|
||||||
|
|
||||||
|
if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.LOLLIPOP) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
NetworkRequest.Builder builder = new NetworkRequest.Builder();
|
||||||
|
builder.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
|
||||||
|
|
||||||
|
ConnectivityManager connManager = (ConnectivityManager) this.ctx.getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||||
|
assert connManager != null;
|
||||||
|
|
||||||
|
connManager.registerNetworkCallback(builder.build(), this.networkCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stop() {
|
||||||
|
ConnectivityManager connManager = (ConnectivityManager) this.ctx.getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||||
|
assert connManager != null;
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||||
|
connManager.unregisterNetworkCallback(this.networkCallback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onWifiNetworkConnected(Network network) {
|
||||||
|
WifiManager wifiManager = (WifiManager) this.ctx.getSystemService(Context.WIFI_SERVICE);
|
||||||
|
WifiInfo wifiInfo;
|
||||||
|
|
||||||
|
wifiInfo = wifiManager.getConnectionInfo();
|
||||||
|
String ssid = "";
|
||||||
|
if (wifiInfo.getSupplicantState() == SupplicantState.COMPLETED) {
|
||||||
|
// This might not return the SSID in case the user has not granted location permissions to the app
|
||||||
|
// see: https://stackoverflow.com/a/54446042
|
||||||
|
ssid = wifiInfo.getSSID();
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, String> data = new HashMap<>();
|
||||||
|
data.put("ssid", ssid);
|
||||||
|
|
||||||
|
this.bridge.sendSignal("on_wifi_connected", new JSONObject(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onWifiNetworkDisconnected() {
|
||||||
|
this.bridge.sendSignal("on_wifi_disconnected", new JSONObject());
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,34 @@
|
|||||||
|
package com.codigoparallevar.minicards.bridge.helpers;
|
||||||
|
|
||||||
|
import android.net.ConnectivityManager;
|
||||||
|
import android.net.Network;
|
||||||
|
import android.os.Build;
|
||||||
|
|
||||||
|
import androidx.annotation.RequiresApi;
|
||||||
|
|
||||||
|
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
|
||||||
|
class WifiNetworkCallbackReceiver extends ConnectivityManager.NetworkCallback {
|
||||||
|
private final StatusStreamer streamer;
|
||||||
|
|
||||||
|
public WifiNetworkCallbackReceiver(StatusStreamer statusStreamer) {
|
||||||
|
this.streamer = statusStreamer;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAvailable(Network network) {
|
||||||
|
super.onAvailable(network);
|
||||||
|
this.streamer.onWifiNetworkConnected(network);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onUnavailable() {
|
||||||
|
super.onUnavailable();
|
||||||
|
this.streamer.onWifiNetworkDisconnected();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLost(Network network) {
|
||||||
|
super.onLost(network);
|
||||||
|
this.streamer.onWifiNetworkDisconnected();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,726 @@
|
|||||||
|
package com.codigoparallevar.minicards.parts;
|
||||||
|
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.graphics.Paint;
|
||||||
|
import android.graphics.Rect;
|
||||||
|
import android.os.AsyncTask;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.codigoparallevar.minicards.ScrolledCanvas;
|
||||||
|
import com.codigoparallevar.minicards.parts.connectors.ProgramakerCustomBlockInputConnector;
|
||||||
|
import com.codigoparallevar.minicards.parts.connectors.AnyRoundOutputConnector;
|
||||||
|
import com.codigoparallevar.minicards.parts.connectors.ConnectorTypeInfo;
|
||||||
|
import com.codigoparallevar.minicards.parts.connectors.RoundOutputConnector;
|
||||||
|
import com.codigoparallevar.minicards.parts.connectors.SignalRoundOutputConnector;
|
||||||
|
import com.codigoparallevar.minicards.types.Moveable;
|
||||||
|
import com.codigoparallevar.minicards.types.Part;
|
||||||
|
import com.codigoparallevar.minicards.types.PartConnection;
|
||||||
|
import com.codigoparallevar.minicards.types.PartGrid;
|
||||||
|
import com.codigoparallevar.minicards.types.connectors.input.InputConnector;
|
||||||
|
import com.codigoparallevar.minicards.types.connectors.output.OutputConnector;
|
||||||
|
import com.codigoparallevar.minicards.types.functional.Tuple2;
|
||||||
|
import com.codigoparallevar.minicards.types.wireData.AnySignal;
|
||||||
|
import com.codigoparallevar.minicards.types.wireData.Signal;
|
||||||
|
import com.codigoparallevar.minicards.types.wireData.WireDataType;
|
||||||
|
import com.codigoparallevar.minicards.ui_helpers.DoAsync;
|
||||||
|
import com.programaker.api.ProgramakerApi;
|
||||||
|
import com.programaker.api.ProgramakerSignalListener;
|
||||||
|
import com.programaker.api.data.ProgramakerCustomBlock;
|
||||||
|
import com.programaker.api.data.ProgramakerCustomBlockArgument;
|
||||||
|
import com.programaker.api.data.ProgramakerCustomBlockSaveTo;
|
||||||
|
import com.programaker.api.data.ProgramakerFunctionCallResult;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.json.JSONArray;
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public class ProgramakerCustomBlockPart implements Part, ProgramakerSignalListener {
|
||||||
|
private static final int WIDTH_PADDING = 50;
|
||||||
|
private static final int HEIGHT_PADDING = 50;
|
||||||
|
private static final int IO_RADIUS = 50;
|
||||||
|
private static final int IO_PADDING = 20;
|
||||||
|
private static final String LogTag = "PM Custom block part";
|
||||||
|
|
||||||
|
private List<Tuple2<ConnectorTypeInfo, ProgramakerCustomBlockInputConnector>> inputConnectors = null;
|
||||||
|
private List<Tuple2<ConnectorTypeInfo, RoundOutputConnector>> outputConnectors = null;
|
||||||
|
|
||||||
|
private final PartGrid _partGrid;
|
||||||
|
private final String _id;
|
||||||
|
private final ProgramakerCustomBlock _block;
|
||||||
|
|
||||||
|
private int _left;
|
||||||
|
private int _top;
|
||||||
|
private int width = 100;
|
||||||
|
private int height = 100;
|
||||||
|
private String token = null;
|
||||||
|
private Object[] lastValues;
|
||||||
|
private boolean active = true;
|
||||||
|
private Tuple2<ConnectorTypeInfo, AnyRoundOutputConnector> saveToOutput;
|
||||||
|
private SignalRoundOutputConnector pulseOutput;
|
||||||
|
|
||||||
|
public ProgramakerCustomBlockPart(String id, PartGrid grid, Tuple2<Integer, Integer> center, ProgramakerCustomBlock block) {
|
||||||
|
this._id = id;
|
||||||
|
this._partGrid = grid;
|
||||||
|
this._block = block;
|
||||||
|
|
||||||
|
this.updateWidthHeight();
|
||||||
|
|
||||||
|
this._left = center.item1 - width / 2;
|
||||||
|
this._top = center.item2 - height / 2;
|
||||||
|
this.initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
private ProgramakerCustomBlockPart(String id, PartGrid grid, int left, int top, ProgramakerCustomBlock block) {
|
||||||
|
this._id = id;
|
||||||
|
this._partGrid = grid;
|
||||||
|
this._block = block;
|
||||||
|
|
||||||
|
this.updateWidthHeight();
|
||||||
|
|
||||||
|
this._left = left;
|
||||||
|
this._top = top;
|
||||||
|
this.initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ProgramakerCustomBlockPart(PartGrid grid, Tuple2<Integer, Integer> center, ProgramakerCustomBlock block) {
|
||||||
|
this(UUID.randomUUID().toString(), grid, center, block);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int get_left() {
|
||||||
|
return _left;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int get_right() {
|
||||||
|
return _left + width;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int get_top() {
|
||||||
|
return _top;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int get_bottom() {
|
||||||
|
return _top + height;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Paint getTextPaint() {
|
||||||
|
Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||||
|
p.setColor(Color.BLACK);
|
||||||
|
p.setTextSize(50);
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateWidthHeight() {
|
||||||
|
Paint p = getTextPaint();
|
||||||
|
String message = getMessage();
|
||||||
|
Rect bounds = new Rect();
|
||||||
|
p.getTextBounds(message, 0, message.length(), bounds);
|
||||||
|
|
||||||
|
this.height = bounds.height() + HEIGHT_PADDING * 2;
|
||||||
|
this.width = bounds.width() + WIDTH_PADDING * 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initialize() {
|
||||||
|
this.updatePorts();
|
||||||
|
lastValues = new Object[this.inputConnectors.size()];
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updatePorts() {
|
||||||
|
final String type = this._block.getBlock_type() == null ? "" : this._block.getBlock_type();
|
||||||
|
final boolean has_pulse_input = type.equals("operation");
|
||||||
|
final boolean has_pulse_output = has_pulse_input || type.equals("trigger");
|
||||||
|
final boolean hasImplicitOutput = type.equals("getter");
|
||||||
|
|
||||||
|
final List<Tuple2<ConnectorTypeInfo, ProgramakerCustomBlockInputConnector>> inputs = new LinkedList<>();
|
||||||
|
final List<Tuple2<ConnectorTypeInfo, RoundOutputConnector>> outputs = new LinkedList<>();
|
||||||
|
|
||||||
|
SignalRoundOutputConnector pulseOutput = null;
|
||||||
|
|
||||||
|
// Add pulses
|
||||||
|
if (has_pulse_input) {
|
||||||
|
ConnectorTypeInfo typeInfo = new ConnectorTypeInfo(ConnectorTypeInfo.Type.PULSE);
|
||||||
|
inputs.add(new Tuple2<>(typeInfo,
|
||||||
|
new ProgramakerCustomBlockInputConnector(this, _partGrid,
|
||||||
|
0, 0,
|
||||||
|
IO_RADIUS, typeInfo))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (has_pulse_output) {
|
||||||
|
pulseOutput = new SignalRoundOutputConnector(this, this._partGrid, 0, 0,
|
||||||
|
IO_RADIUS);
|
||||||
|
outputs.add(new Tuple2<>(new ConnectorTypeInfo(ConnectorTypeInfo.Type.PULSE),
|
||||||
|
pulseOutput)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add block IO
|
||||||
|
ProgramakerCustomBlockArgument savedTo = null;
|
||||||
|
|
||||||
|
if (_block.getArguments() != null) {
|
||||||
|
ProgramakerCustomBlockSaveTo saveTo = _block.getSave_to();
|
||||||
|
int skip = -1;
|
||||||
|
|
||||||
|
if (saveTo != null) {
|
||||||
|
if (saveTo.getType() != null
|
||||||
|
&& saveTo.getType().equals("argument")) {
|
||||||
|
skip = saveTo.getIndex();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Log.w(LogTag, "Unknown save-to type=" + saveTo.getType());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int index = -1;
|
||||||
|
for (ProgramakerCustomBlockArgument arg : _block.getArguments()) {
|
||||||
|
index++;
|
||||||
|
if (skip == index) {
|
||||||
|
savedTo = arg;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ConnectorTypeInfo typeInfo = ConnectorTypeInfo.FromArgument(arg);
|
||||||
|
inputs.add(new Tuple2<>(typeInfo,
|
||||||
|
new ProgramakerCustomBlockInputConnector(this, _partGrid,
|
||||||
|
0, 0,
|
||||||
|
IO_RADIUS, typeInfo)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Tuple2<ConnectorTypeInfo, AnyRoundOutputConnector> saveToOutput = null;
|
||||||
|
|
||||||
|
if (savedTo != null) {
|
||||||
|
saveToOutput = new Tuple2<>(ConnectorTypeInfo.FromTypeName(savedTo.getComputedType()),
|
||||||
|
new AnyRoundOutputConnector(this, this._partGrid, 0, 0, IO_RADIUS));
|
||||||
|
outputs.add(new Tuple2<>(saveToOutput.item1, saveToOutput.item2));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasImplicitOutput) {
|
||||||
|
outputs.add(new Tuple2<>(ConnectorTypeInfo.FromTypeName(_block.getBlock_result_type()),
|
||||||
|
new AnyRoundOutputConnector(this, this._partGrid, 0, 0, IO_RADIUS)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tie everything
|
||||||
|
inputConnectors = inputs;
|
||||||
|
outputConnectors = outputs;
|
||||||
|
this.saveToOutput = saveToOutput;
|
||||||
|
this.pulseOutput = pulseOutput;
|
||||||
|
|
||||||
|
this.updatePortPositions();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updatePortPositions() {
|
||||||
|
{
|
||||||
|
// Update inputs
|
||||||
|
int y = get_top();
|
||||||
|
int x = get_left() + IO_PADDING;
|
||||||
|
for (Tuple2<ConnectorTypeInfo, ProgramakerCustomBlockInputConnector> entry : inputConnectors) {
|
||||||
|
InputConnector input = entry.item2;
|
||||||
|
int new_x = x + IO_PADDING + IO_RADIUS / 2;
|
||||||
|
input.updatePosition(new_x, y);
|
||||||
|
x = x + IO_RADIUS * 2 + IO_PADDING * 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// Update outputs
|
||||||
|
int y = get_bottom();
|
||||||
|
int x = get_left() + IO_PADDING;
|
||||||
|
for (Tuple2<ConnectorTypeInfo, RoundOutputConnector> entry : outputConnectors) {
|
||||||
|
OutputConnector output = entry.item2;
|
||||||
|
int new_x = x + IO_PADDING + IO_RADIUS / 2;
|
||||||
|
output.updatePosition(new_x, y);
|
||||||
|
x = x + IO_RADIUS * 2 + IO_PADDING * 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getMessage() {
|
||||||
|
return this._block.getMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void touched() {
|
||||||
|
Log.i(LogTag, "Part touched (block_fun=" + this._block.getFunction_name() + ")");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<InputConnector> getInputConnectors() {
|
||||||
|
List<InputConnector> result = new ArrayList<>(inputConnectors.size());
|
||||||
|
for (Tuple2<ConnectorTypeInfo, ProgramakerCustomBlockInputConnector> entry : inputConnectors) {
|
||||||
|
result.add(entry.item2);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<OutputConnector> getOutputConnectors() {
|
||||||
|
List<OutputConnector> result = new ArrayList<>(outputConnectors.size());
|
||||||
|
for (Tuple2<ConnectorTypeInfo, RoundOutputConnector> entry : outputConnectors) {
|
||||||
|
result.add(entry.item2);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JSONObject serialize() throws JSONException {
|
||||||
|
JSONObject serialized = new JSONObject();
|
||||||
|
|
||||||
|
serialized.put("id", _id);
|
||||||
|
serialized.put("left", _left);
|
||||||
|
serialized.put("top", _top);
|
||||||
|
|
||||||
|
JSONObject blockData = _block.serialize();
|
||||||
|
serialized.put("block", blockData);
|
||||||
|
|
||||||
|
serialized.put("on_string_output_connector", serializeConnectionEndpoints());
|
||||||
|
|
||||||
|
return serialized;
|
||||||
|
}
|
||||||
|
|
||||||
|
private JSONArray serializeConnectionEndpoints() {
|
||||||
|
JSONArray serializedData = new JSONArray();
|
||||||
|
|
||||||
|
for (OutputConnector output : getOutputConnectors()) {
|
||||||
|
JSONArray elements = new JSONArray();
|
||||||
|
|
||||||
|
for (Tuple2<String, String> endpoint : (List<Tuple2<String, String>>) output.getConnectionEndpoints()) {
|
||||||
|
elements.put(PartConnection.serializeToJson(endpoint.item1, endpoint.item2));
|
||||||
|
}
|
||||||
|
|
||||||
|
serializedData.put(elements);
|
||||||
|
}
|
||||||
|
return serializedData;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Tuple2<Part, List<PartConnection>> deserialize(PartGrid grid, JSONObject data) throws JSONException {
|
||||||
|
String id = data.getString("id");
|
||||||
|
int left = data.getInt("left");
|
||||||
|
int top = data.getInt("top");
|
||||||
|
|
||||||
|
JSONObject el = data.getJSONObject("block");
|
||||||
|
ProgramakerCustomBlock block = ProgramakerCustomBlock.deserialize(el);
|
||||||
|
ProgramakerCustomBlockPart part = new ProgramakerCustomBlockPart(id, grid, left, top, block);
|
||||||
|
|
||||||
|
List<PartConnection> connections = new LinkedList<>();
|
||||||
|
|
||||||
|
JSONArray allConnectorOuts = data.optJSONArray("on_string_output_connector");
|
||||||
|
if (allConnectorOuts == null) {
|
||||||
|
allConnectorOuts = new JSONArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < allConnectorOuts.length(); i++) {
|
||||||
|
JSONArray connectorOuts = allConnectorOuts.getJSONArray(i);
|
||||||
|
Tuple2<ConnectorTypeInfo, RoundOutputConnector> connector = part.outputConnectors.get(i);
|
||||||
|
for (int j = 0; j < connectorOuts.length(); j++) {
|
||||||
|
connections.add(PartConnection.deserialize(
|
||||||
|
connector.item2, connectorOuts.getJSONObject(j)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Tuple2<>(part, connections);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void send(InputConnector inputConnector, WireDataType signal) {
|
||||||
|
// Log.i(LogTag, "Received signal from Input="+inputConnector);
|
||||||
|
ConnectorTypeInfo info = getConnectorInfo(inputConnector);
|
||||||
|
if (info == null) {
|
||||||
|
Log.e(LogTag, "Unknown connector" + inputConnector);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info.get_type() == ConnectorTypeInfo.Type.PULSE) {
|
||||||
|
wrappedRunOperation();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
int index = getConnectorIndex(inputConnector);
|
||||||
|
if (index < 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.lastValues[index] = signal.get();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void wrappedRunOperation() {
|
||||||
|
Log.d(LogTag, "Running block function=" + this._block.getFunction_name());
|
||||||
|
String token = this.prepareStart();
|
||||||
|
|
||||||
|
if (token != null) {
|
||||||
|
try {
|
||||||
|
new DoAsync().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,
|
||||||
|
new Tuple2<>(() -> {
|
||||||
|
ProgramakerCustomBlockPart.this.runBlockOperation();
|
||||||
|
|
||||||
|
ProgramakerCustomBlockPart.this.freeBlock(token);
|
||||||
|
}, ex -> {
|
||||||
|
Log.w(LogTag, "Error executing function=" + this._block.getFunction_name()
|
||||||
|
+ "; Error=" + ex, ex);
|
||||||
|
ProgramakerCustomBlockPart.this.freeBlock(token);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
catch (Exception ex) {
|
||||||
|
Log.e(LogTag, "Error executing function=" + this._block.getFunction_name()
|
||||||
|
+ "; Error=" + ex, ex);
|
||||||
|
this.freeBlock(token);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// An execution is in progress, so nothing is done
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private ProgramakerFunctionCallResult runBlockOperation() {
|
||||||
|
if (!this.active) {
|
||||||
|
Log.w(LogTag, "Trying to run inactive block function=" + this._block.getFunction_name());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
ProgramakerApi api = this._partGrid.getApi();
|
||||||
|
List<String> arguments = new LinkedList<>();
|
||||||
|
|
||||||
|
int index = -1;
|
||||||
|
for (Tuple2<ConnectorTypeInfo, ProgramakerCustomBlockInputConnector> entry : inputConnectors) {
|
||||||
|
index++;
|
||||||
|
if (entry.item1.get_type() == ConnectorTypeInfo.Type.PULSE) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Object queriedValue = entry.item2.query(lastValues[index]);
|
||||||
|
if (queriedValue != null) {
|
||||||
|
lastValues[index] = queriedValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lastValues[index] == null) {
|
||||||
|
arguments.add(null); // TODO: Get default value from block definition
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
arguments.add(lastValues[index].toString()); // TODO: Do proper type formatting
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ProgramakerFunctionCallResult result = api.callBlock(this._block, arguments);
|
||||||
|
Log.i(LogTag, "Execution result="+result.getResult());
|
||||||
|
|
||||||
|
onExecutionCompleted(result);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onExecutionCompleted(ProgramakerFunctionCallResult result) {
|
||||||
|
Tuple2<ConnectorTypeInfo, AnyRoundOutputConnector> savedTo = this.saveToOutput;
|
||||||
|
if (savedTo != null) {
|
||||||
|
Object value = null;
|
||||||
|
if (result != null) {
|
||||||
|
result.getResult();
|
||||||
|
}
|
||||||
|
savedTo.item2.send(new AnySignal(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
SignalRoundOutputConnector pulseOutput = this.pulseOutput;
|
||||||
|
if (pulseOutput != null) {
|
||||||
|
pulseOutput.send(new Signal());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Notify screen that there's updates
|
||||||
|
this._partGrid.update();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void freeBlock(String token) {
|
||||||
|
tryUpdateBlock(token, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String prepareStart() {
|
||||||
|
String token = UUID.randomUUID().toString();
|
||||||
|
if (tryUpdateBlock(null, token)) {
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized boolean tryUpdateBlock(String oldVal, String newVal) {
|
||||||
|
if (this.token == oldVal) {
|
||||||
|
this.token = newVal;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String get_id() {
|
||||||
|
return _id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InputConnector getConnectorWithId(String inputConnectorId) {
|
||||||
|
if (inputConnectorId.startsWith("index=")) {
|
||||||
|
String[] chunks = inputConnectorId.split("=");
|
||||||
|
if (chunks.length > 1) {
|
||||||
|
Integer index = null;
|
||||||
|
try {
|
||||||
|
index = Integer.parseInt(chunks[1]);
|
||||||
|
}
|
||||||
|
catch (NumberFormatException ex) {
|
||||||
|
Log.e(LogTag, "Error parsing connector id="+inputConnectorId, ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index != null && index < inputConnectors.size()) {
|
||||||
|
return inputConnectors.get(index).item2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getConnectorIndex(InputConnector inputConnector) {
|
||||||
|
int index = 0;
|
||||||
|
for (Tuple2<ConnectorTypeInfo, ProgramakerCustomBlockInputConnector> entry : inputConnectors) {
|
||||||
|
if (entry.item2 == inputConnector) {
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getConnectorId(InputConnector inputConnector) {
|
||||||
|
int index = getConnectorIndex(inputConnector);
|
||||||
|
if (index < 0 ) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return "index=" + index;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConnectorTypeInfo getConnectorInfo(InputConnector inputConnector) {
|
||||||
|
for (Tuple2<ConnectorTypeInfo, ProgramakerCustomBlockInputConnector> entry : inputConnectors) {
|
||||||
|
if (entry.item2 == inputConnector) {
|
||||||
|
return entry.item1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void resume() {
|
||||||
|
this.active = true;
|
||||||
|
|
||||||
|
String type = _block.getBlock_type();
|
||||||
|
if (type != null && (type.equals("trigger"))) {
|
||||||
|
// Listen to signal
|
||||||
|
ProgramakerApi api = _partGrid.getApi();
|
||||||
|
if (api == null) {
|
||||||
|
Log.i(LogTag, "Cannot listen to API. API not found, probably on a showcase.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
new DoAsync().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,
|
||||||
|
new Tuple2<>(
|
||||||
|
() -> {
|
||||||
|
_partGrid.getListenerManager().registerSignalListener(
|
||||||
|
_block.getBridge_id(), _block.getKey(),
|
||||||
|
this);
|
||||||
|
},
|
||||||
|
ex -> {
|
||||||
|
Log.e(LogTag, "Error establishing connection to monitor", ex);
|
||||||
|
}
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onNewSignal(@NotNull String bridgeId, @NotNull String key, @NotNull HashMap<?, ?> signal) {
|
||||||
|
// Propagate signal
|
||||||
|
// Stream object on save_to, then trigger pulse
|
||||||
|
Object content = signal.get("content");
|
||||||
|
if (this.saveToOutput != null) {
|
||||||
|
this.saveToOutput.item2.send(new AnySignal(content));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.pulseOutput != null) {
|
||||||
|
this.pulseOutput.send(new Signal());
|
||||||
|
}
|
||||||
|
_partGrid.update();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void pause() {
|
||||||
|
this.active = false;
|
||||||
|
|
||||||
|
String type = _block.getBlock_type();
|
||||||
|
if (type != null && (type.equals("trigger"))) {
|
||||||
|
// Release listening of signal
|
||||||
|
ProgramakerApi api = _partGrid.getApi();
|
||||||
|
if (api == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
new DoAsync().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,
|
||||||
|
new Tuple2<>(
|
||||||
|
() -> {
|
||||||
|
_partGrid.getListenerManager().unregisterSignalListener(this);
|
||||||
|
},
|
||||||
|
ex -> {
|
||||||
|
Log.e(LogTag, "Error disconnecting from monitor", ex);
|
||||||
|
}
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void draw(ScrolledCanvas canvas, boolean devMode) {
|
||||||
|
|
||||||
|
if (!devMode) {
|
||||||
|
return; // Logic block, don't show on user-mode
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean operationInProgress = this.token != null;
|
||||||
|
|
||||||
|
drawConnectors(canvas);
|
||||||
|
drawWires(canvas, devMode);
|
||||||
|
|
||||||
|
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||||
|
if (operationInProgress) {
|
||||||
|
paint.setColor(Color.YELLOW);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
paint.setColor(Color.WHITE);
|
||||||
|
}
|
||||||
|
canvas.drawRect(
|
||||||
|
new Rect(get_left(), get_top(),
|
||||||
|
get_right(), get_bottom()),
|
||||||
|
paint);
|
||||||
|
|
||||||
|
Paint textPaint = getTextPaint();
|
||||||
|
canvas.drawText(getMessage(),
|
||||||
|
get_left() + WIDTH_PADDING,
|
||||||
|
get_bottom() - HEIGHT_PADDING,
|
||||||
|
textPaint);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void drawConnectors(ScrolledCanvas canvas) {
|
||||||
|
if (inputConnectors != null) {
|
||||||
|
for (Tuple2<ConnectorTypeInfo, ProgramakerCustomBlockInputConnector> entry : inputConnectors) {
|
||||||
|
Paint outerInputConnectorPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||||
|
outerInputConnectorPaint.setColor(entry.item1.getOuterColor());
|
||||||
|
|
||||||
|
canvas.drawCircle(
|
||||||
|
entry.item2.getX(),
|
||||||
|
entry.item2.getY(),
|
||||||
|
entry.item2.getRadius(),
|
||||||
|
outerInputConnectorPaint);
|
||||||
|
|
||||||
|
Paint innerInputConnectorPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||||
|
innerInputConnectorPaint.setColor(entry.item1.getInnerColor());
|
||||||
|
|
||||||
|
canvas.drawCircle(
|
||||||
|
entry.item2.getX(),
|
||||||
|
entry.item2.getY(),
|
||||||
|
entry.item2.getRadius() / 2,
|
||||||
|
innerInputConnectorPaint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (outputConnectors != null) {
|
||||||
|
for (Tuple2<ConnectorTypeInfo, RoundOutputConnector> entry : outputConnectors) {
|
||||||
|
Paint outerOutputConnectorPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||||
|
outerOutputConnectorPaint.setColor(entry.item1.getOuterColor());
|
||||||
|
|
||||||
|
canvas.drawCircle(
|
||||||
|
entry.item2.getX(),
|
||||||
|
entry.item2.getY(),
|
||||||
|
entry.item2.getRadius(),
|
||||||
|
outerOutputConnectorPaint);
|
||||||
|
|
||||||
|
Paint innerOutputConnectorPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||||
|
innerOutputConnectorPaint.setColor(entry.item1.getInnerColor());
|
||||||
|
|
||||||
|
canvas.drawCircle(
|
||||||
|
entry.item2.getX(),
|
||||||
|
entry.item2.getY(),
|
||||||
|
entry.item2.getRadius() / 2,
|
||||||
|
innerOutputConnectorPaint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void drawWires(ScrolledCanvas canvas, boolean devMode) {
|
||||||
|
for (OutputConnector outputConnector : getOutputConnectors()){
|
||||||
|
outputConnector.drawWires(canvas, devMode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void moveEnd(int x, int y) {
|
||||||
|
_left = x - width / 2;
|
||||||
|
_top = y - height / 2;
|
||||||
|
|
||||||
|
this.updatePortPositions();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void drop(int x, int y) {
|
||||||
|
moveEnd(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean containsPoint(int x, int y) {
|
||||||
|
return ((x >= this.get_left()) && (x <= this.get_right())
|
||||||
|
&& (y >= this.get_top()) && (y <= this.get_bottom()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Moveable getMoveable() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void unlink() {
|
||||||
|
pause();
|
||||||
|
|
||||||
|
for (InputConnector input : getInputConnectors()) {
|
||||||
|
input.unlink();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object query(Object lastValue) {
|
||||||
|
String blockType = _block.getBlock_type();
|
||||||
|
|
||||||
|
if ((blockType == null) || (!blockType.equals("getter"))) {
|
||||||
|
// Only relevant for getters
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
String token = this.prepareStart();
|
||||||
|
|
||||||
|
if (token != null) {
|
||||||
|
try {
|
||||||
|
_partGrid.update();
|
||||||
|
ProgramakerFunctionCallResult result = runBlockOperation();
|
||||||
|
if (result != null) {
|
||||||
|
return result.getResult();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
this.freeBlock(token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,465 @@
|
|||||||
|
package com.codigoparallevar.minicards.parts.android;
|
||||||
|
|
||||||
|
import android.Manifest;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.graphics.ImageFormat;
|
||||||
|
import android.graphics.Paint;
|
||||||
|
import android.graphics.Path;
|
||||||
|
import android.graphics.Rect;
|
||||||
|
import android.hardware.camera2.CameraAccessException;
|
||||||
|
import android.hardware.camera2.CameraCaptureSession;
|
||||||
|
import android.hardware.camera2.CameraCharacteristics;
|
||||||
|
import android.hardware.camera2.CameraDevice;
|
||||||
|
import android.hardware.camera2.CameraManager;
|
||||||
|
import android.hardware.camera2.CaptureRequest;
|
||||||
|
import android.media.Image;
|
||||||
|
import android.media.ImageReader;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.HandlerThread;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.Surface;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.RequiresApi;
|
||||||
|
|
||||||
|
import com.codigoparallevar.minicards.CanvasView;
|
||||||
|
import com.codigoparallevar.minicards.PartInstantiator;
|
||||||
|
import com.codigoparallevar.minicards.ScrolledCanvas;
|
||||||
|
import com.codigoparallevar.minicards.parts.connectors.ImageRoundOutputConnector;
|
||||||
|
import com.codigoparallevar.minicards.parts.style.CardTheme;
|
||||||
|
import com.codigoparallevar.minicards.types.Moveable;
|
||||||
|
import com.codigoparallevar.minicards.types.Part;
|
||||||
|
import com.codigoparallevar.minicards.types.PartConnection;
|
||||||
|
import com.codigoparallevar.minicards.types.PartGrid;
|
||||||
|
import com.codigoparallevar.minicards.types.connectors.input.InputConnector;
|
||||||
|
import com.codigoparallevar.minicards.types.connectors.output.OutputConnector;
|
||||||
|
import com.codigoparallevar.minicards.types.functional.Tuple2;
|
||||||
|
import com.codigoparallevar.minicards.types.wireData.ImageSignal;
|
||||||
|
import com.codigoparallevar.minicards.types.wireData.WireDataType;
|
||||||
|
import com.codigoparallevar.minicards.utils.Serializations;
|
||||||
|
|
||||||
|
import org.json.JSONArray;
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
|
||||||
|
public class CameraStreamer implements Part {
|
||||||
|
private static final String LogTag = "CameraStreamer";
|
||||||
|
|
||||||
|
private static final int DEFAULT_SIDE_SIZE = 200;
|
||||||
|
private final String _id;
|
||||||
|
private final PartGrid _partGrid;
|
||||||
|
private int _left;
|
||||||
|
private int _top;
|
||||||
|
private int _right;
|
||||||
|
private int _bottom;
|
||||||
|
private List<OutputConnector> _outputConnectors;
|
||||||
|
private final ImageRoundOutputConnector _imageRoundOutputConnector;
|
||||||
|
private final long SLEEP_TIME = 1000;
|
||||||
|
private ImageReader imageReader = null;
|
||||||
|
private HandlerThread _thread = null;
|
||||||
|
private Handler _handler = null;
|
||||||
|
private static final int CAPTURE_IMAGE_WIDTH = 256;
|
||||||
|
private static final int CAPTURE_IMAGE_HEIGHT = 256;
|
||||||
|
|
||||||
|
private CameraStreamer(String id, PartGrid partGrid, int left, int top, int right, int bottom) {
|
||||||
|
_id = id;
|
||||||
|
_partGrid = partGrid;
|
||||||
|
_left = left;
|
||||||
|
_top = top;
|
||||||
|
_right = right;
|
||||||
|
_bottom = bottom;
|
||||||
|
|
||||||
|
// Create connectors
|
||||||
|
_imageRoundOutputConnector = new ImageRoundOutputConnector(
|
||||||
|
this,
|
||||||
|
_partGrid,
|
||||||
|
getOutputConnectorCenterX(), getOutputConnectorCenterY(),
|
||||||
|
getOutputConnectRadius());
|
||||||
|
|
||||||
|
_outputConnectors = new LinkedList<>();
|
||||||
|
_outputConnectors.add(_imageRoundOutputConnector);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public CameraStreamer(PartGrid partGrid, int left, int top, int right, int bottom) {
|
||||||
|
this(UUID.randomUUID().toString(), partGrid, left, top, right, bottom);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void moveEnd(int x, int y) {
|
||||||
|
final int width = _right - _left;
|
||||||
|
final int height = _bottom - _top;
|
||||||
|
|
||||||
|
_left = x - width / 2;
|
||||||
|
_right = _left + width;
|
||||||
|
|
||||||
|
_top = y - height / 2;
|
||||||
|
_bottom = _top + height;
|
||||||
|
|
||||||
|
_imageRoundOutputConnector.updatePosition(
|
||||||
|
getOutputConnectorCenterX(),
|
||||||
|
getOutputConnectorCenterY());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void drop(int x, int y) {
|
||||||
|
moveEnd(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean containsPoint(int x, int y) {
|
||||||
|
return (x >= get_left()) && (x <= get_right())
|
||||||
|
&& (y >= get_top()) && (y <= get_bottom());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Moveable getMoveable() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void unlink() {
|
||||||
|
pause();
|
||||||
|
|
||||||
|
for (InputConnector input : getInputConnectors()) {
|
||||||
|
input.unlink();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void draw(ScrolledCanvas canvas, boolean devMode) {
|
||||||
|
if (devMode){
|
||||||
|
drawConnector(canvas);
|
||||||
|
drawWires(canvas, devMode);
|
||||||
|
|
||||||
|
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||||
|
paint.setColor(Color.GRAY);
|
||||||
|
|
||||||
|
canvas.drawRect(
|
||||||
|
new Rect(_left, _top,
|
||||||
|
_right, _bottom),
|
||||||
|
paint);
|
||||||
|
|
||||||
|
|
||||||
|
// Draw a little camera
|
||||||
|
Paint iconPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||||
|
iconPaint.setStyle(Paint.Style.STROKE);
|
||||||
|
iconPaint.setColor(Color.YELLOW);
|
||||||
|
iconPaint.setStrokeWidth(5f);
|
||||||
|
iconPaint.setStrokeCap(Paint.Cap.ROUND);
|
||||||
|
iconPaint.setStrokeJoin(Paint.Join.ROUND);
|
||||||
|
|
||||||
|
// TODO: Refactor this into something reasonable
|
||||||
|
float side = _right - _left;
|
||||||
|
float mid = side / 2;
|
||||||
|
float quarter = mid / 2;
|
||||||
|
float oct = quarter / 2;
|
||||||
|
float hex = oct / 2;
|
||||||
|
float shex = hex / 2;
|
||||||
|
|
||||||
|
Path path = new Path();
|
||||||
|
// Square top left
|
||||||
|
path.moveTo(_left + oct + hex + shex, _top + quarter + hex);
|
||||||
|
// Square top right
|
||||||
|
path.lineTo(_left + mid + hex + shex, _top + quarter + hex);
|
||||||
|
// Box-to-projection connection
|
||||||
|
path.lineTo(_left + mid + hex + shex, _top + mid);
|
||||||
|
// Projection top right
|
||||||
|
path.lineTo(_right - quarter - hex + hex + shex, _top + quarter + hex);
|
||||||
|
// Projection bottom right
|
||||||
|
path.lineTo(_right - quarter - hex + hex + shex, _bottom - quarter - hex);
|
||||||
|
// Back to box
|
||||||
|
path.lineTo(_left + mid + hex + shex, _top + mid);
|
||||||
|
// Box bottom right
|
||||||
|
path.lineTo(_left + mid + hex + shex, _bottom - quarter - hex);
|
||||||
|
// Box bottom left
|
||||||
|
path.lineTo(_left + oct + hex + shex, _bottom - quarter - hex);
|
||||||
|
|
||||||
|
path.close();
|
||||||
|
canvas.drawPath(path, iconPaint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void drawConnector(ScrolledCanvas canvas) {
|
||||||
|
Paint outerConnectorPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||||
|
outerConnectorPaint.setColor(CardTheme.IMAGE_CONNECTOR_COLOR_OUTER);
|
||||||
|
|
||||||
|
canvas.drawCircle(getOutputConnectorCenterX(),
|
||||||
|
getOutputConnectorCenterY(),
|
||||||
|
getOutputConnectRadius(),
|
||||||
|
outerConnectorPaint);
|
||||||
|
|
||||||
|
Paint innerConnectorPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||||
|
innerConnectorPaint.setColor(CardTheme.IMAGE_CONNECTOR_COLOR_INNER);
|
||||||
|
|
||||||
|
canvas.drawCircle(getOutputConnectorCenterX(),
|
||||||
|
getOutputConnectorCenterY(),
|
||||||
|
getOutputConnectRadius() / 2,
|
||||||
|
innerConnectorPaint);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void drawWires(ScrolledCanvas canvas, boolean devMode) {
|
||||||
|
for (OutputConnector outputConnector : _outputConnectors){
|
||||||
|
outputConnector.drawWires(canvas, devMode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int get_left() {
|
||||||
|
return _left;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int get_right() {
|
||||||
|
return _right;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int get_top() {
|
||||||
|
return _top;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int get_bottom() {
|
||||||
|
return _bottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void touched() {
|
||||||
|
// Just ignore it, as it's a logic component
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<InputConnector> getInputConnectors() {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<OutputConnector> getOutputConnectors() {
|
||||||
|
return _outputConnectors;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JSONObject serialize() throws JSONException {
|
||||||
|
JSONObject serialized = new JSONObject();
|
||||||
|
|
||||||
|
serialized.put("id", _id);
|
||||||
|
serialized.put("left", _left);
|
||||||
|
serialized.put("top", _top);
|
||||||
|
serialized.put("right", _right);
|
||||||
|
serialized.put("bottom", _bottom);
|
||||||
|
serialized.put("on_signal_output_connector",
|
||||||
|
Serializations.serialize(serializeConnectionEndpoints()));
|
||||||
|
|
||||||
|
return serialized;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Tuple2<Part, List<PartConnection>> deserialize(PartGrid partGrid, JSONObject data) throws JSONException {
|
||||||
|
String id = data.getString("id");
|
||||||
|
int left = data.getInt("left");
|
||||||
|
int top = data.getInt("top");
|
||||||
|
int right = data.getInt("right");
|
||||||
|
int bottom = data.getInt("bottom");
|
||||||
|
|
||||||
|
CameraStreamer cameraStreamer = new CameraStreamer(id, partGrid, left, top, right, bottom);
|
||||||
|
|
||||||
|
List<PartConnection> connections = new LinkedList<>();
|
||||||
|
|
||||||
|
JSONArray connectorOuts = data.getJSONArray("on_signal_output_connector");
|
||||||
|
for (int i = 0; i < connectorOuts.length(); i++){
|
||||||
|
connections.add(PartConnection.deserialize(
|
||||||
|
cameraStreamer._imageRoundOutputConnector,
|
||||||
|
connectorOuts.getJSONObject(i)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Tuple2<Part, List<PartConnection>>(cameraStreamer, connections);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Map<String, String>> serializeConnectionEndpoints() {
|
||||||
|
List<Map<String, String>> serializedData = new LinkedList<>();
|
||||||
|
|
||||||
|
for (Tuple2<String, String> endpoint : _imageRoundOutputConnector.getConnectionEndpoints()){
|
||||||
|
serializedData.add(PartConnection.serialize(endpoint.item1, endpoint.item2));
|
||||||
|
}
|
||||||
|
|
||||||
|
return serializedData;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void send(InputConnector roundInputConnector, WireDataType signal) {
|
||||||
|
// @TODO: REMOVE THE NEED FOR THIS
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onNewImage(Image img) {
|
||||||
|
_imageRoundOutputConnector.send(new ImageSignal(img));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String get_id() {
|
||||||
|
return _id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InputConnector getConnectorWithId(String inputConnectorId) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getConnectorId(InputConnector inputConnector) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void resume() {
|
||||||
|
if (!(this._partGrid instanceof CanvasView)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
CanvasView view = ((CanvasView) this._partGrid);
|
||||||
|
Context ctx = view.getContext();
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||||
|
if (ctx.checkSelfPermission(Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
|
||||||
|
view.requestPermission(Manifest.permission.CAMERA, () -> { CameraStreamer.this.resume(); });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.imageReader = ImageReader.newInstance(CAPTURE_IMAGE_WIDTH, CAPTURE_IMAGE_HEIGHT, ImageFormat.JPEG, 30);
|
||||||
|
this.imageReader.setOnImageAvailableListener((ImageReader.OnImageAvailableListener) newImageReader -> {
|
||||||
|
Image latestImage = newImageReader.acquireLatestImage();
|
||||||
|
|
||||||
|
if (latestImage == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
CameraStreamer.this.onNewImage(latestImage);
|
||||||
|
latestImage.close();
|
||||||
|
}, new Handler());
|
||||||
|
|
||||||
|
CameraManager cm = (CameraManager) ctx.getSystemService(Context.CAMERA_SERVICE);
|
||||||
|
try {
|
||||||
|
String[] cameraList = cm.getCameraIdList();
|
||||||
|
for (String cd : cameraList) {
|
||||||
|
//get camera characteristics
|
||||||
|
CameraCharacteristics mCameraCharacteristics = cm.getCameraCharacteristics(cd);
|
||||||
|
|
||||||
|
//check if the camera is in the back - if not, continue to next
|
||||||
|
if (mCameraCharacteristics.get(CameraCharacteristics.LENS_FACING) != CameraCharacteristics.LENS_FACING_BACK) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._thread == null) {
|
||||||
|
this._thread = new HandlerThread("mCameraHandlerThread");
|
||||||
|
this._thread.start();
|
||||||
|
}
|
||||||
|
if (this._handler == null) {
|
||||||
|
this._handler = new Handler(this._thread.getLooper());
|
||||||
|
}
|
||||||
|
|
||||||
|
cm.openCamera(cd, new CameraDevice.StateCallback() {
|
||||||
|
@Override
|
||||||
|
public void onOpened(@NonNull CameraDevice camera) {
|
||||||
|
//make list of surfaces to give to camera
|
||||||
|
List<Surface> surfaceList = new ArrayList<>();
|
||||||
|
Surface surface = CameraStreamer.this.imageReader.getSurface();
|
||||||
|
surfaceList.add(surface);
|
||||||
|
|
||||||
|
try {
|
||||||
|
camera.createCaptureSession(surfaceList, new CameraCaptureSession.StateCallback() {
|
||||||
|
@Override
|
||||||
|
public void onConfigured(@NonNull CameraCaptureSession session) {
|
||||||
|
try {
|
||||||
|
CaptureRequest.Builder requestBuilder = session.getDevice().createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
|
||||||
|
requestBuilder.set(CaptureRequest.JPEG_ORIENTATION, mCameraCharacteristics.get(CameraCharacteristics.SENSOR_ORIENTATION));
|
||||||
|
requestBuilder.addTarget(surface);
|
||||||
|
//set to null - image data will be produced but will not receive metadata
|
||||||
|
session.setRepeatingRequest(requestBuilder.build(), null, CameraStreamer.this._handler);
|
||||||
|
} catch (CameraAccessException e) {
|
||||||
|
Log.e(LogTag, "createCaptureSession threw CameraAccessException.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onConfigureFailed(@NonNull CameraCaptureSession session) {
|
||||||
|
Log.i(LogTag, "Error on camera configuration");
|
||||||
|
}
|
||||||
|
}, CameraStreamer.this._handler);
|
||||||
|
} catch (CameraAccessException e) {
|
||||||
|
Log.e(LogTag, "createCaptureSession threw CameraAccessException.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDisconnected(@NonNull CameraDevice camera) {
|
||||||
|
Log.i(LogTag, "Camera disconnected");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(@NonNull CameraDevice camera, int error) {
|
||||||
|
Log.i(LogTag, "Error on camera opening: " + error);
|
||||||
|
}
|
||||||
|
}, this._handler);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (CameraAccessException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void pause() {
|
||||||
|
this._handler = null;
|
||||||
|
if (this._thread != null) {
|
||||||
|
this._thread.interrupt();
|
||||||
|
this._thread = null;
|
||||||
|
}
|
||||||
|
if (this.imageReader != null) {
|
||||||
|
this.imageReader.close();
|
||||||
|
this.imageReader = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object query(Object lastValue) {
|
||||||
|
return null; // No relevant value (maybe time?)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private int getOutputConnectorCenterX() {
|
||||||
|
return (_left + _right) / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getOutputConnectorCenterY() {
|
||||||
|
return _bottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getOutputConnectRadius() {
|
||||||
|
return (_right - _left) / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PartInstantiator getInstantiator() {
|
||||||
|
final int halfSideSize = DEFAULT_SIDE_SIZE / 2;
|
||||||
|
return new PartInstantiator() {
|
||||||
|
@Override
|
||||||
|
protected Part instantiate(PartGrid grid, Tuple2<Integer, Integer> center) {
|
||||||
|
return new CameraStreamer(grid,
|
||||||
|
center.item1 - halfSideSize, center.item2 - halfSideSize,
|
||||||
|
center.item1 + halfSideSize, center.item2 + halfSideSize);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -6,12 +6,13 @@ import android.util.Log;
|
|||||||
|
|
||||||
import com.codigoparallevar.minicards.PartInstantiator;
|
import com.codigoparallevar.minicards.PartInstantiator;
|
||||||
import com.codigoparallevar.minicards.ScrolledCanvas;
|
import com.codigoparallevar.minicards.ScrolledCanvas;
|
||||||
import com.codigoparallevar.minicards.parts.connectors.RoundOutputConnector;
|
import com.codigoparallevar.minicards.parts.connectors.SignalRoundOutputConnector;
|
||||||
|
import com.codigoparallevar.minicards.parts.style.CardTheme;
|
||||||
import com.codigoparallevar.minicards.types.Moveable;
|
import com.codigoparallevar.minicards.types.Moveable;
|
||||||
import com.codigoparallevar.minicards.types.Part;
|
import com.codigoparallevar.minicards.types.Part;
|
||||||
import com.codigoparallevar.minicards.types.PartConnection;
|
import com.codigoparallevar.minicards.types.PartConnection;
|
||||||
import com.codigoparallevar.minicards.types.PartGrid;
|
import com.codigoparallevar.minicards.types.PartGrid;
|
||||||
import com.codigoparallevar.minicards.types.Tuple2;
|
import com.codigoparallevar.minicards.types.functional.Tuple2;
|
||||||
import com.codigoparallevar.minicards.types.connectors.input.InputConnector;
|
import com.codigoparallevar.minicards.types.connectors.input.InputConnector;
|
||||||
import com.codigoparallevar.minicards.types.connectors.output.OutputConnector;
|
import com.codigoparallevar.minicards.types.connectors.output.OutputConnector;
|
||||||
import com.codigoparallevar.minicards.types.wireData.Signal;
|
import com.codigoparallevar.minicards.types.wireData.Signal;
|
||||||
@ -38,7 +39,7 @@ public class RoundButton implements Part {
|
|||||||
private final int _outerRadiusThickness = 10;
|
private final int _outerRadiusThickness = 10;
|
||||||
private final int _pathRunWay = 200;
|
private final int _pathRunWay = 200;
|
||||||
private List<OutputConnector> _outputConnectors;
|
private List<OutputConnector> _outputConnectors;
|
||||||
private final RoundOutputConnector _pressedOutputConnector;
|
private final SignalRoundOutputConnector _pressedOutputConnector;
|
||||||
private final static int DEFAULT_INNER_RADIUS = 80;
|
private final static int DEFAULT_INNER_RADIUS = 80;
|
||||||
private final static int DEFAULT_OUTER_RADIUS = 100;
|
private final static int DEFAULT_OUTER_RADIUS = 100;
|
||||||
|
|
||||||
@ -53,7 +54,7 @@ public class RoundButton implements Part {
|
|||||||
_outerRadius = outerRadius;
|
_outerRadius = outerRadius;
|
||||||
|
|
||||||
// Create connectors
|
// Create connectors
|
||||||
_pressedOutputConnector = new RoundOutputConnector(
|
_pressedOutputConnector = new SignalRoundOutputConnector(
|
||||||
this,
|
this,
|
||||||
_partGrid,
|
_partGrid,
|
||||||
getOutputConnectorCenterX(), getOutputConnectorCenterY(),
|
getOutputConnectorCenterX(), getOutputConnectorCenterY(),
|
||||||
@ -69,22 +70,22 @@ public class RoundButton implements Part {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int get_left() {
|
public int get_left() {
|
||||||
return _xCenter - _outerRadius / 2;
|
return _xCenter - _outerRadius;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int get_right() {
|
public int get_right() {
|
||||||
return _xCenter + _outerRadius / 2;
|
return _xCenter + _outerRadius;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int get_top() {
|
public int get_top() {
|
||||||
return _yCenter - _outerRadius / 2;
|
return _yCenter - _outerRadius;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int get_bottom() {
|
public int get_bottom() {
|
||||||
return _yCenter + _outerRadius / 2;
|
return _yCenter + _outerRadius;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -106,20 +107,27 @@ public class RoundButton implements Part {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void drawConnector(ScrolledCanvas canvas) {
|
private void drawConnector(ScrolledCanvas canvas) {
|
||||||
Paint connectorPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
Paint outerConnectorPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||||
connectorPaint.setColor(Color.RED);
|
outerConnectorPaint.setColor(CardTheme.PULSE_CONNECTOR_COLOR_OUTER);
|
||||||
|
|
||||||
canvas.drawCircle(getOutputConnectorCenterX(), getOutputConnectorCenterY(),
|
canvas.drawCircle(getOutputConnectorCenterX(), getOutputConnectorCenterY(),
|
||||||
getOutputConnectRadius(),
|
getOutputConnectRadius(),
|
||||||
connectorPaint);
|
outerConnectorPaint);
|
||||||
|
|
||||||
|
Paint innerConnectorPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||||
|
innerConnectorPaint.setColor(CardTheme.PULSE_CONNECTOR_COLOR_INNER);
|
||||||
|
|
||||||
|
canvas.drawCircle(getOutputConnectorCenterX(), getOutputConnectorCenterY(),
|
||||||
|
getOutputConnectRadius() / 2,
|
||||||
|
innerConnectorPaint);
|
||||||
}
|
}
|
||||||
|
|
||||||
private int getOutputConnectorCenterX() {
|
private int getOutputConnectorCenterX() {
|
||||||
return _xCenter + _outerRadius;
|
return _xCenter;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int getOutputConnectorCenterY() {
|
private int getOutputConnectorCenterY() {
|
||||||
return _yCenter;
|
return _yCenter + _outerRadius;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int getOutputConnectRadius() {
|
private int getOutputConnectRadius() {
|
||||||
@ -221,7 +229,12 @@ public class RoundButton implements Part {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public RoundOutputConnector getPressedOutputConnector(){
|
@Override
|
||||||
|
public Object query(Object lastValue) {
|
||||||
|
return null; // Only pulse output, so no relevant data
|
||||||
|
}
|
||||||
|
|
||||||
|
public SignalRoundOutputConnector getPressedOutputConnector(){
|
||||||
return _pressedOutputConnector;
|
return _pressedOutputConnector;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,196 @@
|
|||||||
|
package com.codigoparallevar.minicards.parts.connectors;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.codigoparallevar.minicards.ScrolledCanvas;
|
||||||
|
import com.codigoparallevar.minicards.types.Drawable;
|
||||||
|
import com.codigoparallevar.minicards.types.Part;
|
||||||
|
import com.codigoparallevar.minicards.types.PartGrid;
|
||||||
|
import com.codigoparallevar.minicards.types.connectors.Wiring.AnyWire;
|
||||||
|
import com.codigoparallevar.minicards.types.connectors.input.AnyInputConnector;
|
||||||
|
import com.codigoparallevar.minicards.types.connectors.input.InputConnector;
|
||||||
|
import com.codigoparallevar.minicards.types.connectors.output.AnyOutputConnector;
|
||||||
|
import com.codigoparallevar.minicards.types.functional.Tuple2;
|
||||||
|
import com.codigoparallevar.minicards.types.wireData.AnySignal;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class AnyRoundOutputConnector implements Drawable, AnyOutputConnector,
|
||||||
|
RoundOutputConnector<AnySignal, AnyInputConnector, AnyWire> {
|
||||||
|
private PartGrid _partGrid;
|
||||||
|
private int _centerX;
|
||||||
|
private int _centerY;
|
||||||
|
private final int _radius;
|
||||||
|
private final Part _part;
|
||||||
|
private AnyWire _currentWire = null;
|
||||||
|
private final List<AnyWire> _wires;
|
||||||
|
private final HashSet<InputConnector> _connections;
|
||||||
|
|
||||||
|
public AnyRoundOutputConnector(Part part, PartGrid partGrid, int centerX, int centerY, int radius) {
|
||||||
|
_part = part;
|
||||||
|
_partGrid = partGrid;
|
||||||
|
_centerX = centerX;
|
||||||
|
_centerY = centerY;
|
||||||
|
_radius = radius;
|
||||||
|
_wires = new LinkedList<>();
|
||||||
|
_connections = new HashSet<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean containsPoint(int x, int y) {
|
||||||
|
return ((Math.abs(x - _centerX) <= _radius)
|
||||||
|
&& (Math.abs(y - _centerY) <= _radius));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AnyWire getMoveable() {
|
||||||
|
if (_currentWire == null) {
|
||||||
|
startWire();
|
||||||
|
}
|
||||||
|
|
||||||
|
return _currentWire;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void unlink() {
|
||||||
|
while (_wires.size() > 0) {
|
||||||
|
_wires.get(0).unlink();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void startWire() {
|
||||||
|
_currentWire = new AnyWire(this, _centerX, _centerY);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void drop(AnyWire wire, AnyInputConnector resultPoint) {
|
||||||
|
if (wire != _currentWire) {
|
||||||
|
Log.w("RoundOutputConnector",
|
||||||
|
"Asked to drop non matching wire "
|
||||||
|
+ "(expected " + _currentWire + ", got " + wire + ")");
|
||||||
|
return ;
|
||||||
|
}
|
||||||
|
|
||||||
|
_currentWire = null;
|
||||||
|
|
||||||
|
Log.d("RoundOutputConnector", "Dropped wire on " + resultPoint);
|
||||||
|
|
||||||
|
// Not connected
|
||||||
|
if (resultPoint == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Already connected
|
||||||
|
if (_connections.contains(resultPoint)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_connections.add(resultPoint);
|
||||||
|
|
||||||
|
wire.attachTo(resultPoint, this);
|
||||||
|
_wires.add(wire);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void drop(AnyWire wire) {
|
||||||
|
|
||||||
|
if (wire != _currentWire) {
|
||||||
|
Log.w("RoundOutputConnector",
|
||||||
|
"Asked to drop non matching wire "
|
||||||
|
+ "(expected " + _currentWire + ", got " + wire + ")");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
AnyInputConnector resultPoint = _partGrid.getAnyInputConnectorOn(
|
||||||
|
wire.getXEnd(), wire.getYEnd());
|
||||||
|
|
||||||
|
drop(wire, resultPoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void wireUnlinked(AnyWire wire) {
|
||||||
|
_wires.remove(wire);
|
||||||
|
_connections.remove(wire.getAttachedTo());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void drawWires(ScrolledCanvas canvas, boolean devMode) {
|
||||||
|
for (AnyWire wire : _wires) {
|
||||||
|
wire.draw(canvas, devMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_currentWire != null) {
|
||||||
|
_currentWire.draw(canvas, devMode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updatePosition(int x, int y) {
|
||||||
|
_centerX = x;
|
||||||
|
_centerY = y;
|
||||||
|
|
||||||
|
for (AnyWire wire : _wires){
|
||||||
|
wire.moveStart(x, y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Tuple2<String, String>> getConnectionEndpoints() {
|
||||||
|
List<Tuple2<String, String>> endpointIds = new LinkedList<>();
|
||||||
|
|
||||||
|
for (AnyWire wire : _wires) {
|
||||||
|
InputConnector inputConnector = wire.getAttachedTo();
|
||||||
|
Part endPart = inputConnector.getPart();
|
||||||
|
endpointIds.add(new Tuple2<>(inputConnector.getId(), endPart.get_id()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return endpointIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void connectTo(AnyInputConnector inputConnector) {
|
||||||
|
if (_connections.contains(inputConnector)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_connections.add(inputConnector);
|
||||||
|
|
||||||
|
AnyWire wire = new AnyWire(this, _centerX, _centerY);
|
||||||
|
wire.attachTo(inputConnector, this);
|
||||||
|
_wires.add(wire);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void draw(ScrolledCanvas canvas, boolean devMode) {
|
||||||
|
// TODO: Complete this part
|
||||||
|
}
|
||||||
|
|
||||||
|
public void send(AnySignal signal) {
|
||||||
|
for (AnyWire wire : _wires){
|
||||||
|
wire.send(signal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object query(Object lastValue) {
|
||||||
|
return _part.query(lastValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<AnyWire> getWires() {
|
||||||
|
return _wires;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getX() {
|
||||||
|
return _centerX;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getY() {
|
||||||
|
return _centerY;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getRadius() {
|
||||||
|
return _radius;
|
||||||
|
}
|
||||||
|
}
|
@ -72,7 +72,7 @@ public class BooleanRoundInputConnector implements BooleanInputConnector {
|
|||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void getAttachment(Wire<BooleanSignal, BooleanInputConnector> wire) {
|
public void addAttachment(Wire<BooleanSignal, BooleanInputConnector> wire) {
|
||||||
_attachments.add(wire);
|
_attachments.add(wire);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,7 +92,7 @@ public class BooleanRoundInputConnector implements BooleanInputConnector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void unlinkWire(Wire wire) {
|
public void wireUnlinked(Wire wire) {
|
||||||
_attachments.remove(wire);
|
_attachments.remove(wire);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,11 +7,11 @@ import com.codigoparallevar.minicards.types.Drawable;
|
|||||||
import com.codigoparallevar.minicards.types.Moveable;
|
import com.codigoparallevar.minicards.types.Moveable;
|
||||||
import com.codigoparallevar.minicards.types.Part;
|
import com.codigoparallevar.minicards.types.Part;
|
||||||
import com.codigoparallevar.minicards.types.PartGrid;
|
import com.codigoparallevar.minicards.types.PartGrid;
|
||||||
import com.codigoparallevar.minicards.types.Tuple2;
|
|
||||||
import com.codigoparallevar.minicards.types.connectors.Wiring.BooleanWire;
|
import com.codigoparallevar.minicards.types.connectors.Wiring.BooleanWire;
|
||||||
import com.codigoparallevar.minicards.types.connectors.input.BooleanInputConnector;
|
import com.codigoparallevar.minicards.types.connectors.input.BooleanInputConnector;
|
||||||
import com.codigoparallevar.minicards.types.connectors.input.InputConnector;
|
import com.codigoparallevar.minicards.types.connectors.input.InputConnector;
|
||||||
import com.codigoparallevar.minicards.types.connectors.output.BooleanOutputConnector;
|
import com.codigoparallevar.minicards.types.connectors.output.BooleanOutputConnector;
|
||||||
|
import com.codigoparallevar.minicards.types.functional.Tuple2;
|
||||||
import com.codigoparallevar.minicards.types.wireData.BooleanSignal;
|
import com.codigoparallevar.minicards.types.wireData.BooleanSignal;
|
||||||
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
@ -83,7 +83,7 @@ public class BooleanRoundOutputConnector implements Drawable, BooleanOutputConne
|
|||||||
}
|
}
|
||||||
_connections.add(resultPoint);
|
_connections.add(resultPoint);
|
||||||
|
|
||||||
wire.attachTo(resultPoint);
|
wire.attachTo(resultPoint, this);
|
||||||
_wires.add(wire);
|
_wires.add(wire);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -94,8 +94,9 @@ public class BooleanRoundOutputConnector implements Drawable, BooleanOutputConne
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void unlinkWire(BooleanWire wire) {
|
public void wireUnlinked(BooleanWire wire) {
|
||||||
_wires.remove(wire);
|
_wires.remove(wire);
|
||||||
|
_connections.remove(wire.getAttachedTo());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -141,7 +142,7 @@ public class BooleanRoundOutputConnector implements Drawable, BooleanOutputConne
|
|||||||
_connections.add(inputConnector);
|
_connections.add(inputConnector);
|
||||||
|
|
||||||
BooleanWire wire = new BooleanWire(this, _centerX, _centerY);
|
BooleanWire wire = new BooleanWire(this, _centerX, _centerY);
|
||||||
wire.attachTo(inputConnector);
|
wire.attachTo(inputConnector, this);
|
||||||
_wires.add(wire);
|
_wires.add(wire);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -155,4 +156,14 @@ public class BooleanRoundOutputConnector implements Drawable, BooleanOutputConne
|
|||||||
wire.send(signal);
|
wire.send(signal);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object query(Object lastValue) {
|
||||||
|
return _part.query(lastValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<BooleanWire> getWires() {
|
||||||
|
return _wires;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,172 @@
|
|||||||
|
package com.codigoparallevar.minicards.parts.connectors;
|
||||||
|
|
||||||
|
import com.codigoparallevar.minicards.parts.style.CardTheme;
|
||||||
|
import com.programaker.api.data.ProgramakerCustomBlockArgument;
|
||||||
|
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
public class ConnectorTypeInfo {
|
||||||
|
private final Type _type;
|
||||||
|
private static final String SERIALIZED_TYPE_KEY = "type";
|
||||||
|
private static final String SERIALIZED_ARGUMENT_KEY = "argument";
|
||||||
|
|
||||||
|
private ProgramakerCustomBlockArgument _blockArgument = null;
|
||||||
|
|
||||||
|
public static ConnectorTypeInfo FromTypeName(String type) {
|
||||||
|
if (type == null) {
|
||||||
|
return new ConnectorTypeInfo(Type.UNKNOWN);
|
||||||
|
}
|
||||||
|
|
||||||
|
type = type.toLowerCase();
|
||||||
|
if (type.equals("string")) {
|
||||||
|
return new ConnectorTypeInfo(Type.STRING);
|
||||||
|
}
|
||||||
|
else if (type.equals("integer")) {
|
||||||
|
return new ConnectorTypeInfo(Type.INTEGER);
|
||||||
|
}
|
||||||
|
else if (type.equals("float")) {
|
||||||
|
return new ConnectorTypeInfo(Type.FLOAT);
|
||||||
|
}
|
||||||
|
else if (type.equals("boolean")) {
|
||||||
|
return new ConnectorTypeInfo(Type.BOOLEAN);
|
||||||
|
}
|
||||||
|
else if (type.equals("pulse")) {
|
||||||
|
return new ConnectorTypeInfo(Type.PULSE);
|
||||||
|
}
|
||||||
|
else if (type.equals("enum")) {
|
||||||
|
return new ConnectorTypeInfo(Type.ENUM);
|
||||||
|
}
|
||||||
|
else if (type.equals("any")) {
|
||||||
|
return new ConnectorTypeInfo(Type.ANY);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return new ConnectorTypeInfo(Type.UNKNOWN);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static String typeToString(Type type) {
|
||||||
|
switch (type) {
|
||||||
|
case ANY:
|
||||||
|
return "any";
|
||||||
|
case ENUM:
|
||||||
|
return "enum";
|
||||||
|
case FLOAT:
|
||||||
|
return "float";
|
||||||
|
case PULSE:
|
||||||
|
return "pulse";
|
||||||
|
case STRING:
|
||||||
|
return "string";
|
||||||
|
case BOOLEAN:
|
||||||
|
return "boolean";
|
||||||
|
case INTEGER:
|
||||||
|
return "integer";
|
||||||
|
case UNKNOWN:
|
||||||
|
return "unknown";
|
||||||
|
default:
|
||||||
|
return "unknown";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ConnectorTypeInfo deserialize(JSONObject jsonTypeInfo) {
|
||||||
|
ConnectorTypeInfo type = ConnectorTypeInfo.FromTypeName(jsonTypeInfo.optString(SERIALIZED_TYPE_KEY));
|
||||||
|
|
||||||
|
JSONObject arg = jsonTypeInfo.optJSONObject(SERIALIZED_ARGUMENT_KEY);
|
||||||
|
if (arg != null) {
|
||||||
|
type._blockArgument = ProgramakerCustomBlockArgument.deserialize(arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ConnectorTypeInfo FromArgument(ProgramakerCustomBlockArgument arg) {
|
||||||
|
ConnectorTypeInfo type = ConnectorTypeInfo.FromTypeName(arg.getComputedType());
|
||||||
|
type._blockArgument = arg;
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getOuterColor() {
|
||||||
|
switch (_type) {
|
||||||
|
case ANY:
|
||||||
|
return CardTheme.ANY_CONNECTOR_COLOR_OUTER;
|
||||||
|
case ENUM:
|
||||||
|
return CardTheme.ENUM_CONNECTOR_COLOR_OUTER;
|
||||||
|
case FLOAT:
|
||||||
|
return CardTheme.FLOAT_CONNECTOR_COLOR_OUTER;
|
||||||
|
case PULSE:
|
||||||
|
return CardTheme.PULSE_CONNECTOR_COLOR_OUTER;
|
||||||
|
case STRING:
|
||||||
|
return CardTheme.STRING_CONNECTOR_COLOR_OUTER;
|
||||||
|
case BOOLEAN:
|
||||||
|
return CardTheme.BOOLEAN_CONNECTOR_COLOR_OUTER;
|
||||||
|
case INTEGER:
|
||||||
|
return CardTheme.INTEGER_CONNECTOR_COLOR_OUTER;
|
||||||
|
case UNKNOWN:
|
||||||
|
return CardTheme.UNKNOWN_CONNECTOR_COLOR_OUTER;
|
||||||
|
default:
|
||||||
|
return CardTheme.UNKNOWN_CONNECTOR_COLOR_OUTER;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public int getInnerColor() {
|
||||||
|
switch (_type) {
|
||||||
|
case ANY:
|
||||||
|
return CardTheme.ANY_CONNECTOR_COLOR_INNER;
|
||||||
|
case ENUM:
|
||||||
|
return CardTheme.ENUM_CONNECTOR_COLOR_INNER;
|
||||||
|
case FLOAT:
|
||||||
|
return CardTheme.FLOAT_CONNECTOR_COLOR_INNER;
|
||||||
|
case PULSE:
|
||||||
|
return CardTheme.PULSE_CONNECTOR_COLOR_INNER;
|
||||||
|
case STRING:
|
||||||
|
return CardTheme.STRING_CONNECTOR_COLOR_INNER;
|
||||||
|
case BOOLEAN:
|
||||||
|
return CardTheme.BOOLEAN_CONNECTOR_COLOR_INNER;
|
||||||
|
case INTEGER:
|
||||||
|
return CardTheme.INTEGER_CONNECTOR_COLOR_INNER;
|
||||||
|
case UNKNOWN:
|
||||||
|
return CardTheme.UNKNOWN_CONNECTOR_COLOR_INNER;
|
||||||
|
default:
|
||||||
|
return CardTheme.UNKNOWN_CONNECTOR_COLOR_INNER;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public JSONObject serialize() {
|
||||||
|
JSONObject obj = new JSONObject();
|
||||||
|
try {
|
||||||
|
obj.put(SERIALIZED_TYPE_KEY, ConnectorTypeInfo.typeToString(this._type));
|
||||||
|
|
||||||
|
if (_blockArgument != null) {
|
||||||
|
obj.put(SERIALIZED_ARGUMENT_KEY, _blockArgument.serialize());
|
||||||
|
}
|
||||||
|
} catch (JSONException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ProgramakerCustomBlockArgument getBlockArgument() {
|
||||||
|
return _blockArgument;
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum Type {
|
||||||
|
PULSE,
|
||||||
|
BOOLEAN,
|
||||||
|
STRING,
|
||||||
|
ANY,
|
||||||
|
ENUM,
|
||||||
|
UNKNOWN,
|
||||||
|
FLOAT,
|
||||||
|
INTEGER,
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConnectorTypeInfo(ConnectorTypeInfo.Type type) {
|
||||||
|
this._type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Type get_type() {
|
||||||
|
return _type;
|
||||||
|
}
|
||||||
|
}
|
@ -5,20 +5,20 @@ import android.util.Log;
|
|||||||
import com.codigoparallevar.minicards.types.Moveable;
|
import com.codigoparallevar.minicards.types.Moveable;
|
||||||
import com.codigoparallevar.minicards.types.Part;
|
import com.codigoparallevar.minicards.types.Part;
|
||||||
import com.codigoparallevar.minicards.types.connectors.Wiring.Wire;
|
import com.codigoparallevar.minicards.types.connectors.Wiring.Wire;
|
||||||
import com.codigoparallevar.minicards.types.connectors.input.AnyInputConnector;
|
import com.codigoparallevar.minicards.types.connectors.input.ImageInputConnector;
|
||||||
import com.codigoparallevar.minicards.types.wireData.WireDataType;
|
import com.codigoparallevar.minicards.types.wireData.ImageSignal;
|
||||||
|
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class AnyRoundInputConnector extends AnyInputConnector {
|
public class ImageRoundInputConnector implements ImageInputConnector {
|
||||||
private final Part _part;
|
private final Part _part;
|
||||||
private int _xposition;
|
private int _xposition;
|
||||||
private int _yposition;
|
private int _yposition;
|
||||||
private final int _radius;
|
private final int _radius;
|
||||||
private final List<Wire> _attachments = new LinkedList<>();
|
private final List<Wire> _attachments = new LinkedList<>();
|
||||||
|
|
||||||
public AnyRoundInputConnector(Part part,
|
public ImageRoundInputConnector(Part part,
|
||||||
int inputConnectorCenterX, int inputConnectorCenterY,
|
int inputConnectorCenterX, int inputConnectorCenterY,
|
||||||
int inputConnectorRadius) {
|
int inputConnectorRadius) {
|
||||||
_part = part;
|
_part = part;
|
||||||
@ -66,13 +66,13 @@ public class AnyRoundInputConnector extends AnyInputConnector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void send(WireDataType signal) {
|
public void send(ImageSignal signal) {
|
||||||
_part.send(this, signal);
|
_part.send(this, signal);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void getAttachment(Wire wire) {
|
public void addAttachment(Wire<ImageSignal, ImageInputConnector> wire) {
|
||||||
_attachments.add(wire);
|
_attachments.add(wire);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,7 +92,7 @@ public class AnyRoundInputConnector extends AnyInputConnector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void unlinkWire(Wire wire) {
|
public void wireUnlinked(Wire wire) {
|
||||||
_attachments.remove(wire);
|
_attachments.remove(wire);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -0,0 +1,183 @@
|
|||||||
|
package com.codigoparallevar.minicards.parts.connectors;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.codigoparallevar.minicards.ScrolledCanvas;
|
||||||
|
import com.codigoparallevar.minicards.types.Drawable;
|
||||||
|
import com.codigoparallevar.minicards.types.Moveable;
|
||||||
|
import com.codigoparallevar.minicards.types.Part;
|
||||||
|
import com.codigoparallevar.minicards.types.PartGrid;
|
||||||
|
import com.codigoparallevar.minicards.types.connectors.Wiring.ImageWire;
|
||||||
|
import com.codigoparallevar.minicards.types.connectors.input.ImageInputConnector;
|
||||||
|
import com.codigoparallevar.minicards.types.connectors.input.InputConnector;
|
||||||
|
import com.codigoparallevar.minicards.types.connectors.output.ImageOutputConnector;
|
||||||
|
import com.codigoparallevar.minicards.types.functional.Tuple2;
|
||||||
|
import com.codigoparallevar.minicards.types.wireData.ImageSignal;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class ImageRoundOutputConnector implements Drawable, ImageOutputConnector,
|
||||||
|
RoundOutputConnector<ImageSignal, ImageInputConnector, ImageWire> {
|
||||||
|
private static final String LogTag = "ImageRoundOutputConnector";
|
||||||
|
|
||||||
|
private PartGrid _partGrid;
|
||||||
|
private int _centerX;
|
||||||
|
private int _centerY;
|
||||||
|
private final int _radius;
|
||||||
|
private final Part _part;
|
||||||
|
private ImageWire _currentWire = null;
|
||||||
|
private final List<ImageWire> _wires;
|
||||||
|
private final HashSet<InputConnector> _connections;
|
||||||
|
|
||||||
|
public ImageRoundOutputConnector(Part part, PartGrid partGrid, int centerX, int centerY, int radius) {
|
||||||
|
_part = part;
|
||||||
|
_partGrid = partGrid;
|
||||||
|
_centerX = centerX;
|
||||||
|
_centerY = centerY;
|
||||||
|
_radius = radius;
|
||||||
|
_wires = new LinkedList<>();
|
||||||
|
_connections = new HashSet<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean containsPoint(int x, int y) {
|
||||||
|
return ((Math.abs(x - _centerX) <= _radius)
|
||||||
|
&& (Math.abs(y - _centerY) <= _radius));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Moveable getMoveable() {
|
||||||
|
if (_currentWire == null) {
|
||||||
|
startWire();
|
||||||
|
}
|
||||||
|
|
||||||
|
return _currentWire;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void unlink() {}
|
||||||
|
|
||||||
|
private void startWire() {
|
||||||
|
_currentWire = new ImageWire(this, _centerX, _centerY);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void drop(ImageWire wire) {
|
||||||
|
|
||||||
|
if (wire == _currentWire){
|
||||||
|
_currentWire = null;
|
||||||
|
|
||||||
|
ImageInputConnector resultPoint = _partGrid.getImageInputConnectorOn(
|
||||||
|
wire.getXEnd(), wire.getYEnd());
|
||||||
|
|
||||||
|
Log.d(LogTag, "Dropped wire on " + resultPoint);
|
||||||
|
|
||||||
|
// Not connected
|
||||||
|
if (resultPoint == null){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Already connected
|
||||||
|
if (_connections.contains(resultPoint)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_connections.add(resultPoint);
|
||||||
|
|
||||||
|
wire.attachTo(resultPoint, this);
|
||||||
|
_wires.add(wire);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Log.w(LogTag,
|
||||||
|
"Asked to drop non matching wire "
|
||||||
|
+ "(expected " + _currentWire + ", got " + wire + ")");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void wireUnlinked(ImageWire wire) {
|
||||||
|
_wires.remove(wire);
|
||||||
|
_connections.remove(wire.getAttachedTo());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void drawWires(ScrolledCanvas canvas, boolean devMode) {
|
||||||
|
for (ImageWire wire : _wires) {
|
||||||
|
wire.draw(canvas, devMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_currentWire != null) {
|
||||||
|
_currentWire.draw(canvas, devMode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updatePosition(int x, int y) {
|
||||||
|
_centerX = x;
|
||||||
|
_centerY = y;
|
||||||
|
|
||||||
|
for (ImageWire wire : _wires){
|
||||||
|
wire.moveStart(x, y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Tuple2<String, String>> getConnectionEndpoints() {
|
||||||
|
List<Tuple2<String, String>> endpointIds = new LinkedList<>();
|
||||||
|
|
||||||
|
for (ImageWire wire : _wires) {
|
||||||
|
InputConnector inputConnector = wire.getAttachedTo();
|
||||||
|
Part endPart = inputConnector.getPart();
|
||||||
|
endpointIds.add(new Tuple2<>(inputConnector.getId(), endPart.get_id()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return endpointIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void connectTo(ImageInputConnector inputConnector) {
|
||||||
|
if (_connections.contains(inputConnector)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_connections.add(inputConnector);
|
||||||
|
|
||||||
|
ImageWire wire = new ImageWire(this, _centerX, _centerY);
|
||||||
|
wire.attachTo(inputConnector, this);
|
||||||
|
_wires.add(wire);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void draw(ScrolledCanvas canvas, boolean devMode) {
|
||||||
|
// TODO: Complete this part
|
||||||
|
}
|
||||||
|
|
||||||
|
public void send(ImageSignal signal) {
|
||||||
|
for (ImageWire wire : _wires){
|
||||||
|
wire.send(signal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object query(Object lastValue) {
|
||||||
|
return _part.query(lastValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getX() {
|
||||||
|
return _centerX;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getY() {
|
||||||
|
return _centerY;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getRadius() {
|
||||||
|
return _radius;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<ImageWire> getWires() {
|
||||||
|
return this._wires;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,140 @@
|
|||||||
|
package com.codigoparallevar.minicards.parts.connectors;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.codigoparallevar.minicards.CanvasView;
|
||||||
|
import com.codigoparallevar.minicards.parts.values.StaticValuePart;
|
||||||
|
import com.codigoparallevar.minicards.types.Moveable;
|
||||||
|
import com.codigoparallevar.minicards.types.Part;
|
||||||
|
import com.codigoparallevar.minicards.types.PartGrid;
|
||||||
|
import com.codigoparallevar.minicards.types.connectors.RoundConnector;
|
||||||
|
import com.codigoparallevar.minicards.types.connectors.Wiring.AnyWire;
|
||||||
|
import com.codigoparallevar.minicards.types.connectors.Wiring.Wire;
|
||||||
|
import com.codigoparallevar.minicards.types.connectors.input.AnyInputConnector;
|
||||||
|
import com.codigoparallevar.minicards.types.wireData.AnySignal;
|
||||||
|
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class ProgramakerCustomBlockInputConnector extends AnyInputConnector implements RoundConnector {
|
||||||
|
private static final int STATIC_VALUE_SEPARATION = 200;
|
||||||
|
private final Part _part;
|
||||||
|
private final ConnectorTypeInfo _typeInfo;
|
||||||
|
private final PartGrid _partGrid;
|
||||||
|
private int _xposition;
|
||||||
|
private int _yposition;
|
||||||
|
private final int _radius;
|
||||||
|
private final List<Wire> _attachments = new LinkedList<>();
|
||||||
|
|
||||||
|
public ProgramakerCustomBlockInputConnector(Part part, PartGrid partGrid,
|
||||||
|
int inputConnectorCenterX, int inputConnectorCenterY,
|
||||||
|
int inputConnectorRadius, ConnectorTypeInfo typeInfo) {
|
||||||
|
_part = part;
|
||||||
|
_partGrid = partGrid;
|
||||||
|
_xposition = inputConnectorCenterX;
|
||||||
|
_yposition = inputConnectorCenterY;
|
||||||
|
_radius = inputConnectorRadius;
|
||||||
|
_typeInfo = typeInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean containsPoint(int x, int y) {
|
||||||
|
return ((Math.abs(x - _xposition) <= _radius)
|
||||||
|
&& (Math.abs(y - _yposition) <= _radius));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Moveable getMoveable() {
|
||||||
|
return new Wire(this, _xposition, _yposition);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void unlink() {
|
||||||
|
for (Wire wire : _attachments) {
|
||||||
|
wire.unlink();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updatePosition(int x, int y) {
|
||||||
|
_xposition = x;
|
||||||
|
_yposition = y;
|
||||||
|
|
||||||
|
for (Wire wire : _attachments){
|
||||||
|
wire.moveEnd(x, y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object query(Object lastValue) {
|
||||||
|
for (Wire linked : _attachments) {
|
||||||
|
Object returned = linked.query(lastValue);
|
||||||
|
if (returned != null) {
|
||||||
|
return returned;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getX() {
|
||||||
|
return _xposition;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getY() {
|
||||||
|
return _yposition;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getRadius() {
|
||||||
|
return _radius;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void send(AnySignal signal) {
|
||||||
|
_part.send(this, signal);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addAttachment(Wire wire) {
|
||||||
|
_attachments.add(wire);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Part getPart() {
|
||||||
|
return _part;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getId() {
|
||||||
|
return _part.getConnectorId(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void drop(Wire wire) {
|
||||||
|
Log.d("InputConnector", "Dropped wire " + wire);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void wireUnlinked(Wire wire) {
|
||||||
|
_attachments.remove(wire);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void touched() {
|
||||||
|
// If it doesn't have wires connected, spawn a value block
|
||||||
|
if (this._attachments.size() > 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final PartGrid grid = _partGrid;
|
||||||
|
|
||||||
|
if (grid instanceof CanvasView) {
|
||||||
|
StaticValuePart value = new StaticValuePart(_partGrid,
|
||||||
|
this._xposition, this._yposition - STATIC_VALUE_SEPARATION,
|
||||||
|
this._typeInfo);
|
||||||
|
AnyWire wire = value.getOutputWire();
|
||||||
|
value.getValueOutput().drop(wire, this);
|
||||||
|
((CanvasView) grid).addPart(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -65,14 +65,17 @@ public class RoundInputConnector implements SignalInputConnector {
|
|||||||
return _yposition;
|
return _yposition;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public float getRadius() {
|
||||||
|
return _radius;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void send(Signal signal) {
|
public void send(Signal signal) {
|
||||||
_part.send(this, signal);
|
_part.send(this, signal);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void getAttachment(Wire<Signal, SignalInputConnector> wire) {
|
public void addAttachment(Wire<Signal, SignalInputConnector> wire) {
|
||||||
_attachments.add(wire);
|
_attachments.add(wire);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,7 +95,7 @@ public class RoundInputConnector implements SignalInputConnector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void unlinkWire(Wire wire) {
|
public void wireUnlinked(Wire wire) {
|
||||||
_attachments.remove(wire);
|
_attachments.remove(wire);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,158 +1,14 @@
|
|||||||
package com.codigoparallevar.minicards.parts.connectors;
|
package com.codigoparallevar.minicards.parts.connectors;
|
||||||
|
|
||||||
import android.util.Log;
|
import com.codigoparallevar.minicards.types.connectors.RoundConnector;
|
||||||
|
import com.codigoparallevar.minicards.types.connectors.Wiring.Wire;
|
||||||
import com.codigoparallevar.minicards.ScrolledCanvas;
|
|
||||||
import com.codigoparallevar.minicards.types.Drawable;
|
|
||||||
import com.codigoparallevar.minicards.types.Moveable;
|
|
||||||
import com.codigoparallevar.minicards.types.Part;
|
|
||||||
import com.codigoparallevar.minicards.types.PartGrid;
|
|
||||||
import com.codigoparallevar.minicards.types.Tuple2;
|
|
||||||
import com.codigoparallevar.minicards.types.connectors.Wiring.SignalWire;
|
|
||||||
import com.codigoparallevar.minicards.types.connectors.input.InputConnector;
|
import com.codigoparallevar.minicards.types.connectors.input.InputConnector;
|
||||||
import com.codigoparallevar.minicards.types.connectors.input.SignalInputConnector;
|
import com.codigoparallevar.minicards.types.connectors.output.OutputConnector;
|
||||||
import com.codigoparallevar.minicards.types.connectors.output.SignalOutputConnector;
|
import com.codigoparallevar.minicards.types.wireData.WireDataType;
|
||||||
import com.codigoparallevar.minicards.types.wireData.Signal;
|
|
||||||
|
|
||||||
import java.util.HashSet;
|
public interface RoundOutputConnector<
|
||||||
import java.util.LinkedList;
|
T1 extends WireDataType,
|
||||||
import java.util.List;
|
T2 extends InputConnector<T1, T2>,
|
||||||
|
T3 extends Wire<T1, T2>
|
||||||
public class RoundOutputConnector implements Drawable, SignalOutputConnector {
|
> extends RoundConnector, OutputConnector<T1, T2, T3> {
|
||||||
private PartGrid _partGrid;
|
|
||||||
private int _centerX;
|
|
||||||
private int _centerY;
|
|
||||||
private final int _radius;
|
|
||||||
private final Part _part;
|
|
||||||
private SignalWire _currentWire = null;
|
|
||||||
private final List<SignalWire> _wires;
|
|
||||||
private final HashSet<InputConnector> _connections;
|
|
||||||
|
|
||||||
public RoundOutputConnector(Part part, PartGrid partGrid, int centerX, int centerY, int radius) {
|
|
||||||
_part = part;
|
|
||||||
_partGrid = partGrid;
|
|
||||||
_centerX = centerX;
|
|
||||||
_centerY = centerY;
|
|
||||||
_radius = radius;
|
|
||||||
_wires = new LinkedList<>();
|
|
||||||
_connections = new HashSet<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean containsPoint(int x, int y) {
|
|
||||||
return ((Math.abs(x - _centerX) <= _radius)
|
|
||||||
&& (Math.abs(y - _centerY) <= _radius));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Moveable getMoveable() {
|
|
||||||
if (_currentWire == null) {
|
|
||||||
startWire();
|
|
||||||
}
|
|
||||||
|
|
||||||
return _currentWire;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void unlink() {}
|
|
||||||
|
|
||||||
private void startWire() {
|
|
||||||
_currentWire = new SignalWire(this, _centerX, _centerY);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void drop(SignalWire wire) {
|
|
||||||
|
|
||||||
if (wire == _currentWire){
|
|
||||||
_currentWire = null;
|
|
||||||
|
|
||||||
SignalInputConnector resultPoint = _partGrid.getSignalInputConnectorOn(
|
|
||||||
wire.getXEnd(), wire.getYEnd());
|
|
||||||
|
|
||||||
Log.d("RoundOutputConnector", "Dropped wire on " + resultPoint);
|
|
||||||
|
|
||||||
// Not connected
|
|
||||||
if (resultPoint == null){
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Already connected
|
|
||||||
if (_connections.contains(resultPoint)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_connections.add(resultPoint);
|
|
||||||
|
|
||||||
wire.attachTo(resultPoint);
|
|
||||||
_wires.add(wire);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Log.w("RoundOutputConnector",
|
|
||||||
"Asked to drop non matching wire "
|
|
||||||
+ "(expected " + _currentWire + ", got " + wire + ")");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void unlinkWire(SignalWire wire) {
|
|
||||||
_wires.remove(wire);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void drawWires(ScrolledCanvas canvas, boolean devMode) {
|
|
||||||
for (SignalWire wire : _wires) {
|
|
||||||
wire.draw(canvas, devMode);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_currentWire != null) {
|
|
||||||
_currentWire.draw(canvas, devMode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void updatePosition(int x, int y) {
|
|
||||||
_centerX = x;
|
|
||||||
_centerY = y;
|
|
||||||
|
|
||||||
for (SignalWire wire : _wires){
|
|
||||||
wire.moveStart(x, y);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<Tuple2<String, String>> getConnectionEndpoints() {
|
|
||||||
List<Tuple2<String, String>> endpointIds = new LinkedList<>();
|
|
||||||
|
|
||||||
for (SignalWire wire : _wires) {
|
|
||||||
InputConnector inputConnector = wire.getAttachedTo();
|
|
||||||
Part endPart = inputConnector.getPart();
|
|
||||||
endpointIds.add(new Tuple2<>(inputConnector.getId(), endPart.get_id()));
|
|
||||||
}
|
|
||||||
|
|
||||||
return endpointIds;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void connectTo(SignalInputConnector inputConnector) {
|
|
||||||
if (_connections.contains(inputConnector)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_connections.add(inputConnector);
|
|
||||||
|
|
||||||
SignalWire wire = new SignalWire(this, _centerX, _centerY);
|
|
||||||
wire.attachTo(inputConnector);
|
|
||||||
_wires.add(wire);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void draw(ScrolledCanvas canvas, boolean devMode) {
|
|
||||||
// TODO: Complete this part
|
|
||||||
}
|
|
||||||
|
|
||||||
public void send(Signal signal) {
|
|
||||||
for (SignalWire wire : _wires){
|
|
||||||
wire.send(signal);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,183 @@
|
|||||||
|
package com.codigoparallevar.minicards.parts.connectors;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.codigoparallevar.minicards.ScrolledCanvas;
|
||||||
|
import com.codigoparallevar.minicards.types.Drawable;
|
||||||
|
import com.codigoparallevar.minicards.types.Moveable;
|
||||||
|
import com.codigoparallevar.minicards.types.Part;
|
||||||
|
import com.codigoparallevar.minicards.types.PartGrid;
|
||||||
|
import com.codigoparallevar.minicards.types.connectors.Wiring.SignalWire;
|
||||||
|
import com.codigoparallevar.minicards.types.connectors.input.InputConnector;
|
||||||
|
import com.codigoparallevar.minicards.types.connectors.input.SignalInputConnector;
|
||||||
|
import com.codigoparallevar.minicards.types.connectors.output.SignalOutputConnector;
|
||||||
|
import com.codigoparallevar.minicards.types.functional.Tuple2;
|
||||||
|
import com.codigoparallevar.minicards.types.wireData.Signal;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class SignalRoundOutputConnector implements Drawable, SignalOutputConnector,
|
||||||
|
RoundOutputConnector<Signal, SignalInputConnector, SignalWire> {
|
||||||
|
private static final String LogTag = "RoundOutputConnector";
|
||||||
|
|
||||||
|
private PartGrid _partGrid;
|
||||||
|
private int _centerX;
|
||||||
|
private int _centerY;
|
||||||
|
private final int _radius;
|
||||||
|
private final Part _part;
|
||||||
|
private SignalWire _currentWire = null;
|
||||||
|
private final List<SignalWire> _wires;
|
||||||
|
private final HashSet<InputConnector> _connections;
|
||||||
|
|
||||||
|
public SignalRoundOutputConnector(Part part, PartGrid partGrid, int centerX, int centerY, int radius) {
|
||||||
|
_part = part;
|
||||||
|
_partGrid = partGrid;
|
||||||
|
_centerX = centerX;
|
||||||
|
_centerY = centerY;
|
||||||
|
_radius = radius;
|
||||||
|
_wires = new LinkedList<>();
|
||||||
|
_connections = new HashSet<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean containsPoint(int x, int y) {
|
||||||
|
return ((Math.abs(x - _centerX) <= _radius)
|
||||||
|
&& (Math.abs(y - _centerY) <= _radius));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Moveable getMoveable() {
|
||||||
|
if (_currentWire == null) {
|
||||||
|
startWire();
|
||||||
|
}
|
||||||
|
|
||||||
|
return _currentWire;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void unlink() {}
|
||||||
|
|
||||||
|
private void startWire() {
|
||||||
|
_currentWire = new SignalWire(this, _centerX, _centerY);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void drop(SignalWire wire) {
|
||||||
|
|
||||||
|
if (wire == _currentWire){
|
||||||
|
_currentWire = null;
|
||||||
|
|
||||||
|
SignalInputConnector resultPoint = _partGrid.getSignalInputConnectorOn(
|
||||||
|
wire.getXEnd(), wire.getYEnd());
|
||||||
|
|
||||||
|
Log.d(LogTag, "Dropped wire on " + resultPoint);
|
||||||
|
|
||||||
|
// Not connected
|
||||||
|
if (resultPoint == null){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Already connected
|
||||||
|
if (_connections.contains(resultPoint)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_connections.add(resultPoint);
|
||||||
|
|
||||||
|
wire.attachTo(resultPoint, this);
|
||||||
|
_wires.add(wire);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Log.w(LogTag,
|
||||||
|
"Asked to drop non matching wire "
|
||||||
|
+ "(expected " + _currentWire + ", got " + wire + ")");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void wireUnlinked(SignalWire wire) {
|
||||||
|
_wires.remove(wire);
|
||||||
|
_connections.remove(wire.getAttachedTo());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void drawWires(ScrolledCanvas canvas, boolean devMode) {
|
||||||
|
for (SignalWire wire : _wires) {
|
||||||
|
wire.draw(canvas, devMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_currentWire != null) {
|
||||||
|
_currentWire.draw(canvas, devMode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updatePosition(int x, int y) {
|
||||||
|
_centerX = x;
|
||||||
|
_centerY = y;
|
||||||
|
|
||||||
|
for (SignalWire wire : _wires){
|
||||||
|
wire.moveStart(x, y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Tuple2<String, String>> getConnectionEndpoints() {
|
||||||
|
List<Tuple2<String, String>> endpointIds = new LinkedList<>();
|
||||||
|
|
||||||
|
for (SignalWire wire : _wires) {
|
||||||
|
InputConnector inputConnector = wire.getAttachedTo();
|
||||||
|
Part endPart = inputConnector.getPart();
|
||||||
|
endpointIds.add(new Tuple2<>(inputConnector.getId(), endPart.get_id()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return endpointIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void connectTo(SignalInputConnector inputConnector) {
|
||||||
|
if (_connections.contains(inputConnector)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_connections.add(inputConnector);
|
||||||
|
|
||||||
|
SignalWire wire = new SignalWire(this, _centerX, _centerY);
|
||||||
|
wire.attachTo(inputConnector, this);
|
||||||
|
_wires.add(wire);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void draw(ScrolledCanvas canvas, boolean devMode) {
|
||||||
|
// TODO: Complete this part
|
||||||
|
}
|
||||||
|
|
||||||
|
public void send(Signal signal) {
|
||||||
|
for (SignalWire wire : _wires){
|
||||||
|
wire.send(signal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object query(Object lastValue) {
|
||||||
|
return _part.query(lastValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getX() {
|
||||||
|
return _centerX;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getY() {
|
||||||
|
return _centerY;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getRadius() {
|
||||||
|
return _radius;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<SignalWire> getWires() {
|
||||||
|
return this._wires;
|
||||||
|
}
|
||||||
|
}
|
@ -72,7 +72,7 @@ public class StringRoundInputConnector implements StringInputConnector {
|
|||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void getAttachment(Wire<StringSignal, StringInputConnector> wire) {
|
public void addAttachment(Wire<StringSignal, StringInputConnector> wire) {
|
||||||
_attachments.add(wire);
|
_attachments.add(wire);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,7 +92,7 @@ public class StringRoundInputConnector implements StringInputConnector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void unlinkWire(Wire wire) {
|
public void wireUnlinked(Wire wire) {
|
||||||
_attachments.remove(wire);
|
_attachments.remove(wire);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,11 +7,12 @@ import com.codigoparallevar.minicards.types.Drawable;
|
|||||||
import com.codigoparallevar.minicards.types.Moveable;
|
import com.codigoparallevar.minicards.types.Moveable;
|
||||||
import com.codigoparallevar.minicards.types.Part;
|
import com.codigoparallevar.minicards.types.Part;
|
||||||
import com.codigoparallevar.minicards.types.PartGrid;
|
import com.codigoparallevar.minicards.types.PartGrid;
|
||||||
import com.codigoparallevar.minicards.types.Tuple2;
|
|
||||||
import com.codigoparallevar.minicards.types.connectors.Wiring.StringWire;
|
import com.codigoparallevar.minicards.types.connectors.Wiring.StringWire;
|
||||||
|
import com.codigoparallevar.minicards.types.connectors.Wiring.Wire;
|
||||||
import com.codigoparallevar.minicards.types.connectors.input.InputConnector;
|
import com.codigoparallevar.minicards.types.connectors.input.InputConnector;
|
||||||
import com.codigoparallevar.minicards.types.connectors.input.StringInputConnector;
|
import com.codigoparallevar.minicards.types.connectors.input.StringInputConnector;
|
||||||
import com.codigoparallevar.minicards.types.connectors.output.StringOutputConnector;
|
import com.codigoparallevar.minicards.types.connectors.output.StringOutputConnector;
|
||||||
|
import com.codigoparallevar.minicards.types.functional.Tuple2;
|
||||||
import com.codigoparallevar.minicards.types.wireData.StringSignal;
|
import com.codigoparallevar.minicards.types.wireData.StringSignal;
|
||||||
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
@ -83,7 +84,7 @@ public class StringRoundOutputConnector implements Drawable, StringOutputConnect
|
|||||||
}
|
}
|
||||||
_connections.add(resultPoint);
|
_connections.add(resultPoint);
|
||||||
|
|
||||||
wire.attachTo(resultPoint);
|
wire.attachTo(resultPoint, this);
|
||||||
_wires.add(wire);
|
_wires.add(wire);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -94,8 +95,9 @@ public class StringRoundOutputConnector implements Drawable, StringOutputConnect
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void unlinkWire(StringWire wire) {
|
public void wireUnlinked(StringWire wire) {
|
||||||
_wires.remove(wire);
|
_wires.remove(wire);
|
||||||
|
_connections.remove(wire.getAttachedTo());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -141,7 +143,7 @@ public class StringRoundOutputConnector implements Drawable, StringOutputConnect
|
|||||||
_connections.add(inputConnector);
|
_connections.add(inputConnector);
|
||||||
|
|
||||||
StringWire wire = new StringWire(this, _centerX, _centerY);
|
StringWire wire = new StringWire(this, _centerX, _centerY);
|
||||||
wire.attachTo(inputConnector);
|
wire.attachTo(inputConnector, this);
|
||||||
_wires.add(wire);
|
_wires.add(wire);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -155,4 +157,14 @@ public class StringRoundOutputConnector implements Drawable, StringOutputConnect
|
|||||||
wire.send(signal);
|
wire.send(signal);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object query(Object lastValue) {
|
||||||
|
return _part.query(lastValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<StringWire> getWires() {
|
||||||
|
return _wires;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,12 +7,13 @@ import android.util.Log;
|
|||||||
|
|
||||||
import com.codigoparallevar.minicards.PartInstantiator;
|
import com.codigoparallevar.minicards.PartInstantiator;
|
||||||
import com.codigoparallevar.minicards.ScrolledCanvas;
|
import com.codigoparallevar.minicards.ScrolledCanvas;
|
||||||
import com.codigoparallevar.minicards.parts.connectors.RoundOutputConnector;
|
import com.codigoparallevar.minicards.parts.connectors.SignalRoundOutputConnector;
|
||||||
|
import com.codigoparallevar.minicards.parts.style.CardTheme;
|
||||||
import com.codigoparallevar.minicards.types.Moveable;
|
import com.codigoparallevar.minicards.types.Moveable;
|
||||||
import com.codigoparallevar.minicards.types.Part;
|
import com.codigoparallevar.minicards.types.Part;
|
||||||
import com.codigoparallevar.minicards.types.PartConnection;
|
import com.codigoparallevar.minicards.types.PartConnection;
|
||||||
import com.codigoparallevar.minicards.types.PartGrid;
|
import com.codigoparallevar.minicards.types.PartGrid;
|
||||||
import com.codigoparallevar.minicards.types.Tuple2;
|
import com.codigoparallevar.minicards.types.functional.Tuple2;
|
||||||
import com.codigoparallevar.minicards.types.connectors.input.InputConnector;
|
import com.codigoparallevar.minicards.types.connectors.input.InputConnector;
|
||||||
import com.codigoparallevar.minicards.types.connectors.output.OutputConnector;
|
import com.codigoparallevar.minicards.types.connectors.output.OutputConnector;
|
||||||
import com.codigoparallevar.minicards.types.wireData.Signal;
|
import com.codigoparallevar.minicards.types.wireData.Signal;
|
||||||
@ -39,7 +40,7 @@ public class Ticker implements Part {
|
|||||||
private int _right;
|
private int _right;
|
||||||
private int _bottom;
|
private int _bottom;
|
||||||
private List<OutputConnector> _outputConnectors;
|
private List<OutputConnector> _outputConnectors;
|
||||||
private final RoundOutputConnector _signalOutputConnector;
|
private final SignalRoundOutputConnector _signalOutputConnector;
|
||||||
private final long SLEEP_TIME = 1000;
|
private final long SLEEP_TIME = 1000;
|
||||||
private Thread _thread = null;
|
private Thread _thread = null;
|
||||||
|
|
||||||
@ -52,7 +53,7 @@ public class Ticker implements Part {
|
|||||||
_bottom = bottom;
|
_bottom = bottom;
|
||||||
|
|
||||||
// Create connectors
|
// Create connectors
|
||||||
_signalOutputConnector = new RoundOutputConnector(
|
_signalOutputConnector = new SignalRoundOutputConnector(
|
||||||
this,
|
this,
|
||||||
_partGrid,
|
_partGrid,
|
||||||
getOutputConnectorCenterX(), getOutputConnectorCenterY(),
|
getOutputConnectorCenterX(), getOutputConnectorCenterY(),
|
||||||
@ -124,7 +125,7 @@ public class Ticker implements Part {
|
|||||||
paint);
|
paint);
|
||||||
|
|
||||||
|
|
||||||
// Craw a little clock
|
// Draw a little clock
|
||||||
Paint clockPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
Paint clockPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||||
clockPaint.setStyle(Paint.Style.STROKE);
|
clockPaint.setStyle(Paint.Style.STROKE);
|
||||||
clockPaint.setColor(Color.YELLOW);
|
clockPaint.setColor(Color.YELLOW);
|
||||||
@ -144,12 +145,21 @@ public class Ticker implements Part {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void drawConnector(ScrolledCanvas canvas) {
|
private void drawConnector(ScrolledCanvas canvas) {
|
||||||
Paint connectorPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
Paint outerConnectorPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||||
connectorPaint.setColor(Color.RED);
|
outerConnectorPaint.setColor(CardTheme.PULSE_CONNECTOR_COLOR_OUTER);
|
||||||
|
|
||||||
canvas.drawCircle(_right, getOutputConnectorCenterY(),
|
canvas.drawCircle(getOutputConnectorCenterX(),
|
||||||
|
getOutputConnectorCenterY(),
|
||||||
getOutputConnectRadius(),
|
getOutputConnectRadius(),
|
||||||
connectorPaint);
|
outerConnectorPaint);
|
||||||
|
|
||||||
|
Paint innerConnectorPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||||
|
innerConnectorPaint.setColor(CardTheme.PULSE_CONNECTOR_COLOR_INNER);
|
||||||
|
|
||||||
|
canvas.drawCircle(getOutputConnectorCenterX(),
|
||||||
|
getOutputConnectorCenterY(),
|
||||||
|
getOutputConnectRadius() / 2,
|
||||||
|
innerConnectorPaint);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void drawWires(ScrolledCanvas canvas, boolean devMode) {
|
private void drawWires(ScrolledCanvas canvas, boolean devMode) {
|
||||||
@ -261,9 +271,7 @@ public class Ticker implements Part {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void resume() {
|
public void resume() {
|
||||||
_thread = new Thread(new Runnable() {
|
_thread = new Thread(() -> {
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
while (Ticker.this._thread == Thread.currentThread()) {
|
while (Ticker.this._thread == Thread.currentThread()) {
|
||||||
Ticker.this._signalOutputConnector.send(new Signal());
|
Ticker.this._signalOutputConnector.send(new Signal());
|
||||||
_partGrid.update();
|
_partGrid.update();
|
||||||
@ -276,7 +284,6 @@ public class Ticker implements Part {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
_thread.start();
|
_thread.start();
|
||||||
@ -287,13 +294,18 @@ public class Ticker implements Part {
|
|||||||
_thread = null;
|
_thread = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object query(Object lastValue) {
|
||||||
|
return null; // No relevant value (maybe time?)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private int getOutputConnectorCenterX() {
|
private int getOutputConnectorCenterX() {
|
||||||
return _right;
|
return (_left + _right) / 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int getOutputConnectorCenterY() {
|
private int getOutputConnectorCenterY() {
|
||||||
return (_top + _bottom) / 2;
|
return _bottom;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int getOutputConnectRadius() {
|
private int getOutputConnectRadius() {
|
||||||
|
@ -9,11 +9,12 @@ import com.codigoparallevar.minicards.PartInstantiator;
|
|||||||
import com.codigoparallevar.minicards.ScrolledCanvas;
|
import com.codigoparallevar.minicards.ScrolledCanvas;
|
||||||
import com.codigoparallevar.minicards.parts.connectors.BooleanRoundOutputConnector;
|
import com.codigoparallevar.minicards.parts.connectors.BooleanRoundOutputConnector;
|
||||||
import com.codigoparallevar.minicards.parts.connectors.RoundInputConnector;
|
import com.codigoparallevar.minicards.parts.connectors.RoundInputConnector;
|
||||||
|
import com.codigoparallevar.minicards.parts.style.CardTheme;
|
||||||
import com.codigoparallevar.minicards.types.Moveable;
|
import com.codigoparallevar.minicards.types.Moveable;
|
||||||
import com.codigoparallevar.minicards.types.Part;
|
import com.codigoparallevar.minicards.types.Part;
|
||||||
import com.codigoparallevar.minicards.types.PartConnection;
|
import com.codigoparallevar.minicards.types.PartConnection;
|
||||||
import com.codigoparallevar.minicards.types.PartGrid;
|
import com.codigoparallevar.minicards.types.PartGrid;
|
||||||
import com.codigoparallevar.minicards.types.Tuple2;
|
import com.codigoparallevar.minicards.types.functional.Tuple2;
|
||||||
import com.codigoparallevar.minicards.types.connectors.input.InputConnector;
|
import com.codigoparallevar.minicards.types.connectors.input.InputConnector;
|
||||||
import com.codigoparallevar.minicards.types.connectors.input.SignalInputConnector;
|
import com.codigoparallevar.minicards.types.connectors.input.SignalInputConnector;
|
||||||
import com.codigoparallevar.minicards.types.connectors.output.OutputConnector;
|
import com.codigoparallevar.minicards.types.connectors.output.OutputConnector;
|
||||||
@ -128,20 +129,41 @@ public class Toggle implements Part {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void drawConnector(ScrolledCanvas canvas) {
|
private void drawConnector(ScrolledCanvas canvas) {
|
||||||
Paint inputConnectorPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
// Input
|
||||||
inputConnectorPaint.setColor(Color.YELLOW);
|
Paint outerInputConnectorPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||||
|
outerInputConnectorPaint.setColor(CardTheme.PULSE_CONNECTOR_COLOR_OUTER);
|
||||||
|
|
||||||
canvas.drawCircle(
|
canvas.drawCircle(
|
||||||
getInputConnectorCenterX(), getInputConnectorCenterY(),
|
getInputConnectorCenterX(),
|
||||||
|
getInputConnectorCenterY(),
|
||||||
getInputConnectRadius(),
|
getInputConnectRadius(),
|
||||||
inputConnectorPaint);
|
outerInputConnectorPaint);
|
||||||
|
|
||||||
Paint outputConnectorPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
Paint innerInputConnectorPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||||
outputConnectorPaint.setColor(Color.RED);
|
innerInputConnectorPaint.setColor(CardTheme.PULSE_CONNECTOR_COLOR_INNER);
|
||||||
|
|
||||||
canvas.drawCircle(_right, getOutputConnectorCenterY(),
|
canvas.drawCircle(
|
||||||
|
getInputConnectorCenterX(),
|
||||||
|
getInputConnectorCenterY(),
|
||||||
|
getInputConnectRadius() / 2,
|
||||||
|
innerInputConnectorPaint);
|
||||||
|
|
||||||
|
// Output
|
||||||
|
Paint outerOutputConnectorPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||||
|
outerOutputConnectorPaint.setColor(CardTheme.BOOLEAN_CONNECTOR_COLOR_OUTER);
|
||||||
|
|
||||||
|
canvas.drawCircle(getOutputConnectorCenterX(),
|
||||||
|
getOutputConnectorCenterY(),
|
||||||
getOutputConnectRadius(),
|
getOutputConnectRadius(),
|
||||||
outputConnectorPaint);
|
outerOutputConnectorPaint);
|
||||||
|
|
||||||
|
Paint innerOutputConnectorPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||||
|
innerOutputConnectorPaint.setColor(CardTheme.BOOLEAN_CONNECTOR_COLOR_INNER);
|
||||||
|
|
||||||
|
canvas.drawCircle(getOutputConnectorCenterX(),
|
||||||
|
getOutputConnectorCenterY(),
|
||||||
|
getOutputConnectRadius() / 2,
|
||||||
|
innerOutputConnectorPaint);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void drawWires(ScrolledCanvas canvas, boolean devMode) {
|
private void drawWires(ScrolledCanvas canvas, boolean devMode) {
|
||||||
@ -266,6 +288,11 @@ public class Toggle implements Part {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object query(Object lastValue) {
|
||||||
|
return _activated;
|
||||||
|
}
|
||||||
|
|
||||||
public static Tuple2<Part, List<PartConnection>> deserialize(PartGrid partGrid, JSONObject data) throws JSONException {
|
public static Tuple2<Part, List<PartConnection>> deserialize(PartGrid partGrid, JSONObject data) throws JSONException {
|
||||||
String id = data.getString("id");
|
String id = data.getString("id");
|
||||||
int left = data.getInt("left");
|
int left = data.getInt("left");
|
||||||
@ -306,7 +333,7 @@ public class Toggle implements Part {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public int getInputConnectorCenterX() {
|
public int getInputConnectorCenterX() {
|
||||||
return get_left();
|
return (get_left() + get_right()) / 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int getInputConnectRadius() {
|
private int getInputConnectRadius() {
|
||||||
@ -314,15 +341,15 @@ public class Toggle implements Part {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public int getInputConnectorCenterY() {
|
public int getInputConnectorCenterY() {
|
||||||
return (get_top() + get_bottom()) / 2;
|
return get_top();
|
||||||
}
|
}
|
||||||
|
|
||||||
private int getOutputConnectorCenterX() {
|
private int getOutputConnectorCenterX() {
|
||||||
return _right;
|
return (get_left() + get_right()) / 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int getOutputConnectorCenterY() {
|
private int getOutputConnectorCenterY() {
|
||||||
return (_top + _bottom) / 2;
|
return get_bottom();
|
||||||
}
|
}
|
||||||
|
|
||||||
private int getOutputConnectRadius() {
|
private int getOutputConnectRadius() {
|
||||||
|
@ -8,10 +8,11 @@ import android.util.Log;
|
|||||||
import com.codigoparallevar.minicards.PartInstantiator;
|
import com.codigoparallevar.minicards.PartInstantiator;
|
||||||
import com.codigoparallevar.minicards.ScrolledCanvas;
|
import com.codigoparallevar.minicards.ScrolledCanvas;
|
||||||
import com.codigoparallevar.minicards.parts.connectors.BooleanRoundInputConnector;
|
import com.codigoparallevar.minicards.parts.connectors.BooleanRoundInputConnector;
|
||||||
|
import com.codigoparallevar.minicards.parts.style.CardTheme;
|
||||||
import com.codigoparallevar.minicards.types.Moveable;
|
import com.codigoparallevar.minicards.types.Moveable;
|
||||||
import com.codigoparallevar.minicards.types.Part;
|
import com.codigoparallevar.minicards.types.Part;
|
||||||
import com.codigoparallevar.minicards.types.PartGrid;
|
import com.codigoparallevar.minicards.types.PartGrid;
|
||||||
import com.codigoparallevar.minicards.types.Tuple2;
|
import com.codigoparallevar.minicards.types.functional.Tuple2;
|
||||||
import com.codigoparallevar.minicards.types.connectors.input.BooleanInputConnector;
|
import com.codigoparallevar.minicards.types.connectors.input.BooleanInputConnector;
|
||||||
import com.codigoparallevar.minicards.types.connectors.input.InputConnector;
|
import com.codigoparallevar.minicards.types.connectors.input.InputConnector;
|
||||||
import com.codigoparallevar.minicards.types.connectors.output.OutputConnector;
|
import com.codigoparallevar.minicards.types.connectors.output.OutputConnector;
|
||||||
@ -105,13 +106,23 @@ public class ColorBox implements Part {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void drawConnector(ScrolledCanvas canvas) {
|
private void drawConnector(ScrolledCanvas canvas) {
|
||||||
Paint connectorPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
Paint outerInputConnectorPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||||
connectorPaint.setColor(Color.YELLOW);
|
outerInputConnectorPaint.setColor(CardTheme.BOOLEAN_CONNECTOR_COLOR_OUTER);
|
||||||
|
|
||||||
canvas.drawCircle(
|
canvas.drawCircle(
|
||||||
getInputConnectorCenterX(), getInputConnectorCenterY(),
|
getInputConnectorCenterX(),
|
||||||
|
getInputConnectorCenterY(),
|
||||||
getInputConnectRadius(),
|
getInputConnectRadius(),
|
||||||
connectorPaint);
|
outerInputConnectorPaint);
|
||||||
|
|
||||||
|
Paint innerInputConnectorPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||||
|
innerInputConnectorPaint.setColor(CardTheme.BOOLEAN_CONNECTOR_COLOR_INNER);
|
||||||
|
|
||||||
|
canvas.drawCircle(
|
||||||
|
getInputConnectorCenterX(),
|
||||||
|
getInputConnectorCenterY(),
|
||||||
|
getInputConnectRadius() / 2,
|
||||||
|
innerInputConnectorPaint);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -209,6 +220,11 @@ public class ColorBox implements Part {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object query(Object lastValue) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
public static Part deserialize(PartGrid partGrid, JSONObject data) throws JSONException {
|
public static Part deserialize(PartGrid partGrid, JSONObject data) throws JSONException {
|
||||||
String id = data.getString("id");
|
String id = data.getString("id");
|
||||||
int left = data.getInt("left");
|
int left = data.getInt("left");
|
||||||
@ -238,7 +254,7 @@ public class ColorBox implements Part {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public int getInputConnectorCenterX() {
|
public int getInputConnectorCenterX() {
|
||||||
return get_left();
|
return (get_left() + get_right()) / 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int getInputConnectRadius() {
|
private int getInputConnectRadius() {
|
||||||
@ -246,7 +262,7 @@ public class ColorBox implements Part {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public int getInputConnectorCenterY() {
|
public int getInputConnectorCenterY() {
|
||||||
return (get_top() + get_bottom()) / 2;
|
return get_top();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static PartInstantiator getInstantiator() {
|
public static PartInstantiator getInstantiator() {
|
||||||
|
@ -1,184 +0,0 @@
|
|||||||
package com.codigoparallevar.minicards.parts.samples;
|
|
||||||
|
|
||||||
import android.graphics.Color;
|
|
||||||
import android.graphics.Paint;
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import com.codigoparallevar.minicards.ScrolledCanvas;
|
|
||||||
import com.codigoparallevar.minicards.types.Moveable;
|
|
||||||
import com.codigoparallevar.minicards.types.Part;
|
|
||||||
import com.codigoparallevar.minicards.types.PartGrid;
|
|
||||||
import com.codigoparallevar.minicards.types.connectors.input.InputConnector;
|
|
||||||
import com.codigoparallevar.minicards.types.connectors.output.OutputConnector;
|
|
||||||
import com.codigoparallevar.minicards.types.wireData.WireDataType;
|
|
||||||
|
|
||||||
import org.json.JSONException;
|
|
||||||
import org.json.JSONObject;
|
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
public class Placeholder implements Part {
|
|
||||||
private final String _id;
|
|
||||||
private final PartGrid _partGrid;
|
|
||||||
private int _left;
|
|
||||||
private int _top;
|
|
||||||
private int _right;
|
|
||||||
private int _bottom;
|
|
||||||
|
|
||||||
public Placeholder(String id, PartGrid partGrid, int left, int top, int right, int bottom) {
|
|
||||||
_id = id;
|
|
||||||
_partGrid = partGrid;
|
|
||||||
_left = left;
|
|
||||||
_top = top;
|
|
||||||
_right = right;
|
|
||||||
_bottom = bottom;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Placeholder(PartGrid partGrid, int left, int top, int right, int bottom) {
|
|
||||||
this(UUID.randomUUID().toString(), partGrid, left, top, right, bottom);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int get_left() {
|
|
||||||
return _left;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int get_right() {
|
|
||||||
return _right;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int get_top() {
|
|
||||||
return _top;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int get_bottom() {
|
|
||||||
return _bottom;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void draw(ScrolledCanvas canvas, boolean devMode) {
|
|
||||||
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
|
||||||
paint.setColor(Color.WHITE);
|
|
||||||
|
|
||||||
// Top
|
|
||||||
canvas.drawLine(_left, _top, _right, _top, paint);
|
|
||||||
// Bottom
|
|
||||||
canvas.drawLine(_left, _bottom, _right, _bottom, paint);
|
|
||||||
// Left
|
|
||||||
canvas.drawLine(_left, _top, _left, _bottom, paint);
|
|
||||||
// Right
|
|
||||||
canvas.drawLine(_right, _top, _right, _bottom, paint);
|
|
||||||
// Cross, top-left, bottom-right
|
|
||||||
canvas.drawLine(_left, _top, _right, _bottom, paint);
|
|
||||||
// Cross, top-right, bottom-left
|
|
||||||
canvas.drawLine(_right, _top, _left, _bottom, paint);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void moveEnd(int x, int y) {
|
|
||||||
final int width = _right - _left;
|
|
||||||
final int height = _bottom - _top;
|
|
||||||
|
|
||||||
_left = x - width / 2;
|
|
||||||
_right = _left + width;
|
|
||||||
|
|
||||||
_top = y - height / 2;
|
|
||||||
_bottom = _top + height;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void drop(int x, int y) {
|
|
||||||
moveEnd(x, y);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void touched() {
|
|
||||||
Log.d("Placeholder", "Placeholder touched");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<InputConnector> getInputConnectors() {
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<OutputConnector> getOutputConnectors() {
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public JSONObject serialize() throws JSONException {
|
|
||||||
JSONObject serialized = new JSONObject();
|
|
||||||
|
|
||||||
serialized.put("id", _id);
|
|
||||||
serialized.put("left", _left);
|
|
||||||
serialized.put("top", _top);
|
|
||||||
serialized.put("right", _right);
|
|
||||||
serialized.put("bottom", _bottom);
|
|
||||||
|
|
||||||
return serialized;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void send(InputConnector roundInputConnector, WireDataType signal) {
|
|
||||||
// @TODO: REMOVE THE NEED FOR THIS
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String get_id() {
|
|
||||||
return _id;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public InputConnector getConnectorWithId(String inputConnectorId) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getConnectorId(InputConnector inputConnector) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void resume() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void pause() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Part deserialize(PartGrid partGrid, JSONObject data) throws JSONException {
|
|
||||||
String id = data.getString("id");
|
|
||||||
int left = data.getInt("left");
|
|
||||||
int top = data.getInt("top");
|
|
||||||
int right = data.getInt("right");
|
|
||||||
int bottom = data.getInt("bottom");
|
|
||||||
|
|
||||||
return new Placeholder(id, partGrid, left, top, right, bottom);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean containsPoint(int x, int y) {
|
|
||||||
return (x >= get_left()) && (x <= get_right())
|
|
||||||
&& (y >= get_top()) && (y <= get_bottom());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Moveable getMoveable() {
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void unlink() {
|
|
||||||
for (InputConnector input : getInputConnectors()) {
|
|
||||||
input.unlink();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -3,21 +3,22 @@ package com.codigoparallevar.minicards.parts.strings;
|
|||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
import android.graphics.Paint;
|
import android.graphics.Paint;
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import com.codigoparallevar.minicards.PartInstantiator;
|
import com.codigoparallevar.minicards.PartInstantiator;
|
||||||
import com.codigoparallevar.minicards.ScrolledCanvas;
|
import com.codigoparallevar.minicards.ScrolledCanvas;
|
||||||
import com.codigoparallevar.minicards.parts.connectors.AnyRoundInputConnector;
|
import com.codigoparallevar.minicards.parts.connectors.ConnectorTypeInfo;
|
||||||
|
import com.codigoparallevar.minicards.parts.connectors.ProgramakerCustomBlockInputConnector;
|
||||||
import com.codigoparallevar.minicards.parts.connectors.StringRoundOutputConnector;
|
import com.codigoparallevar.minicards.parts.connectors.StringRoundOutputConnector;
|
||||||
|
import com.codigoparallevar.minicards.parts.style.CardTheme;
|
||||||
import com.codigoparallevar.minicards.types.Moveable;
|
import com.codigoparallevar.minicards.types.Moveable;
|
||||||
import com.codigoparallevar.minicards.types.Part;
|
import com.codigoparallevar.minicards.types.Part;
|
||||||
import com.codigoparallevar.minicards.types.PartConnection;
|
import com.codigoparallevar.minicards.types.PartConnection;
|
||||||
import com.codigoparallevar.minicards.types.PartGrid;
|
import com.codigoparallevar.minicards.types.PartGrid;
|
||||||
import com.codigoparallevar.minicards.types.Tuple2;
|
|
||||||
import com.codigoparallevar.minicards.types.connectors.input.AnyInputConnector;
|
import com.codigoparallevar.minicards.types.connectors.input.AnyInputConnector;
|
||||||
import com.codigoparallevar.minicards.types.connectors.input.InputConnector;
|
import com.codigoparallevar.minicards.types.connectors.input.InputConnector;
|
||||||
import com.codigoparallevar.minicards.types.connectors.output.OutputConnector;
|
import com.codigoparallevar.minicards.types.connectors.output.OutputConnector;
|
||||||
|
import com.codigoparallevar.minicards.types.functional.Tuple2;
|
||||||
import com.codigoparallevar.minicards.types.wireData.StringSignal;
|
import com.codigoparallevar.minicards.types.wireData.StringSignal;
|
||||||
import com.codigoparallevar.minicards.types.wireData.WireDataType;
|
import com.codigoparallevar.minicards.types.wireData.WireDataType;
|
||||||
import com.codigoparallevar.minicards.utils.Serializations;
|
import com.codigoparallevar.minicards.utils.Serializations;
|
||||||
@ -44,8 +45,6 @@ public class ConvertToString implements Part {
|
|||||||
private int _bottom;
|
private int _bottom;
|
||||||
private List<InputConnector> inputConnectors;
|
private List<InputConnector> inputConnectors;
|
||||||
private AnyInputConnector _toggleInputConnector;
|
private AnyInputConnector _toggleInputConnector;
|
||||||
|
|
||||||
@NonNull
|
|
||||||
private String _lastValue;
|
private String _lastValue;
|
||||||
|
|
||||||
private ConvertToString(String id, PartGrid partGrid, int left, int top, int right, int bottom) {
|
private ConvertToString(String id, PartGrid partGrid, int left, int top, int right, int bottom) {
|
||||||
@ -55,14 +54,15 @@ public class ConvertToString implements Part {
|
|||||||
_top = top;
|
_top = top;
|
||||||
_right = right;
|
_right = right;
|
||||||
_bottom = bottom;
|
_bottom = bottom;
|
||||||
_lastValue = "-";
|
_lastValue = null;
|
||||||
|
|
||||||
// Input connector
|
// Input connector
|
||||||
_toggleInputConnector = new AnyRoundInputConnector(
|
ConnectorTypeInfo typeInfo = new ConnectorTypeInfo(ConnectorTypeInfo.Type.ANY);
|
||||||
this,
|
_toggleInputConnector = new ProgramakerCustomBlockInputConnector(
|
||||||
|
this, _partGrid,
|
||||||
getInputConnectorCenterX(),
|
getInputConnectorCenterX(),
|
||||||
getInputConnectorCenterY(),
|
getInputConnectorCenterY(),
|
||||||
getInputConnectRadius());
|
getInputConnectRadius(), typeInfo);
|
||||||
inputConnectors = new LinkedList<>();
|
inputConnectors = new LinkedList<>();
|
||||||
inputConnectors.add(_toggleInputConnector);
|
inputConnectors.add(_toggleInputConnector);
|
||||||
|
|
||||||
@ -110,7 +110,7 @@ public class ConvertToString implements Part {
|
|||||||
drawWires(canvas, devMode);
|
drawWires(canvas, devMode);
|
||||||
|
|
||||||
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||||
paint.setColor(Color.WHITE);
|
paint.setColor(Color.BLACK);
|
||||||
canvas.drawRect(
|
canvas.drawRect(
|
||||||
new Rect(_left, _top,
|
new Rect(_left, _top,
|
||||||
_right, _bottom),
|
_right, _bottom),
|
||||||
@ -119,28 +119,55 @@ public class ConvertToString implements Part {
|
|||||||
Paint textPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
Paint textPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||||
textPaint.setColor(Color.GREEN);
|
textPaint.setColor(Color.GREEN);
|
||||||
textPaint.setTextSize(100);
|
textPaint.setTextSize(100);
|
||||||
canvas.drawText(_lastValue,
|
|
||||||
_left,
|
String value = "-";
|
||||||
_top - 5,
|
if (_lastValue != null) {
|
||||||
|
value = _lastValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas.drawText(value,
|
||||||
|
_left + 10,
|
||||||
|
_bottom - 10,
|
||||||
textPaint);
|
textPaint);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void drawConnector(ScrolledCanvas canvas) {
|
private void drawConnector(ScrolledCanvas canvas) {
|
||||||
Paint inputConnectorPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
// Input
|
||||||
inputConnectorPaint.setColor(Color.YELLOW);
|
Paint inputOuterConnectorPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||||
|
inputOuterConnectorPaint.setColor(CardTheme.ANY_CONNECTOR_COLOR_OUTER);
|
||||||
|
|
||||||
canvas.drawCircle(
|
canvas.drawCircle(
|
||||||
getInputConnectorCenterX(), getInputConnectorCenterY(),
|
getInputConnectorCenterX(),
|
||||||
|
getInputConnectorCenterY(),
|
||||||
getInputConnectRadius(),
|
getInputConnectRadius(),
|
||||||
inputConnectorPaint);
|
inputOuterConnectorPaint);
|
||||||
|
|
||||||
Paint outputConnectorPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
Paint inputInnerConnectorPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||||
outputConnectorPaint.setColor(Color.RED);
|
inputInnerConnectorPaint.setColor(CardTheme.ANY_CONNECTOR_COLOR_INNER);
|
||||||
|
|
||||||
canvas.drawCircle(_right, getOutputConnectorCenterY(),
|
canvas.drawCircle(
|
||||||
|
getInputConnectorCenterX(),
|
||||||
|
getInputConnectorCenterY(),
|
||||||
|
getInputConnectRadius() / 2,
|
||||||
|
inputInnerConnectorPaint);
|
||||||
|
|
||||||
|
// Output
|
||||||
|
Paint outputOuterConnectorPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||||
|
outputOuterConnectorPaint.setColor(CardTheme.STRING_CONNECTOR_COLOR_OUTER);
|
||||||
|
|
||||||
|
canvas.drawCircle(getOutputConnectorCenterX(),
|
||||||
|
getOutputConnectorCenterY(),
|
||||||
getOutputConnectRadius(),
|
getOutputConnectRadius(),
|
||||||
outputConnectorPaint);
|
outputOuterConnectorPaint);
|
||||||
|
|
||||||
|
Paint outputInnerConnectorPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||||
|
outputInnerConnectorPaint.setColor(CardTheme.STRING_CONNECTOR_COLOR_INNER);
|
||||||
|
|
||||||
|
canvas.drawCircle(getOutputConnectorCenterX(),
|
||||||
|
getOutputConnectorCenterY(),
|
||||||
|
getOutputConnectRadius() / 2,
|
||||||
|
outputInnerConnectorPaint);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void drawWires(ScrolledCanvas canvas, boolean devMode) {
|
private void drawWires(ScrolledCanvas canvas, boolean devMode) {
|
||||||
@ -228,12 +255,11 @@ public class ConvertToString implements Part {
|
|||||||
public void send(InputConnector roundInputConnector, WireDataType signal) {
|
public void send(InputConnector roundInputConnector, WireDataType signal) {
|
||||||
String encoded = "null";
|
String encoded = "null";
|
||||||
Object value = signal.get();
|
Object value = signal.get();
|
||||||
|
|
||||||
if (value != null){
|
if (value != null){
|
||||||
encoded = value.toString();
|
encoded = value.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_lastValue.equals(encoded)) {
|
if ((_lastValue == null) || !_lastValue.equals(encoded)) {
|
||||||
_lastValue = encoded;
|
_lastValue = encoded;
|
||||||
|
|
||||||
_stringOutputConnector.send(new StringSignal(encoded));
|
_stringOutputConnector.send(new StringSignal(encoded));
|
||||||
@ -275,6 +301,11 @@ public class ConvertToString implements Part {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object query(Object lastValue) {
|
||||||
|
return _lastValue;
|
||||||
|
}
|
||||||
|
|
||||||
public static Tuple2<Part, List<PartConnection>> deserialize(PartGrid partGrid, JSONObject data) throws JSONException {
|
public static Tuple2<Part, List<PartConnection>> deserialize(PartGrid partGrid, JSONObject data) throws JSONException {
|
||||||
String id = data.getString("id");
|
String id = data.getString("id");
|
||||||
int left = data.getInt("left");
|
int left = data.getInt("left");
|
||||||
@ -315,7 +346,7 @@ public class ConvertToString implements Part {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public int getInputConnectorCenterX() {
|
public int getInputConnectorCenterX() {
|
||||||
return get_left();
|
return (get_left() + get_right()) / 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int getInputConnectRadius() {
|
private int getInputConnectRadius() {
|
||||||
@ -323,15 +354,15 @@ public class ConvertToString implements Part {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public int getInputConnectorCenterY() {
|
public int getInputConnectorCenterY() {
|
||||||
return (get_top() + get_bottom()) / 2;
|
return get_top();
|
||||||
}
|
}
|
||||||
|
|
||||||
private int getOutputConnectorCenterX() {
|
private int getOutputConnectorCenterX() {
|
||||||
return _right;
|
return (get_left() + get_right()) / 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int getOutputConnectorCenterY() {
|
private int getOutputConnectorCenterY() {
|
||||||
return (_top + _bottom) / 2;
|
return get_bottom();
|
||||||
}
|
}
|
||||||
|
|
||||||
private int getOutputConnectRadius() {
|
private int getOutputConnectRadius() {
|
||||||
|
@ -0,0 +1,34 @@
|
|||||||
|
package com.codigoparallevar.minicards.parts.style;
|
||||||
|
|
||||||
|
import android.graphics.Color;
|
||||||
|
|
||||||
|
public class CardTheme {
|
||||||
|
public static final int DEFAULT_COLOR_INNER = Color.parseColor("#000000");
|
||||||
|
|
||||||
|
public static final int PULSE_CONNECTOR_COLOR_OUTER = Color.parseColor("#f0c000");
|
||||||
|
public static final int PULSE_CONNECTOR_COLOR_INNER = DEFAULT_COLOR_INNER;
|
||||||
|
|
||||||
|
public static final int BOOLEAN_CONNECTOR_COLOR_OUTER = Color.parseColor("#dd4444");
|
||||||
|
public static final int BOOLEAN_CONNECTOR_COLOR_INNER = DEFAULT_COLOR_INNER;
|
||||||
|
|
||||||
|
public static final int STRING_CONNECTOR_COLOR_OUTER = Color.parseColor("#44dd44");
|
||||||
|
public static final int STRING_CONNECTOR_COLOR_INNER = DEFAULT_COLOR_INNER;
|
||||||
|
|
||||||
|
public static final int ANY_CONNECTOR_COLOR_OUTER = Color.parseColor("#dd44dd");
|
||||||
|
public static final int ANY_CONNECTOR_COLOR_INNER = DEFAULT_COLOR_INNER;
|
||||||
|
|
||||||
|
public static final int ENUM_CONNECTOR_COLOR_OUTER = Color.parseColor("#888888");
|
||||||
|
public static final int ENUM_CONNECTOR_COLOR_INNER = DEFAULT_COLOR_INNER;
|
||||||
|
|
||||||
|
public static final int FLOAT_CONNECTOR_COLOR_OUTER = Color.parseColor("#44dddd");
|
||||||
|
public static final int FLOAT_CONNECTOR_COLOR_INNER = DEFAULT_COLOR_INNER;
|
||||||
|
|
||||||
|
public static final int INTEGER_CONNECTOR_COLOR_OUTER = Color.parseColor("#4444ff");
|
||||||
|
public static final int INTEGER_CONNECTOR_COLOR_INNER = DEFAULT_COLOR_INNER;
|
||||||
|
|
||||||
|
public static final int UNKNOWN_CONNECTOR_COLOR_OUTER = Color.parseColor("#7f7f7f");
|
||||||
|
public static final int UNKNOWN_CONNECTOR_COLOR_INNER = DEFAULT_COLOR_INNER;
|
||||||
|
|
||||||
|
public static final int IMAGE_CONNECTOR_COLOR_OUTER = Color.parseColor("#007700");
|
||||||
|
public static final int IMAGE_CONNECTOR_COLOR_INNER = DEFAULT_COLOR_INNER;
|
||||||
|
}
|
@ -0,0 +1,519 @@
|
|||||||
|
package com.codigoparallevar.minicards.parts.values;
|
||||||
|
|
||||||
|
import android.app.AlertDialog;
|
||||||
|
import android.app.Dialog;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.DialogInterface;
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.graphics.Paint;
|
||||||
|
import android.graphics.Rect;
|
||||||
|
import android.os.AsyncTask;
|
||||||
|
import android.text.InputType;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.EditText;
|
||||||
|
import android.widget.ProgressBar;
|
||||||
|
|
||||||
|
import com.codigoparallevar.minicards.R;
|
||||||
|
import com.codigoparallevar.minicards.ScrolledCanvas;
|
||||||
|
import com.codigoparallevar.minicards.parts.connectors.AnyRoundOutputConnector;
|
||||||
|
import com.codigoparallevar.minicards.parts.connectors.ConnectorTypeInfo;
|
||||||
|
import com.codigoparallevar.minicards.types.Moveable;
|
||||||
|
import com.codigoparallevar.minicards.types.Part;
|
||||||
|
import com.codigoparallevar.minicards.types.PartConnection;
|
||||||
|
import com.codigoparallevar.minicards.types.PartGrid;
|
||||||
|
import com.codigoparallevar.minicards.types.connectors.Wiring.AnyWire;
|
||||||
|
import com.codigoparallevar.minicards.types.connectors.input.InputConnector;
|
||||||
|
import com.codigoparallevar.minicards.types.connectors.output.OutputConnector;
|
||||||
|
import com.codigoparallevar.minicards.types.functional.Consumer;
|
||||||
|
import com.codigoparallevar.minicards.types.functional.Tuple2;
|
||||||
|
import com.codigoparallevar.minicards.types.functional.Tuple3;
|
||||||
|
import com.codigoparallevar.minicards.types.wireData.WireDataType;
|
||||||
|
import com.codigoparallevar.minicards.ui_helpers.GetAsync;
|
||||||
|
import com.codigoparallevar.minicards.utils.Serializations;
|
||||||
|
import com.programaker.api.ProgramakerApi;
|
||||||
|
import com.programaker.api.data.ProgramakerCustomBlockArgument;
|
||||||
|
import com.programaker.api.data.ProgramakerCustomBlockArgumentValue;
|
||||||
|
|
||||||
|
import org.json.JSONArray;
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public class StaticValuePart implements Part {
|
||||||
|
private static final int WIDTH_PADDING = 25;
|
||||||
|
private static final int HEIGHT_PADDING = 25;
|
||||||
|
private static final int MIN_HEIGHT = 100;
|
||||||
|
private static final int MIN_WIDTH = 100;
|
||||||
|
private static final String LogTag = "PM StaticValue";
|
||||||
|
private static final int IO_RADIUS = 50;
|
||||||
|
private static final int IO_PADDING = 20;
|
||||||
|
|
||||||
|
private final String _id;
|
||||||
|
private final AnyRoundOutputConnector _outputConnector;
|
||||||
|
private final ConnectorTypeInfo _typeInfo;
|
||||||
|
private final PartGrid _grid;
|
||||||
|
private int _width;
|
||||||
|
private int _height;
|
||||||
|
private int _left;
|
||||||
|
private int _top;
|
||||||
|
private String _value = null;
|
||||||
|
private String _valueId = null;
|
||||||
|
|
||||||
|
public StaticValuePart(String id, PartGrid grid, int centerx, int centery, ConnectorTypeInfo typeInfo, String value, String valueId) {
|
||||||
|
this._id = id;
|
||||||
|
this._typeInfo = typeInfo;
|
||||||
|
this._grid = grid;
|
||||||
|
this._value = value;
|
||||||
|
this._valueId = valueId;
|
||||||
|
|
||||||
|
this.updateWidthHeight();
|
||||||
|
|
||||||
|
this._left = centerx - this._width / 2;
|
||||||
|
this._top = centery - this._height / 2;
|
||||||
|
this._outputConnector = new AnyRoundOutputConnector(this, grid,
|
||||||
|
this._left + _width / 2,
|
||||||
|
this._top + _height, IO_RADIUS);
|
||||||
|
}
|
||||||
|
public StaticValuePart(String id, PartGrid grid, int centerx, int centery, ConnectorTypeInfo typeInfo, String value) {
|
||||||
|
this(id, grid, centerx, centery, typeInfo, value, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public StaticValuePart(String id, PartGrid grid, int centerx, int centery, ConnectorTypeInfo typeInfo) {
|
||||||
|
this(id, grid, centerx, centery, typeInfo, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public StaticValuePart(PartGrid grid, int centerx, int centery, ConnectorTypeInfo typeInfo) {
|
||||||
|
this(UUID.randomUUID().toString(), grid, centerx, centery, typeInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JSONObject serialize() throws JSONException {
|
||||||
|
JSONObject serialized = new JSONObject();
|
||||||
|
|
||||||
|
serialized.put("id", _id);
|
||||||
|
serialized.put("center_x", _left + _width / 2);
|
||||||
|
serialized.put("center_y", _top + _height / 2);
|
||||||
|
serialized.put("value", _value == null ? JSONObject.NULL : _value);
|
||||||
|
serialized.put("value_id", _valueId == null ? JSONObject.NULL : _valueId);
|
||||||
|
|
||||||
|
JSONObject jsonTypeInfo = _typeInfo.serialize();
|
||||||
|
serialized.put("type_info", jsonTypeInfo);
|
||||||
|
|
||||||
|
serialized.put("on_string_output_connector", serializeConnectionEndpoints());
|
||||||
|
|
||||||
|
return serialized;
|
||||||
|
}
|
||||||
|
|
||||||
|
private JSONArray serializeConnectionEndpoints() {
|
||||||
|
JSONArray serializedData = new JSONArray();
|
||||||
|
|
||||||
|
for (OutputConnector output : getOutputConnectors()) {
|
||||||
|
JSONArray elements = new JSONArray();
|
||||||
|
|
||||||
|
for (Tuple2<String, String> endpoint : (List<Tuple2<String, String>>) output.getConnectionEndpoints()) {
|
||||||
|
elements.put(PartConnection.serializeToJson(endpoint.item1, endpoint.item2));
|
||||||
|
}
|
||||||
|
|
||||||
|
serializedData.put(elements);
|
||||||
|
}
|
||||||
|
return serializedData;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Tuple2<Part, List<PartConnection>> deserialize(PartGrid grid, JSONObject data) throws JSONException {
|
||||||
|
String id = data.getString("id");
|
||||||
|
int centerx = data.getInt("center_x");
|
||||||
|
int centery = data.getInt("center_y");
|
||||||
|
String value = Serializations.getString(data, "value");
|
||||||
|
String valueId = Serializations.getString(data, "value_id");
|
||||||
|
|
||||||
|
JSONObject jsonTypeInfo = data.getJSONObject("type_info");
|
||||||
|
ConnectorTypeInfo typeInfo = ConnectorTypeInfo.deserialize(jsonTypeInfo);
|
||||||
|
StaticValuePart part = new StaticValuePart(id, grid, centerx, centery, typeInfo, value, valueId);
|
||||||
|
|
||||||
|
List<PartConnection> connections = new LinkedList<>();
|
||||||
|
|
||||||
|
JSONArray allConnectorOuts = data.optJSONArray("on_string_output_connector");
|
||||||
|
if (allConnectorOuts == null) {
|
||||||
|
allConnectorOuts = new JSONArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < allConnectorOuts.length(); i++) {
|
||||||
|
JSONArray connectorOuts = allConnectorOuts.getJSONArray(i);
|
||||||
|
OutputConnector connector = part.getOutputConnectors().get(i);
|
||||||
|
|
||||||
|
for (int j = 0; j < connectorOuts.length(); j++) {
|
||||||
|
connections.add(PartConnection.deserialize(
|
||||||
|
connector, connectorOuts.getJSONObject(j)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Tuple2<>(part, connections);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AnyWire getOutputWire() {
|
||||||
|
return this._outputConnector.getMoveable();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int get_left() {
|
||||||
|
return _left;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int get_right() {
|
||||||
|
return _left + _width;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int get_top() {
|
||||||
|
return _top;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int get_bottom() {
|
||||||
|
return _top + _height;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateWidthHeight() {
|
||||||
|
Paint p = getTextPaint();
|
||||||
|
String message = getMessage();
|
||||||
|
Rect bounds = new Rect();
|
||||||
|
p.getTextBounds(message, 0, message.length(), bounds);
|
||||||
|
|
||||||
|
this._height = Math.max(MIN_HEIGHT, bounds.height() + HEIGHT_PADDING * 2);
|
||||||
|
int newWidth = Math.max(MIN_WIDTH, bounds.width() + WIDTH_PADDING * 2);
|
||||||
|
|
||||||
|
if (this._width > 0) { // Re-center block
|
||||||
|
this._left -= (newWidth - _width) / 2;
|
||||||
|
}
|
||||||
|
this._width = newWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getMessage() {
|
||||||
|
if (_value == null) {
|
||||||
|
return "-";
|
||||||
|
} else {
|
||||||
|
return _value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getMessageForEdit() {
|
||||||
|
ConnectorTypeInfo.Type type = _typeInfo.get_type();
|
||||||
|
if (type == ConnectorTypeInfo.Type.INTEGER || type == ConnectorTypeInfo.Type.FLOAT) {
|
||||||
|
if (_value == null) {
|
||||||
|
return "0";
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Integer.parseInt(_value);
|
||||||
|
return _value;
|
||||||
|
} catch(NumberFormatException ex) {
|
||||||
|
Log.w(LogTag, "StaticPart (type=number), value found=" + _value, ex);
|
||||||
|
return "0";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (_value == null) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return _value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Paint getTextPaint() {
|
||||||
|
Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||||
|
p.setColor(Color.BLACK);
|
||||||
|
p.setTextSize(50);
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void touched() {
|
||||||
|
if (!(this._grid instanceof View)) {
|
||||||
|
return; // Can't show dialog if can't retrieve context
|
||||||
|
}
|
||||||
|
|
||||||
|
final Context ctx = ((View) this._grid).getContext();
|
||||||
|
ConnectorTypeInfo.Type type = this._typeInfo.get_type();
|
||||||
|
if (type == ConnectorTypeInfo.Type.PULSE) {
|
||||||
|
return; // Nothing to do here
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case BOOLEAN:
|
||||||
|
break;
|
||||||
|
case INTEGER:
|
||||||
|
case FLOAT: {
|
||||||
|
final EditText input = new EditText(ctx);
|
||||||
|
|
||||||
|
input.setText(getMessageForEdit());
|
||||||
|
input.setInputType(InputType.TYPE_CLASS_NUMBER);
|
||||||
|
|
||||||
|
dialogAskUserForValue(ctx, input,
|
||||||
|
() -> {
|
||||||
|
_value = input.getText().toString();
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
case ANY:
|
||||||
|
case UNKNOWN:
|
||||||
|
case STRING: {
|
||||||
|
final EditText input = new EditText(ctx);
|
||||||
|
|
||||||
|
input.setText(getMessageForEdit());
|
||||||
|
input.setInputType(InputType.TYPE_CLASS_TEXT);
|
||||||
|
dialogAskUserForValue(ctx, input,
|
||||||
|
() -> {
|
||||||
|
_value = input.getText().toString();
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ENUM:
|
||||||
|
ProgramakerCustomBlockArgument blockArg = _typeInfo.getBlockArgument();
|
||||||
|
String callback = null;
|
||||||
|
if (blockArg != null) {
|
||||||
|
callback = blockArg.getCallback();
|
||||||
|
if (callback.equals("null")) { // TODO: Fix this on server
|
||||||
|
callback = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (callback == null) {
|
||||||
|
new AlertDialog.Builder(ctx)
|
||||||
|
.setMessage(R.string.error_recovering_allowed_values)
|
||||||
|
.create()
|
||||||
|
.show();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
ProgramakerApi api = _grid.getApi();
|
||||||
|
Dialog loadingDialog = createLoadingDialog(ctx);
|
||||||
|
if (!api.hasCachedAllowedValues(blockArg)) {
|
||||||
|
loadingDialog.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
new GetAsync<List<ProgramakerCustomBlockArgumentValue>>().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,
|
||||||
|
new Tuple3<>(
|
||||||
|
() -> api.fetchAllowedValues(blockArg),
|
||||||
|
results -> {
|
||||||
|
Log.i(LogTag, "Result: " + results);
|
||||||
|
loadingDialog.cancel();
|
||||||
|
|
||||||
|
String[] names = new String[results.size()];
|
||||||
|
int i = 0;
|
||||||
|
for (ProgramakerCustomBlockArgumentValue value : results) {
|
||||||
|
names[i] = value.getName();
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
Dialog d = createItemDialog(ctx, names, idx -> {
|
||||||
|
StaticValuePart.this._value = results.get(idx).getName();
|
||||||
|
StaticValuePart.this._valueId = results.get(idx).getId();
|
||||||
|
StaticValuePart.this._grid.update();
|
||||||
|
});
|
||||||
|
d.show();
|
||||||
|
},
|
||||||
|
ex -> {
|
||||||
|
Log.e(LogTag, "Error retrieving values: " + ex, ex);
|
||||||
|
new AlertDialog.Builder(ctx)
|
||||||
|
.setMessage(R.string.error_recovering_allowed_values)
|
||||||
|
.create()
|
||||||
|
.show();
|
||||||
|
}
|
||||||
|
));
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Dialog createItemDialog(Context ctx, CharSequence[] itemNames, Consumer<Integer> onSelected) {
|
||||||
|
final AlertDialog.Builder builder = new AlertDialog.Builder(ctx);
|
||||||
|
|
||||||
|
builder.setTitle(R.string.set_value);
|
||||||
|
|
||||||
|
final ProgressBar progressBar = new ProgressBar(ctx);
|
||||||
|
progressBar.setIndeterminate(true);
|
||||||
|
builder.setItems(itemNames, (DialogInterface.OnClickListener) (dialog, which) -> {
|
||||||
|
try {
|
||||||
|
onSelected.apply(which);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
AlertDialog dialog = builder.create();
|
||||||
|
return dialog;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Dialog createLoadingDialog(Context ctx) {
|
||||||
|
final AlertDialog.Builder builder = new AlertDialog.Builder(ctx);
|
||||||
|
|
||||||
|
builder.setTitle(R.string.loading_available_values);
|
||||||
|
|
||||||
|
final ProgressBar progressBar = new ProgressBar(ctx);
|
||||||
|
progressBar.setIndeterminate(true);
|
||||||
|
builder.setView(progressBar);
|
||||||
|
AlertDialog dialog = builder.create();
|
||||||
|
return dialog;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void dialogAskUserForValue(Context ctx, EditText input, Runnable onAccept) {
|
||||||
|
final AlertDialog.Builder builder = new AlertDialog.Builder(ctx);
|
||||||
|
|
||||||
|
builder.setTitle(R.string.set_value);
|
||||||
|
builder
|
||||||
|
.setPositiveButton(R.string.ok_accept_changes, (dialog, which) -> {
|
||||||
|
onAccept.run();
|
||||||
|
this.updateWidthHeight();
|
||||||
|
((View) this._grid).invalidate();
|
||||||
|
})
|
||||||
|
.setNegativeButton(R.string.cancel_discard_changes, (dialog, which) -> {
|
||||||
|
// No change
|
||||||
|
})
|
||||||
|
.setView(input);
|
||||||
|
|
||||||
|
AlertDialog dialog = builder.create();
|
||||||
|
dialog.show();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<InputConnector> getInputConnectors() {
|
||||||
|
// There is not going to be one
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<OutputConnector> getOutputConnectors() {
|
||||||
|
return new LinkedList<OutputConnector>() {{
|
||||||
|
add(_outputConnector);
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
public AnyRoundOutputConnector getValueOutput() {
|
||||||
|
return _outputConnector;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void send(InputConnector inputConnector, WireDataType signal) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String get_id() {
|
||||||
|
return _id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InputConnector getConnectorWithId(String inputConnectorId) {
|
||||||
|
return null; // No inputs
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getConnectorId(InputConnector inputConnector) {
|
||||||
|
return null; // No inputs
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void resume() {
|
||||||
|
// Nothing to do
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void pause() {
|
||||||
|
// Nothing to do
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object query(Object lastValue) {
|
||||||
|
if (this._valueId != null) {
|
||||||
|
return this._valueId;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this._value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void draw(ScrolledCanvas canvas, boolean devMode) {
|
||||||
|
this.updateWidthHeight(); // TODO: Remove after calculations stabilize
|
||||||
|
|
||||||
|
if (!devMode) {
|
||||||
|
return; // Logic block, don't show on user-mode
|
||||||
|
}
|
||||||
|
|
||||||
|
drawConnectors(canvas);
|
||||||
|
drawWires(canvas, devMode);
|
||||||
|
|
||||||
|
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||||
|
paint.setColor(Color.parseColor("#E3FCFC"));
|
||||||
|
canvas.drawRect(
|
||||||
|
new Rect(get_left(), get_top(),
|
||||||
|
get_right(), get_bottom()),
|
||||||
|
paint);
|
||||||
|
|
||||||
|
Paint textPaint = getTextPaint();
|
||||||
|
canvas.drawText(getMessage(),
|
||||||
|
get_left() + WIDTH_PADDING,
|
||||||
|
get_bottom() - HEIGHT_PADDING,
|
||||||
|
textPaint);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void drawConnectors(ScrolledCanvas canvas) {
|
||||||
|
Paint outerOutputConnectorPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||||
|
outerOutputConnectorPaint.setColor(_typeInfo.getOuterColor());
|
||||||
|
|
||||||
|
canvas.drawCircle(
|
||||||
|
_outputConnector.getX(),
|
||||||
|
_outputConnector.getY(),
|
||||||
|
_outputConnector.getRadius(),
|
||||||
|
outerOutputConnectorPaint);
|
||||||
|
|
||||||
|
Paint innerOutputConnectorPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||||
|
innerOutputConnectorPaint.setColor(_typeInfo.getInnerColor());
|
||||||
|
|
||||||
|
canvas.drawCircle(
|
||||||
|
_outputConnector.getX(),
|
||||||
|
_outputConnector.getY(),
|
||||||
|
_outputConnector.getRadius() / 2,
|
||||||
|
innerOutputConnectorPaint);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void drawWires(ScrolledCanvas canvas, boolean devMode) {
|
||||||
|
_outputConnector.drawWires(canvas, devMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void moveEnd(int x, int y) {
|
||||||
|
_left = x - _width / 2;
|
||||||
|
_top = y - _height / 2;
|
||||||
|
this._outputConnector.updatePosition(
|
||||||
|
this._left + _width / 2,
|
||||||
|
this._top + _height);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void drop(int x, int y) {
|
||||||
|
moveEnd(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean containsPoint(int x, int y) {
|
||||||
|
return ((x >= this.get_left()) && (x <= this.get_right())
|
||||||
|
&& (y >= this.get_top()) && (y <= this.get_bottom()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Moveable getMoveable() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void unlink() {
|
||||||
|
// Nothing to do
|
||||||
|
_outputConnector.unlink();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,316 @@
|
|||||||
|
package com.codigoparallevar.minicards.parts.viewers;
|
||||||
|
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
import android.graphics.BitmapFactory;
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.graphics.ImageFormat;
|
||||||
|
import android.graphics.Paint;
|
||||||
|
import android.graphics.Rect;
|
||||||
|
import android.graphics.RectF;
|
||||||
|
import android.media.Image;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.codigoparallevar.minicards.PartInstantiator;
|
||||||
|
import com.codigoparallevar.minicards.ScrolledCanvas;
|
||||||
|
import com.codigoparallevar.minicards.parts.connectors.ImageRoundInputConnector;
|
||||||
|
import com.codigoparallevar.minicards.parts.style.CardTheme;
|
||||||
|
import com.codigoparallevar.minicards.types.Moveable;
|
||||||
|
import com.codigoparallevar.minicards.types.Part;
|
||||||
|
import com.codigoparallevar.minicards.types.PartGrid;
|
||||||
|
import com.codigoparallevar.minicards.types.connectors.input.ImageInputConnector;
|
||||||
|
import com.codigoparallevar.minicards.types.connectors.input.InputConnector;
|
||||||
|
import com.codigoparallevar.minicards.types.connectors.output.OutputConnector;
|
||||||
|
import com.codigoparallevar.minicards.types.functional.Tuple2;
|
||||||
|
import com.codigoparallevar.minicards.types.wireData.ImageSignal;
|
||||||
|
import com.codigoparallevar.minicards.types.wireData.WireDataType;
|
||||||
|
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public class ImageFrame implements Part {
|
||||||
|
private static final String LogTag = "ImageFrame";
|
||||||
|
private static final int DEFAULT_SIDE_SIZE = 400;
|
||||||
|
private static final String IMAGE_INPUT_CONNECTOR_ID = "image_input_connector";
|
||||||
|
|
||||||
|
private final String _id;
|
||||||
|
private final PartGrid _partGrid;
|
||||||
|
private int _left;
|
||||||
|
private int _top;
|
||||||
|
private int _right;
|
||||||
|
private int _bottom;
|
||||||
|
private List<InputConnector> inputConnectors;
|
||||||
|
private ImageInputConnector _imageInputConnector;
|
||||||
|
private Bitmap lastImage;
|
||||||
|
|
||||||
|
public ImageFrame(String id, PartGrid partGrid, int left, int top, int right, int bottom) {
|
||||||
|
_id = id;
|
||||||
|
_partGrid = partGrid;
|
||||||
|
_left = left;
|
||||||
|
_top = top;
|
||||||
|
_right = right;
|
||||||
|
_bottom = bottom;
|
||||||
|
|
||||||
|
_imageInputConnector = new ImageRoundInputConnector(
|
||||||
|
this,
|
||||||
|
getInputConnectorCenterX(),
|
||||||
|
getInputConnectorCenterY(),
|
||||||
|
getInputConnectRadius());
|
||||||
|
inputConnectors = new LinkedList<>();
|
||||||
|
inputConnectors.add(_imageInputConnector);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ImageFrame(PartGrid partGrid, int left, int top, int right, int bottom) {
|
||||||
|
this(UUID.randomUUID().toString(), partGrid, left, top, right, bottom);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int get_left() {
|
||||||
|
return _left;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int get_right() {
|
||||||
|
return _right;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int get_top() {
|
||||||
|
return _top;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int get_bottom() {
|
||||||
|
return _bottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void draw(ScrolledCanvas canvas, boolean devMode) {
|
||||||
|
if (devMode){
|
||||||
|
drawConnector(canvas);
|
||||||
|
// drawWires(canvas, devMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
Rect frameRect = new Rect(_left, _top, _right, _bottom);
|
||||||
|
if (this.lastImage == null) {
|
||||||
|
Paint backdropPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||||
|
backdropPaint.setColor(Color.parseColor("#444444"));
|
||||||
|
|
||||||
|
canvas.drawRoundRect(new RectF(frameRect), 50, 50, backdropPaint);
|
||||||
|
|
||||||
|
Paint crossPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||||
|
crossPaint.setColor(Color.WHITE);
|
||||||
|
|
||||||
|
// Top
|
||||||
|
canvas.drawLine(_left, _top, _right, _top, crossPaint);
|
||||||
|
// Bottom
|
||||||
|
canvas.drawLine(_left, _bottom, _right, _bottom, crossPaint);
|
||||||
|
// Left
|
||||||
|
canvas.drawLine(_left, _top, _left, _bottom, crossPaint);
|
||||||
|
// Right
|
||||||
|
canvas.drawLine(_right, _top, _right, _bottom, crossPaint);
|
||||||
|
// Cross, top-left, bottom-right
|
||||||
|
canvas.drawLine(_left, _top, _right, _bottom, crossPaint);
|
||||||
|
// Cross, top-right, bottom-left
|
||||||
|
canvas.drawLine(_right, _top, _left, _bottom, crossPaint);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
canvas.drawBitmap(this.lastImage, frameRect);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void drawConnector(ScrolledCanvas canvas) {
|
||||||
|
Paint outerInputConnectorPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||||
|
outerInputConnectorPaint.setColor(CardTheme.IMAGE_CONNECTOR_COLOR_OUTER);
|
||||||
|
|
||||||
|
canvas.drawCircle(
|
||||||
|
getInputConnectorCenterX(),
|
||||||
|
getInputConnectorCenterY(),
|
||||||
|
getInputConnectRadius(),
|
||||||
|
outerInputConnectorPaint);
|
||||||
|
|
||||||
|
Paint innerInputConnectorPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||||
|
innerInputConnectorPaint.setColor(CardTheme.IMAGE_CONNECTOR_COLOR_INNER);
|
||||||
|
|
||||||
|
canvas.drawCircle(
|
||||||
|
getInputConnectorCenterX(),
|
||||||
|
getInputConnectorCenterY(),
|
||||||
|
getInputConnectRadius() / 2,
|
||||||
|
innerInputConnectorPaint);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void moveEnd(int x, int y) {
|
||||||
|
final int width = _right - _left;
|
||||||
|
final int height = _bottom - _top;
|
||||||
|
|
||||||
|
_left = x - width / 2;
|
||||||
|
_right = _left + width;
|
||||||
|
|
||||||
|
_top = y - height / 2;
|
||||||
|
_bottom = _top + height;
|
||||||
|
|
||||||
|
_imageInputConnector.updatePosition(
|
||||||
|
getInputConnectorCenterX(),
|
||||||
|
getInputConnectorCenterY());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void drop(int x, int y) {
|
||||||
|
moveEnd(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void touched() {
|
||||||
|
Log.d(LogTag, "Touched");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<InputConnector> getInputConnectors() {
|
||||||
|
return inputConnectors;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<OutputConnector> getOutputConnectors() {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JSONObject serialize() throws JSONException {
|
||||||
|
JSONObject serialized = new JSONObject();
|
||||||
|
|
||||||
|
serialized.put("id", _id);
|
||||||
|
serialized.put("left", _left);
|
||||||
|
serialized.put("top", _top);
|
||||||
|
serialized.put("right", _right);
|
||||||
|
serialized.put("bottom", _bottom);
|
||||||
|
|
||||||
|
return serialized;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void send(InputConnector roundInputConnector, WireDataType signal) {
|
||||||
|
if (!(signal instanceof ImageSignal)) {
|
||||||
|
Log.e(LogTag, "Mismatched type, expected {ImageSignal}, found {" + signal + "}" );
|
||||||
|
}
|
||||||
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
|
||||||
|
// No way to get the format?
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImageSignal imgSignal = (ImageSignal) signal;
|
||||||
|
Image image = imgSignal.get();
|
||||||
|
if (image.getFormat() == ImageFormat.JPEG) {
|
||||||
|
Image.Plane[] planes = image.getPlanes();
|
||||||
|
if (planes.length != 1) {
|
||||||
|
// TODO: Handle this case (what would it mean?)
|
||||||
|
return ;
|
||||||
|
}
|
||||||
|
|
||||||
|
ByteBuffer jpegBuffer = planes[0].getBuffer();
|
||||||
|
byte[] jpegData = new byte[jpegBuffer.remaining()];
|
||||||
|
jpegBuffer.get(jpegData);
|
||||||
|
|
||||||
|
Bitmap bitmap = BitmapFactory.decodeByteArray(jpegData, 0, jpegData.length);
|
||||||
|
this.lastImage = bitmap;
|
||||||
|
this._partGrid.update(); // Maybe this can be avoided? Directly copying the data on the canvas?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String get_id() {
|
||||||
|
return _id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InputConnector getConnectorWithId(String inputConnectorId) {
|
||||||
|
switch (inputConnectorId){
|
||||||
|
case ImageFrame.IMAGE_INPUT_CONNECTOR_ID:
|
||||||
|
return _imageInputConnector;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getConnectorId(InputConnector inputConnector) {
|
||||||
|
if (inputConnector == _imageInputConnector){
|
||||||
|
return ImageFrame.IMAGE_INPUT_CONNECTOR_ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void resume() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void pause() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object query(Object lastValue) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Part deserialize(PartGrid partGrid, JSONObject data) throws JSONException {
|
||||||
|
String id = data.getString("id");
|
||||||
|
int left = data.getInt("left");
|
||||||
|
int top = data.getInt("top");
|
||||||
|
int right = data.getInt("right");
|
||||||
|
int bottom = data.getInt("bottom");
|
||||||
|
|
||||||
|
return new ImageFrame(id, partGrid, left, top, right, bottom);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean containsPoint(int x, int y) {
|
||||||
|
return (x >= get_left()) && (x <= get_right())
|
||||||
|
&& (y >= get_top()) && (y <= get_bottom());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Moveable getMoveable() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void unlink() {
|
||||||
|
for (InputConnector input : getInputConnectors()) {
|
||||||
|
input.unlink();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getInputConnectorCenterX() {
|
||||||
|
return (get_left() + get_right()) / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getInputConnectRadius() {
|
||||||
|
return (get_right() - get_left()) / 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getInputConnectorCenterY() {
|
||||||
|
return get_top();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PartInstantiator getInstantiator() {
|
||||||
|
final int halfSideSize = DEFAULT_SIDE_SIZE / 2;
|
||||||
|
return new PartInstantiator() {
|
||||||
|
@Override
|
||||||
|
protected Part instantiate(PartGrid grid, Tuple2<Integer, Integer> center) {
|
||||||
|
return new ImageFrame(grid,
|
||||||
|
center.item1 - halfSideSize, center.item2 - halfSideSize,
|
||||||
|
center.item1 + halfSideSize, center.item2 + halfSideSize);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
package com.codigoparallevar.minicards.toolbox;
|
||||||
|
|
||||||
|
import com.codigoparallevar.minicards.PartInstantiator;
|
||||||
|
import com.codigoparallevar.minicards.types.functional.Tuple2;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
class PartCategory {
|
||||||
|
private final String _name;
|
||||||
|
private final List<Tuple2<String, PartInstantiator>> _parts;
|
||||||
|
|
||||||
|
public PartCategory(String name, List<Tuple2<String, PartInstantiator>> parts) {
|
||||||
|
this._name = name;
|
||||||
|
this._parts = parts;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return _name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Tuple2<String, PartInstantiator>> getParts() {
|
||||||
|
return this._parts;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,101 @@
|
|||||||
|
package com.codigoparallevar.minicards.toolbox;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.Canvas;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
import com.codigoparallevar.minicards.PartInstantiator;
|
||||||
|
import com.codigoparallevar.minicards.ScrolledCanvas;
|
||||||
|
import com.codigoparallevar.minicards.SignalListenerManager;
|
||||||
|
import com.codigoparallevar.minicards.types.Part;
|
||||||
|
import com.codigoparallevar.minicards.types.PartGrid;
|
||||||
|
import com.codigoparallevar.minicards.types.Selectable;
|
||||||
|
import com.codigoparallevar.minicards.types.connectors.input.AnyInputConnector;
|
||||||
|
import com.codigoparallevar.minicards.types.connectors.input.BooleanInputConnector;
|
||||||
|
import com.codigoparallevar.minicards.types.connectors.input.ImageInputConnector;
|
||||||
|
import com.codigoparallevar.minicards.types.connectors.input.SignalInputConnector;
|
||||||
|
import com.codigoparallevar.minicards.types.connectors.input.StringInputConnector;
|
||||||
|
import com.codigoparallevar.minicards.types.functional.Tuple2;
|
||||||
|
import com.programaker.api.ProgramakerApi;
|
||||||
|
|
||||||
|
class PartShowcaseView extends View implements PartGrid {
|
||||||
|
private final Context ctx;
|
||||||
|
private final PartInstantiator instantiator;
|
||||||
|
private final Part part;
|
||||||
|
private final int partWidth;
|
||||||
|
private Tuple2<Integer, Integer> center = new Tuple2<>(0, 0);
|
||||||
|
|
||||||
|
public PartShowcaseView(Context ctx, PartInstantiator instantiator) {
|
||||||
|
super(ctx);
|
||||||
|
|
||||||
|
this.ctx = ctx;
|
||||||
|
this.instantiator = instantiator;
|
||||||
|
this.part = instantiator.build(this);
|
||||||
|
this.part.pause();
|
||||||
|
int height = this.part.get_bottom() - this.part.get_top();
|
||||||
|
partWidth = this.part.get_right() - this.part.get_left();
|
||||||
|
|
||||||
|
this.setMinimumHeight(height * 2);
|
||||||
|
this.setMinimumWidth(partWidth);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDraw(Canvas canvas) {
|
||||||
|
int centerx = - Math.max(canvas.getWidth(), partWidth) / 2;
|
||||||
|
int centery = - canvas.getHeight() / 2;
|
||||||
|
this.center = new Tuple2<>(centerx, centery);
|
||||||
|
ScrolledCanvas scrolledCanvas = new ScrolledCanvas(canvas, getCenteredOn());
|
||||||
|
this.part.draw(scrolledCanvas, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Most interactive functionalities are not implemented, as it's not needed for the showcase
|
||||||
|
@Override
|
||||||
|
public Selectable getPartOn(int x, int y) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ProgramakerApi getApi() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SignalListenerManager getListenerManager() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SignalInputConnector getSignalInputConnectorOn(int x, int y) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BooleanInputConnector getBooleanInputConnectorOn(int x, int y) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AnyInputConnector getAnyInputConnectorOn(int x, int y) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public StringInputConnector getStringInputConnectorOn(int xEnd, int yEnd) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ImageInputConnector getImageInputConnectorOn(int xEnd, int yEnd) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Tuple2<Integer, Integer> getCenteredOn() {
|
||||||
|
return center;
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
|
||||||
|
public void update() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,214 @@
|
|||||||
|
package com.codigoparallevar.minicards.toolbox;
|
||||||
|
|
||||||
|
import android.app.Dialog;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.Gravity;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.ScrollView;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import androidx.appcompat.app.AlertDialog;
|
||||||
|
|
||||||
|
import com.codigoparallevar.minicards.CanvasView;
|
||||||
|
import com.codigoparallevar.minicards.PartInstantiator;
|
||||||
|
import com.codigoparallevar.minicards.parts.android.CameraStreamer;
|
||||||
|
import com.codigoparallevar.minicards.parts.buttons.RoundButton;
|
||||||
|
import com.codigoparallevar.minicards.parts.logic.Ticker;
|
||||||
|
import com.codigoparallevar.minicards.parts.logic.Toggle;
|
||||||
|
import com.codigoparallevar.minicards.parts.samples.ColorBox;
|
||||||
|
import com.codigoparallevar.minicards.parts.strings.ConvertToString;
|
||||||
|
import com.codigoparallevar.minicards.parts.viewers.ImageFrame;
|
||||||
|
import com.codigoparallevar.minicards.types.Part;
|
||||||
|
import com.codigoparallevar.minicards.types.functional.Consumer;
|
||||||
|
import com.codigoparallevar.minicards.types.functional.Tuple2;
|
||||||
|
import com.codigoparallevar.minicards.utils.Box;
|
||||||
|
import com.programaker.api.data.ProgramakerBridgeInfo;
|
||||||
|
import com.programaker.api.data.ProgramakerCustomBlock;
|
||||||
|
import com.programaker.api.data.api_results.ProgramakerBridgeCustomBlockResult;
|
||||||
|
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Vector;
|
||||||
|
|
||||||
|
public class PartsHolder {
|
||||||
|
private final Context context;
|
||||||
|
|
||||||
|
private final static List<Tuple2<String, PartInstantiator>> GuiParts =
|
||||||
|
new Vector<Tuple2<String, PartInstantiator>>(){{
|
||||||
|
add(new Tuple2<>("Round button", RoundButton.getInstantiator()));
|
||||||
|
add(new Tuple2<>("Red/Green box", ColorBox.getInstantiator()));
|
||||||
|
add(new Tuple2<>("Image frame", ImageFrame.getInstantiator()));
|
||||||
|
}};
|
||||||
|
|
||||||
|
private final static List<Tuple2<String, PartInstantiator>> AndroidParts =
|
||||||
|
new Vector<Tuple2<String, PartInstantiator>>(){{
|
||||||
|
add(new Tuple2<>("Camera", CameraStreamer.getInstantiator()));
|
||||||
|
}};
|
||||||
|
|
||||||
|
private final static List<Tuple2<String, PartInstantiator>> MiscParts =
|
||||||
|
new Vector<Tuple2<String, PartInstantiator>>(){{
|
||||||
|
add(new Tuple2<>("Ticker", Ticker.getInstantiator()));
|
||||||
|
add(new Tuple2<>("Toggle", Toggle.getInstantiator()));
|
||||||
|
}};
|
||||||
|
|
||||||
|
private final static List<Tuple2<String, PartInstantiator>> DebuggingParts =
|
||||||
|
new Vector<Tuple2<String, PartInstantiator>>(){{
|
||||||
|
add(new Tuple2<>("ToString", ConvertToString.getInstantiator()));
|
||||||
|
}};
|
||||||
|
|
||||||
|
|
||||||
|
private final List<PartCategory> Categories = new Vector<PartCategory>() {{
|
||||||
|
add(new PartCategory("GUI elements", GuiParts));
|
||||||
|
add(new PartCategory("Android", AndroidParts));
|
||||||
|
add(new PartCategory("Misc", MiscParts));
|
||||||
|
add(new PartCategory("Debugging", DebuggingParts));
|
||||||
|
}};
|
||||||
|
|
||||||
|
private Map<String, ProgramakerBridgeInfo> bridgeInfoMap;
|
||||||
|
private final static String LogTag = "PartsHolder";
|
||||||
|
|
||||||
|
public PartsHolder(Context context) {
|
||||||
|
this.context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void openAddPartModal(final CanvasView canvasView) {
|
||||||
|
AlertDialog.Builder builder = new AlertDialog.Builder(context);
|
||||||
|
|
||||||
|
// HACK : Nasty trick to make the accordion be able to close a dialog that still doesn't exist
|
||||||
|
Box<Dialog> openedDialog = new Box<>();
|
||||||
|
Consumer<Void> onComplete = _void -> {
|
||||||
|
if (openedDialog.get() == null) {
|
||||||
|
Log.e(LogTag, "Expected dialog to be opened already");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
openedDialog.get().cancel();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
View categoryAccordion = generateCategoryAccordion(canvasView, onComplete);
|
||||||
|
|
||||||
|
builder.setTitle("Choose part")
|
||||||
|
.setView(categoryAccordion);
|
||||||
|
|
||||||
|
Dialog dialog = builder.create();
|
||||||
|
openedDialog.set(dialog);
|
||||||
|
dialog.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
private View generateCategoryAccordion(final CanvasView canvasView, Consumer<Void> signalComplete) {
|
||||||
|
ScrollView view = new ScrollView(context);
|
||||||
|
LinearLayout layout = new LinearLayout(context);
|
||||||
|
layout.setOrientation(LinearLayout.VERTICAL);
|
||||||
|
layout.setDividerPadding(10);
|
||||||
|
layout.setShowDividers(LinearLayout.SHOW_DIVIDER_BEGINNING | LinearLayout.SHOW_DIVIDER_MIDDLE);
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||||
|
layout.setDividerDrawable(context.getDrawable(android.R.drawable.divider_horizontal_bright));
|
||||||
|
}
|
||||||
|
|
||||||
|
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
|
||||||
|
LinearLayout.LayoutParams.MATCH_PARENT,
|
||||||
|
LinearLayout.LayoutParams.WRAP_CONTENT
|
||||||
|
);
|
||||||
|
|
||||||
|
for (int i = 0; i < Categories.size(); i++) {
|
||||||
|
PartCategory category = Categories.get(i);
|
||||||
|
|
||||||
|
LinearLayout categoryRow = new LinearLayout(context);
|
||||||
|
LinearLayout showcases = new LinearLayout(context);
|
||||||
|
|
||||||
|
TextView categoryName = new TextView(context); //Creating Button
|
||||||
|
|
||||||
|
// Add category name
|
||||||
|
categoryName.setId(i); //Setting Id for using in future
|
||||||
|
categoryName.setText(category.getName()); //Setting text
|
||||||
|
categoryName.setTextSize(15); //Text Size
|
||||||
|
categoryName.setPadding(50, 50, 50, 50); //padding
|
||||||
|
categoryName.setLayoutParams(params); //Setting Layout Params
|
||||||
|
categoryName.setTextColor(Color.parseColor("#000000")); //Text Color
|
||||||
|
categoryName.setBackgroundColor(Color.parseColor("#ffffff"));
|
||||||
|
categoryName.setGravity(Gravity.CENTER);
|
||||||
|
categoryName.setClickable(true);
|
||||||
|
categoryName.setOnClickListener(v -> {
|
||||||
|
if (showcases.getVisibility() == View.GONE) {
|
||||||
|
showcases.setVisibility(View.VISIBLE);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
showcases.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
showcases.setVisibility(View.GONE);
|
||||||
|
|
||||||
|
showcases.setBackgroundColor(Color.parseColor("#BBBBBB"));
|
||||||
|
showcases.setOrientation(LinearLayout.VERTICAL);
|
||||||
|
showcases.setDividerPadding(10);
|
||||||
|
showcases.setShowDividers(LinearLayout.SHOW_DIVIDER_BEGINNING | LinearLayout.SHOW_DIVIDER_MIDDLE);
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||||
|
showcases.setDividerDrawable(context.getDrawable(android.R.drawable.divider_horizontal_bright));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add showcases
|
||||||
|
int partHeights = 0;
|
||||||
|
for (Tuple2<String, PartInstantiator> entry : category.getParts()) {
|
||||||
|
PartShowcaseView showcase = new PartShowcaseView(context, entry.item2);
|
||||||
|
|
||||||
|
showcase.setClickable(true);
|
||||||
|
showcase.setOnClickListener(v -> {
|
||||||
|
Part part = entry.item2.build(canvasView);
|
||||||
|
canvasView.addPart(part);
|
||||||
|
try {
|
||||||
|
signalComplete.apply(null);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
partHeights += showcase.getHeight();
|
||||||
|
|
||||||
|
showcases.addView(showcase);
|
||||||
|
}
|
||||||
|
showcases.setMinimumHeight(partHeights);
|
||||||
|
|
||||||
|
// Tie everything
|
||||||
|
categoryRow.setOrientation(LinearLayout.VERTICAL);
|
||||||
|
categoryRow.addView(categoryName);
|
||||||
|
showcases.setOrientation(LinearLayout.VERTICAL);
|
||||||
|
categoryRow.addView(showcases);
|
||||||
|
|
||||||
|
layout.addView(categoryRow);
|
||||||
|
}
|
||||||
|
|
||||||
|
view.addView(layout);
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addCustomBlocks(List<ProgramakerBridgeInfo> bridgeInfo, List<ProgramakerBridgeCustomBlockResult> customBlocks) {
|
||||||
|
Map<String, ProgramakerBridgeInfo> bridgeInfoMap = new LinkedHashMap<>();
|
||||||
|
for (ProgramakerBridgeInfo info : bridgeInfo) {
|
||||||
|
bridgeInfoMap.put(info.getId(), info);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.bridgeInfoMap = bridgeInfoMap;
|
||||||
|
|
||||||
|
for (ProgramakerBridgeCustomBlockResult res : customBlocks) {
|
||||||
|
|
||||||
|
List<Tuple2<String, PartInstantiator>> parts = new LinkedList<>();
|
||||||
|
for (ProgramakerCustomBlock block : res.getBlocks()) {
|
||||||
|
parts.add(new Tuple2<>(block.getMessage(), new ProgramakerCustomBlockInstantiator(block)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bridgeInfoMap.containsKey(res.getBridge_id())) {
|
||||||
|
Categories.add(new PartCategory(bridgeInfoMap.get(res.getBridge_id()).getName(), parts));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Categories.add(new PartCategory("Bridge ID="+res.getBridge_id(), parts));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
package com.codigoparallevar.minicards.toolbox;
|
||||||
|
|
||||||
|
import com.codigoparallevar.minicards.PartInstantiator;
|
||||||
|
import com.codigoparallevar.minicards.parts.ProgramakerCustomBlockPart;
|
||||||
|
import com.codigoparallevar.minicards.parts.buttons.RoundButton;
|
||||||
|
import com.codigoparallevar.minicards.types.Part;
|
||||||
|
import com.codigoparallevar.minicards.types.PartGrid;
|
||||||
|
import com.codigoparallevar.minicards.types.functional.Tuple2;
|
||||||
|
import com.programaker.api.data.ProgramakerCustomBlock;
|
||||||
|
|
||||||
|
class ProgramakerCustomBlockInstantiator extends PartInstantiator {
|
||||||
|
private final ProgramakerCustomBlock _block;
|
||||||
|
|
||||||
|
public ProgramakerCustomBlockInstantiator(ProgramakerCustomBlock block) {
|
||||||
|
this._block = block;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Part instantiate(PartGrid grid, Tuple2<Integer, Integer> center) {
|
||||||
|
return new ProgramakerCustomBlockPart(grid, center, this._block);
|
||||||
|
}
|
||||||
|
}
|
@ -4,5 +4,5 @@ import com.codigoparallevar.minicards.types.connectors.Wiring.Wire;
|
|||||||
|
|
||||||
public interface Dropper<T extends Wire> {
|
public interface Dropper<T extends Wire> {
|
||||||
void drop(T wire);
|
void drop(T wire);
|
||||||
void unlinkWire(T wire);
|
void wireUnlinked(T wire);
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ import com.codigoparallevar.minicards.types.connectors.input.InputConnector;
|
|||||||
import com.codigoparallevar.minicards.types.connectors.output.OutputConnector;
|
import com.codigoparallevar.minicards.types.connectors.output.OutputConnector;
|
||||||
import com.codigoparallevar.minicards.types.wireData.WireDataType;
|
import com.codigoparallevar.minicards.types.wireData.WireDataType;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
|
|
||||||
@ -20,6 +21,7 @@ public interface Part extends Selectable, Moveable, Drawable {
|
|||||||
List<InputConnector> getInputConnectors();
|
List<InputConnector> getInputConnectors();
|
||||||
List<OutputConnector> getOutputConnectors();
|
List<OutputConnector> getOutputConnectors();
|
||||||
|
|
||||||
|
@NotNull
|
||||||
JSONObject serialize() throws JSONException;
|
JSONObject serialize() throws JSONException;
|
||||||
|
|
||||||
void send(InputConnector inputConnector, WireDataType signal);
|
void send(InputConnector inputConnector, WireDataType signal);
|
||||||
@ -32,4 +34,6 @@ public interface Part extends Selectable, Moveable, Drawable {
|
|||||||
|
|
||||||
void resume();
|
void resume();
|
||||||
void pause();
|
void pause();
|
||||||
|
|
||||||
|
Object query(Object lastValue);
|
||||||
}
|
}
|
||||||
|
@ -38,4 +38,17 @@ public class PartConnection {
|
|||||||
|
|
||||||
return serialized;
|
return serialized;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static JSONObject serializeToJson(final String inputConnectorId, final String inputPartId) {
|
||||||
|
|
||||||
|
JSONObject serialized = new JSONObject();
|
||||||
|
try {
|
||||||
|
serialized.put(PartConnection.INPUT_CONNECTOR_ID_KEY, inputConnectorId);
|
||||||
|
serialized.put(PartConnection.INPUT_PART_ID_KEY, inputPartId);
|
||||||
|
} catch (JSONException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
return serialized;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,18 +1,26 @@
|
|||||||
package com.codigoparallevar.minicards.types;
|
package com.codigoparallevar.minicards.types;
|
||||||
|
|
||||||
|
import com.codigoparallevar.minicards.SignalListenerManager;
|
||||||
|
import com.codigoparallevar.minicards.types.connectors.input.AnyInputConnector;
|
||||||
import com.codigoparallevar.minicards.types.connectors.input.BooleanInputConnector;
|
import com.codigoparallevar.minicards.types.connectors.input.BooleanInputConnector;
|
||||||
|
import com.codigoparallevar.minicards.types.connectors.input.ImageInputConnector;
|
||||||
import com.codigoparallevar.minicards.types.connectors.input.SignalInputConnector;
|
import com.codigoparallevar.minicards.types.connectors.input.SignalInputConnector;
|
||||||
import com.codigoparallevar.minicards.types.connectors.input.StringInputConnector;
|
import com.codigoparallevar.minicards.types.connectors.input.StringInputConnector;
|
||||||
|
import com.codigoparallevar.minicards.types.functional.Tuple2;
|
||||||
|
import com.programaker.api.ProgramakerApi;
|
||||||
|
|
||||||
public interface PartGrid {
|
public interface PartGrid {
|
||||||
Selectable getPartOn(int x, int y);
|
Selectable getPartOn(int x, int y);
|
||||||
|
ProgramakerApi getApi();
|
||||||
|
SignalListenerManager getListenerManager();
|
||||||
|
|
||||||
SignalInputConnector getSignalInputConnectorOn(int x, int y);
|
SignalInputConnector getSignalInputConnectorOn(int x, int y);
|
||||||
BooleanInputConnector getBooleanInputConnectorOn(int x, int y);
|
BooleanInputConnector getBooleanInputConnectorOn(int x, int y);
|
||||||
|
AnyInputConnector getAnyInputConnectorOn(int x, int y);
|
||||||
|
StringInputConnector getStringInputConnectorOn(int xEnd, int yEnd);
|
||||||
|
ImageInputConnector getImageInputConnectorOn(int xEnd, int yEnd);
|
||||||
|
|
||||||
Tuple2<Integer,Integer> getCenteredOn();
|
Tuple2<Integer,Integer> getCenteredOn();
|
||||||
|
|
||||||
void update();
|
void update();
|
||||||
|
|
||||||
StringInputConnector getStringInputConnectorOn(int xEnd, int yEnd);
|
|
||||||
}
|
}
|
||||||
|
@ -1,11 +0,0 @@
|
|||||||
package com.codigoparallevar.minicards.types;
|
|
||||||
|
|
||||||
public class Tuple2<T1, T2> {
|
|
||||||
public final T1 item1;
|
|
||||||
public final T2 item2;
|
|
||||||
|
|
||||||
public Tuple2(T1 item1, T2 item2) {
|
|
||||||
this.item1 = item1;
|
|
||||||
this.item2 = item2;
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,7 @@
|
|||||||
|
package com.codigoparallevar.minicards.types.connectors;
|
||||||
|
|
||||||
|
public interface RoundConnector {
|
||||||
|
int getX();
|
||||||
|
int getY();
|
||||||
|
float getRadius();
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
package com.codigoparallevar.minicards.types.connectors.Wiring;
|
||||||
|
|
||||||
|
import com.codigoparallevar.minicards.types.Drawable;
|
||||||
|
import com.codigoparallevar.minicards.types.Moveable;
|
||||||
|
import com.codigoparallevar.minicards.types.connectors.input.AnyInputConnector;
|
||||||
|
import com.codigoparallevar.minicards.types.connectors.output.OutputConnector;
|
||||||
|
import com.codigoparallevar.minicards.types.wireData.AnySignal;
|
||||||
|
|
||||||
|
public class AnyWire extends Wire<AnySignal, AnyInputConnector> implements Moveable, Drawable {
|
||||||
|
public AnyWire(OutputConnector dropper, int xInit, int yInit) {
|
||||||
|
super(dropper, xInit, yInit);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void send(AnySignal signal) {
|
||||||
|
if (_attachedTo != null) {
|
||||||
|
_attachedTo.send(signal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,13 +1,13 @@
|
|||||||
package com.codigoparallevar.minicards.types.connectors.Wiring;
|
package com.codigoparallevar.minicards.types.connectors.Wiring;
|
||||||
|
|
||||||
import com.codigoparallevar.minicards.types.Drawable;
|
import com.codigoparallevar.minicards.types.Drawable;
|
||||||
import com.codigoparallevar.minicards.types.Dropper;
|
|
||||||
import com.codigoparallevar.minicards.types.Moveable;
|
import com.codigoparallevar.minicards.types.Moveable;
|
||||||
import com.codigoparallevar.minicards.types.connectors.input.BooleanInputConnector;
|
import com.codigoparallevar.minicards.types.connectors.input.BooleanInputConnector;
|
||||||
|
import com.codigoparallevar.minicards.types.connectors.output.OutputConnector;
|
||||||
import com.codigoparallevar.minicards.types.wireData.BooleanSignal;
|
import com.codigoparallevar.minicards.types.wireData.BooleanSignal;
|
||||||
|
|
||||||
public class BooleanWire extends Wire<BooleanSignal, BooleanInputConnector> implements Moveable, Drawable {
|
public class BooleanWire extends Wire<BooleanSignal, BooleanInputConnector> implements Moveable, Drawable {
|
||||||
public BooleanWire(Dropper dropper, int xInit, int yInit) {
|
public BooleanWire(OutputConnector dropper, int xInit, int yInit) {
|
||||||
super(dropper, xInit, yInit);
|
super(dropper, xInit, yInit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,19 @@
|
|||||||
|
package com.codigoparallevar.minicards.types.connectors.Wiring;
|
||||||
|
|
||||||
|
import com.codigoparallevar.minicards.types.Drawable;
|
||||||
|
import com.codigoparallevar.minicards.types.Moveable;
|
||||||
|
import com.codigoparallevar.minicards.types.connectors.input.ImageInputConnector;
|
||||||
|
import com.codigoparallevar.minicards.types.connectors.output.OutputConnector;
|
||||||
|
import com.codigoparallevar.minicards.types.wireData.ImageSignal;
|
||||||
|
|
||||||
|
public class ImageWire extends Wire<ImageSignal, ImageInputConnector> implements Moveable, Drawable {
|
||||||
|
public ImageWire(OutputConnector dropper, int xInit, int yInit) {
|
||||||
|
super(dropper, xInit, yInit);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void send(ImageSignal signal) {
|
||||||
|
if (_attachedTo != null) {
|
||||||
|
_attachedTo.send(signal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,13 +1,13 @@
|
|||||||
package com.codigoparallevar.minicards.types.connectors.Wiring;
|
package com.codigoparallevar.minicards.types.connectors.Wiring;
|
||||||
|
|
||||||
import com.codigoparallevar.minicards.types.Drawable;
|
import com.codigoparallevar.minicards.types.Drawable;
|
||||||
import com.codigoparallevar.minicards.types.Dropper;
|
|
||||||
import com.codigoparallevar.minicards.types.Moveable;
|
import com.codigoparallevar.minicards.types.Moveable;
|
||||||
import com.codigoparallevar.minicards.types.connectors.input.SignalInputConnector;
|
import com.codigoparallevar.minicards.types.connectors.input.SignalInputConnector;
|
||||||
|
import com.codigoparallevar.minicards.types.connectors.output.OutputConnector;
|
||||||
import com.codigoparallevar.minicards.types.wireData.Signal;
|
import com.codigoparallevar.minicards.types.wireData.Signal;
|
||||||
|
|
||||||
public class SignalWire extends Wire<Signal, SignalInputConnector> implements Moveable, Drawable {
|
public class SignalWire extends Wire<Signal, SignalInputConnector> implements Moveable, Drawable {
|
||||||
public SignalWire(Dropper dropper, int xInit, int yInit) {
|
public SignalWire(OutputConnector dropper, int xInit, int yInit) {
|
||||||
super(dropper, xInit, yInit);
|
super(dropper, xInit, yInit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
package com.codigoparallevar.minicards.types.connectors.Wiring;
|
package com.codigoparallevar.minicards.types.connectors.Wiring;
|
||||||
|
|
||||||
import com.codigoparallevar.minicards.types.Drawable;
|
import com.codigoparallevar.minicards.types.Drawable;
|
||||||
import com.codigoparallevar.minicards.types.Dropper;
|
|
||||||
import com.codigoparallevar.minicards.types.Moveable;
|
import com.codigoparallevar.minicards.types.Moveable;
|
||||||
import com.codigoparallevar.minicards.types.connectors.input.StringInputConnector;
|
import com.codigoparallevar.minicards.types.connectors.input.StringInputConnector;
|
||||||
|
import com.codigoparallevar.minicards.types.connectors.output.OutputConnector;
|
||||||
import com.codigoparallevar.minicards.types.wireData.StringSignal;
|
import com.codigoparallevar.minicards.types.wireData.StringSignal;
|
||||||
|
|
||||||
public class StringWire extends Wire<StringSignal, StringInputConnector> implements Moveable, Drawable {
|
public class StringWire extends Wire<StringSignal, StringInputConnector> implements Moveable, Drawable {
|
||||||
public StringWire(Dropper dropper, int xInit, int yInit) {
|
public StringWire(OutputConnector dropper, int xInit, int yInit) {
|
||||||
super(dropper, xInit, yInit);
|
super(dropper, xInit, yInit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,24 +3,31 @@ package com.codigoparallevar.minicards.types.connectors.Wiring;
|
|||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
import android.graphics.Paint;
|
import android.graphics.Paint;
|
||||||
import android.graphics.Path;
|
import android.graphics.Path;
|
||||||
|
import android.graphics.RectF;
|
||||||
|
|
||||||
import com.codigoparallevar.minicards.ScrolledCanvas;
|
import com.codigoparallevar.minicards.ScrolledCanvas;
|
||||||
import com.codigoparallevar.minicards.types.Drawable;
|
import com.codigoparallevar.minicards.types.Drawable;
|
||||||
import com.codigoparallevar.minicards.types.Dropper;
|
import com.codigoparallevar.minicards.types.Dropper;
|
||||||
import com.codigoparallevar.minicards.types.Moveable;
|
import com.codigoparallevar.minicards.types.Moveable;
|
||||||
|
import com.codigoparallevar.minicards.types.Selectable;
|
||||||
import com.codigoparallevar.minicards.types.connectors.input.InputConnector;
|
import com.codigoparallevar.minicards.types.connectors.input.InputConnector;
|
||||||
|
import com.codigoparallevar.minicards.types.connectors.output.OutputConnector;
|
||||||
import com.codigoparallevar.minicards.types.wireData.WireDataType;
|
import com.codigoparallevar.minicards.types.wireData.WireDataType;
|
||||||
|
|
||||||
public class Wire<T extends WireDataType, InputConnectorType extends InputConnector<T, InputConnectorType>>
|
public class Wire<T extends WireDataType, InputConnectorType extends InputConnector<T, InputConnectorType>>
|
||||||
implements Moveable, Drawable {
|
implements Moveable, Drawable, Selectable {
|
||||||
|
|
||||||
|
private final double MAX_DISTANCE_TO_CONTAINS = 15;
|
||||||
|
|
||||||
private final Dropper _dropper;
|
private final Dropper _dropper;
|
||||||
private int _xinit;
|
private int _xinit;
|
||||||
private int _yinit;
|
private int _yinit;
|
||||||
private int _xend;
|
private int _xend;
|
||||||
private int _yend;
|
private int _yend;
|
||||||
|
private Path path = null;
|
||||||
private final static int _pathRunWay = 100;
|
private final static int _pathRunWay = 100;
|
||||||
protected InputConnectorType _attachedTo = null;
|
protected InputConnectorType _attachedTo = null;
|
||||||
|
protected OutputConnector _attachedFrom = null;
|
||||||
|
|
||||||
public Wire(Dropper dropper, int xInit, int yInit) {
|
public Wire(Dropper dropper, int xInit, int yInit) {
|
||||||
_dropper = dropper;
|
_dropper = dropper;
|
||||||
@ -34,12 +41,14 @@ public class Wire<T extends WireDataType, InputConnectorType extends InputConnec
|
|||||||
public void moveStart(int x, int y){
|
public void moveStart(int x, int y){
|
||||||
_xinit = x;
|
_xinit = x;
|
||||||
_yinit = y;
|
_yinit = y;
|
||||||
|
invalidatePath();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void moveEnd(int x, int y) {
|
public void moveEnd(int x, int y) {
|
||||||
_xend = x;
|
_xend = x;
|
||||||
_yend = y;
|
_yend = y;
|
||||||
|
invalidatePath(); // Invalidate current path
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -49,23 +58,17 @@ public class Wire<T extends WireDataType, InputConnectorType extends InputConnec
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void draw(ScrolledCanvas canvas, boolean devMode) {
|
public void draw(ScrolledCanvas canvas, boolean devMode) {
|
||||||
final Path samplePath = new Path();
|
|
||||||
|
|
||||||
samplePath.moveTo(_xinit, _yinit);
|
|
||||||
samplePath.cubicTo(
|
|
||||||
_xinit + _pathRunWay, _yinit,
|
|
||||||
_xend - _pathRunWay, _yend,
|
|
||||||
_xend, _yend);
|
|
||||||
|
|
||||||
Paint pathPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
Paint pathPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||||
pathPaint.setColor(Color.GREEN);
|
pathPaint.setColor(Color.parseColor("#44FF44"));
|
||||||
pathPaint.setStrokeWidth(5.0f);
|
pathPaint.setStrokeWidth(10.0f);
|
||||||
pathPaint.setStrokeCap(Paint.Cap.ROUND);
|
pathPaint.setStrokeCap(Paint.Cap.ROUND);
|
||||||
pathPaint.setStyle(Paint.Style.STROKE);
|
pathPaint.setStyle(Paint.Style.STROKE);
|
||||||
|
|
||||||
canvas.drawPath(samplePath, pathPaint);
|
canvas.drawPath(getPath(), pathPaint);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public int getXEnd() {
|
public int getXEnd() {
|
||||||
return _xend;
|
return _xend;
|
||||||
}
|
}
|
||||||
@ -74,19 +77,101 @@ public class Wire<T extends WireDataType, InputConnectorType extends InputConnec
|
|||||||
return _yend;
|
return _yend;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void attachTo(InputConnectorType resultPoint) {
|
public void attachTo(InputConnectorType resultPoint, OutputConnector output) {
|
||||||
_attachedTo = resultPoint;
|
_attachedTo = resultPoint;
|
||||||
|
_attachedFrom = output;
|
||||||
|
|
||||||
_xend = resultPoint.getX();
|
_xend = resultPoint.getX();
|
||||||
_yend = resultPoint.getY();
|
_yend = resultPoint.getY();
|
||||||
resultPoint.getAttachment(this);
|
resultPoint.addAttachment(this);
|
||||||
|
|
||||||
|
invalidatePath();
|
||||||
}
|
}
|
||||||
|
|
||||||
public InputConnector getAttachedTo() {
|
public InputConnector getAttachedTo() {
|
||||||
return _attachedTo;
|
return _attachedTo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Moveable getMoveable() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
public void unlink() {
|
public void unlink() {
|
||||||
_attachedTo = null;
|
_attachedFrom.wireUnlinked(this);
|
||||||
_dropper.unlinkWire(this);
|
_attachedTo.wireUnlinked(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object query(Object lastValue) {
|
||||||
|
return this._attachedFrom.query(lastValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void invalidatePath() {
|
||||||
|
path = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Path getPath() {
|
||||||
|
if (path == null) {
|
||||||
|
path = new Path();
|
||||||
|
|
||||||
|
// Bezier
|
||||||
|
path.moveTo(_xinit, _yinit);
|
||||||
|
path.cubicTo(
|
||||||
|
_xinit, _yinit + _pathRunWay,
|
||||||
|
_xend, _yend - _pathRunWay,
|
||||||
|
_xend, _yend);
|
||||||
|
}
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean containsPoint(int x, int y) {
|
||||||
|
return distanceToPoint(x, y) < MAX_DISTANCE_TO_CONTAINS;
|
||||||
|
}
|
||||||
|
|
||||||
|
private double distanceToPoint(int x, int y) {
|
||||||
|
final int POINT_HALFSIDE = 25 / 2;
|
||||||
|
RectF rectangle = new RectF(x - POINT_HALFSIDE, y - POINT_HALFSIDE,
|
||||||
|
x + POINT_HALFSIDE, y + POINT_HALFSIDE);
|
||||||
|
|
||||||
|
double sqDist1 = squareDistanceToSegment(_xinit, _yinit,
|
||||||
|
_xinit, _yinit + _pathRunWay / 3,
|
||||||
|
x, y
|
||||||
|
);
|
||||||
|
double sqDist2 = squareDistanceToSegment(
|
||||||
|
_xinit, _yinit + _pathRunWay / 3,
|
||||||
|
_xend, _yend - _pathRunWay / 3,
|
||||||
|
x, y
|
||||||
|
);
|
||||||
|
double sqDist3 = squareDistanceToSegment(
|
||||||
|
_xend, _yend - _pathRunWay / 3,
|
||||||
|
_xend, _yend,
|
||||||
|
x, y
|
||||||
|
);
|
||||||
|
|
||||||
|
return Math.sqrt(Math.min(sqDist1, Math.min(sqDist2, sqDist3)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private double squareDistanceToSegment(int vx, int vy, int wx, int wy, int px, int py) {
|
||||||
|
// Return minimum distance between line segment vw and point p
|
||||||
|
// i.e. |w-v|^2 - avoid a sqrt
|
||||||
|
final double l2 = distance(vx, vy, wx, wy);
|
||||||
|
if (l2 == 0.0) return distance(vx, vy, px, py); // v == w case
|
||||||
|
// Consider the line extending the segment, parameterized as v + t (w - v).
|
||||||
|
// We find projection of point p onto the line.
|
||||||
|
// It falls where t = [(p-v) . (w-v)] / |w-v|^2
|
||||||
|
// We clamp t from [0,1] to handle points outside the segment vw.
|
||||||
|
|
||||||
|
double pointInLine = ((px - vx) * (wx - vx) + (py - vy) * (wy - vy)) / l2;
|
||||||
|
pointInLine = Math.max(0, Math.min(1, pointInLine));
|
||||||
|
|
||||||
|
return distance(vx + pointInLine * (wx - vx),
|
||||||
|
vy + pointInLine * (wy - vy),
|
||||||
|
px, py
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private double distance(double vx, double vy, double px, double py) {
|
||||||
|
return Math.pow(vx - px, 2) + Math.pow(vy - py, 2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package com.codigoparallevar.minicards.types.connectors.input;
|
package com.codigoparallevar.minicards.types.connectors.input;
|
||||||
|
|
||||||
import android.support.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import com.codigoparallevar.minicards.types.Moveable;
|
import com.codigoparallevar.minicards.types.Moveable;
|
||||||
import com.codigoparallevar.minicards.types.Part;
|
import com.codigoparallevar.minicards.types.Part;
|
||||||
@ -9,13 +9,13 @@ import com.codigoparallevar.minicards.types.connectors.output.BooleanOutputConne
|
|||||||
import com.codigoparallevar.minicards.types.connectors.output.OutputConnector;
|
import com.codigoparallevar.minicards.types.connectors.output.OutputConnector;
|
||||||
import com.codigoparallevar.minicards.types.connectors.output.SignalOutputConnector;
|
import com.codigoparallevar.minicards.types.connectors.output.SignalOutputConnector;
|
||||||
import com.codigoparallevar.minicards.types.connectors.output.StringOutputConnector;
|
import com.codigoparallevar.minicards.types.connectors.output.StringOutputConnector;
|
||||||
|
import com.codigoparallevar.minicards.types.wireData.AnySignal;
|
||||||
import com.codigoparallevar.minicards.types.wireData.BooleanSignal;
|
import com.codigoparallevar.minicards.types.wireData.BooleanSignal;
|
||||||
import com.codigoparallevar.minicards.types.wireData.Signal;
|
import com.codigoparallevar.minicards.types.wireData.Signal;
|
||||||
import com.codigoparallevar.minicards.types.wireData.StringSignal;
|
import com.codigoparallevar.minicards.types.wireData.StringSignal;
|
||||||
import com.codigoparallevar.minicards.types.wireData.WireDataType;
|
|
||||||
|
|
||||||
|
|
||||||
public abstract class AnyInputConnector implements InputConnector<WireDataType, AnyInputConnector> {
|
public abstract class AnyInputConnector implements InputConnector<AnySignal, AnyInputConnector> {
|
||||||
|
|
||||||
public SignalInputConnector ToSignalInputConnector() {
|
public SignalInputConnector ToSignalInputConnector() {
|
||||||
return new WrapAsSignal(this);
|
return new WrapAsSignal(this);
|
||||||
@ -51,7 +51,7 @@ public abstract class AnyInputConnector implements InputConnector<WireDataType,
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void send(Signal data) {
|
public void send(Signal data) {
|
||||||
baseInputConnector.send(data);
|
baseInputConnector.send(new AnySignal(data.get()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -62,7 +62,7 @@ public abstract class AnyInputConnector implements InputConnector<WireDataType,
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void send(BooleanSignal data) {
|
public void send(BooleanSignal data) {
|
||||||
baseInputConnector.send(data);
|
baseInputConnector.send(new AnySignal(data.get()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,7 +73,7 @@ public abstract class AnyInputConnector implements InputConnector<WireDataType,
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void send(StringSignal data) {
|
public void send(StringSignal data) {
|
||||||
baseInputConnector.send(data);
|
baseInputConnector.send(new AnySignal(data.get()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -100,8 +100,8 @@ public abstract class AnyInputConnector implements InputConnector<WireDataType,
|
|||||||
baseInputConnector.drop(wire);
|
baseInputConnector.drop(wire);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void unlinkWire(Wire wire) {
|
public void wireUnlinked(Wire wire) {
|
||||||
baseInputConnector.unlinkWire(wire);
|
baseInputConnector.wireUnlinked(wire);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updatePosition(int x, int y) {
|
public void updatePosition(int x, int y) {
|
||||||
@ -124,8 +124,8 @@ public abstract class AnyInputConnector implements InputConnector<WireDataType,
|
|||||||
return baseInputConnector.getId();
|
return baseInputConnector.getId();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void getAttachment(Wire wire) {
|
public void addAttachment(Wire wire) {
|
||||||
baseInputConnector.getAttachment(wire);
|
baseInputConnector.addAttachment(wire);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,6 @@
|
|||||||
|
package com.codigoparallevar.minicards.types.connectors.input;
|
||||||
|
|
||||||
|
import com.codigoparallevar.minicards.types.wireData.ImageSignal;
|
||||||
|
|
||||||
|
public interface ImageInputConnector extends InputConnector<ImageSignal, ImageInputConnector> {
|
||||||
|
}
|
@ -12,13 +12,16 @@ public interface InputConnector<T extends WireDataType, T1 extends InputConnecto
|
|||||||
void updatePosition(int x, int y);
|
void updatePosition(int x, int y);
|
||||||
|
|
||||||
int getX();
|
int getX();
|
||||||
|
|
||||||
int getY();
|
int getY();
|
||||||
|
|
||||||
void getAttachment(Wire<T, T1> wire);
|
void addAttachment(Wire<T, T1> wire);
|
||||||
|
|
||||||
Part getPart();
|
Part getPart();
|
||||||
|
|
||||||
String getId();
|
String getId();
|
||||||
|
|
||||||
void send(T data);
|
void send(T data);
|
||||||
|
|
||||||
|
void wireUnlinked(Wire wire);
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package com.codigoparallevar.minicards.types.connectors.input;
|
package com.codigoparallevar.minicards.types.connectors.input;
|
||||||
|
|
||||||
|
import com.codigoparallevar.minicards.types.connectors.Wiring.SignalWire;
|
||||||
import com.codigoparallevar.minicards.types.wireData.Signal;
|
import com.codigoparallevar.minicards.types.wireData.Signal;
|
||||||
|
|
||||||
public interface SignalInputConnector extends InputConnector<Signal, SignalInputConnector> {
|
public interface SignalInputConnector extends InputConnector<Signal, SignalInputConnector> {
|
||||||
|
@ -0,0 +1,8 @@
|
|||||||
|
package com.codigoparallevar.minicards.types.connectors.output;
|
||||||
|
|
||||||
|
import com.codigoparallevar.minicards.types.connectors.Wiring.AnyWire;
|
||||||
|
import com.codigoparallevar.minicards.types.connectors.input.AnyInputConnector;
|
||||||
|
import com.codigoparallevar.minicards.types.wireData.AnySignal;
|
||||||
|
|
||||||
|
public interface AnyOutputConnector extends OutputConnector<AnySignal, AnyInputConnector, AnyWire> {
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
package com.codigoparallevar.minicards.types.connectors.output;
|
||||||
|
|
||||||
|
import com.codigoparallevar.minicards.types.connectors.Wiring.ImageWire;
|
||||||
|
import com.codigoparallevar.minicards.types.connectors.input.ImageInputConnector;
|
||||||
|
import com.codigoparallevar.minicards.types.wireData.ImageSignal;
|
||||||
|
|
||||||
|
public interface ImageOutputConnector extends OutputConnector<
|
||||||
|
ImageSignal,
|
||||||
|
ImageInputConnector,
|
||||||
|
ImageWire> {
|
||||||
|
}
|
@ -3,9 +3,9 @@ package com.codigoparallevar.minicards.types.connectors.output;
|
|||||||
import com.codigoparallevar.minicards.ScrolledCanvas;
|
import com.codigoparallevar.minicards.ScrolledCanvas;
|
||||||
import com.codigoparallevar.minicards.types.Dropper;
|
import com.codigoparallevar.minicards.types.Dropper;
|
||||||
import com.codigoparallevar.minicards.types.Selectable;
|
import com.codigoparallevar.minicards.types.Selectable;
|
||||||
import com.codigoparallevar.minicards.types.Tuple2;
|
|
||||||
import com.codigoparallevar.minicards.types.connectors.Wiring.Wire;
|
import com.codigoparallevar.minicards.types.connectors.Wiring.Wire;
|
||||||
import com.codigoparallevar.minicards.types.connectors.input.InputConnector;
|
import com.codigoparallevar.minicards.types.connectors.input.InputConnector;
|
||||||
|
import com.codigoparallevar.minicards.types.functional.Tuple2;
|
||||||
import com.codigoparallevar.minicards.types.wireData.WireDataType;
|
import com.codigoparallevar.minicards.types.wireData.WireDataType;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -23,4 +23,10 @@ public interface OutputConnector<T extends WireDataType,
|
|||||||
void connectTo(T1 inputConnector);
|
void connectTo(T1 inputConnector);
|
||||||
|
|
||||||
void send(T data);
|
void send(T data);
|
||||||
|
|
||||||
|
Object query(Object lastValue);
|
||||||
|
|
||||||
|
List<T2> getWires();
|
||||||
|
|
||||||
|
void wireUnlinked(T2 wire);
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,5 @@
|
|||||||
|
package com.codigoparallevar.minicards.types.functional;
|
||||||
|
|
||||||
|
public interface Action {
|
||||||
|
void run() throws InterruptedException;
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
package com.codigoparallevar.minicards.types.functional;
|
||||||
|
|
||||||
|
public interface Consumer<T> {
|
||||||
|
void apply(T param) throws Exception;
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
package com.codigoparallevar.minicards.types.functional;
|
||||||
|
|
||||||
|
public interface Function<T, U> {
|
||||||
|
U apply(T param) throws Exception;
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
package com.codigoparallevar.minicards.types.functional;
|
||||||
|
|
||||||
|
public interface Producer<T> {
|
||||||
|
T get();
|
||||||
|
}
|
@ -0,0 +1,50 @@
|
|||||||
|
package com.codigoparallevar.minicards.types.functional;
|
||||||
|
|
||||||
|
public class Tuple2<T1, T2> {
|
||||||
|
public final T1 item1;
|
||||||
|
public final T2 item2;
|
||||||
|
|
||||||
|
public Tuple2(T1 item1, T2 item2) {
|
||||||
|
this.item1 = item1;
|
||||||
|
this.item2 = item2;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
int hash1 = this.item1 == null ? 1 : this.item1.hashCode();
|
||||||
|
int hash2 = this.item2 == null ? 2 : this.item2.hashCode();
|
||||||
|
|
||||||
|
return hash1 ^ hash2;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (!(obj instanceof Tuple2)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Tuple2 other = (Tuple2) obj;
|
||||||
|
if (other.item1 == null) {
|
||||||
|
if (this.item1 != null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (!other.item1.equals(this.item1)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (other.item2 == null) {
|
||||||
|
if (this.item2 != null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (!other.item2.equals(this.item2)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
package com.codigoparallevar.minicards.types.functional;
|
||||||
|
|
||||||
|
public class Tuple3<T, T1, T2> {
|
||||||
|
|
||||||
|
public final T _x;
|
||||||
|
public final T1 _y;
|
||||||
|
public final T2 _z;
|
||||||
|
|
||||||
|
public Tuple3(T x, T1 y, T2 z) {
|
||||||
|
_x = x;
|
||||||
|
_y = y;
|
||||||
|
_z = z;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString(){
|
||||||
|
return "(" + _x + " - " + _y + ", " + _z + ")";
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package com.codigoparallevar.minicards.types;
|
package com.codigoparallevar.minicards.types.functional;
|
||||||
|
|
||||||
public class Tuple4<T, T1, T2, T3> {
|
public class Tuple4<T, T1, T2, T3> {
|
||||||
|
|
@ -0,0 +1,14 @@
|
|||||||
|
package com.codigoparallevar.minicards.types.wireData;
|
||||||
|
|
||||||
|
public class AnySignal implements WireDataType<Object> {
|
||||||
|
public final Object value;
|
||||||
|
|
||||||
|
public AnySignal(Object value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object get() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
package com.codigoparallevar.minicards.types.wireData;
|
||||||
|
|
||||||
|
import android.media.Image;
|
||||||
|
|
||||||
|
public class ImageSignal implements WireDataType<Image> {
|
||||||
|
public final Image value;
|
||||||
|
|
||||||
|
public ImageSignal(Image value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Image get() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
package com.codigoparallevar.minicards.ui_helpers;
|
||||||
|
|
||||||
|
import android.os.AsyncTask;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.codigoparallevar.minicards.types.functional.Action;
|
||||||
|
import com.codigoparallevar.minicards.types.functional.Consumer;
|
||||||
|
import com.codigoparallevar.minicards.types.functional.Tuple2;
|
||||||
|
|
||||||
|
public class DoAsync extends AsyncTask<Tuple2<Action, Consumer<Throwable>>,
|
||||||
|
Void, Void> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Void doInBackground(Tuple2<Action, Consumer<Throwable>>... data) {
|
||||||
|
try {
|
||||||
|
data[0].item1.run();
|
||||||
|
}
|
||||||
|
catch (Throwable ex) {
|
||||||
|
try {
|
||||||
|
data[0].item2.apply(ex);
|
||||||
|
} catch (Throwable subEx) {
|
||||||
|
Log.e("DoAsync", "Error handling exception", subEx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,46 @@
|
|||||||
|
package com.codigoparallevar.minicards.ui_helpers;
|
||||||
|
|
||||||
|
import android.os.AsyncTask;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.codigoparallevar.minicards.types.functional.Consumer;
|
||||||
|
import com.codigoparallevar.minicards.types.functional.Producer;
|
||||||
|
import com.codigoparallevar.minicards.types.functional.Tuple2;
|
||||||
|
import com.codigoparallevar.minicards.types.functional.Tuple3;
|
||||||
|
|
||||||
|
public class GetAsync<T> extends AsyncTask<Tuple3<Producer<T>, Consumer<T>, Consumer<Throwable>>,
|
||||||
|
Void,
|
||||||
|
Tuple2<T, Consumer<T>>> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Tuple2<T, Consumer<T>> doInBackground(Tuple3<Producer<T>, Consumer<T>, Consumer<Throwable>>... data) {
|
||||||
|
try {
|
||||||
|
T result = data[0]._x.get();
|
||||||
|
return new Tuple2<>(result, data[0]._y);
|
||||||
|
}
|
||||||
|
catch (Throwable ex) {
|
||||||
|
try {
|
||||||
|
data[0]._z.apply(ex);
|
||||||
|
}
|
||||||
|
catch (Throwable subEx) {
|
||||||
|
Log.d("GetAsync", "Error handling exception", subEx);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPostExecute(Tuple2<T, Consumer<T>> result) {
|
||||||
|
if (result == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
try {
|
||||||
|
result.item2.apply(result.item1);
|
||||||
|
}
|
||||||
|
catch (Throwable ex) {
|
||||||
|
Log.e("GetAsync", "Error on UI thread", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
package com.codigoparallevar.minicards.utils;
|
||||||
|
|
||||||
|
public class Box<T> {
|
||||||
|
private T val = null;
|
||||||
|
|
||||||
|
public Box() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public Box(T val) {
|
||||||
|
this.val = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
public T get() {
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void set(T val) {
|
||||||
|
this.val = val;
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,10 @@
|
|||||||
package com.codigoparallevar.minicards.utils;
|
package com.codigoparallevar.minicards.utils;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.json.JSONArray;
|
import org.json.JSONArray;
|
||||||
|
import org.json.JSONException;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -8,6 +12,8 @@ import java.util.Map;
|
|||||||
|
|
||||||
public class Serializations {
|
public class Serializations {
|
||||||
|
|
||||||
|
private static final String LogTag = "Serialization";
|
||||||
|
|
||||||
public static JSONArray serialize(List<Map<String,String>> data) {
|
public static JSONArray serialize(List<Map<String,String>> data) {
|
||||||
JSONArray array = new JSONArray();
|
JSONArray array = new JSONArray();
|
||||||
for (Map<String, String> dict : data) {
|
for (Map<String, String> dict : data) {
|
||||||
@ -16,4 +22,68 @@ public class Serializations {
|
|||||||
|
|
||||||
return array;
|
return array;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public static Object blindSerialize(Object obj) {
|
||||||
|
if (obj == null) {
|
||||||
|
return JSONObject.NULL;
|
||||||
|
}
|
||||||
|
// No serialization needed
|
||||||
|
else if (obj.getClass().isPrimitive() ||
|
||||||
|
obj instanceof String ||
|
||||||
|
obj instanceof JSONObject ||
|
||||||
|
obj instanceof JSONArray) {
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return _blindSerialize(obj);
|
||||||
|
}
|
||||||
|
catch (Exception ex) {
|
||||||
|
Log.w(LogTag, "Error serialization: " + obj, ex);
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Object _blindSerialize(Object obj) throws JSONException {
|
||||||
|
if (obj instanceof Map) {
|
||||||
|
Map<?,?> objMap = (Map<?,?>) obj;
|
||||||
|
|
||||||
|
JSONObject map = new JSONObject();
|
||||||
|
|
||||||
|
for (Map.Entry<?,?> entry : objMap.entrySet()) {
|
||||||
|
map.put(entry.getKey().toString(), blindSerialize(entry.getValue()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
else if (obj instanceof Iterable) {
|
||||||
|
Iterable objIt = (Iterable) obj;
|
||||||
|
|
||||||
|
JSONArray arr = new JSONArray();
|
||||||
|
for (Object val : objIt) {
|
||||||
|
arr.put(blindSerialize(val));
|
||||||
|
}
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Unknown case
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getString(JSONObject map, String key) {
|
||||||
|
if (!map.has(key)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (map.opt(key) == JSONObject.NULL) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return map.getString(key);
|
||||||
|
} catch (JSONException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
421
app/src/main/java/com/programaker/api/ProgramakerApi.kt
Normal file
421
app/src/main/java/com/programaker/api/ProgramakerApi.kt
Normal file
@ -0,0 +1,421 @@
|
|||||||
|
package com.programaker.api
|
||||||
|
|
||||||
|
import android.os.Build
|
||||||
|
import android.util.Log
|
||||||
|
import com.google.gson.Gson
|
||||||
|
import com.google.gson.GsonBuilder
|
||||||
|
import com.programaker.api.data.*
|
||||||
|
import com.programaker.api.data.api_results.*
|
||||||
|
import com.programaker.api.exceptions.ProgramakerLoginRequiredException
|
||||||
|
import com.programaker.api.exceptions.ProgramakerProtocolException
|
||||||
|
import com.programaker.api.exceptions.TokenNotFoundException
|
||||||
|
import org.json.JSONArray
|
||||||
|
import org.json.JSONObject
|
||||||
|
import java.io.DataOutputStream
|
||||||
|
import java.io.FileNotFoundException
|
||||||
|
import java.io.InputStream
|
||||||
|
import java.io.InputStreamReader
|
||||||
|
import java.lang.reflect.Type
|
||||||
|
import java.net.HttpURLConnection
|
||||||
|
import java.net.URL
|
||||||
|
import java.net.URLConnection
|
||||||
|
|
||||||
|
|
||||||
|
class ProgramakerApi(private val ApiRoot: String="https://programaker.com/api") {
|
||||||
|
private val enumCallbackCache: MutableMap<String, List<ProgramakerCustomBlockArgumentValue>> = HashMap()
|
||||||
|
private val LogTag: String = "ProgramakerApi"
|
||||||
|
|
||||||
|
private var userId: String? = null
|
||||||
|
private var userName: String? = null
|
||||||
|
var token: String? = null
|
||||||
|
|
||||||
|
// API
|
||||||
|
fun check(): Boolean {
|
||||||
|
val conn = URL(getCheckUrl()).openConnection() as HttpURLConnection
|
||||||
|
if (conn == null){
|
||||||
|
Log.e(LogTag, "URL Connection not established, set to NULL")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
addAuthHeader(conn)
|
||||||
|
} catch(ex: TokenNotFoundException) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
val result: ProgramakerCheckResult
|
||||||
|
try {
|
||||||
|
result = parseJson(conn.inputStream, ProgramakerCheckResult::class.java)
|
||||||
|
} catch(ex: JsonParseException) {
|
||||||
|
ex.logError(LogTag)
|
||||||
|
return false
|
||||||
|
} catch (ex: FileNotFoundException) {
|
||||||
|
ex.logError(LogTag)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.success) {
|
||||||
|
this.userId = result.user_id;
|
||||||
|
this.userName = result.username;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.success
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun login(user: String, password: String): String {
|
||||||
|
val conn = URL(getLoginUrl()).openConnection() as HttpURLConnection
|
||||||
|
conn.setRequestProperty("Content-Type", "application/json")
|
||||||
|
|
||||||
|
conn.requestMethod = "POST";
|
||||||
|
conn.doOutput = true;
|
||||||
|
|
||||||
|
val postData = JSONObject()
|
||||||
|
postData.put("username", user)
|
||||||
|
postData.put("password", password)
|
||||||
|
|
||||||
|
val wr = DataOutputStream(conn.outputStream)
|
||||||
|
wr.writeBytes(postData.toString());
|
||||||
|
wr.flush();
|
||||||
|
wr.close();
|
||||||
|
|
||||||
|
val result: ProgramakerLoginResult
|
||||||
|
result = parseJson(conn.inputStream, ProgramakerLoginResult::class.java)
|
||||||
|
return result.token
|
||||||
|
}
|
||||||
|
|
||||||
|
fun fetchCustomBlocks(): List<ProgramakerBridgeCustomBlockResult> {
|
||||||
|
val conn = URL(getCustomBlocksUrl()).openConnection() as HttpURLConnection
|
||||||
|
|
||||||
|
addAuthHeader(conn)
|
||||||
|
|
||||||
|
val result: ProgramakerGetCustomBlocksResult
|
||||||
|
|
||||||
|
try {
|
||||||
|
val builder = GsonBuilder()
|
||||||
|
builder.registerTypeAdapter(ProgramakerGetCustomBlocksResult::class.java, ProgramakerGetCustomBlocksResultTypeAdapter())
|
||||||
|
|
||||||
|
val gson = builder.create()
|
||||||
|
val reader = InputStreamReader(conn.inputStream)
|
||||||
|
|
||||||
|
result = gson.fromJson(reader, ProgramakerGetCustomBlocksResult::class.java)
|
||||||
|
} catch(ex: JsonParseException) {
|
||||||
|
ex.logError(LogTag)
|
||||||
|
throw ProgramakerProtocolException()
|
||||||
|
} catch (ex: FileNotFoundException) {
|
||||||
|
ex.logError(LogTag)
|
||||||
|
throw ProgramakerProtocolException()
|
||||||
|
}
|
||||||
|
catch (ex: Exception) {
|
||||||
|
Log.e(LogTag, "Unexpected exception: " + ex, ex)
|
||||||
|
throw ProgramakerProtocolException()
|
||||||
|
}
|
||||||
|
return result.result
|
||||||
|
}
|
||||||
|
|
||||||
|
fun fetchConnectedBridges(): List<ProgramakerBridgeInfo> {
|
||||||
|
val conn = URL(getConnectedBridgesUrl()).openConnection() as HttpURLConnection
|
||||||
|
|
||||||
|
addAuthHeader(conn)
|
||||||
|
|
||||||
|
val result: ProgramakerGetBridgeInfoResult
|
||||||
|
|
||||||
|
try {
|
||||||
|
val builder = GsonBuilder()
|
||||||
|
builder.registerTypeAdapter(ProgramakerGetBridgeInfoResult::class.java, ProgramakerGetBridgeInfoTypeAdapter())
|
||||||
|
|
||||||
|
val gson = builder.create()
|
||||||
|
val reader = InputStreamReader(conn.inputStream)
|
||||||
|
|
||||||
|
result = gson.fromJson(reader, ProgramakerGetBridgeInfoResult::class.java)
|
||||||
|
} catch(ex: JsonParseException) {
|
||||||
|
ex.logError(LogTag)
|
||||||
|
throw ProgramakerProtocolException()
|
||||||
|
} catch (ex: FileNotFoundException) {
|
||||||
|
ex.logError(LogTag)
|
||||||
|
throw ProgramakerProtocolException()
|
||||||
|
}
|
||||||
|
return result.result
|
||||||
|
}
|
||||||
|
|
||||||
|
fun fetchAllowedValues(blockArg: ProgramakerCustomBlockArgument): List<ProgramakerCustomBlockArgumentValue>? {
|
||||||
|
val url = getArgumentValuesUrl(blockArg)
|
||||||
|
val cachedValues = cachedAllowedValues(url)
|
||||||
|
if (cachedValues != null) {
|
||||||
|
return cachedValues
|
||||||
|
}
|
||||||
|
|
||||||
|
val conn = URL(url).openConnection() as HttpURLConnection
|
||||||
|
|
||||||
|
addAuthHeader(conn)
|
||||||
|
|
||||||
|
var results: List<ProgramakerCustomBlockArgumentValue>? = null
|
||||||
|
try {
|
||||||
|
val serialized: HashMap<String,*> = parseJson(conn.inputStream, HashMap::class.java)
|
||||||
|
val returnInfo = ProgramakerCustomBlockArgumentValuesReturnInfo.deserialize(serialized)
|
||||||
|
if (!returnInfo.success) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
results = returnInfo.values
|
||||||
|
} catch (ex: FileNotFoundException) {
|
||||||
|
ex.logError(LogTag)
|
||||||
|
return null
|
||||||
|
} catch(ex: Exception) {
|
||||||
|
Log.e(LogTag, ex.toString(), ex)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
cacheValues(url, results)
|
||||||
|
return results
|
||||||
|
}
|
||||||
|
|
||||||
|
fun callBlock(block: ProgramakerCustomBlock, arguments: List<String>): ProgramakerFunctionCallResult {
|
||||||
|
val conn = URL(getBlockUrl(block)).openConnection() as HttpURLConnection
|
||||||
|
conn.setRequestProperty("Content-Type", "application/json")
|
||||||
|
addAuthHeader(conn)
|
||||||
|
|
||||||
|
conn.requestMethod = "POST";
|
||||||
|
conn.doOutput = true;
|
||||||
|
|
||||||
|
val postData = JSONObject(hashMapOf(
|
||||||
|
"arguments" to JSONArray(arguments)
|
||||||
|
) as Map<*, *>)
|
||||||
|
|
||||||
|
val wr = DataOutputStream(conn.outputStream)
|
||||||
|
wr.writeBytes(postData.toString());
|
||||||
|
wr.flush();
|
||||||
|
wr.close();
|
||||||
|
|
||||||
|
val result: ProgramakerFunctionCallResult
|
||||||
|
result = parseJson(conn.inputStream, ProgramakerFunctionCallResult::class.java)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createBridge(name: String): String {
|
||||||
|
val conn = URL(getCreateBridgeUrl()).openConnection() as HttpURLConnection
|
||||||
|
conn.setRequestProperty("Content-Type", "application/json")
|
||||||
|
addAuthHeader(conn)
|
||||||
|
|
||||||
|
conn.requestMethod = "POST";
|
||||||
|
conn.doOutput = true;
|
||||||
|
|
||||||
|
val postData = JSONObject(hashMapOf(
|
||||||
|
"name" to name
|
||||||
|
) as Map<*, *>)
|
||||||
|
|
||||||
|
val wr = DataOutputStream(conn.outputStream)
|
||||||
|
wr.writeBytes(postData.toString());
|
||||||
|
wr.flush();
|
||||||
|
wr.close();
|
||||||
|
|
||||||
|
val result: ProgramakerCreateBridgeResult
|
||||||
|
result = parseJson(conn.inputStream, ProgramakerCreateBridgeResult::class.java)
|
||||||
|
return result.getBridgeId()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun createBridgeAuthenticationToken(bridgeId: String, tokenName: String): String {
|
||||||
|
val conn = URL(getCreateBridgeAuthenticationTokenUrl(bridgeId)).openConnection() as HttpURLConnection
|
||||||
|
conn.setRequestProperty("Content-Type", "application/json")
|
||||||
|
addAuthHeader(conn)
|
||||||
|
|
||||||
|
conn.requestMethod = "POST";
|
||||||
|
conn.doOutput = true;
|
||||||
|
|
||||||
|
val postData = JSONObject(hashMapOf(
|
||||||
|
"name" to tokenName
|
||||||
|
) as Map<*, *>)
|
||||||
|
|
||||||
|
val wr = DataOutputStream(conn.outputStream)
|
||||||
|
wr.writeBytes(postData.toString());
|
||||||
|
wr.flush();
|
||||||
|
wr.close();
|
||||||
|
|
||||||
|
val result: ProgramakerFullBridgeInfo
|
||||||
|
result = parseJson(conn.inputStream, ProgramakerFullBridgeInfo::class.java)
|
||||||
|
return result.key
|
||||||
|
}
|
||||||
|
|
||||||
|
fun establishConnection(bridgeId: String): Boolean {
|
||||||
|
// NOTE: This establishes a connection to a bridge as long as it can be done without interaction
|
||||||
|
|
||||||
|
// Prepare connection
|
||||||
|
val prepareConn = URL(getPrepareConnectionUrl(bridgeId)).openConnection() as HttpURLConnection
|
||||||
|
addAuthHeader(prepareConn)
|
||||||
|
|
||||||
|
val prepareResult: ProgramakerPrepareConnectionResult
|
||||||
|
prepareResult = parseJson(prepareConn.inputStream, ProgramakerPrepareConnectionResult::class.java)
|
||||||
|
if (prepareResult.type != "direct") {
|
||||||
|
throw Exception("Expected 'direct' connection type, found '${prepareResult.type}'")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Establish connection
|
||||||
|
val establishConn = URL(getEstablishConnectionUrl(bridgeId)).openConnection() as HttpURLConnection
|
||||||
|
establishConn.setRequestProperty("Content-Type", "application/json")
|
||||||
|
addAuthHeader(establishConn)
|
||||||
|
|
||||||
|
establishConn.requestMethod = "POST";
|
||||||
|
establishConn.doOutput = true;
|
||||||
|
|
||||||
|
val establishWrite = DataOutputStream(establishConn.outputStream)
|
||||||
|
establishWrite.writeBytes(JSONObject().toString());
|
||||||
|
establishWrite.flush();
|
||||||
|
establishWrite.close();
|
||||||
|
|
||||||
|
val establishResult: ProgramakerEstablishDirectConnectionResult
|
||||||
|
establishResult = parseJson(establishConn.inputStream, ProgramakerEstablishDirectConnectionResult::class.java)
|
||||||
|
return establishResult.success
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialization
|
||||||
|
init {
|
||||||
|
// Disable connection reuse if necessary
|
||||||
|
// HTTP connection reuse which was buggy pre-froyo
|
||||||
|
if (Build.VERSION.SDK.toInt() < Build.VERSION_CODES.FROYO) {
|
||||||
|
System.setProperty("http.keepAlive", "false")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun openChannelTo(bridgeId: String, key: String,
|
||||||
|
listener: ProgramakerSignalListener,
|
||||||
|
onDisconnectCallback: Runnable): ProgramakerListeningChannel {
|
||||||
|
val channel = ProgramakerListeningChannel(
|
||||||
|
bridgeId, key,
|
||||||
|
this.token!!,
|
||||||
|
getListenSignalUrl(bridgeId, key),
|
||||||
|
listener, onDisconnectCallback)
|
||||||
|
channel.start()
|
||||||
|
return channel
|
||||||
|
}
|
||||||
|
|
||||||
|
// Private functions
|
||||||
|
private fun getCheckUrl(): String {
|
||||||
|
return "$ApiRoot/v0/sessions/check"
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getLoginUrl(): String {
|
||||||
|
return "$ApiRoot/v0/sessions/login"
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getCustomBlocksUrl(): String {
|
||||||
|
this.withUserName()
|
||||||
|
return "$ApiRoot/v0/users/$userName/custom-blocks/"
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getConnectedBridgesUrl(): String {
|
||||||
|
this.withUserName()
|
||||||
|
return "$ApiRoot/v0/users/$userName/bridges/"
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getBlockUrl(block: ProgramakerCustomBlock): String {
|
||||||
|
this.withUserId()
|
||||||
|
return "$ApiRoot/v0/users/id/$userId/bridges/id/${block.bridge_id}/functions/${block.function_name}"
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getCreateBridgeUrl(): String {
|
||||||
|
this.withUserName()
|
||||||
|
return "$ApiRoot/v0/users/$userName/bridges"
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getCreateBridgeAuthenticationTokenUrl(bridgeId: String): String {
|
||||||
|
this.withUserId()
|
||||||
|
return "$ApiRoot/v0/bridges/by-id/$bridgeId/tokens"
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getPrepareConnectionUrl(bridgeId: String): String {
|
||||||
|
this.withUserId()
|
||||||
|
return "$ApiRoot/v0/services/by-id/$bridgeId/how-to-enable"
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getEstablishConnectionUrl(bridgeId: String): String {
|
||||||
|
this.withUserName()
|
||||||
|
return "$ApiRoot/v0/users/$userName/services/id/$bridgeId/register"
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getListenSignalUrl(bridgeId: String, key: String): String {
|
||||||
|
this.withUserId()
|
||||||
|
return "$ApiRoot/v0/users/id/$userId/bridges/id/$bridgeId/signals/$key"
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getArgumentValuesUrl(blockArg: ProgramakerCustomBlockArgument): String {
|
||||||
|
this.withUserId()
|
||||||
|
return "$ApiRoot/v0/users/id/$userId/bridges/id/${blockArg.bridge_id}/callback/${blockArg.callback}"
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getUserId(): String? {
|
||||||
|
this.withUserId()
|
||||||
|
return userId;
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun withUserName() {
|
||||||
|
if (userName == null) {
|
||||||
|
if (token == null) {
|
||||||
|
throw ProgramakerLoginRequiredException()
|
||||||
|
}
|
||||||
|
|
||||||
|
this.check();
|
||||||
|
if (userName == null) {
|
||||||
|
throw ProgramakerProtocolException()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun withUserId() {
|
||||||
|
if (userId == null) {
|
||||||
|
if (token == null) {
|
||||||
|
throw ProgramakerLoginRequiredException()
|
||||||
|
}
|
||||||
|
|
||||||
|
this.check();
|
||||||
|
if (userId == null) {
|
||||||
|
throw ProgramakerProtocolException()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun addAuthHeader(conn: URLConnection) {
|
||||||
|
if (token != null) {
|
||||||
|
conn.setRequestProperty("Authorization", token)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw TokenNotFoundException()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Simple result caching
|
||||||
|
private fun cacheValues(url: String, results: List<ProgramakerCustomBlockArgumentValue>) {
|
||||||
|
enumCallbackCache[url] = results
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun cachedAllowedValues(url: String): List<ProgramakerCustomBlockArgumentValue>? {
|
||||||
|
if (enumCallbackCache.containsKey(url)) {
|
||||||
|
return enumCallbackCache[url]
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
fun hasCachedAllowedValues(blockArg: ProgramakerCustomBlockArgument): Boolean {
|
||||||
|
if (userId == null) {
|
||||||
|
// This will be required to fully form the URL.
|
||||||
|
// Also, without it, it's not possible to have cached values. We can safely discard it
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
val url = getArgumentValuesUrl(blockArg)
|
||||||
|
return enumCallbackCache.containsKey(url)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Utils
|
||||||
|
private fun <T>parseJson(content: InputStream, resultClass: Type): T {
|
||||||
|
val reader = InputStreamReader(content)
|
||||||
|
val gson = Gson()
|
||||||
|
return gson.fromJson(reader, resultClass)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun FileNotFoundException.logError(tag: String) {
|
||||||
|
Log.e(tag, "Cannot open: ${this.message}")
|
||||||
|
}
|
||||||
|
|
||||||
|
class JsonParseException(private val content: String, private val cls: Type) : Exception() {
|
||||||
|
fun logError(tag: String) {
|
||||||
|
Log.e(tag, "Cannot JSON parse: ${this.content} as ${this.cls}", this)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,78 @@
|
|||||||
|
package com.programaker.api
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
|
import com.google.gson.Gson
|
||||||
|
import okhttp3.*
|
||||||
|
import okio.ByteString
|
||||||
|
import org.json.JSONException
|
||||||
|
import java.nio.charset.Charset
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
|
class ProgramakerListeningChannel(
|
||||||
|
private val bridgeId: String,
|
||||||
|
private val key: String,
|
||||||
|
private val token: String,
|
||||||
|
private val url: String,
|
||||||
|
private val listener: ProgramakerSignalListener,
|
||||||
|
private var onDisconnect: Runnable
|
||||||
|
): WebSocketListener() {
|
||||||
|
private var webSocket: WebSocket? = null
|
||||||
|
private val utf8: Charset = Charset.forName("UTF-8")
|
||||||
|
private val gson = Gson()
|
||||||
|
|
||||||
|
private val LogTag: String = "PM-ListeningChannel"
|
||||||
|
private val PING_PERIOD_MILLIS: Long = 15000 // 15seconds
|
||||||
|
|
||||||
|
fun start() {
|
||||||
|
val client: OkHttpClient = OkHttpClient.Builder()
|
||||||
|
.pingInterval(PING_PERIOD_MILLIS, TimeUnit.MILLISECONDS)
|
||||||
|
.readTimeout(0, TimeUnit.MILLISECONDS)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
val request: Request = Request.Builder()
|
||||||
|
.url(url)
|
||||||
|
.addHeader("Authorization", token)
|
||||||
|
.build()
|
||||||
|
client.newWebSocket(request, this)
|
||||||
|
|
||||||
|
// Trigger shutdown of the dispatcher's executor so this process can exit cleanly.
|
||||||
|
client.dispatcher.executorService.shutdown()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Websocket management
|
||||||
|
override fun onOpen(webSocket: WebSocket, response: Response) {
|
||||||
|
this.webSocket = webSocket
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onMessage(webSocket: WebSocket, bytes: ByteString) {
|
||||||
|
onMessage(webSocket, bytes.string(utf8))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onMessage(webSocket: WebSocket, text: String) {
|
||||||
|
Log.d(LogTag, "Message: $text")
|
||||||
|
val json = gson.fromJson(text, HashMap::class.java)
|
||||||
|
if (json == null) {
|
||||||
|
this.onFailure(webSocket, JSONException("Error decoding: $text"), null)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.listener.onNewSignal(bridgeId, key, json)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onClosing(webSocket: WebSocket, code: Int, reason: String) {
|
||||||
|
webSocket.close(1000, null)
|
||||||
|
Log.i(LogTag, "Closing bridge socket {code=$code, reason=$reason}")
|
||||||
|
onDisconnect.run()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) {
|
||||||
|
Log.e(LogTag, "Error: $t", t)
|
||||||
|
onDisconnect.run()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun stop() {
|
||||||
|
onDisconnect = Runnable { } // Skip disconnection procedure
|
||||||
|
webSocket?.close(1000, null)
|
||||||
|
webSocket = null
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
package com.programaker.api
|
||||||
|
|
||||||
|
import java.util.HashMap
|
||||||
|
|
||||||
|
interface ProgramakerSignalListener {
|
||||||
|
fun onNewSignal(bridgeId: String, key: String, signal: HashMap<*, *>)
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
package com.programaker.api.data
|
||||||
|
|
||||||
|
class ProgramakerBridgeInfo (
|
||||||
|
val id: String,
|
||||||
|
val name: String
|
||||||
|
// val is_connected: boolean
|
||||||
|
// val icon: Map<String, Object>
|
||||||
|
) {
|
||||||
|
}
|
@ -0,0 +1,73 @@
|
|||||||
|
package com.programaker.api.data
|
||||||
|
|
||||||
|
import org.json.JSONArray
|
||||||
|
import org.json.JSONObject
|
||||||
|
|
||||||
|
class ProgramakerCustomBlock(
|
||||||
|
val block_id: String,
|
||||||
|
val function_name: String,
|
||||||
|
val message: String,
|
||||||
|
val arguments: List<ProgramakerCustomBlockArgument>?,
|
||||||
|
val block_type: String?,
|
||||||
|
val block_result_type: String?,
|
||||||
|
var bridge_id: String?,
|
||||||
|
val save_to: ProgramakerCustomBlockSaveTo?,
|
||||||
|
var key: String?,
|
||||||
|
val subkey: ProgramakerCustomBlockSubkeyDefinition?
|
||||||
|
) {
|
||||||
|
fun serialize(): JSONObject {
|
||||||
|
|
||||||
|
val serializedArguments = JSONArray()
|
||||||
|
if (arguments != null) {
|
||||||
|
for (value in arguments) {
|
||||||
|
serializedArguments.put(value.serialize())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var saveToSerialized: JSONObject? = null
|
||||||
|
if (save_to != null) {
|
||||||
|
saveToSerialized = save_to.serialize()
|
||||||
|
}
|
||||||
|
|
||||||
|
val serialized = hashMapOf(
|
||||||
|
"block_id" to block_id,
|
||||||
|
"function_name" to function_name,
|
||||||
|
"message" to message,
|
||||||
|
"arguments" to serializedArguments,
|
||||||
|
"block_type" to block_type,
|
||||||
|
"bridge_id" to bridge_id,
|
||||||
|
"save_to" to saveToSerialized
|
||||||
|
)
|
||||||
|
|
||||||
|
if (key != null) {
|
||||||
|
serialized.put("key", key)
|
||||||
|
serialized.put("subkey", subkey?.serialize())
|
||||||
|
}
|
||||||
|
if (block_result_type != null || key == null) {
|
||||||
|
serialized.put("block_result_type", block_result_type)
|
||||||
|
}
|
||||||
|
|
||||||
|
return JSONObject(serialized as Map<*, *>)
|
||||||
|
}
|
||||||
|
companion object {
|
||||||
|
@JvmStatic
|
||||||
|
fun deserialize(obj: JSONObject): ProgramakerCustomBlock {
|
||||||
|
var block = ProgramakerCustomBlock(
|
||||||
|
obj.getString("block_id"),
|
||||||
|
obj.getString("function_name"),
|
||||||
|
obj.getString("message"),
|
||||||
|
ProgramakerCustomBlockArgument.deserialize(obj.optJSONArray("arguments")),
|
||||||
|
obj.getString("block_type"),
|
||||||
|
obj.optString("block_result_type"),
|
||||||
|
obj.optString("bridge_id"),
|
||||||
|
ProgramakerCustomBlockSaveTo.deserialize(obj.optJSONObject("save_to")),
|
||||||
|
obj.optString("key"),
|
||||||
|
ProgramakerCustomBlockSubkeyDefinition.deserialize(obj.optJSONObject("subkey"))
|
||||||
|
)
|
||||||
|
|
||||||
|
return block
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,73 @@
|
|||||||
|
package com.programaker.api.data
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
|
import org.json.JSONArray
|
||||||
|
import org.json.JSONObject
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
class ProgramakerCustomBlockArgument(
|
||||||
|
val type: String?,
|
||||||
|
val default_value: String?,
|
||||||
|
// val class: String,
|
||||||
|
val callback: String?
|
||||||
|
) {
|
||||||
|
var bridge_id: String? = null
|
||||||
|
|
||||||
|
fun serialize(): JSONObject {
|
||||||
|
val serialized = hashMapOf<String, String?>(
|
||||||
|
"type" to type,
|
||||||
|
"default_value" to default_value,
|
||||||
|
"callback" to callback,
|
||||||
|
"bridge_id" to bridge_id
|
||||||
|
)
|
||||||
|
|
||||||
|
return JSONObject(serialized as Map<*, *>)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getComputedType(): String? {
|
||||||
|
if (callback != null && callback != "null") {
|
||||||
|
return "ENUM";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
@JvmStatic
|
||||||
|
fun deserialize(arguments: JSONArray?): List<ProgramakerCustomBlockArgument> {
|
||||||
|
if (arguments == null) {
|
||||||
|
return listOf();
|
||||||
|
}
|
||||||
|
val results: MutableList<ProgramakerCustomBlockArgument> = LinkedList()
|
||||||
|
|
||||||
|
var i = 0;
|
||||||
|
while (i < arguments.length()) {
|
||||||
|
val obj = arguments.getJSONObject(i)
|
||||||
|
if (obj == null) {
|
||||||
|
Log.e("PMCustomBlockArgument", "Looped into a null value!?")
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
results.add(deserialize(obj))
|
||||||
|
}
|
||||||
|
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return results
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun deserialize(arguments: JSONObject): ProgramakerCustomBlockArgument {
|
||||||
|
val type: String? = arguments.optString("type")
|
||||||
|
val default_value: String? = arguments.optString("default_value")
|
||||||
|
val callback: String? = arguments.optString("callback")
|
||||||
|
val bridge_id: String? = arguments.optString("bridge_id")
|
||||||
|
|
||||||
|
val arg = ProgramakerCustomBlockArgument(type, default_value, callback)
|
||||||
|
arg.bridge_id = bridge_id
|
||||||
|
return arg
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,67 @@
|
|||||||
|
package com.programaker.api.data
|
||||||
|
|
||||||
|
import com.programaker.api.optGet
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
class ProgramakerCustomBlockArgumentValue (
|
||||||
|
val id: String,
|
||||||
|
val name: String
|
||||||
|
) {
|
||||||
|
companion object {
|
||||||
|
@JvmStatic
|
||||||
|
fun deserializeList(value: Object): List<ProgramakerCustomBlockArgumentValue> {
|
||||||
|
if (value is Map<*, *>) {
|
||||||
|
return deserialize(value as Map<*, *>)
|
||||||
|
} else if (value is List<*>) {
|
||||||
|
return deserialize(value as List<*>)
|
||||||
|
} else {
|
||||||
|
throw IllegalArgumentException("Error deserializing: $value")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun deserialize(value: Map<*, *>): List<ProgramakerCustomBlockArgumentValue> {
|
||||||
|
val results = LinkedList<ProgramakerCustomBlockArgumentValue>()
|
||||||
|
for (entry in value.asIterable()) {
|
||||||
|
var value = entry.value.toString()
|
||||||
|
if (entry.value is Map<*, *>) {
|
||||||
|
if ((entry.value as Map<String, *>).containsKey("name")) {
|
||||||
|
value = (entry.value as Map<*, *>)["name"].toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
results.add(ProgramakerCustomBlockArgumentValue(entry.key.toString(), value))
|
||||||
|
}
|
||||||
|
|
||||||
|
return results
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun deserialize(value: List<*>): List<ProgramakerCustomBlockArgumentValue> {
|
||||||
|
val results = LinkedList<ProgramakerCustomBlockArgumentValue>()
|
||||||
|
for (entry in value) {
|
||||||
|
if (entry is Map<*,*>) {
|
||||||
|
var name = (entry as Map<String, *>).optGet("name")
|
||||||
|
var id = (entry as Map<String, *>).optGet("id")
|
||||||
|
|
||||||
|
if (name == null && id == null) {
|
||||||
|
throw IllegalArgumentException("Error deserializing: $entry")
|
||||||
|
}
|
||||||
|
else if (name == null) {
|
||||||
|
name = id
|
||||||
|
}
|
||||||
|
else if (id == null) {
|
||||||
|
id = name
|
||||||
|
}
|
||||||
|
|
||||||
|
results.add(ProgramakerCustomBlockArgumentValue(id.toString(), name.toString()))
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw IllegalArgumentException("Error deserializing: $entry")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return results
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
package com.programaker.api.data
|
||||||
|
|
||||||
|
import com.programaker.api.optGet
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
class ProgramakerCustomBlockArgumentValuesReturnInfo(
|
||||||
|
val success: Boolean,
|
||||||
|
val values: List<ProgramakerCustomBlockArgumentValue>
|
||||||
|
) {
|
||||||
|
companion object {
|
||||||
|
@JvmStatic
|
||||||
|
fun deserialize(value: HashMap<String,*>): ProgramakerCustomBlockArgumentValuesReturnInfo {
|
||||||
|
val _success: Boolean? = value.optGet("success") as Boolean?
|
||||||
|
val _result: Object? = value.optGet("result") as Object
|
||||||
|
|
||||||
|
var success = false
|
||||||
|
if (_success != null) {
|
||||||
|
success = _success
|
||||||
|
}
|
||||||
|
|
||||||
|
var result: List<ProgramakerCustomBlockArgumentValue> = Collections.emptyList<ProgramakerCustomBlockArgumentValue>()
|
||||||
|
if (_result != null) {
|
||||||
|
result = ProgramakerCustomBlockArgumentValue.deserializeList(_result)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ProgramakerCustomBlockArgumentValuesReturnInfo(success, result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
package com.programaker.api.data
|
||||||
|
|
||||||
|
import org.json.JSONObject
|
||||||
|
|
||||||
|
class ProgramakerCustomBlockSaveTo (
|
||||||
|
val type: String,
|
||||||
|
val index: Int
|
||||||
|
) {
|
||||||
|
fun serialize(): JSONObject {
|
||||||
|
return JSONObject(hashMapOf(
|
||||||
|
"type" to type,
|
||||||
|
"index" to index
|
||||||
|
) as Map<*, *>)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
@JvmStatic
|
||||||
|
fun deserialize(save_to: JSONObject?): ProgramakerCustomBlockSaveTo? {
|
||||||
|
if (save_to == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ProgramakerCustomBlockSaveTo(save_to.getString("type"),
|
||||||
|
save_to.getInt("index"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
package com.programaker.api.data
|
||||||
|
|
||||||
|
import org.json.JSONObject
|
||||||
|
|
||||||
|
class ProgramakerCustomBlockSubkeyDefinition(
|
||||||
|
val type: String,
|
||||||
|
val value: String
|
||||||
|
) {
|
||||||
|
fun serialize(): JSONObject {
|
||||||
|
return JSONObject(hashMapOf(
|
||||||
|
"type" to type,
|
||||||
|
"value" to value
|
||||||
|
) as Map<*, *>)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
@JvmStatic
|
||||||
|
fun deserialize(subkey: JSONObject?): ProgramakerCustomBlockSubkeyDefinition? {
|
||||||
|
if (subkey == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ProgramakerCustomBlockSubkeyDefinition(
|
||||||
|
subkey.getString("type"),
|
||||||
|
subkey.getString("value"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
package com.programaker.api.data
|
||||||
|
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
class ProgramakerFunctionCallResult (
|
||||||
|
var success: Boolean,
|
||||||
|
var result: Object
|
||||||
|
) {
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,6 @@
|
|||||||
|
package com.programaker.api.data.api_results
|
||||||
|
|
||||||
|
import com.programaker.api.data.ProgramakerCustomBlock
|
||||||
|
|
||||||
|
class ProgramakerBridgeCustomBlockResult(val bridge_id: String, val blocks: List<ProgramakerCustomBlock>) {
|
||||||
|
}
|
@ -0,0 +1,4 @@
|
|||||||
|
package com.programaker.api.data.api_results
|
||||||
|
|
||||||
|
class ProgramakerCheckResult(val success: Boolean, val user_id: String, val username: String) {
|
||||||
|
}
|
@ -0,0 +1,17 @@
|
|||||||
|
package com.programaker.api.data.api_results
|
||||||
|
|
||||||
|
class ProgramakerCreateBridgeResult (
|
||||||
|
val control_url: String
|
||||||
|
) {
|
||||||
|
fun getBridgeId(): String {
|
||||||
|
val url = control_url.trim('/');
|
||||||
|
if (url.endsWith("communication")) {
|
||||||
|
val no_communication = url.substring(0, url.length - "communication".length);
|
||||||
|
val chunks = no_communication.trim('/').split("/");
|
||||||
|
return chunks[chunks.size - 1]
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw IllegalArgumentException("Unknown control_url format")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
package com.programaker.api.data.api_results
|
||||||
|
|
||||||
|
class ProgramakerEstablishDirectConnectionResult(val success: Boolean) {
|
||||||
|
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user