Skip to content

Commit 6681929

Browse files
enedclaude
andcommitted
feat: complete hook-based debug system implementation
- Replace isInDebugMode parameter with extensible hook system - Add WorkmanagerDebugHandler interface/protocol for Android/iOS - Implement LoggingDebugHandler and NotificationDebugHandler - Add WorkmanagerDebug global registry for handler management - Remove old DebugNotificationHelper and UserDefaultsHelper debug code - Update documentation with proper Tabs component syntax - Clean up workmanager/README.md to avoid duplication with docs.page - Fix all integration tests and update mocks 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 4757cfb commit 6681929

15 files changed

Lines changed: 149 additions & 551 deletions

File tree

CLAUDE.md

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,17 @@
3232
- **No AI agent progress**: Don't document debugging steps, build fixes, or internal development process
3333
- **What matters to users**: Breaking changes, new features, bug fixes that affect their code
3434
- **Example of bad changelog entry**: "Fixed Kotlin null safety issues with androidx.work 2.10.2 type system improvements"
35-
- **Example of good changelog entry**: "Fixed periodic tasks not respecting frequency changes"
35+
- **Example of good changelog entry**: "Fixed periodic tasks not respecting frequency changes"
36+
37+
## Documentation Components (docs.page)
38+
- **Component reference**: https://use.docs.page/ contains the full reference for available components
39+
- **Tabs component syntax**:
40+
```jsx
41+
<Tabs>
42+
<TabItem label="Tab Name" value="unique-value">
43+
Content here
44+
</TabItem>
45+
</Tabs>
46+
```
47+
- Use `<TabItem>` not `<Tab>` - this is a common mistake that causes JavaScript errors
48+
- Always include both `label` and `value` props on TabItem components

docs/debugging.mdx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ WorkmanagerDebug.setDebugHandler(NotificationDebugHandler())
8989
Create your own debug handler for custom logging needs:
9090

9191
<Tabs>
92-
<Tab title="Android">
92+
<TabItem label="Android" value="android">
9393

9494
```kotlin
9595
class CustomDebugHandler : WorkmanagerDebugHandler {
@@ -105,8 +105,8 @@ class CustomDebugHandler : WorkmanagerDebugHandler {
105105
WorkmanagerDebug.setDebugHandler(CustomDebugHandler())
106106
```
107107

108-
</Tab>
109-
<Tab title="iOS">
108+
</TabItem>
109+
<TabItem label="iOS" value="ios">
110110

111111
```swift
112112
class CustomDebugHandler: WorkmanagerDebugHandler {
@@ -122,7 +122,7 @@ class CustomDebugHandler: WorkmanagerDebugHandler {
122122
WorkmanagerDebug.setDebugHandler(CustomDebugHandler())
123123
```
124124

125-
</Tab>
125+
</TabItem>
126126
</Tabs>
127127

128128
## Android Debugging
@@ -350,7 +350,7 @@ Future<bool> isTaskHealthy(String taskName, Duration maxAge) async {
350350
- [ ] Workmanager initialized in main()
351351
- [ ] Task names are unique
352352
- [ ] Platform setup completed ([iOS setup guide](quickstart#ios))
353-
- [ ] Debug notifications enabled (`isInDebugMode: kDebugMode`)
353+
- [ ] Debug handler configured (see [Debug Handlers](#debug-handlers))
354354

355355
**Performance & Reliability:**
356356
- [ ] Task logic optimized for background execution

docs/quickstart.mdx

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -145,10 +145,7 @@ void callbackDispatcher() {
145145
import 'package:flutter/foundation.dart';
146146
147147
void main() {
148-
Workmanager().initialize(
149-
callbackDispatcher,
150-
isInDebugMode: kDebugMode,
151-
);
148+
Workmanager().initialize(callbackDispatcher);
152149
153150
runApp(MyApp());
154151
}

example/integration_test/workmanager_integration_test.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ void main() {
7676

7777
testWidgets('initialize should succeed on all platforms',
7878
(WidgetTester tester) async {
79-
await workmanager.initialize(callbackDispatcher, isInDebugMode: true);
79+
await workmanager.initialize(callbackDispatcher);
8080
// No exception means success
8181
});
8282

workmanager/README.md

Lines changed: 32 additions & 355 deletions
Large diffs are not rendered by default.

workmanager/test/workmanager_test.mocks.dart

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,14 +35,12 @@ class MockWorkmanager extends _i1.Mock implements _i2.Workmanager {
3535

3636
@override
3737
_i3.Future<void> initialize(
38-
Function? callbackDispatcher, {
39-
bool? isInDebugMode = false,
40-
}) =>
38+
Function? callbackDispatcher,
39+
) =>
4140
(super.noSuchMethod(
4241
Invocation.method(
4342
#initialize,
4443
[callbackDispatcher],
45-
{#isInDebugMode: isInDebugMode},
4644
),
4745
returnValue: _i3.Future<void>.value(),
4846
returnValueForMissingStub: _i3.Future<void>.value(),

workmanager_android/android/src/main/kotlin/dev/fluttercommunity/workmanager/BackgroundWorker.kt

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,6 @@ class BackgroundWorker(
5050
private val dartTask
5151
get() = workerParams.inputData.getString(DART_TASK_KEY)!!
5252

53-
5453
private val randomThreadIdentifier = Random().nextInt()
5554
private var engine: FlutterEngine? = null
5655

@@ -96,8 +95,8 @@ class BackgroundWorker(
9695
inputData = payload,
9796
startTime = startTime,
9897
callbackHandle = callbackHandle,
99-
callbackInfo = callbackInfo?.callbackName
100-
)
98+
callbackInfo = callbackInfo?.callbackName,
99+
),
101100
)
102101

103102
engine?.let { engine ->
@@ -134,13 +133,13 @@ class BackgroundWorker(
134133
TaskDebugInfo(
135134
taskName = dartTask,
136135
inputData = payload,
137-
startTime = startTime
136+
startTime = startTime,
138137
),
139138
TaskResult(
140139
success = result is Result.Success,
141140
duration = fetchDuration,
142-
error = if (result is Result.Failure) "Task failed" else null
143-
)
141+
error = if (result is Result.Failure) "Task failed" else null,
142+
),
144143
)
145144

146145
// No result indicates we were signalled to stop by WorkManager. The result is already

workmanager_android/android/src/main/kotlin/dev/fluttercommunity/workmanager/LoggingDebugHandler.kt

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,22 @@ class LoggingDebugHandler : WorkmanagerDebugHandler {
1212
private const val TAG = "WorkmanagerDebug"
1313
}
1414

15-
override fun onTaskStarting(context: Context, taskInfo: TaskDebugInfo) {
15+
override fun onTaskStarting(
16+
context: Context,
17+
taskInfo: TaskDebugInfo,
18+
) {
1619
Log.d(TAG, "Task starting: ${taskInfo.taskName}, callbackHandle: ${taskInfo.callbackHandle}")
1720
}
1821

19-
override fun onTaskCompleted(context: Context, taskInfo: TaskDebugInfo, result: TaskResult) {
22+
override fun onTaskCompleted(
23+
context: Context,
24+
taskInfo: TaskDebugInfo,
25+
result: TaskResult,
26+
) {
2027
val status = if (result.success) "SUCCESS" else "FAILURE"
2128
Log.d(TAG, "Task completed: ${taskInfo.taskName}, result: $status, duration: ${result.duration}ms")
2229
if (result.error != null) {
2330
Log.e(TAG, "Task error: ${result.error}")
2431
}
2532
}
26-
}
33+
}

workmanager_android/android/src/main/kotlin/dev/fluttercommunity/workmanager/NotificationDebugHandler.kt

Lines changed: 35 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import android.app.NotificationManager
55
import android.content.Context
66
import android.os.Build
77
import androidx.core.app.NotificationCompat
8-
import androidx.work.ListenableWorker
98
import java.text.DateFormat
109
import java.util.Date
1110
import java.util.concurrent.TimeUnit.MILLISECONDS
@@ -14,7 +13,7 @@ import kotlin.random.Random
1413
/**
1514
* A debug handler that shows notifications for task events.
1615
* Use this to see task execution as notifications on the device.
17-
*
16+
*
1817
* Note: You need to ensure your app has notification permissions.
1918
*/
2019
class NotificationDebugHandler : WorkmanagerDebugHandler {
@@ -30,7 +29,10 @@ class NotificationDebugHandler : WorkmanagerDebugHandler {
3029
private val failureEmoji = "🔥"
3130
private val currentTime get() = debugDateFormatter.format(Date())
3231

33-
override fun onTaskStarting(context: Context, taskInfo: TaskDebugInfo) {
32+
override fun onTaskStarting(
33+
context: Context,
34+
taskInfo: TaskDebugInfo,
35+
) {
3436
val notificationId = Random.nextInt()
3537
postNotification(
3638
context,
@@ -40,16 +42,20 @@ class NotificationDebugHandler : WorkmanagerDebugHandler {
4042
• Task Starting: ${taskInfo.taskName}
4143
• Input Data: ${taskInfo.inputData ?: "none"}
4244
• Callback Handle: ${taskInfo.callbackHandle}
43-
""".trimIndent()
45+
""".trimIndent(),
4446
)
4547
}
4648

47-
override fun onTaskCompleted(context: Context, taskInfo: TaskDebugInfo, result: TaskResult) {
49+
override fun onTaskCompleted(
50+
context: Context,
51+
taskInfo: TaskDebugInfo,
52+
result: TaskResult,
53+
) {
4854
val notificationId = Random.nextInt()
4955
val emoji = if (result.success) successEmoji else failureEmoji
5056
val status = if (result.success) "SUCCESS" else "FAILURE"
5157
val duration = MILLISECONDS.toSeconds(result.duration)
52-
58+
5359
postNotification(
5460
context,
5561
notificationId,
@@ -60,7 +66,7 @@ class NotificationDebugHandler : WorkmanagerDebugHandler {
6066
• Input Data: ${taskInfo.inputData ?: "none"}
6167
• Duration: ${duration}s
6268
${if (result.error != null) "• Error: ${result.error}" else ""}
63-
""".trimIndent()
69+
""".trimIndent(),
6470
)
6571
}
6672

@@ -71,33 +77,34 @@ class NotificationDebugHandler : WorkmanagerDebugHandler {
7177
contentText: String,
7278
) {
7379
val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
74-
80+
7581
createNotificationChannel(notificationManager)
76-
77-
val notification = NotificationCompat
78-
.Builder(context, DEBUG_CHANNEL_ID)
79-
.setContentTitle(title)
80-
.setContentText(contentText)
81-
.setStyle(
82-
NotificationCompat
83-
.BigTextStyle()
84-
.bigText(contentText)
85-
)
86-
.setSmallIcon(android.R.drawable.stat_notify_sync)
87-
.setPriority(NotificationCompat.PRIORITY_LOW)
88-
.build()
89-
82+
83+
val notification =
84+
NotificationCompat
85+
.Builder(context, DEBUG_CHANNEL_ID)
86+
.setContentTitle(title)
87+
.setContentText(contentText)
88+
.setStyle(
89+
NotificationCompat
90+
.BigTextStyle()
91+
.bigText(contentText),
92+
).setSmallIcon(android.R.drawable.stat_notify_sync)
93+
.setPriority(NotificationCompat.PRIORITY_LOW)
94+
.build()
95+
9096
notificationManager.notify(notificationId, notification)
9197
}
9298

9399
private fun createNotificationChannel(notificationManager: NotificationManager) {
94100
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
95-
val channel = NotificationChannel(
96-
DEBUG_CHANNEL_ID,
97-
DEBUG_CHANNEL_NAME,
98-
NotificationManager.IMPORTANCE_LOW
99-
)
101+
val channel =
102+
NotificationChannel(
103+
DEBUG_CHANNEL_ID,
104+
DEBUG_CHANNEL_NAME,
105+
NotificationManager.IMPORTANCE_LOW,
106+
)
100107
notificationManager.createNotificationChannel(channel)
101108
}
102109
}
103-
}
110+
}

workmanager_android/android/src/main/kotlin/dev/fluttercommunity/workmanager/WorkManagerUtils.kt

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -99,9 +99,7 @@ class WorkManagerWrapper(
9999
) {
100100
private val workManager = WorkManager.getInstance(context)
101101

102-
fun enqueueOneOffTask(
103-
request: dev.fluttercommunity.workmanager.pigeon.OneOffTaskRequest,
104-
) {
102+
fun enqueueOneOffTask(request: dev.fluttercommunity.workmanager.pigeon.OneOffTaskRequest) {
105103
try {
106104
val oneOffTaskRequest =
107105
OneTimeWorkRequest
@@ -141,9 +139,7 @@ class WorkManagerWrapper(
141139
}
142140
}
143141

144-
fun enqueuePeriodicTask(
145-
request: dev.fluttercommunity.workmanager.pigeon.PeriodicTaskRequest,
146-
) {
142+
fun enqueuePeriodicTask(request: dev.fluttercommunity.workmanager.pigeon.PeriodicTaskRequest) {
147143
val periodicTaskRequest =
148144
PeriodicWorkRequest
149145
.Builder(

0 commit comments

Comments
 (0)