Implement base bridge connection.
This commit is contained in:
parent
a9b5c8f02b
commit
da3dfd5d2e
@ -41,6 +41,8 @@ dependencies {
|
||||
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()
|
||||
|
@ -7,6 +7,7 @@
|
||||
android:required="true" />
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
|
@ -12,8 +12,6 @@ import androidx.appcompat.app.ActionBar;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import com.codigoparallevar.minicards.toolbox.PartsHolder;
|
||||
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;
|
||||
import com.codigoparallevar.minicards.ui_helpers.GetAsync;
|
||||
@ -67,26 +65,15 @@ public class CardActivity extends AppCompatActivity {
|
||||
|
||||
new GetAsync<Tuple2<List<ProgramakerBridgeInfo>, List<ProgramakerBridgeCustomBlockResult>>>()
|
||||
.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,
|
||||
new Tuple3<>(new Producer<Tuple2<List<ProgramakerBridgeInfo>, List<ProgramakerBridgeCustomBlockResult>>>() {
|
||||
@Override
|
||||
public Tuple2<List<ProgramakerBridgeInfo>, List<ProgramakerBridgeCustomBlockResult>> get() {
|
||||
return new Tuple2<>(
|
||||
CardActivity.this.ProgramakerApi.fetchConnectedBridges(),
|
||||
CardActivity.this.ProgramakerApi.fetchCustomBlocks());
|
||||
}
|
||||
}, new Consumer<Tuple2<List<ProgramakerBridgeInfo>,List<ProgramakerBridgeCustomBlockResult>>>() {
|
||||
@Override
|
||||
public void apply(Tuple2<List<ProgramakerBridgeInfo>,List<ProgramakerBridgeCustomBlockResult>> result) {
|
||||
partsHolder.addCustomBlocks(result.item1, result.item2);
|
||||
Log.d("CARDActivity", "custom blocks: " + result.toString());
|
||||
}
|
||||
}, new Consumer<Throwable> () {
|
||||
@Override
|
||||
public void apply(Throwable exception) {
|
||||
Log.e("CARDActivity", "error retrieving custom blocks: " + exception.toString());
|
||||
}
|
||||
}));
|
||||
|
||||
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
|
||||
ActionBar actionBar = getSupportActionBar();
|
||||
|
@ -2,14 +2,14 @@ package com.codigoparallevar.minicards;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.view.View;
|
||||
|
||||
import static android.content.Context.MODE_PRIVATE;
|
||||
|
||||
public class ConfigManager {
|
||||
private final Context context;
|
||||
private static final String TOKEN_KEY = "PROGRAMAKER_API_TOKEN";
|
||||
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";
|
||||
|
||||
public ConfigManager(Context ctx) {
|
||||
this.context = ctx;
|
||||
@ -20,16 +20,33 @@ public class ConfigManager {
|
||||
SharedPreferences.Editor edit = preferences.edit();
|
||||
|
||||
edit.putString(TOKEN_KEY, token);
|
||||
edit.commit();
|
||||
edit.apply();
|
||||
}
|
||||
|
||||
public String getToken() {
|
||||
SharedPreferences preferences = this.context.getSharedPreferences(PREFERENCES_NAME, MODE_PRIVATE);
|
||||
if (!preferences.contains(TOKEN_KEY)) {
|
||||
return null;
|
||||
}
|
||||
else {
|
||||
return preferences.getString(TOKEN_KEY, null);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -4,14 +4,6 @@ import android.app.Dialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
|
||||
import com.codigoparallevar.minicards.types.functional.Consumer;
|
||||
import com.codigoparallevar.minicards.types.functional.Tuple2;
|
||||
import com.codigoparallevar.minicards.types.functional.Tuple3;
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
import android.util.Log;
|
||||
@ -23,19 +15,32 @@ import android.widget.ListView;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
|
||||
import com.codigoparallevar.minicards.bridge.ProgramakerAndroidBridge;
|
||||
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.DoAsync;
|
||||
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.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Vector;
|
||||
|
||||
import com.programaker.api.ProgramakerApi;
|
||||
|
||||
public class DeckPreviewActivity extends ReloadableAppCompatActivity {
|
||||
public static final String INTENT = "com.codigoparallevar.minicards.DECK";
|
||||
private static final String LogTag = "DeckPreview";
|
||||
|
||||
private ListView listView;
|
||||
private CardPreviewArrayAdapter cardArrayAdapter;
|
||||
private ProgramakerApi ProgramakerApi;
|
||||
private ConfigManager Config;
|
||||
private ProgramakerAndroidBridge bridge = null;
|
||||
|
||||
protected void openLoginDialog(View view) {
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
@ -92,22 +97,20 @@ public class DeckPreviewActivity extends ReloadableAppCompatActivity {
|
||||
new Tuple3<>(
|
||||
loginUsernameText.getText().toString(),
|
||||
loginPasswordText.getText().toString(),
|
||||
new Consumer<String>() {
|
||||
public void apply(String 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);
|
||||
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
|
||||
new CheckNeededLoginButton().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,
|
||||
new Tuple2<>(DeckPreviewActivity.this.ProgramakerApi,
|
||||
loginToProgramakerButton));
|
||||
dialog.cancel();
|
||||
}
|
||||
loginToProgramakerButton.setVisibility(View.GONE);
|
||||
// Re-check... just in case
|
||||
checkNeededLoginButton(
|
||||
DeckPreviewActivity.this.ProgramakerApi,
|
||||
loginToProgramakerButton);
|
||||
dialog.cancel();
|
||||
}
|
||||
}));
|
||||
}
|
||||
@ -119,15 +122,14 @@ public class DeckPreviewActivity extends ReloadableAppCompatActivity {
|
||||
@Override
|
||||
protected Tuple2<String, Consumer<String>> doInBackground(Tuple3<String, String, Consumer<String>>... tuples) {
|
||||
ProgramakerApi api = new ProgramakerApi();
|
||||
boolean logged = false;
|
||||
String token = null;
|
||||
try {
|
||||
token = api.login(tuples[0]._x, tuples[0]._y);
|
||||
}
|
||||
catch (Exception e) {
|
||||
Log.e("Login to PrograMaker", e.toString());
|
||||
Log.e("Login to PrograMaker", e.toString(), e);
|
||||
}
|
||||
return new Tuple2<String, Consumer<String>>(token, tuples[0]._z);
|
||||
return new Tuple2<>(token, tuples[0]._z);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -136,20 +138,6 @@ public class DeckPreviewActivity extends ReloadableAppCompatActivity {
|
||||
}
|
||||
}
|
||||
|
||||
static class CheckNeededLoginButton extends AsyncTask<Tuple2<ProgramakerApi, Button>, Void, Tuple2<Boolean, Button>>{
|
||||
@Override
|
||||
protected Tuple2<Boolean, Button> doInBackground(Tuple2<ProgramakerApi, Button>... tuples) {
|
||||
return new Tuple2<>(tuples[0].item1.check(), tuples[0].item2);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Tuple2<Boolean, Button> result) {
|
||||
if (!result.item1) {
|
||||
result.item2.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
@ -183,14 +171,54 @@ public class DeckPreviewActivity extends ReloadableAppCompatActivity {
|
||||
loginButton.setVisibility(View.VISIBLE);
|
||||
}
|
||||
else {
|
||||
|
||||
this.ProgramakerApi.setToken(token);
|
||||
loginButton.setVisibility(View.GONE);
|
||||
// Double check that is not needed, token might have been deleted
|
||||
new CheckNeededLoginButton().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,
|
||||
new Tuple2<>(this.ProgramakerApi, loginButton));
|
||||
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 {
|
||||
String bridgeId = DeckPreviewActivity.this.Config.getBridgeId();
|
||||
if (bridgeId == null) {
|
||||
new GetAsync<String>().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,
|
||||
new Tuple3<>(
|
||||
() -> api.createBridge(ProgramakerAndroidBridge.GetBridgeName(this)),
|
||||
newBridgeId -> {
|
||||
DeckPreviewActivity.this.Config.setBridgeId(newBridgeId);
|
||||
this.bridge = ProgramakerAndroidBridge.configure(this, api.getUserId(), newBridgeId);
|
||||
new DoAsync().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,
|
||||
new Tuple2<>(
|
||||
() -> this.bridge.start(),
|
||||
ex -> Log.e(LogTag, "Error on bridge: " + ex, ex)
|
||||
));
|
||||
},
|
||||
ex -> Log.e(LogTag, "Error creating bridge: " + ex, ex)
|
||||
));
|
||||
}
|
||||
else {
|
||||
this.bridge = ProgramakerAndroidBridge.configure(this, api.getUserId(), bridgeId);
|
||||
new DoAsync().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,
|
||||
new Tuple2<>(
|
||||
() -> this.bridge.start(),
|
||||
ex -> Log.e(LogTag, "Error on bridge: " + ex, ex)
|
||||
));
|
||||
}
|
||||
}
|
||||
},
|
||||
ex -> Log.e(LogTag, "Error checking API:" + ex, ex)
|
||||
));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
|
@ -0,0 +1,52 @@
|
||||
package com.codigoparallevar.minicards.bridge;
|
||||
|
||||
import android.content.Context;
|
||||
import android.provider.Settings;
|
||||
|
||||
import com.programaker.bridge.ProgramakerBridge;
|
||||
import com.programaker.bridge.ProgramakerBridgeConfiguration;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
public class ProgramakerAndroidBridge {
|
||||
private static final String LogTag = "PM Android Bridge";
|
||||
private ProgramakerBridge bridgeRunner = null;
|
||||
|
||||
// Static
|
||||
public static ProgramakerAndroidBridge configure(Context ctx, String userId, String bridgeId) {
|
||||
return new ProgramakerAndroidBridge(ctx, userId, bridgeId);
|
||||
}
|
||||
|
||||
public static String GetBridgeName(Context ctx) {
|
||||
String deviceName = Settings.Secure.getString(ctx.getContentResolver(), "bluetooth_name");
|
||||
String serviceName = "MiniCards on " + deviceName;
|
||||
return serviceName;
|
||||
}
|
||||
|
||||
// public static ProgramakerBridgeConfiguration GetConfiguration(Context ctx) {
|
||||
// List<ProgramakerBridgeConfigurationBlock> blocks = new LinkedList<>();
|
||||
// return new ProgramakerBridgeConfiguration(serviceName, blocks);
|
||||
// }
|
||||
// Builder
|
||||
|
||||
private final Context ctx;
|
||||
private final String userId;
|
||||
private final String bridgeId;
|
||||
|
||||
private ProgramakerAndroidBridge(Context ctx, String userId, String bridgeId) {
|
||||
this.ctx = ctx;
|
||||
this.userId = userId;
|
||||
this.bridgeId = bridgeId;
|
||||
}
|
||||
|
||||
public void start() {
|
||||
ProgramakerBridgeConfiguration configuration = new ProgramakerBridgeConfiguration(
|
||||
ProgramakerAndroidBridge.GetBridgeName(this.ctx),
|
||||
Collections.emptyList()
|
||||
);
|
||||
|
||||
this.bridgeRunner = new ProgramakerBridge(this.bridgeId, this.userId, configuration);
|
||||
this.bridgeRunner.run();
|
||||
}
|
||||
|
||||
}
|
@ -354,15 +354,15 @@ public class ProgramakerCustomBlockPart implements Part {
|
||||
ProgramakerCustomBlockPart.this.runBlockOperation();
|
||||
|
||||
ProgramakerCustomBlockPart.this.freeBlock(token);
|
||||
}, param -> {
|
||||
}, ex -> {
|
||||
Log.e(LogTag, "Error executing function=" + this._block.getFunction_name()
|
||||
+ "; Error=" + param);
|
||||
+ "; Error=" + ex, ex);
|
||||
ProgramakerCustomBlockPart.this.freeBlock(token);
|
||||
}));
|
||||
}
|
||||
catch (Exception ex) {
|
||||
Log.e(LogTag, "Error executing function=" + this._block.getFunction_name()
|
||||
+ "; Error=" + ex);
|
||||
+ "; Error=" + ex, ex);
|
||||
this.freeBlock(token);
|
||||
}
|
||||
} else {
|
||||
@ -444,7 +444,7 @@ public class ProgramakerCustomBlockPart implements Part {
|
||||
index = Integer.parseInt(chunks[1]);
|
||||
}
|
||||
catch (NumberFormatException ex) {
|
||||
Log.e(LogTag, "Error parsing connector id="+inputConnectorId);
|
||||
Log.e(LogTag, "Error parsing connector id="+inputConnectorId, ex);
|
||||
}
|
||||
|
||||
if (index != null && index < inputConnectors.size()) {
|
||||
|
@ -107,7 +107,7 @@ class ProgramakerApi(private val ApiRoot: String="https://programaker.com/api")
|
||||
throw ProgramakerProtocolException()
|
||||
}
|
||||
catch (ex: Exception) {
|
||||
Log.e(LogTag, "Unexpected exception: " + ex)
|
||||
Log.e(LogTag, "Unexpected exception: " + ex, ex)
|
||||
throw ProgramakerProtocolException()
|
||||
}
|
||||
return result.result
|
||||
@ -137,6 +137,7 @@ class ProgramakerApi(private val ApiRoot: String="https://programaker.com/api")
|
||||
}
|
||||
return result.result
|
||||
}
|
||||
|
||||
fun callBlock(block: ProgramakerCustomBlock, arguments: List<String>): ProgramakerFunctionCallResult {
|
||||
val conn = URL(getBlockUrl(block)).openConnection() as HttpURLConnection
|
||||
conn.setRequestProperty("Content-Type", "application/json")
|
||||
@ -159,6 +160,28 @@ class ProgramakerApi(private val ApiRoot: String="https://programaker.com/api")
|
||||
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()
|
||||
}
|
||||
|
||||
// Initialization
|
||||
init {
|
||||
// Disable connection reuse if necessary
|
||||
@ -192,6 +215,16 @@ class ProgramakerApi(private val ApiRoot: String="https://programaker.com/api")
|
||||
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"
|
||||
}
|
||||
|
||||
fun getUserId(): String? {
|
||||
this.withUserId()
|
||||
return userId;
|
||||
}
|
||||
|
||||
private fun withUserName() {
|
||||
if (userName == null) {
|
||||
if (token == null) {
|
||||
@ -240,6 +273,6 @@ private fun FileNotFoundException.logError(tag: String) {
|
||||
|
||||
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}")
|
||||
Log.e(tag, "Cannot JSON parse: ${this.content} as ${this.cls}", this)
|
||||
}
|
||||
}
|
||||
|
@ -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")
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
package com.programaker.api.exceptions
|
||||
|
||||
class ProgramakerProtocolException : Throwable() {
|
||||
import java.lang.Exception
|
||||
|
||||
class ProgramakerProtocolException() : Exception() {
|
||||
}
|
||||
|
@ -0,0 +1,59 @@
|
||||
package com.programaker.bridge
|
||||
|
||||
import android.util.Log
|
||||
import okhttp3.*
|
||||
import okio.ByteString
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
|
||||
class ProgramakerBridge(
|
||||
val bridge_id: String,
|
||||
val user_id: String,
|
||||
val config: ProgramakerBridgeConfiguration
|
||||
) : WebSocketListener() {
|
||||
private val PING_PERIOD_MILLIS: Long = 15000
|
||||
private val PING_TIMEOUT_MILLIS: Long = 15000
|
||||
private val apiRoot: String = "wss://programaker.com/api"
|
||||
private val LogTag = "ProgramakerBridge"
|
||||
|
||||
fun run() {
|
||||
val client: OkHttpClient = OkHttpClient.Builder()
|
||||
.pingInterval(PING_PERIOD_MILLIS, TimeUnit.MILLISECONDS)
|
||||
.readTimeout(0, TimeUnit.MILLISECONDS)
|
||||
.build()
|
||||
|
||||
val request: Request = Request.Builder()
|
||||
.url(getBridgeControlUrl())
|
||||
.build()
|
||||
client.newWebSocket(request, this)
|
||||
|
||||
// Trigger shutdown of the dispatcher's executor so this process can exit cleanly.
|
||||
client.dispatcher.executorService.shutdown()
|
||||
}
|
||||
|
||||
|
||||
override fun onOpen(webSocket: WebSocket, response: Response) {
|
||||
webSocket.send(config.serialize())
|
||||
}
|
||||
|
||||
override fun onMessage(webSocket: WebSocket, text: String) {
|
||||
println("MESSAGE: $text")
|
||||
}
|
||||
|
||||
override fun onMessage(webSocket: WebSocket, bytes: ByteString) {
|
||||
println("MESSAGE: " + bytes.hex())
|
||||
}
|
||||
|
||||
override fun onClosing(webSocket: WebSocket, code: Int, reason: String) {
|
||||
webSocket.close(1000, null)
|
||||
Log.i(LogTag, "Closing bridge socket {code=$code, reason=$reason}")
|
||||
}
|
||||
|
||||
override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) {
|
||||
Log.e(LogTag, t.toString(), t)
|
||||
}
|
||||
|
||||
private fun getBridgeControlUrl(): String {
|
||||
return "$apiRoot/v0/users/id/$user_id/bridges/id/$bridge_id/communication"
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
package com.programaker.bridge
|
||||
|
||||
import org.json.JSONObject
|
||||
|
||||
class ProgramakerBridgeConfiguration(
|
||||
val service_name: String,
|
||||
val blocks: List<ProgramakerBridgeConfigurationBlock>
|
||||
// val is_public: boolean, // No reason for this use case
|
||||
// val registration: ???, // No reason for this use case
|
||||
// val allow_multiple_connections: boolean // No reason for this use case
|
||||
// val icon: ???, // Not supported yet // TODO: Add support
|
||||
) {
|
||||
fun serialize(): String {
|
||||
var serializedBlocks = listOf<JSONObject>()
|
||||
if (blocks != null) {
|
||||
serializedBlocks = blocks.map { it.serialize() }
|
||||
}
|
||||
|
||||
val config = JSONObject(hashMapOf(
|
||||
"service_name" to service_name,
|
||||
"is_public" to false,
|
||||
"registration" to null,
|
||||
"allow_multiple_connections" to null,
|
||||
"icon" to null,
|
||||
"blocks" to serializedBlocks
|
||||
) as Map<*, *>)
|
||||
|
||||
val wrapper = JSONObject(hashMapOf(
|
||||
"type" to "CONFIGURATION",
|
||||
"value" to config
|
||||
) as Map<*, *>)
|
||||
|
||||
return wrapper.toString()
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package com.programaker.bridge
|
||||
|
||||
import org.json.JSONObject
|
||||
|
||||
class ProgramakerBridgeConfigurationBlock {
|
||||
fun serialize() : JSONObject {
|
||||
val obj = JSONObject();
|
||||
return obj;
|
||||
}
|
||||
|
||||
}
|
@ -2,6 +2,7 @@
|
||||
|
||||
buildscript {
|
||||
ext.kotlin_version = '1.3.61'
|
||||
ext.ktor_version = '1.3.0'
|
||||
|
||||
repositories {
|
||||
google()
|
||||
|
Loading…
Reference in New Issue
Block a user