Fix these issues:
Location: NotificationListenerService.kt:13
Problem: Service creates coroutine scope with SupervisorJob but never cancels it in onDestroy(), leading to potential memory leaks when service restarts.
// Current problematic code
Read ./docs/001_recap.md and ./docs/002_recap.md to understand the context of this project.
Your goal is to restructure the notification processing logic.
First, introduce configuration as code to manage which URL to send which notifications. The config should be a separate file, e.g. WebhookConfig.ts
. Config contains a list of URLs. For each URL, contain a list of filter rules. Each rule must target a specific package name, and optionally filter by title or text (regex). If a notification matches any of the rules for a URL, it should be sent to that URL. A notification can be sent to multiple URLs if it matches multiple rules. Config also contains a list of blacklisted packages to ignore, which will not trigger any actions. Notifications that are not sent to any URL or blacklisted should be inserted into the Room database for review.
Second, change the Room database to store notifications that could not be sent (network failures, etc.) AND notifications that were undecided (not sent to any URL or blackl
Read ./docs/001_recap.md to understand the context of this project.
Write ./docs/002_plan.md to verify the following comments. Give suggestions for improvements or confirm that the implementation is correct.
Dependency Injection: The NotificationRepository
is instantiated directly in MainActivity
and NotificationListenerService
. Using a dependency injection framework like Hilt or Koin would create a single instance (singleton) of the repository, improving resource management and making testing even easier.
Hardcoded Values: The webhook URL and other constants are hardcoded in Constants.kt
and NetworkModule.kt
. For better flexibility and security, these should be externalized.
build.gradle.kts
file's buildConfigField
. This allows for different URLs for debug and release builds.Android app requirements:
enabled_notification_listeners
and enable button to open settings if needed, keep the button disabled if permission is already granted(async () => { | |
const unviewedDetails = [...document.querySelectorAll('.Details[data-file-type=".png"]')] | |
.filter(details => !details.hidden) | |
const loadDiffs = unviewedDetails.map(details => details.querySelector('.js-button-text')) | |
.filter(btn => btn !== null && btn.innerText === 'Load diff'); | |
for (const loadDiff of loadDiffs) { | |
loadDiff.click(); | |
await new Promise(r => setTimeout(r, 2000)); | |
} | |
})(); |
import 'package:flutter/material.dart'; | |
import 'package:flutter_widget_from_html_core/flutter_widget_from_html_core.dart'; | |
void main() { | |
runApp(MyApp()); | |
} | |
class MyApp extends StatelessWidget { | |
@override | |
Widget build(BuildContext context) { |
import java.io.UnsupportedEncodingException; | |
import java.security.InvalidKeyException; | |
import java.security.NoSuchAlgorithmException; | |
import java.util.Base64; | |
import javax.crypto.Mac; | |
import javax.crypto.spec.SecretKeySpec; | |
class Main { | |
public static String encode(String data, String key) throws InvalidKeyException, NoSuchAlgorithmException, UnsupportedEncodingException { | |
Mac sha256 = Mac.getInstance("HmacSHA256"); |
import 'package:flutter/material.dart'; | |
import 'package:camera/camera.dart'; | |
void main() => runApp(const CameraApp()); | |
class CameraApp extends StatefulWidget { | |
const CameraApp({Key? key}) : super(key: key); | |
@override | |
_CameraAppState createState() => _CameraAppState(); |
import 'package:flutter/material.dart'; | |
import 'package:flutter_widget_from_html_core/flutter_widget_from_html_core.dart'; | |
const Color darkBlue = Color.fromARGB(255, 18, 32, 47); | |
void main() { | |
runApp(MyApp()); | |
} | |
class MyApp extends StatelessWidget { |
import 'package:carousel_slider/carousel_slider.dart'; | |
import 'package:flutter/material.dart'; | |
import 'package:flutter_widget_from_html_core/flutter_widget_from_html_core.dart'; | |
const Color darkBlue = Color.fromARGB(255, 18, 32, 47); | |
void main() { | |
runApp(MyApp()); | |
} |