Move programaker bridge to service.

This commit is contained in:
Sergio Martínez Portela 2020-05-26 18:10:30 +02:00
parent da3dfd5d2e
commit 188f3290cf
5 changed files with 99 additions and 32 deletions

View File

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<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 --> <!-- OpenGL ES 2.0 -->
<uses-feature <uses-feature
android:glEsVersion="0x00020000" android:glEsVersion="0x00020000"
@ -16,6 +17,11 @@
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"></service>
<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" />

View File

@ -2,6 +2,7 @@ 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.AsyncTask;
import android.os.Bundle; import android.os.Bundle;
import android.text.Editable; import android.text.Editable;
@ -18,11 +19,10 @@ import android.widget.Toast;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.widget.Toolbar; import androidx.appcompat.widget.Toolbar;
import com.codigoparallevar.minicards.bridge.ProgramakerAndroidBridge; import com.codigoparallevar.minicards.bridge.ProgramakerBridgeService;
import com.codigoparallevar.minicards.types.functional.Consumer; import com.codigoparallevar.minicards.types.functional.Consumer;
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.DoAsync;
import com.codigoparallevar.minicards.ui_helpers.GetAsync; import com.codigoparallevar.minicards.ui_helpers.GetAsync;
import com.google.android.material.floatingactionbutton.FloatingActionButton; import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.programaker.api.ProgramakerApi; import com.programaker.api.ProgramakerApi;
@ -40,7 +40,6 @@ public class DeckPreviewActivity extends ReloadableAppCompatActivity {
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);
@ -188,31 +187,8 @@ public class DeckPreviewActivity extends ReloadableAppCompatActivity {
loginButton.setVisibility(View.VISIBLE); loginButton.setVisibility(View.VISIBLE);
DeckPreviewActivity.this.Config.removeBridgeId(); DeckPreviewActivity.this.Config.removeBridgeId();
} else { } else {
String bridgeId = DeckPreviewActivity.this.Config.getBridgeId(); Intent intent = new Intent(this, ProgramakerBridgeService.class);
if (bridgeId == null) { startService(intent);
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) ex -> Log.e(LogTag, "Error checking API:" + ex, ex)

View File

@ -39,13 +39,13 @@ public class ProgramakerAndroidBridge {
this.bridgeId = bridgeId; this.bridgeId = bridgeId;
} }
public void start() { public void start(Runnable onComplete) {
ProgramakerBridgeConfiguration configuration = new ProgramakerBridgeConfiguration( ProgramakerBridgeConfiguration configuration = new ProgramakerBridgeConfiguration(
ProgramakerAndroidBridge.GetBridgeName(this.ctx), ProgramakerAndroidBridge.GetBridgeName(this.ctx),
Collections.emptyList() Collections.emptyList()
); );
this.bridgeRunner = new ProgramakerBridge(this.bridgeId, this.userId, configuration); this.bridgeRunner = new ProgramakerBridge(this.bridgeId, this.userId, configuration, onComplete);
this.bridgeRunner.run(); this.bridgeRunner.run();
} }

View File

@ -0,0 +1,84 @@
package com.codigoparallevar.minicards.bridge;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.Process;
import android.util.Log;
import android.widget.Toast;
import com.codigoparallevar.minicards.ConfigManager;
import com.programaker.api.ProgramakerApi;
public class ProgramakerBridgeService extends Service {
private ProgramakerAndroidBridge bridge = null;
private static final String LogTag = "PM BridgeService";
@Override
public void onCreate() {
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
ConfigManager config = new ConfigManager(this);
String token = config.getToken();
String bridgeId = config.getBridgeId();
if (token == null || bridgeId == null) {
Toast.makeText(this, "Cannot start bridge", Toast.LENGTH_SHORT).show();
if (token == null) {
Log.e(LogTag, "Cannot start bridge: Token is null");
}
if (bridgeId == null) {
Log.e(LogTag, "Cannot start bridge: BridgeId is null (not created?)");
}
}
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();
ProgramakerBridgeService.this.bridge = ProgramakerAndroidBridge.configure(
this,
userId,
bridgeId);
ProgramakerBridgeService.this.bridge.start(() -> {
ProgramakerBridgeService.this.bridge = null;
});
}
catch (Throwable ex) {
Log.e(LogTag, "Error on bridge", ex);
ProgramakerBridgeService.this.bridge = null;
}
}, "ServiceStartArguments");
thread.setPriority(Process.THREAD_PRIORITY_BACKGROUND);
thread.start();
Toast.makeText(this, "Starting bridge", Toast.LENGTH_SHORT).show();
// If we get killed, after returning from here, restart
return START_STICKY;
}
@Override
public IBinder onBind(Intent intent) {
// We don't provide binding, so return null
return null;
}
@Override
public void onDestroy() {
Toast.makeText(this, "Bridge stopped", Toast.LENGTH_SHORT).show();
}
}

View File

@ -9,10 +9,10 @@ import java.util.concurrent.TimeUnit
class ProgramakerBridge( class ProgramakerBridge(
val bridge_id: String, val bridge_id: String,
val user_id: String, val user_id: String,
val config: ProgramakerBridgeConfiguration val config: ProgramakerBridgeConfiguration,
val onComplete: Runnable
) : WebSocketListener() { ) : WebSocketListener() {
private val PING_PERIOD_MILLIS: Long = 15000 private val PING_PERIOD_MILLIS: Long = 15000
private val PING_TIMEOUT_MILLIS: Long = 15000
private val apiRoot: String = "wss://programaker.com/api" private val apiRoot: String = "wss://programaker.com/api"
private val LogTag = "ProgramakerBridge" private val LogTag = "ProgramakerBridge"
@ -47,6 +47,7 @@ class ProgramakerBridge(
override fun onClosing(webSocket: WebSocket, code: Int, reason: String) { override fun onClosing(webSocket: WebSocket, code: Int, reason: String) {
webSocket.close(1000, null) webSocket.close(1000, null)
Log.i(LogTag, "Closing bridge socket {code=$code, reason=$reason}") Log.i(LogTag, "Closing bridge socket {code=$code, reason=$reason}")
onComplete.run()
} }
override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) { override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) {