Skip to content

Instantly share code, notes, and snippets.

Fix these issues:

Service Lifecycle Memory Leaks

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.

  1. 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.

  2. 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.

    • Suggestion: Store the base URL and webhook path in the build.gradle.kts file's buildConfigField. This allows for different URLs for debug and release builds.

Android app requirements:

  1. General:
    • This is an internal app, not published on the Play Store
    • Support Android 15+ only
    • Keep the UI as simple as possible, no theming, no animations
    • Keep the implementation as short as possible, prefer libraries over self-implemented solutions
    • No settings whatever, all configuration is done through constants in the code
  2. Main Activity:
  • Check 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));
}
})();
@daohoangson
daohoangson / main.dart
Created May 28, 2022 14:40
Nested HTML table in Flutter.
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) {
@daohoangson
daohoangson / Main.java
Created April 20, 2022 08:50
HMAC / SHA256 in different languagues
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");
@daohoangson
daohoangson / main.dart
Created March 22, 2022 12:32
Flutter CameraPreview in ClipRRect example
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 {
@daohoangson
daohoangson / main.dart
Created November 18, 2021 00:03
`HtmlWidget.customWidgetBuilder` example
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());
}