diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index b5ca93e..3d78711 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,6 +1,7 @@ + + + diff --git a/app/src/main/java/com/codigoparallevar/minicards/DeckPreviewActivity.java b/app/src/main/java/com/codigoparallevar/minicards/DeckPreviewActivity.java index 194924d..ac6e9dd 100644 --- a/app/src/main/java/com/codigoparallevar/minicards/DeckPreviewActivity.java +++ b/app/src/main/java/com/codigoparallevar/minicards/DeckPreviewActivity.java @@ -2,6 +2,7 @@ package com.codigoparallevar.minicards; import android.app.Dialog; import android.content.DialogInterface; +import android.content.Intent; import android.os.AsyncTask; import android.os.Bundle; import android.text.Editable; @@ -18,11 +19,10 @@ import android.widget.Toast; import androidx.appcompat.app.AlertDialog; 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.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; @@ -40,7 +40,6 @@ public class DeckPreviewActivity extends ReloadableAppCompatActivity { 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); @@ -188,31 +187,8 @@ public class DeckPreviewActivity extends ReloadableAppCompatActivity { loginButton.setVisibility(View.VISIBLE); DeckPreviewActivity.this.Config.removeBridgeId(); } else { - String bridgeId = DeckPreviewActivity.this.Config.getBridgeId(); - if (bridgeId == null) { - new GetAsync().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) - )); - } + Intent intent = new Intent(this, ProgramakerBridgeService.class); + startService(intent); } }, ex -> Log.e(LogTag, "Error checking API:" + ex, ex) diff --git a/app/src/main/java/com/codigoparallevar/minicards/bridge/ProgramakerAndroidBridge.java b/app/src/main/java/com/codigoparallevar/minicards/bridge/ProgramakerAndroidBridge.java index 3ef3705..90d8ccd 100644 --- a/app/src/main/java/com/codigoparallevar/minicards/bridge/ProgramakerAndroidBridge.java +++ b/app/src/main/java/com/codigoparallevar/minicards/bridge/ProgramakerAndroidBridge.java @@ -39,13 +39,13 @@ public class ProgramakerAndroidBridge { this.bridgeId = bridgeId; } - public void start() { + public void start(Runnable onComplete) { ProgramakerBridgeConfiguration configuration = new ProgramakerBridgeConfiguration( ProgramakerAndroidBridge.GetBridgeName(this.ctx), Collections.emptyList() ); - this.bridgeRunner = new ProgramakerBridge(this.bridgeId, this.userId, configuration); + this.bridgeRunner = new ProgramakerBridge(this.bridgeId, this.userId, configuration, onComplete); this.bridgeRunner.run(); } diff --git a/app/src/main/java/com/codigoparallevar/minicards/bridge/ProgramakerBridgeService.java b/app/src/main/java/com/codigoparallevar/minicards/bridge/ProgramakerBridgeService.java new file mode 100644 index 0000000..cbd615f --- /dev/null +++ b/app/src/main/java/com/codigoparallevar/minicards/bridge/ProgramakerBridgeService.java @@ -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(); + } +} diff --git a/app/src/main/java/com/programaker/bridge/ProgramakerBridge.kt b/app/src/main/java/com/programaker/bridge/ProgramakerBridge.kt index b182831..c8c62c2 100644 --- a/app/src/main/java/com/programaker/bridge/ProgramakerBridge.kt +++ b/app/src/main/java/com/programaker/bridge/ProgramakerBridge.kt @@ -9,10 +9,10 @@ import java.util.concurrent.TimeUnit class ProgramakerBridge( val bridge_id: String, val user_id: String, - val config: ProgramakerBridgeConfiguration + val config: ProgramakerBridgeConfiguration, + val onComplete: Runnable ) : 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" @@ -47,6 +47,7 @@ class ProgramakerBridge( override fun onClosing(webSocket: WebSocket, code: Int, reason: String) { webSocket.close(1000, null) Log.i(LogTag, "Closing bridge socket {code=$code, reason=$reason}") + onComplete.run() } override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) {