diff --git a/.auto-claude-status b/.auto-claude-status
index fc1dc4c..6fd3edb 100644
--- a/.auto-claude-status
+++ b/.auto-claude-status
@@ -1,25 +1,25 @@
-{
- "active": true,
- "spec": "001-improve-code-quality-based-on-analysis",
- "state": "building",
- "subtasks": {
- "completed": 11,
- "total": 20,
- "in_progress": 0,
- "failed": 0
- },
- "phase": {
- "current": "Dependency Update - JSch",
- "id": null,
- "total": 2
- },
- "workers": {
- "active": 0,
- "max": 1
- },
- "session": {
- "number": 9,
- "started_at": "2026-02-05T13:28:18.356172"
- },
- "last_update": "2026-02-05T14:07:07.637286"
-}
\ No newline at end of file
+{
+ "active": true,
+ "spec": "003-home-screen-widget",
+ "state": "building",
+ "subtasks": {
+ "completed": 11,
+ "total": 12,
+ "in_progress": 1,
+ "failed": 0
+ },
+ "phase": {
+ "current": "Integration & Testing",
+ "id": null,
+ "total": 2
+ },
+ "workers": {
+ "active": 0,
+ "max": 1
+ },
+ "session": {
+ "number": 13,
+ "started_at": "2026-02-07T01:52:14.093243"
+ },
+ "last_update": "2026-02-07T02:38:46.645178"
+}
diff --git a/.claude_settings.json b/.claude_settings.json
index 06c8d50..1ec7e75 100644
--- a/.claude_settings.json
+++ b/.claude_settings.json
@@ -11,22 +11,14 @@
"Edit(./**)",
"Glob(./**)",
"Grep(./**)",
- "Read(D:\AI\Android\APK\Android-Apps\apps\wirelessadb\.auto-claude\worktrees\tasks\001-improve-code-quality-based-on-analysis/**)",
- "Write(D:\AI\Android\APK\Android-Apps\apps\wirelessadb\.auto-claude\worktrees\tasks\001-improve-code-quality-based-on-analysis/**)",
- "Edit(D:\AI\Android\APK\Android-Apps\apps\wirelessadb\.auto-claude\worktrees\tasks\001-improve-code-quality-based-on-analysis/**)",
- "Glob(D:\AI\Android\APK\Android-Apps\apps\wirelessadb\.auto-claude\worktrees\tasks\001-improve-code-quality-based-on-analysis/**)",
- "Grep(D:\AI\Android\APK\Android-Apps\apps\wirelessadb\.auto-claude\worktrees\tasks\001-improve-code-quality-based-on-analysis/**)",
- "Read(D:\AI\Android\APK\Android-Apps\apps\wirelessadb\.auto-claude\worktrees\tasks\001-improve-code-quality-based-on-analysis\.auto-claude\specs\001-improve-code-quality-based-on-analysis/**)",
- "Write(D:\AI\Android\APK\Android-Apps\apps\wirelessadb\.auto-claude\worktrees\tasks\001-improve-code-quality-based-on-analysis\.auto-claude\specs\001-improve-code-quality-based-on-analysis/**)",
- "Edit(D:\AI\Android\APK\Android-Apps\apps\wirelessadb\.auto-claude\worktrees\tasks\001-improve-code-quality-based-on-analysis\.auto-claude\specs\001-improve-code-quality-based-on-analysis/**)",
- "Read(D:\AI\Android\APK\Android-Apps\apps\wirelessadb\.auto-claude\worktrees\tasks\007-auto-reconnect-on-network-change/**)",
- "Write(D:\AI\Android\APK\Android-Apps\apps\wirelessadb\.auto-claude\worktrees\tasks\007-auto-reconnect-on-network-change/**)",
- "Edit(D:\AI\Android\APK\Android-Apps\apps\wirelessadb\.auto-claude\worktrees\tasks\007-auto-reconnect-on-network-change/**)",
- "Glob(D:\AI\Android\APK\Android-Apps\apps\wirelessadb\.auto-claude\worktrees\tasks\007-auto-reconnect-on-network-change/**)",
- "Grep(D:\AI\Android\APK\Android-Apps\apps\wirelessadb\.auto-claude\worktrees\tasks\007-auto-reconnect-on-network-change/**)",
- "Read(D:\AI\Android\APK\Android-Apps\apps\wirelessadb\.auto-claude\worktrees\tasks\007-auto-reconnect-on-network-change\.auto-claude\specs\007-auto-reconnect-on-network-change/**)",
- "Write(D:\AI\Android\APK\Android-Apps\apps\wirelessadb\.auto-claude\worktrees\tasks\007-auto-reconnect-on-network-change\.auto-claude\specs\007-auto-reconnect-on-network-change/**)",
- "Edit(D:\AI\Android\APK\Android-Apps\apps\wirelessadb\.auto-claude\worktrees\tasks\007-auto-reconnect-on-network-change\.auto-claude\specs\007-auto-reconnect-on-network-change/**)",
+ "Read(D:\AI\Android\APK\Android-Apps\apps\wirelessadb\.auto-claude\worktrees\tasks\003-home-screen-widget/**)",
+ "Write(D:\AI\Android\APK\Android-Apps\apps\wirelessadb\.auto-claude\worktrees\tasks\003-home-screen-widget/**)",
+ "Edit(D:\AI\Android\APK\Android-Apps\apps\wirelessadb\.auto-claude\worktrees\tasks\003-home-screen-widget/**)",
+ "Glob(D:\AI\Android\APK\Android-Apps\apps\wirelessadb\.auto-claude\worktrees\tasks\003-home-screen-widget/**)",
+ "Grep(D:\AI\Android\APK\Android-Apps\apps\wirelessadb\.auto-claude\worktrees\tasks\003-home-screen-widget/**)",
+ "Read(D:\AI\Android\APK\Android-Apps\apps\wirelessadb\.auto-claude\worktrees\tasks\003-home-screen-widget\.auto-claude\specs\003-home-screen-widget/**)",
+ "Write(D:\AI\Android\APK\Android-Apps\apps\wirelessadb\.auto-claude\worktrees\tasks\003-home-screen-widget\.auto-claude\specs\003-home-screen-widget/**)",
+ "Edit(D:\AI\Android\APK\Android-Apps\apps\wirelessadb\.auto-claude\worktrees\tasks\003-home-screen-widget\.auto-claude\specs\003-home-screen-widget/**)",
"Read(D:\AI\Android\APK\Android-Apps\apps\wirelessadb\.auto-claude/**)",
"Write(D:\AI\Android\APK\Android-Apps\apps\wirelessadb\.auto-claude/**)",
"Edit(D:\AI\Android\APK\Android-Apps\apps\wirelessadb\.auto-claude/**)",
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index fd2135a..f592036 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -59,6 +59,18 @@
+
+
+
+
+
+
+
+
_error.value = e.message
}
diff --git a/app/src/main/java/com/phenix/wirelessadb/widget/AdbWidgetProvider.kt b/app/src/main/java/com/phenix/wirelessadb/widget/AdbWidgetProvider.kt
new file mode 100644
index 0000000..654457e
--- /dev/null
+++ b/app/src/main/java/com/phenix/wirelessadb/widget/AdbWidgetProvider.kt
@@ -0,0 +1,413 @@
+package com.phenix.wirelessadb.widget
+
+import android.app.PendingIntent
+import android.appwidget.AppWidgetManager
+import android.appwidget.AppWidgetProvider
+import android.content.BroadcastReceiver
+import android.content.ClipData
+import android.content.ClipboardManager
+import android.content.ComponentName
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.util.Log
+import android.view.View
+import android.widget.RemoteViews
+import android.widget.Toast
+import androidx.localbroadcastmanager.content.LocalBroadcastManager
+import com.phenix.wirelessadb.AdbManager
+import com.phenix.wirelessadb.R
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+
+/**
+ * Home screen widget provider for ADB status display and control.
+ *
+ * Features:
+ * - Display current ADB status (enabled/disabled)
+ * - Show IP:Port when ADB is enabled
+ * - Toggle button to enable/disable ADB
+ * - Copy button to copy connection command to clipboard
+ */
+class AdbWidgetProvider : AppWidgetProvider() {
+
+ companion object {
+ private const val TAG = "AdbWidgetProvider"
+ const val ACTION_TOGGLE_ADB = "com.phenix.wirelessadb.widget.TOGGLE_ADB"
+ const val ACTION_COPY_COMMAND = "com.phenix.wirelessadb.widget.COPY_COMMAND"
+ const val ACTION_ADB_STATUS_CHANGED = "com.phenix.wirelessadb.ADB_STATUS_CHANGED"
+
+ /**
+ * Broadcasts ADB status change to all widget instances.
+ * This should be called whenever ADB is enabled/disabled from any part of the app.
+ */
+ fun notifyStatusChanged(context: Context) {
+ Log.d(TAG, "notifyStatusChanged: Broadcasting ADB status change")
+ val intent = Intent(ACTION_ADB_STATUS_CHANGED)
+ LocalBroadcastManager.getInstance(context).sendBroadcast(intent)
+ }
+
+ /**
+ * Manually updates all widget instances.
+ * Useful for forcing a refresh from external components.
+ */
+ fun updateAllWidgets(context: Context) {
+ Log.d(TAG, "updateAllWidgets: Manually updating all widgets")
+ val appWidgetManager = AppWidgetManager.getInstance(context)
+ val componentName = ComponentName(context, AdbWidgetProvider::class.java)
+ val appWidgetIds = appWidgetManager.getAppWidgetIds(componentName)
+ val intent = Intent(context, AdbWidgetProvider::class.java).apply {
+ action = AppWidgetManager.ACTION_APPWIDGET_UPDATE
+ putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds)
+ }
+ context.sendBroadcast(intent)
+ }
+ }
+
+ /**
+ * Called when widget receives a broadcast.
+ * Handles custom actions like toggle, copy, and status change notifications.
+ */
+ override fun onReceive(context: Context, intent: Intent) {
+ super.onReceive(context, intent)
+
+ when (intent.action) {
+ ACTION_TOGGLE_ADB -> {
+ Log.d(TAG, "onReceive: Toggle ADB action received")
+ handleToggleAdb(context)
+ }
+ ACTION_COPY_COMMAND -> {
+ Log.d(TAG, "onReceive: Copy command action received")
+ handleCopyCommand(context)
+ }
+ ACTION_ADB_STATUS_CHANGED -> {
+ Log.d(TAG, "onReceive: ADB status changed - updating all widgets")
+ updateAllWidgetsInstances(context)
+ }
+ "android.net.conn.CONNECTIVITY_CHANGE" -> {
+ Log.d(TAG, "onReceive: Network connectivity changed - updating all widgets")
+ updateAllWidgetsInstances(context)
+ }
+ }
+ }
+
+ /**
+ * Called when widget needs to be updated.
+ * Updates all instances with current ADB status.
+ */
+ override fun onUpdate(
+ context: Context,
+ appWidgetManager: AppWidgetManager,
+ appWidgetIds: IntArray
+ ) {
+ // Update each widget instance
+ for (appWidgetId in appWidgetIds) {
+ updateWidget(context, appWidgetManager, appWidgetId)
+ }
+ }
+
+ // Broadcast receiver for real-time status updates
+ private var statusChangeReceiver: BroadcastReceiver? = null
+ private var networkChangeReceiver: BroadcastReceiver? = null
+
+ /**
+ * Called when the first widget instance is added.
+ * Registers broadcast receiver for real-time ADB status updates.
+ */
+ override fun onEnabled(context: Context) {
+ super.onEnabled(context)
+ Log.d(TAG, "onEnabled: First widget added - registering receivers")
+
+ // Register receiver for ADB status changes
+ if (statusChangeReceiver == null) {
+ statusChangeReceiver = object : BroadcastReceiver() {
+ override fun onReceive(context: Context, intent: Intent) {
+ if (intent.action == ACTION_ADB_STATUS_CHANGED) {
+ Log.d(TAG, "statusChangeReceiver: ADB status changed - updating widgets")
+ updateAllWidgetsInstances(context)
+ }
+ }
+ }
+ LocalBroadcastManager.getInstance(context).registerReceiver(
+ statusChangeReceiver!!,
+ IntentFilter(ACTION_ADB_STATUS_CHANGED)
+ )
+ Log.d(TAG, "onEnabled: Status change receiver registered")
+ }
+
+ // Register receiver for network connectivity changes
+ if (networkChangeReceiver == null) {
+ networkChangeReceiver = object : BroadcastReceiver() {
+ override fun onReceive(context: Context, intent: Intent) {
+ Log.d(TAG, "networkChangeReceiver: Network connectivity changed - updating widgets")
+ updateAllWidgetsInstances(context)
+ }
+ }
+ val networkFilter = IntentFilter().apply {
+ addAction("android.net.conn.CONNECTIVITY_CHANGE")
+ }
+ context.registerReceiver(networkChangeReceiver!!, networkFilter)
+ Log.d(TAG, "onEnabled: Network change receiver registered")
+ }
+ }
+
+ /**
+ * Called when the last widget instance is removed.
+ * Unregisters broadcast receivers.
+ */
+ override fun onDisabled(context: Context) {
+ super.onDisabled(context)
+ Log.d(TAG, "onDisabled: Last widget removed - unregistering receivers")
+
+ // Unregister status change receiver
+ statusChangeReceiver?.let {
+ try {
+ LocalBroadcastManager.getInstance(context).unregisterReceiver(it)
+ Log.d(TAG, "onDisabled: Status change receiver unregistered")
+ } catch (e: Exception) {
+ Log.e(TAG, "onDisabled: Failed to unregister status receiver", e)
+ }
+ statusChangeReceiver = null
+ }
+
+ // Unregister network change receiver
+ networkChangeReceiver?.let {
+ try {
+ context.unregisterReceiver(it)
+ Log.d(TAG, "onDisabled: Network change receiver unregistered")
+ } catch (e: Exception) {
+ Log.e(TAG, "onDisabled: Failed to unregister network receiver", e)
+ }
+ networkChangeReceiver = null
+ }
+ }
+
+ /**
+ * Called when a widget instance is removed.
+ */
+ override fun onDeleted(context: Context, appWidgetIds: IntArray) {
+ super.onDeleted(context, appWidgetIds)
+ // Widget instance removed - cleanup if needed
+ }
+
+ /**
+ * Handles toggle ADB button click.
+ * Fetches current status, toggles ADB state, broadcasts change, and updates all widgets.
+ */
+ private fun handleToggleAdb(context: Context) {
+ CoroutineScope(Dispatchers.IO).launch {
+ try {
+ // Get current status
+ val status = AdbManager.getStatus(context)
+ Log.d(TAG, "handleToggleAdb: Current status = enabled:${status.enabled}")
+
+ // Toggle ADB state
+ val result = if (status.enabled) {
+ Log.d(TAG, "handleToggleAdb: Disabling ADB")
+ AdbManager.disable()
+ } else {
+ Log.d(TAG, "handleToggleAdb: Enabling ADB on port ${status.port}")
+ AdbManager.enable(status.port)
+ }
+
+ // Check result and broadcast change
+ if (result.isSuccess) {
+ Log.d(TAG, "handleToggleAdb: Toggle successful - broadcasting status change")
+ // Broadcast status change to notify other components (like ViewModel, other widgets, etc.)
+ notifyStatusChanged(context)
+ } else {
+ Log.e(TAG, "handleToggleAdb: Toggle failed - ${result.exceptionOrNull()?.message}")
+ }
+
+ // Update all widgets to reflect new state
+ updateAllWidgetsInstances(context)
+ } catch (e: Exception) {
+ Log.e(TAG, "handleToggleAdb: Failed to toggle ADB", e)
+ }
+ }
+ }
+
+ /**
+ * Updates all widget instances.
+ * Called when ADB status changes or broadcast is received.
+ */
+ private fun updateAllWidgetsInstances(context: Context) {
+ try {
+ val appWidgetManager = AppWidgetManager.getInstance(context)
+ val componentName = ComponentName(context, AdbWidgetProvider::class.java)
+ val appWidgetIds = appWidgetManager.getAppWidgetIds(componentName)
+
+ Log.d(TAG, "updateAllWidgetsInstances: Updating ${appWidgetIds.size} widget(s)")
+ for (appWidgetId in appWidgetIds) {
+ updateWidget(context, appWidgetManager, appWidgetId)
+ }
+ } catch (e: Exception) {
+ Log.e(TAG, "updateAllWidgetsInstances: Failed to update widgets", e)
+ }
+ }
+
+ /**
+ * Handles copy command button click.
+ * Copies the ADB connect command to clipboard and shows a toast notification.
+ */
+ private fun handleCopyCommand(context: Context) {
+ CoroutineScope(Dispatchers.IO).launch {
+ try {
+ // Get current status
+ val status = AdbManager.getStatus(context)
+ Log.d(TAG, "handleCopyCommand: Current status = enabled:${status.enabled}, ip:${status.ip}")
+
+ // Check if ADB is enabled and has an IP address
+ if (status.enabled && status.ip != null) {
+ // Format the ADB connect command
+ val command = "adb connect ${status.ip}:${status.port}"
+ Log.d(TAG, "handleCopyCommand: Command = $command")
+
+ // Copy to clipboard on main thread
+ withContext(Dispatchers.Main) {
+ val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
+ val clip = ClipData.newPlainText("ADB Command", command)
+ clipboard.setPrimaryClip(clip)
+
+ // Show toast notification
+ Toast.makeText(context, R.string.copied, Toast.LENGTH_SHORT).show()
+ Log.d(TAG, "handleCopyCommand: Command copied to clipboard")
+ }
+ } else {
+ // ADB is disabled or no WiFi connection - show warning
+ withContext(Dispatchers.Main) {
+ val message = if (!status.enabled) {
+ "ADB is disabled"
+ } else {
+ "No WiFi connection"
+ }
+ Toast.makeText(context, message, Toast.LENGTH_SHORT).show()
+ Log.d(TAG, "handleCopyCommand: Cannot copy - $message")
+ }
+ }
+ } catch (e: Exception) {
+ Log.e(TAG, "handleCopyCommand: Failed to copy command", e)
+ withContext(Dispatchers.Main) {
+ Toast.makeText(context, "Failed to copy command", Toast.LENGTH_SHORT).show()
+ }
+ }
+ }
+ }
+
+ /**
+ * Updates a single widget instance with current ADB status.
+ * Fetches status asynchronously and updates UI on main thread.
+ */
+ private fun updateWidget(
+ context: Context,
+ appWidgetManager: AppWidgetManager,
+ appWidgetId: Int
+ ) {
+ CoroutineScope(Dispatchers.IO).launch {
+ try {
+ Log.d(TAG, "updateWidget: Fetching ADB status for widget $appWidgetId")
+
+ // Fetch ADB status on IO thread
+ val status = AdbManager.getStatus(context)
+ Log.d(TAG, "updateWidget: Status = enabled:${status.enabled}, ip:${status.ip}, port:${status.port}")
+
+ // Switch to main thread for UI updates
+ withContext(Dispatchers.Main) {
+ val views = RemoteViews(context.packageName, R.layout.widget_adb_status)
+
+ // Update status icon and text
+ if (status.enabled && status.ip != null) {
+ // ADB is enabled and connected
+ views.setImageViewResource(R.id.widgetStatusIcon, R.drawable.ic_indicator_wifi)
+ views.setInt(R.id.widgetStatusIcon, "setColorFilter", context.getColor(R.color.status_active))
+ views.setTextViewText(R.id.widgetStatusText, context.getString(R.string.status_connected))
+
+ // Show IP:Port
+ views.setViewVisibility(R.id.widgetConnectionInfo, View.VISIBLE)
+ views.setViewVisibility(R.id.widgetPlaceholder, View.GONE)
+ views.setTextViewText(R.id.widgetIpPortText, "${status.ip}:${status.port}")
+
+ Log.d(TAG, "updateWidget: Widget shows connected state - ${status.ip}:${status.port}")
+ } else if (status.enabled && status.ip == null) {
+ // ADB is enabled but no WiFi connection
+ views.setImageViewResource(R.id.widgetStatusIcon, R.drawable.ic_indicator_wifi)
+ views.setInt(R.id.widgetStatusIcon, "setColorFilter", context.getColor(R.color.warning_orange))
+ views.setTextViewText(R.id.widgetStatusText, context.getString(R.string.status_enabled))
+
+ // Show port only
+ views.setViewVisibility(R.id.widgetConnectionInfo, View.VISIBLE)
+ views.setViewVisibility(R.id.widgetPlaceholder, View.GONE)
+ views.setTextViewText(R.id.widgetIpPortText, "Port ${status.port}")
+
+ Log.d(TAG, "updateWidget: Widget shows enabled (no WiFi) state - port:${status.port}")
+ } else {
+ // ADB is disabled
+ views.setImageViewResource(R.id.widgetStatusIcon, R.drawable.ic_indicator_wifi)
+ views.setInt(R.id.widgetStatusIcon, "setColorFilter", context.getColor(R.color.status_inactive))
+ views.setTextViewText(R.id.widgetStatusText, context.getString(R.string.status_disabled))
+
+ // Hide IP:Port, show placeholder
+ views.setViewVisibility(R.id.widgetConnectionInfo, View.GONE)
+ views.setViewVisibility(R.id.widgetPlaceholder, View.VISIBLE)
+
+ Log.d(TAG, "updateWidget: Widget shows disabled state")
+ }
+
+ // Update toggle button icon (check for enabled, close for disabled)
+ if (status.enabled) {
+ views.setImageViewResource(R.id.widgetToggleButton, R.drawable.ic_check)
+ } else {
+ views.setImageViewResource(R.id.widgetToggleButton, R.drawable.ic_close)
+ }
+
+ // Set up PendingIntent for toggle button
+ val toggleIntent = Intent(context, AdbWidgetProvider::class.java).apply {
+ action = ACTION_TOGGLE_ADB
+ }
+ val togglePendingIntent = PendingIntent.getBroadcast(
+ context,
+ 0,
+ toggleIntent,
+ PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
+ )
+ views.setOnClickPendingIntent(R.id.widgetToggleButton, togglePendingIntent)
+
+ // Set up PendingIntent for copy button
+ val copyIntent = Intent(context, AdbWidgetProvider::class.java).apply {
+ action = ACTION_COPY_COMMAND
+ }
+ val copyPendingIntent = PendingIntent.getBroadcast(
+ context,
+ 1,
+ copyIntent,
+ PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
+ )
+ views.setOnClickPendingIntent(R.id.widgetCopyButton, copyPendingIntent)
+
+ // Update widget on main thread
+ appWidgetManager.updateAppWidget(appWidgetId, views)
+ Log.d(TAG, "updateWidget: Widget $appWidgetId updated successfully")
+ }
+ } catch (e: Exception) {
+ Log.e(TAG, "updateWidget: Failed to update widget $appWidgetId", e)
+
+ // On error, show error state on main thread
+ withContext(Dispatchers.Main) {
+ try {
+ val errorViews = RemoteViews(context.packageName, R.layout.widget_adb_status)
+ errorViews.setImageViewResource(R.id.widgetStatusIcon, R.drawable.ic_warning)
+ errorViews.setInt(R.id.widgetStatusIcon, "setColorFilter", context.getColor(R.color.error_red))
+ errorViews.setTextViewText(R.id.widgetStatusText, context.getString(R.string.status_error))
+ errorViews.setViewVisibility(R.id.widgetConnectionInfo, View.GONE)
+ errorViews.setViewVisibility(R.id.widgetPlaceholder, View.VISIBLE)
+ appWidgetManager.updateAppWidget(appWidgetId, errorViews)
+ } catch (innerE: Exception) {
+ Log.e(TAG, "updateWidget: Failed to show error state", innerE)
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/app/src/main/res/drawable/ic_widget_adb.xml b/app/src/main/res/drawable/ic_widget_adb.xml
new file mode 100644
index 0000000..bfaa019
--- /dev/null
+++ b/app/src/main/res/drawable/ic_widget_adb.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/widget_adb_status.xml b/app/src/main/res/layout/widget_adb_status.xml
new file mode 100644
index 0000000..82bb6ec
--- /dev/null
+++ b/app/src/main/res/layout/widget_adb_status.xml
@@ -0,0 +1,119 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index d46462f..99f37b9 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -1,353 +1,357 @@
-
-
-
- RootADB Pro
- RootADB Pro
- RootADB Pro
- by Phenix
- Version %1$s
-
-
- Control
- Devices
- Help
-
- Dashboard
- Local ADB
- Remote Relay
-
-
- QUICK STATUS
- QUICK TOGGLES
- SETTINGS
- Local ADB
- Tailscale Relay
- Warpgate Tunnel
- Current connection command:
- Manage
-
-
-
- - %d device
- - %d devices
-
-
-
- Connected
- Disabled
- Checking…
-
-
- Enable
- Disable
- Approve
- Deny
- GitHub
- Email
- Report Bug
- Download PC Scripts
-
-
- Enable on boot
- Enable relay server
-
-
- Port
-
-
- LOCAL NETWORK (WiFi)
- REMOTE RELAY (Tailscale)
-
-
- Local connect command:
- ADB not active
- Copy command
- Copied!
- Root access required
- Port must be 1024-65535
-
-
- Relay Type
- Tailscale
- SSH Tunnel (Coming Soon)
- Custom Relay (Coming Soon)
- TAILSCALE STATUS
- Connected
- Not connected
- Tailscale not active
- Remote connect command:
- Relay port
- Relay port: %1$d
- Trusted devices
- PENDING APPROVAL
- A device wants to connect
- Device %1$s wants to connect
-
-
- Getting Started
- 1. Enable wireless ADB from the Local ADB tab\n2. Note the IP address and port shown\n3. On your PC, run: adb connect IP:PORT\n4. Accept the connection prompt on your device\n5. You can now use ADB wirelessly!
-
- Remote Access via Tailscale
- 1. Install Tailscale on both your phone and PC\n2. Sign in with the same account on both\n3. Enable the relay switch in Remote Relay tab\n4. Use the Tailscale IP to connect from anywhere\n5. Approve new devices when prompted
-
- PC Setup Scripts
- Download auto-connect scripts for your PC. Available for Windows (PowerShell) and Linux/Mac (bash). Scripts auto-detect your device and connect automatically.
-
- Recommended: Split Tunnel Setup
- For best performance with Tailscale remote access:\n\n1. Open Tailscale app on your phone\n2. Go to Settings → Split Tunneling\n3. Enable \"Use Tailscale for specific apps\"\n4. Add only: RootADB Pro\n\nThis routes only ADB traffic through Tailscale, keeping other apps at full speed while maintaining remote ADB access.
-
-
- Developer
- Phenix (Alaa Qweider)
- alaa@nulled.ai
-
-
- RootADB Pro - Contact
- RootADB Pro - Bug Report
-
-
- Requires root access
-
-
- Local ADB status indicator
- Tailscale status indicator
- Warpgate status indicator
-
-
- Connection Mode
- Local WiFi
- Tailscale Direct
- Tailscale Relay
- Warpgate
- Tailscale Direct (Port 5555)
- Tailscale Relay (Port 5556)
- Warpgate Bastion
-
-
- WARPGATE CONFIGURATION
- Warpgate Host
- Port
- Username
- Password
- Target Name
- Local Port
- Connect
- Disconnect
- Connected via Warpgate
- Not connected
- Connecting…
-
-
- Developer Notification
- Hide USB debugging notification
- Hides the system notification when ADB is enabled (requires root)
-
-
- Shizuku Mode
- Using Shizuku for ADB control (limited features)
- Shizuku not available
- Grant Shizuku Permission
-
-
- P2P TOKEN CONNECTION
- Connect device-to-device without relay server
- Your Token
- ---–---–---
- Generate Token
- Revoke
- Show
- Hide
- Copy Token
- Connect Using Token
- Enter token (ABC-123-XYZ)
- No active token
- Token ready - share with peer
- Connecting…
- Connected
- Connection failed
- Expires in %1$s
- Security Warning
- Sharing this token allows another device to connect to your ADB. Only share with trusted devices.\n\nThe token expires in 30 minutes and changes after each disconnect.
- I Understand
- Mark as trusted device
- P2P Token
-
-
- Theme Settings
- Theme Mode
- System
- Light
- Dark
- Accent Color
- Blue
- Teal
- Purple
- Orange
- Pink
- Green
-
-
- ADB Pairing
- How to pair:
- 1. Go to Settings → Developer Options\n2. Enable \'Wireless debugging\'\n3. Tap \'Pair device with pairing code\'\n4. Enter the port and 6-digit code below
- Pairing Port
- The 5-digit port shown in Wireless debugging
- Pairing Code
- The 6-digit code shown on screen
- Pair
- Connecting…
- Pairing successful!
- Pairing failed
- ADB Pairing requires Android 11 or higher
- Auto-Pair (Android 11+)
- Pair wirelessly without PC setup
- Start Pairing
-
-
- Download Tools
- Get the tools you need for wireless ADB:
- Android Platform Tools (ADB)
- Tailscale VPN
- ngrok Tunneling
-
-
- P2P Token QR Code
- Scan this QR code with another device running RootADB Pro to connect instantly via P2P token.
- Show QR
- Close
-
-
- P2P Connection Token
- Scan to connect via P2P token
- Failed to generate QR code
- Expires in
- Expires in %1$s
- Token expired
- Token copied to clipboard
- P2P Connection Token
- Show QR Code
-
-
- Scan QR Code
- Point your camera at a QR code to scan the pairing information
- Camera permission required to scan QR codes
- Camera permission denied. Please enable it in Settings.
- Grant Permission
- Failed to scan QR code
- Invalid QR code format
- Invalid QR code format
- QR code scanned successfully
- Processing QR code…
- Scanning…
- Scan QR Code
- Scan QR Code
- or scan QR code:
- Cancel
-
-
- Close
- Share
- Copy
-
-
- Local ADB status, %s
- Tailscale status, %s
- Warpgate status, %s
- P2P status, %s
-
- Toggle wireless ADB, currently %s
- Toggle relay mode, currently %s
- Toggle Warpgate tunnel, currently %s
- Enable on boot, currently %s
- Toggle notification hiding, currently %s
- Manage trusted devices
- Start ADB pairing
-
- Copy connection command to clipboard
- Show P2P token as QR code
- Revoke P2P token
- Connect with P2P token
- Disconnect P2P connection
- Generate new P2P token
-
- Connection mode: %s
- Wireless ADB port, current value %d
-
- Trusted devices count: %s
- Pending approval from device %s
-
- Local ADB settings
- Tailscale VPN settings
- Warpgate tunnel configuration
- P2P token connection
- Theme and appearance settings
-
- IP address
- ADB connection command
- Status indicator icon
-
-
- Control tab
- Devices tab
- Help tab
-
- Dashboard tab
- Local ADB tab
- Remote Relay tab
-
-
- enabled
- disabled
- connected
- disconnected
- connecting
- active
- inactive
- on
- off
- checked
- unchecked
-
-
- Wireless ADB enabled on port %d
- Wireless ADB disabled
- Device %s connected
- Device disconnected
- P2P token generated, expires in 30 minutes
- P2P token revoked
- P2P connection established
- P2P connection failed
- Copied to clipboard
- Settings updated
- Theme changed to %s
-
-
- Quick Status
- Quick Toggles
- Settings
- Local Network
- Remote Relay
- P2P Token
- Warpgate
- Theme
-
-
- Local
- Tailscale
- Warpgate
- P2P
-
-
- No trusted devices
- Devices you approve will appear here
- Unknown Device
- Unknown IP
- Added: %1$s
- Requested: Now
- Remove device
-
-
- Wireless ADB
- Active on port %1$d
- Tap to enable
-
+
+
+
+ RootADB Pro
+ RootADB Pro
+ RootADB Pro
+ by Phenix
+ Version %1$s
+
+
+ Control
+ Devices
+ Help
+
+ Dashboard
+ Local ADB
+ Remote Relay
+
+
+ QUICK STATUS
+ QUICK TOGGLES
+ SETTINGS
+ Local ADB
+ Tailscale Relay
+ Warpgate Tunnel
+ Current connection command:
+ Manage
+
+
+
+ - %d device
+ - %d devices
+
+
+
+ Connected
+ Enabled
+ Disabled
+ Error
+ Checking…
+
+
+ Enable
+ Disable
+ Approve
+ Deny
+ GitHub
+ Email
+ Report Bug
+ Download PC Scripts
+
+
+ Enable on boot
+ Enable relay server
+
+
+ Port
+
+
+ LOCAL NETWORK (WiFi)
+ REMOTE RELAY (Tailscale)
+
+
+ Local connect command:
+ ADB not active
+ Copy command
+ Copied!
+ Root access required
+ Port must be 1024-65535
+
+
+ Relay Type
+ Tailscale
+ SSH Tunnel (Coming Soon)
+ Custom Relay (Coming Soon)
+ TAILSCALE STATUS
+ Connected
+ Not connected
+ Tailscale not active
+ Remote connect command:
+ Relay port
+ Relay port: %1$d
+ Trusted devices
+ PENDING APPROVAL
+ A device wants to connect
+ Device %1$s wants to connect
+
+
+ Getting Started
+ 1. Enable wireless ADB from the Local ADB tab\n2. Note the IP address and port shown\n3. On your PC, run: adb connect IP:PORT\n4. Accept the connection prompt on your device\n5. You can now use ADB wirelessly!
+
+ Remote Access via Tailscale
+ 1. Install Tailscale on both your phone and PC\n2. Sign in with the same account on both\n3. Enable the relay switch in Remote Relay tab\n4. Use the Tailscale IP to connect from anywhere\n5. Approve new devices when prompted
+
+ PC Setup Scripts
+ Download auto-connect scripts for your PC. Available for Windows (PowerShell) and Linux/Mac (bash). Scripts auto-detect your device and connect automatically.
+
+ Recommended: Split Tunnel Setup
+ For best performance with Tailscale remote access:\n\n1. Open Tailscale app on your phone\n2. Go to Settings → Split Tunneling\n3. Enable \"Use Tailscale for specific apps\"\n4. Add only: RootADB Pro\n\nThis routes only ADB traffic through Tailscale, keeping other apps at full speed while maintaining remote ADB access.
+
+
+ Developer
+ Phenix (Alaa Qweider)
+ alaa@nulled.ai
+
+
+ RootADB Pro - Contact
+ RootADB Pro - Bug Report
+
+
+ Requires root access
+
+
+ Local ADB status indicator
+ Tailscale status indicator
+ Warpgate status indicator
+
+
+ Connection Mode
+ Local WiFi
+ Tailscale Direct
+ Tailscale Relay
+ Warpgate
+ Tailscale Direct (Port 5555)
+ Tailscale Relay (Port 5556)
+ Warpgate Bastion
+
+
+ WARPGATE CONFIGURATION
+ Warpgate Host
+ Port
+ Username
+ Password
+ Target Name
+ Local Port
+ Connect
+ Disconnect
+ Connected via Warpgate
+ Not connected
+ Connecting…
+
+
+ Developer Notification
+ Hide USB debugging notification
+ Hides the system notification when ADB is enabled (requires root)
+
+
+ Shizuku Mode
+ Using Shizuku for ADB control (limited features)
+ Shizuku not available
+ Grant Shizuku Permission
+
+
+ P2P TOKEN CONNECTION
+ Connect device-to-device without relay server
+ Your Token
+ ---–---–---
+ Generate Token
+ Revoke
+ Show
+ Hide
+ Copy Token
+ Connect Using Token
+ Enter token (ABC-123-XYZ)
+ No active token
+ Token ready - share with peer
+ Connecting…
+ Connected
+ Connection failed
+ Expires in %1$s
+ Security Warning
+ Sharing this token allows another device to connect to your ADB. Only share with trusted devices.\n\nThe token expires in 30 minutes and changes after each disconnect.
+ I Understand
+ Mark as trusted device
+ P2P Token
+
+
+ Theme Settings
+ Theme Mode
+ System
+ Light
+ Dark
+ Accent Color
+ Blue
+ Teal
+ Purple
+ Orange
+ Pink
+ Green
+
+
+ ADB Pairing
+ How to pair:
+ 1. Go to Settings → Developer Options\n2. Enable \'Wireless debugging\'\n3. Tap \'Pair device with pairing code\'\n4. Enter the port and 6-digit code below
+ Pairing Port
+ The 5-digit port shown in Wireless debugging
+ Pairing Code
+ The 6-digit code shown on screen
+ Pair
+ Connecting…
+ Pairing successful!
+ Pairing failed
+ ADB Pairing requires Android 11 or higher
+ Auto-Pair (Android 11+)
+ Pair wirelessly without PC setup
+ Start Pairing
+
+
+ Download Tools
+ Get the tools you need for wireless ADB:
+ Android Platform Tools (ADB)
+ Tailscale VPN
+ ngrok Tunneling
+
+
+ P2P Token QR Code
+ Scan this QR code with another device running RootADB Pro to connect instantly via P2P token.
+ Show QR
+ Close
+
+
+ P2P Connection Token
+ Scan to connect via P2P token
+ Failed to generate QR code
+ Expires in
+ Expires in %1$s
+ Token expired
+ Token copied to clipboard
+ P2P Connection Token
+ Show QR Code
+
+
+ Scan QR Code
+ Point your camera at a QR code to scan the pairing information
+ Camera permission required to scan QR codes
+ Camera permission denied. Please enable it in Settings.
+ Grant Permission
+ Failed to scan QR code
+ Invalid QR code format
+ Invalid QR code format
+ QR code scanned successfully
+ Processing QR code…
+ Scanning…
+ Scan QR Code
+ Scan QR Code
+ or scan QR code:
+ Cancel
+
+
+ Close
+ Share
+ Copy
+
+
+ Local ADB status, %s
+ Tailscale status, %s
+ Warpgate status, %s
+ P2P status, %s
+
+ Toggle wireless ADB, currently %s
+ Toggle relay mode, currently %s
+ Toggle Warpgate tunnel, currently %s
+ Enable on boot, currently %s
+ Toggle notification hiding, currently %s
+ Manage trusted devices
+ Start ADB pairing
+
+ Copy connection command to clipboard
+ Show P2P token as QR code
+ Revoke P2P token
+ Connect with P2P token
+ Disconnect P2P connection
+ Generate new P2P token
+
+ Connection mode: %s
+ Wireless ADB port, current value %d
+
+ Trusted devices count: %s
+ Pending approval from device %s
+
+ Local ADB settings
+ Tailscale VPN settings
+ Warpgate tunnel configuration
+ P2P token connection
+ Theme and appearance settings
+
+ IP address
+ ADB connection command
+ Status indicator icon
+
+
+ Control tab
+ Devices tab
+ Help tab
+
+ Dashboard tab
+ Local ADB tab
+ Remote Relay tab
+
+
+ enabled
+ disabled
+ connected
+ disconnected
+ connecting
+ active
+ inactive
+ on
+ off
+ checked
+ unchecked
+
+
+ Wireless ADB enabled on port %d
+ Wireless ADB disabled
+ Device %s connected
+ Device disconnected
+ P2P token generated, expires in 30 minutes
+ P2P token revoked
+ P2P connection established
+ P2P connection failed
+ Copied to clipboard
+ Settings updated
+ Theme changed to %s
+
+
+ Quick Status
+ Quick Toggles
+ Settings
+ Local Network
+ Remote Relay
+ P2P Token
+ Warpgate
+ Theme
+
+
+ Local
+ Tailscale
+ Warpgate
+ P2P
+
+
+ No trusted devices
+ Devices you approve will appear here
+ Unknown Device
+ Unknown IP
+ Added: %1$s
+ Requested: Now
+ Remove device
+
+ Quick ADB status and toggle control
+
+
+ Wireless ADB
+ Active on port %1$d
+ Tap to enable
+
diff --git a/app/src/main/res/xml/adb_widget_info.xml b/app/src/main/res/xml/adb_widget_info.xml
new file mode 100644
index 0000000..0db9ab2
--- /dev/null
+++ b/app/src/main/res/xml/adb_widget_info.xml
@@ -0,0 +1,26 @@
+
+
+