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 'androidx.cardview:cardview:1.0.0'
|
||||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||||
implementation 'com.google.code.gson:gson:2.8.6'
|
implementation 'com.google.code.gson:gson:2.8.6'
|
||||||
|
|
||||||
|
implementation group: 'com.squareup.okhttp3', name: 'okhttp', version: '4.7.2'
|
||||||
}
|
}
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
android:required="true" />
|
android:required="true" />
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
|
@ -12,8 +12,6 @@ import androidx.appcompat.app.ActionBar;
|
|||||||
import androidx.appcompat.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
|
||||||
import com.codigoparallevar.minicards.toolbox.PartsHolder;
|
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.Tuple2;
|
||||||
import com.codigoparallevar.minicards.types.functional.Tuple3;
|
import com.codigoparallevar.minicards.types.functional.Tuple3;
|
||||||
import com.codigoparallevar.minicards.ui_helpers.GetAsync;
|
import com.codigoparallevar.minicards.ui_helpers.GetAsync;
|
||||||
@ -67,26 +65,15 @@ public class CardActivity extends AppCompatActivity {
|
|||||||
|
|
||||||
new GetAsync<Tuple2<List<ProgramakerBridgeInfo>, List<ProgramakerBridgeCustomBlockResult>>>()
|
new GetAsync<Tuple2<List<ProgramakerBridgeInfo>, List<ProgramakerBridgeCustomBlockResult>>>()
|
||||||
.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,
|
.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,
|
||||||
new Tuple3<>(new Producer<Tuple2<List<ProgramakerBridgeInfo>, List<ProgramakerBridgeCustomBlockResult>>>() {
|
new Tuple3<>(() ->
|
||||||
@Override
|
new Tuple2<>(
|
||||||
public Tuple2<List<ProgramakerBridgeInfo>, List<ProgramakerBridgeCustomBlockResult>> get() {
|
CardActivity.this.ProgramakerApi.fetchConnectedBridges(),
|
||||||
return new Tuple2<>(
|
CardActivity.this.ProgramakerApi.fetchCustomBlocks()),
|
||||||
CardActivity.this.ProgramakerApi.fetchConnectedBridges(),
|
result -> {
|
||||||
CardActivity.this.ProgramakerApi.fetchCustomBlocks());
|
partsHolder.addCustomBlocks(result.item1, result.item2);
|
||||||
}
|
Log.d("CARDActivity", "custom blocks: " + result.toString());
|
||||||
}, new Consumer<Tuple2<List<ProgramakerBridgeInfo>,List<ProgramakerBridgeCustomBlockResult>>>() {
|
},
|
||||||
@Override
|
exception -> Log.e("CARDActivity", "error retrieving custom blocks: " + exception.toString())));
|
||||||
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());
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
|
|
||||||
// Hide action bar
|
// Hide action bar
|
||||||
ActionBar actionBar = getSupportActionBar();
|
ActionBar actionBar = getSupportActionBar();
|
||||||
|
@ -2,14 +2,14 @@ package com.codigoparallevar.minicards;
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.view.View;
|
|
||||||
|
|
||||||
import static android.content.Context.MODE_PRIVATE;
|
import static android.content.Context.MODE_PRIVATE;
|
||||||
|
|
||||||
public class ConfigManager {
|
public class ConfigManager {
|
||||||
private final Context context;
|
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 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) {
|
public ConfigManager(Context ctx) {
|
||||||
this.context = ctx;
|
this.context = ctx;
|
||||||
@ -20,16 +20,33 @@ public class ConfigManager {
|
|||||||
SharedPreferences.Editor edit = preferences.edit();
|
SharedPreferences.Editor edit = preferences.edit();
|
||||||
|
|
||||||
edit.putString(TOKEN_KEY, token);
|
edit.putString(TOKEN_KEY, token);
|
||||||
edit.commit();
|
edit.apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getToken() {
|
public String getToken() {
|
||||||
SharedPreferences preferences = this.context.getSharedPreferences(PREFERENCES_NAME, MODE_PRIVATE);
|
SharedPreferences preferences = this.context.getSharedPreferences(PREFERENCES_NAME, MODE_PRIVATE);
|
||||||
if (!preferences.contains(TOKEN_KEY)) {
|
return preferences.getString(TOKEN_KEY, null);
|
||||||
return null;
|
}
|
||||||
}
|
|
||||||
else {
|
public void removeBridgeId() {
|
||||||
return preferences.getString(TOKEN_KEY, null);
|
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.content.DialogInterface;
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
import android.os.Bundle;
|
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.Editable;
|
||||||
import android.text.TextWatcher;
|
import android.text.TextWatcher;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
@ -23,19 +15,32 @@ import android.widget.ListView;
|
|||||||
import android.widget.TextView;
|
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.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.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;
|
||||||
|
|
||||||
import com.programaker.api.ProgramakerApi;
|
|
||||||
|
|
||||||
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 ProgramakerApi ProgramakerApi;
|
||||||
private ConfigManager Config;
|
private ConfigManager Config;
|
||||||
|
private ProgramakerAndroidBridge bridge = null;
|
||||||
|
|
||||||
protected void openLoginDialog(View view) {
|
protected void openLoginDialog(View view) {
|
||||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||||
@ -92,22 +97,20 @@ public class DeckPreviewActivity extends ReloadableAppCompatActivity {
|
|||||||
new Tuple3<>(
|
new Tuple3<>(
|
||||||
loginUsernameText.getText().toString(),
|
loginUsernameText.getText().toString(),
|
||||||
loginPasswordText.getText().toString(),
|
loginPasswordText.getText().toString(),
|
||||||
new Consumer<String>() {
|
token -> {
|
||||||
public void apply(String token) {
|
if (token == null) {
|
||||||
if (token == null) {
|
messageLabel.setText(R.string.invalid_user_pass);
|
||||||
messageLabel.setText(R.string.invalid_user_pass);
|
} else {
|
||||||
} else {
|
DeckPreviewActivity.this.Config.setToken(token);
|
||||||
DeckPreviewActivity.this.Config.setToken(token);
|
DeckPreviewActivity.this.ProgramakerApi.setToken(token);
|
||||||
DeckPreviewActivity.this.ProgramakerApi.setToken(token);
|
final Button loginToProgramakerButton = findViewById(R.id.login_in_programaker_button);
|
||||||
final Button loginToProgramakerButton = findViewById(R.id.login_in_programaker_button);
|
|
||||||
|
|
||||||
loginToProgramakerButton.setVisibility(View.GONE);
|
loginToProgramakerButton.setVisibility(View.GONE);
|
||||||
// Re-check... just in case
|
// Re-check... just in case
|
||||||
new CheckNeededLoginButton().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,
|
checkNeededLoginButton(
|
||||||
new Tuple2<>(DeckPreviewActivity.this.ProgramakerApi,
|
DeckPreviewActivity.this.ProgramakerApi,
|
||||||
loginToProgramakerButton));
|
loginToProgramakerButton);
|
||||||
dialog.cancel();
|
dialog.cancel();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
@ -119,15 +122,14 @@ public class DeckPreviewActivity extends ReloadableAppCompatActivity {
|
|||||||
@Override
|
@Override
|
||||||
protected Tuple2<String, Consumer<String>> doInBackground(Tuple3<String, String, Consumer<String>>... tuples) {
|
protected Tuple2<String, Consumer<String>> doInBackground(Tuple3<String, String, Consumer<String>>... tuples) {
|
||||||
ProgramakerApi api = new ProgramakerApi();
|
ProgramakerApi api = new ProgramakerApi();
|
||||||
boolean logged = false;
|
|
||||||
String token = null;
|
String token = null;
|
||||||
try {
|
try {
|
||||||
token = api.login(tuples[0]._x, tuples[0]._y);
|
token = api.login(tuples[0]._x, tuples[0]._y);
|
||||||
}
|
}
|
||||||
catch (Exception e) {
|
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
|
@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
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
@ -183,14 +171,54 @@ public class DeckPreviewActivity extends ReloadableAppCompatActivity {
|
|||||||
loginButton.setVisibility(View.VISIBLE);
|
loginButton.setVisibility(View.VISIBLE);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
||||||
this.ProgramakerApi.setToken(token);
|
this.ProgramakerApi.setToken(token);
|
||||||
loginButton.setVisibility(View.GONE);
|
loginButton.setVisibility(View.GONE);
|
||||||
// Double check that is not needed, token might have been deleted
|
// Double check that is not needed, token might have been deleted
|
||||||
new CheckNeededLoginButton().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,
|
checkNeededLoginButton(this.ProgramakerApi, loginButton);
|
||||||
new Tuple2<>(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
|
@Override
|
||||||
protected void onResume() {
|
protected void onResume() {
|
||||||
super.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.runBlockOperation();
|
||||||
|
|
||||||
ProgramakerCustomBlockPart.this.freeBlock(token);
|
ProgramakerCustomBlockPart.this.freeBlock(token);
|
||||||
}, param -> {
|
}, ex -> {
|
||||||
Log.e(LogTag, "Error executing function=" + this._block.getFunction_name()
|
Log.e(LogTag, "Error executing function=" + this._block.getFunction_name()
|
||||||
+ "; Error=" + param);
|
+ "; Error=" + ex, ex);
|
||||||
ProgramakerCustomBlockPart.this.freeBlock(token);
|
ProgramakerCustomBlockPart.this.freeBlock(token);
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
catch (Exception ex) {
|
catch (Exception ex) {
|
||||||
Log.e(LogTag, "Error executing function=" + this._block.getFunction_name()
|
Log.e(LogTag, "Error executing function=" + this._block.getFunction_name()
|
||||||
+ "; Error=" + ex);
|
+ "; Error=" + ex, ex);
|
||||||
this.freeBlock(token);
|
this.freeBlock(token);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -444,7 +444,7 @@ public class ProgramakerCustomBlockPart implements Part {
|
|||||||
index = Integer.parseInt(chunks[1]);
|
index = Integer.parseInt(chunks[1]);
|
||||||
}
|
}
|
||||||
catch (NumberFormatException ex) {
|
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()) {
|
if (index != null && index < inputConnectors.size()) {
|
||||||
|
@ -107,7 +107,7 @@ class ProgramakerApi(private val ApiRoot: String="https://programaker.com/api")
|
|||||||
throw ProgramakerProtocolException()
|
throw ProgramakerProtocolException()
|
||||||
}
|
}
|
||||||
catch (ex: Exception) {
|
catch (ex: Exception) {
|
||||||
Log.e(LogTag, "Unexpected exception: " + ex)
|
Log.e(LogTag, "Unexpected exception: " + ex, ex)
|
||||||
throw ProgramakerProtocolException()
|
throw ProgramakerProtocolException()
|
||||||
}
|
}
|
||||||
return result.result
|
return result.result
|
||||||
@ -137,6 +137,7 @@ class ProgramakerApi(private val ApiRoot: String="https://programaker.com/api")
|
|||||||
}
|
}
|
||||||
return result.result
|
return result.result
|
||||||
}
|
}
|
||||||
|
|
||||||
fun callBlock(block: ProgramakerCustomBlock, arguments: List<String>): ProgramakerFunctionCallResult {
|
fun callBlock(block: ProgramakerCustomBlock, arguments: List<String>): ProgramakerFunctionCallResult {
|
||||||
val conn = URL(getBlockUrl(block)).openConnection() as HttpURLConnection
|
val conn = URL(getBlockUrl(block)).openConnection() as HttpURLConnection
|
||||||
conn.setRequestProperty("Content-Type", "application/json")
|
conn.setRequestProperty("Content-Type", "application/json")
|
||||||
@ -159,6 +160,28 @@ class ProgramakerApi(private val ApiRoot: String="https://programaker.com/api")
|
|||||||
return result
|
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
|
// Initialization
|
||||||
init {
|
init {
|
||||||
// Disable connection reuse if necessary
|
// 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}"
|
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() {
|
private fun withUserName() {
|
||||||
if (userName == null) {
|
if (userName == null) {
|
||||||
if (token == 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() {
|
class JsonParseException(private val content: String, private val cls: Type) : Exception() {
|
||||||
fun logError(tag: String) {
|
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
|
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 {
|
buildscript {
|
||||||
ext.kotlin_version = '1.3.61'
|
ext.kotlin_version = '1.3.61'
|
||||||
|
ext.ktor_version = '1.3.0'
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
google()
|
google()
|
||||||
|
Loading…
Reference in New Issue
Block a user