better error handling for server start: show toast

This commit is contained in:
2026-02-06 01:01:44 +05:00
parent 11c4c49b3e
commit 652612e156
4 changed files with 69 additions and 18 deletions

View File

@@ -62,9 +62,13 @@
<receiver <receiver
android:name=".service.BootReceiver" android:name=".service.BootReceiver"
android:enabled="true" android:enabled="true"
android:exported="true"> android:exported="true"
android:directBootAware="true">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" /> <action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.LOCKED_BOOT_COMPLETED" />
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
<action android:name="com.htc.intent.action.QUICKBOOT_POWERON" />
</intent-filter> </intent-filter>
</receiver> </receiver>

View File

@@ -24,6 +24,11 @@ import sh.sar.textpipe.server.routes.webUIRoutes
import sh.sar.textpipe.sim.SimManager import sh.sar.textpipe.sim.SimManager
import java.net.ServerSocket import java.net.ServerSocket
sealed class ServerStartResult {
data object Success : ServerStartResult()
data class Error(val message: String) : ServerStartResult()
}
class TextpipeServer( class TextpipeServer(
private val context: Context, private val context: Context,
private val smsRepository: SmsRepository, private val smsRepository: SmsRepository,
@@ -39,7 +44,7 @@ class TextpipeServer(
@Volatile @Volatile
private var lastError: Throwable? = null private var lastError: Throwable? = null
suspend fun start(port: Int): Boolean { suspend fun start(port: Int): ServerStartResult {
// Stop any existing server first // Stop any existing server first
stop() stop()
@@ -53,7 +58,7 @@ class TextpipeServer(
delay(1000) delay(1000)
if (!isPortAvailable(port)) { if (!isPortAvailable(port)) {
Log.e(TAG, "Port $port still not available after waiting") 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) Log.e(TAG, "Server failed to start", lastError)
server?.stop(100, 100) server?.stop(100, 100)
server = null server = null
return false return ServerStartResult.Error("Server error: ${lastError?.message ?: "Unknown error"}")
} }
// Verify server is actually running by checking the port again // 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") Log.e(TAG, "Server didn't bind to port $port")
server?.stop(100, 100) server?.stop(100, 100)
server = null server = null
return false return ServerStartResult.Error("Server failed to bind to port $port")
} }
Log.i(TAG, "Server started on port $port") Log.i(TAG, "Server started on port $port")
true ServerStartResult.Success
} catch (e: Exception) { } catch (e: Exception) {
Log.e(TAG, "Failed to start server on port $port", e) Log.e(TAG, "Failed to start server on port $port", e)
server?.stop(100, 100) server?.stop(100, 100)
server = null server = null
false ServerStartResult.Error("Failed to start: ${e.message ?: "Unknown error"}")
} }
} }

View File

@@ -3,12 +3,17 @@ package sh.sar.textpipe.service
import android.content.BroadcastReceiver import android.content.BroadcastReceiver
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.os.Handler
import android.os.Looper
import android.util.Log
class BootReceiver : BroadcastReceiver() { class BootReceiver : BroadcastReceiver() {
companion object { companion object {
private const val TAG = "BootReceiver"
private const val PREFS_NAME = "textpipe_settings" private const val PREFS_NAME = "textpipe_settings"
private const val KEY_AUTO_START = "auto_start" 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 { fun isAutoStartEnabled(context: Context): Boolean {
val prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE) val prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
@@ -22,10 +27,28 @@ class BootReceiver : BroadcastReceiver() {
} }
override fun onReceive(context: Context, intent: Intent) { 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)) { 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")
} }
} }
} }

View File

@@ -8,8 +8,11 @@ import android.app.Service
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.os.Build import android.os.Build
import android.os.Handler
import android.os.IBinder import android.os.IBinder
import android.os.Looper
import android.util.Log import android.util.Log
import android.widget.Toast
import androidx.core.app.NotificationCompat import androidx.core.app.NotificationCompat
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@@ -23,6 +26,7 @@ import sh.sar.textpipe.MainActivity
import sh.sar.textpipe.TextpipeApplication import sh.sar.textpipe.TextpipeApplication
import sh.sar.textpipe.data.repository.SmsRepository import sh.sar.textpipe.data.repository.SmsRepository
import sh.sar.textpipe.root.RootManager import sh.sar.textpipe.root.RootManager
import sh.sar.textpipe.server.ServerStartResult
import sh.sar.textpipe.server.TextpipeServer import sh.sar.textpipe.server.TextpipeServer
class TextpipeService : Service() { class TextpipeService : Service() {
@@ -133,15 +137,24 @@ class TextpipeService : Service() {
} }
// Start HTTP server // Start HTTP server
val started = textpipeServer?.start(port) ?: false when (val result = textpipeServer?.start(port)) {
if (started) { is ServerStartResult.Success -> {
_isRunning.value = true _isRunning.value = true
val displayPort = if (redirectPort > 0) redirectPort else port val displayPort = if (redirectPort > 0) redirectPort else port
updateNotification("Running on port $displayPort") updateNotification("Running on port $displayPort")
showToast("Server started on port $displayPort")
Log.i(TAG, "Server started successfully on port $port") Log.i(TAG, "Server started successfully on port $port")
} else { }
updateNotification("Failed to start on port $port") is ServerStartResult.Error -> {
Log.e(TAG, "Failed to start server on port $port") 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) val notificationManager = getSystemService(NotificationManager::class.java)
notificationManager.notify(NOTIFICATION_ID, notification) notificationManager.notify(NOTIFICATION_ID, notification)
} }
private fun showToast(message: String) {
Handler(Looper.getMainLooper()).post {
Toast.makeText(this, message, Toast.LENGTH_LONG).show()
}
}
} }