feet/lib/notifications/tasks.dart

128 lines
4 KiB
Dart

import 'dart:math';
import 'package:feet/api/api.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:workmanager/workmanager.dart';
@pragma('vm:entry-point')
void callbackDispatcher() {
Workmanager().executeTask((taskName, inputData) async {
print("Native called background task: $taskName");
if (taskName == 'fetchNotifications') {
final sharedPreferences = await SharedPreferences.getInstance();
try {
var initialized = await FlutterLocalNotificationsPlugin().initialize(
const InitializationSettings(
android: AndroidInitializationSettings('@mipmap/ic_launcher'),
),
);
if (initialized == false) {
print("Notification plugin did not initialize; cancelling task");
return true;
}
var apiUrl = sharedPreferences.getString('apiUrl'),
apiKey = sharedPreferences.getString('apiKey'),
latest = sharedPreferences.getInt('newestKnownPost');
if (apiUrl == null || apiKey == null) {
print("API URL or API key not set; cancelling task");
return true;
}
if (latest == null || latest == 0) {
print("newestKnownPost is not set; cancelling task");
return true;
}
var api = FeverAPI(
apiKey: apiKey,
apiUrl: apiUrl,
sharedPrefs: sharedPreferences,
);
var response =
await api.request().withItems().sinceId(latest).execute();
var items =
api.cache.items.getAll().values.where((post) => !post.isRead);
print("Found ${items.length} new unread posts (out of "
"${api.cache.items.size} total)");
print(response);
// Only fetch feeds and favicons if necessary to avoid wasting bandwidth
if (items.isNotEmpty) {
print("New posts found, fetching feeds and favicons");
await api.request().withFeeds().withFavicons().execute();
}
for (var item in items) {
var feed = api.cache.feeds.get(item.feedId);
print("Notifying for post: ${item.id}");
AndroidBitmap<Object>? iconData;
var favicon = api.cache.favicons.get(feed?.faviconId ?? -1);
if (favicon != null) {
try {
iconData = ByteArrayAndroidBitmap.fromBase64String(
favicon.data.split(',')[1]);
} catch (e) {
print("Failed to decode favicon: $e - Continuing");
}
}
await FlutterLocalNotificationsPlugin().show(
Random().nextInt(10000000), // Can't use the post ID here because FreshRSS's IDs are too large
feed != null ? 'New post - ${feed.title}' : 'New post',
item.title,
NotificationDetails(
android: AndroidNotificationDetails(
'new_posts',
'New posts',
autoCancel: true,
channelShowBadge: true,
importance: Importance.low,
largeIcon: iconData,
when: item.createdOnTime.millisecondsSinceEpoch,
showWhen: true,
),
),
);
}
} catch (e) {
print(e);
return false;
}
return true;
}
return true;
});
}
void setupBackgroundTasks() async {
WidgetsFlutterBinding.ensureInitialized();
await Workmanager().initialize(
callbackDispatcher,
isInDebugMode: kDebugMode,
);
await Permission.notification.request();
// Runs every 15 minutes
await Workmanager().registerPeriodicTask(
'fetch_notifications',
'fetchNotifications',
constraints: Constraints(networkType: NetworkType.connected),
initialDelay: const Duration(seconds: 60),
);
}