diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 35268ef..119dffa 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -62,9 +62,13 @@ + android:exported="true" + android:directBootAware="true"> + + + diff --git a/app/src/main/java/sh/sar/textpipe/server/TextpipeServer.kt b/app/src/main/java/sh/sar/textpipe/server/TextpipeServer.kt index 9f8c936..237ea80 100644 --- a/app/src/main/java/sh/sar/textpipe/server/TextpipeServer.kt +++ b/app/src/main/java/sh/sar/textpipe/server/TextpipeServer.kt @@ -24,6 +24,11 @@ import sh.sar.textpipe.server.routes.webUIRoutes import sh.sar.textpipe.sim.SimManager import java.net.ServerSocket +sealed class ServerStartResult { + data object Success : ServerStartResult() + data class Error(val message: String) : ServerStartResult() +} + class TextpipeServer( private val context: Context, private val smsRepository: SmsRepository, @@ -39,7 +44,7 @@ class TextpipeServer( @Volatile private var lastError: Throwable? = null - suspend fun start(port: Int): Boolean { + suspend fun start(port: Int): ServerStartResult { // Stop any existing server first stop() @@ -53,7 +58,7 @@ class TextpipeServer( delay(1000) if (!isPortAvailable(port)) { Log.e(TAG, "Port $port still not available after waiting") - return false + return ServerStartResult.Error("Port $port is already in use. Try a different port or wait a moment.") } } @@ -82,7 +87,7 @@ class TextpipeServer( Log.e(TAG, "Server failed to start", lastError) server?.stop(100, 100) server = null - return false + return ServerStartResult.Error("Server error: ${lastError?.message ?: "Unknown error"}") } // Verify server is actually running by checking the port again @@ -91,16 +96,16 @@ class TextpipeServer( Log.e(TAG, "Server didn't bind to port $port") server?.stop(100, 100) server = null - return false + return ServerStartResult.Error("Server failed to bind to port $port") } Log.i(TAG, "Server started on port $port") - true + ServerStartResult.Success } catch (e: Exception) { Log.e(TAG, "Failed to start server on port $port", e) server?.stop(100, 100) server = null - false + ServerStartResult.Error("Failed to start: ${e.message ?: "Unknown error"}") } } diff --git a/app/src/main/java/sh/sar/textpipe/service/BootReceiver.kt b/app/src/main/java/sh/sar/textpipe/service/BootReceiver.kt index 25c1d36..9a4a16e 100644 --- a/app/src/main/java/sh/sar/textpipe/service/BootReceiver.kt +++ b/app/src/main/java/sh/sar/textpipe/service/BootReceiver.kt @@ -3,12 +3,17 @@ package sh.sar.textpipe.service import android.content.BroadcastReceiver import android.content.Context import android.content.Intent +import android.os.Handler +import android.os.Looper +import android.util.Log class BootReceiver : BroadcastReceiver() { companion object { + private const val TAG = "BootReceiver" private const val PREFS_NAME = "textpipe_settings" private const val KEY_AUTO_START = "auto_start" + private const val START_DELAY_MS = 5000L // Wait 5 seconds after boot fun isAutoStartEnabled(context: Context): Boolean { val prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE) @@ -22,10 +27,28 @@ class BootReceiver : BroadcastReceiver() { } override fun onReceive(context: Context, intent: Intent) { - if (intent.action != Intent.ACTION_BOOT_COMPLETED) return + val action = intent.action + if (action != Intent.ACTION_BOOT_COMPLETED && + action != Intent.ACTION_LOCKED_BOOT_COMPLETED && + action != "android.intent.action.QUICKBOOT_POWERON" && + action != "com.htc.intent.action.QUICKBOOT_POWERON") { + return + } + + Log.i(TAG, "Boot completed received: $action") if (isAutoStartEnabled(context)) { - TextpipeService.start(context) + // Delay start slightly to let system settle + Handler(Looper.getMainLooper()).postDelayed({ + Log.i(TAG, "Starting TextpipeService after boot") + try { + TextpipeService.start(context.applicationContext) + } catch (e: Exception) { + Log.e(TAG, "Failed to start service on boot", e) + } + }, START_DELAY_MS) + } else { + Log.i(TAG, "Auto-start is disabled, not starting service") } } } diff --git a/app/src/main/java/sh/sar/textpipe/service/TextpipeService.kt b/app/src/main/java/sh/sar/textpipe/service/TextpipeService.kt index 62326b4..4a2a438 100644 --- a/app/src/main/java/sh/sar/textpipe/service/TextpipeService.kt +++ b/app/src/main/java/sh/sar/textpipe/service/TextpipeService.kt @@ -8,8 +8,11 @@ import android.app.Service import android.content.Context import android.content.Intent import android.os.Build +import android.os.Handler import android.os.IBinder +import android.os.Looper import android.util.Log +import android.widget.Toast import androidx.core.app.NotificationCompat import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -23,6 +26,7 @@ import sh.sar.textpipe.MainActivity import sh.sar.textpipe.TextpipeApplication import sh.sar.textpipe.data.repository.SmsRepository import sh.sar.textpipe.root.RootManager +import sh.sar.textpipe.server.ServerStartResult import sh.sar.textpipe.server.TextpipeServer class TextpipeService : Service() { @@ -133,15 +137,24 @@ class TextpipeService : Service() { } // Start HTTP server - val started = textpipeServer?.start(port) ?: false - if (started) { - _isRunning.value = true - val displayPort = if (redirectPort > 0) redirectPort else port - updateNotification("Running on port $displayPort") - Log.i(TAG, "Server started successfully on port $port") - } else { - updateNotification("Failed to start on port $port") - Log.e(TAG, "Failed to start server on port $port") + when (val result = textpipeServer?.start(port)) { + is ServerStartResult.Success -> { + _isRunning.value = true + val displayPort = if (redirectPort > 0) redirectPort else port + updateNotification("Running on port $displayPort") + showToast("Server started on port $displayPort") + Log.i(TAG, "Server started successfully on port $port") + } + is ServerStartResult.Error -> { + updateNotification("Error: ${result.message}") + showToast(result.message) + Log.e(TAG, "Failed to start server: ${result.message}") + } + null -> { + updateNotification("Error: Server not initialized") + showToast("Server not initialized") + Log.e(TAG, "Server is null, cannot start") + } } } @@ -217,4 +230,10 @@ class TextpipeService : Service() { val notificationManager = getSystemService(NotificationManager::class.java) notificationManager.notify(NOTIFICATION_ID, notification) } + + private fun showToast(message: String) { + Handler(Looper.getMainLooper()).post { + Toast.makeText(this, message, Toast.LENGTH_LONG).show() + } + } }