feat: workmanager

This commit is contained in:
Prateek Sunal 2024-02-10 00:20:31 +05:30
parent 0cd1a54c0d
commit a18dc7feb0
7 changed files with 158 additions and 84 deletions

View file

@ -2,7 +2,7 @@
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:minWidth="100dp"
android:minHeight="100dp"
android:updatePeriodMillis="600000"
android:updatePeriodMillis="900000"
android:initialLayout="@layout/slideshow_layout"
android:resizeMode="horizontal|vertical"
android:widgetCategory="home_screen">

View file

@ -1,5 +1,6 @@
import UIKit
import Flutter
import UIKit
import workmanager
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
@ -9,20 +10,22 @@ import Flutter
) -> Bool {
var flutter_native_splash = 1
UIApplication.shared.isStatusBarHidden = false
UIApplication.shared.setMinimumBackgroundFetchInterval(TimeInterval(60 * 15))
if #available(iOS 10.0, *) {
UNUserNotificationCenter.current().delegate = self as UNUserNotificationCenterDelegate
}
GeneratedPluginRegistrant.register(with: self)
WorkmanagerPlugin.registerTask(withIdentifier: "slideshow-widget")
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
override func applicationDidBecomeActive(_ application: UIApplication) {
signal(SIGPIPE, SIG_IGN);
}
override func applicationDidBecomeActive(_ application: UIApplication) {
signal(SIGPIPE, SIG_IGN)
}
override func applicationWillEnterForeground(_ application: UIApplication) {
signal(SIGPIPE, SIG_IGN);
}
}
override func applicationWillEnterForeground(_ application: UIApplication) {
signal(SIGPIPE, SIG_IGN)
}
}

View file

@ -5,6 +5,7 @@
<key>BGTaskSchedulerPermittedIdentifiers</key>
<array>
<string>com.transistorsoft.fetch</string>
<string>slideshow-widget</string>
</array>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>

View file

@ -68,3 +68,5 @@ const galleryGridSpacing = 2.0;
const searchSectionLimit = 7;
bool isInternalUser = false;
const iOSGroupID = "group.io.ente.frame.SlideshowWidget";

View file

@ -1,5 +1,6 @@
import 'dart:async';
import 'dart:io';
import "dart:math";
import "package:adaptive_theme/adaptive_theme.dart";
import 'package:background_fetch/background_fetch.dart';
@ -9,6 +10,7 @@ import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import "package:flutter/rendering.dart";
import "package:flutter_displaymode/flutter_displaymode.dart";
import 'package:home_widget/home_widget.dart' as hw;
import 'package:logging/logging.dart';
import "package:media_kit/media_kit.dart";
import 'package:path_provider/path_provider.dart';
@ -18,6 +20,7 @@ import 'package:photos/core/constants.dart';
import 'package:photos/core/error-reporting/super_logging.dart';
import 'package:photos/core/errors.dart';
import 'package:photos/core/network/network.dart';
import "package:photos/db/files_db.dart";
import 'package:photos/db/upload_locks_db.dart';
import 'package:photos/ente_theme_data.dart';
import "package:photos/l10n/l10n.dart";
@ -46,7 +49,10 @@ import 'package:photos/ui/tools/lock_screen.dart';
import 'package:photos/utils/crypto_util.dart';
import 'package:photos/utils/file_uploader.dart';
import 'package:photos/utils/local_settings.dart';
import "package:photos/utils/preload_util.dart";
import "package:photos/utils/thumbnail_util.dart";
import 'package:shared_preferences/shared_preferences.dart';
import "package:workmanager/workmanager.dart";
final _logger = Logger("main");
@ -60,10 +66,103 @@ const kBGPushTimeout = Duration(seconds: 28);
const kFGTaskDeathTimeoutInMicroseconds = 5000000;
const kBackgroundLockLatency = Duration(seconds: 3);
@pragma("vm:entry-point")
void initSlideshowWidget() {
Workmanager().executeTask(
(taskName, inputData) async {
await _init(true, via: 'runViaSlideshowWidget');
final collectionID =
await FavoritesService.instance.getFavoriteCollectionID();
if (collectionID == null) {
return false;
}
try {
await hw.HomeWidget.setAppGroupId(iOSGroupID);
final res = await FilesDB.instance.getFilesInCollection(
collectionID,
galleryLoadStartTime,
galleryLoadEndTime,
);
final previousGeneratedId =
await hw.HomeWidget.getWidgetData<int>("home_widget_last_img");
final files = res.files
.where((element) => element.generatedID != previousGeneratedId);
final randomNumber = Random().nextInt(files.length);
final randomFile = files.elementAt(randomNumber);
final cachedThumbnail = await getThumbnailFromServer(randomFile);
var img = Image.memory(cachedThumbnail);
var imgProvider = img.image;
await PreloadImage.loadImage(imgProvider);
img = Image.memory(cachedThumbnail);
imgProvider = img.image;
final image = await decodeImageFromList(cachedThumbnail);
final width = image.width.toDouble();
final height = image.height.toDouble();
final size = min(width, height);
final widget = ClipRRect(
borderRadius: BorderRadius.circular(32),
child: Container(
width: size,
height: size,
decoration: BoxDecoration(
color: Colors.black,
image: DecorationImage(image: imgProvider, fit: BoxFit.cover),
),
),
);
await hw.HomeWidget.renderFlutterWidget(
widget,
logicalSize: Size(size, size),
key: "slideshow",
);
await hw.HomeWidget.updateWidget(
name: 'SlideshowWidgetProvider',
androidName: 'SlideshowWidgetProvider',
qualifiedAndroidName: 'io.ente.photos.SlideshowWidgetProvider',
iOSName: 'SlideshowWidget',
);
if (randomFile.generatedID != null) {
await hw.HomeWidget.saveWidgetData<int>(
"home_widget_last_img",
randomFile.generatedID!,
);
}
_logger.info(
">>> SlideshowWidget rendered with size ${width}x$height",
);
return true;
} catch (e) {
return false;
}
},
);
}
void main() async {
debugRepaintRainbowEnabled = false;
WidgetsFlutterBinding.ensureInitialized();
MediaKit.ensureInitialized();
try {
await Workmanager()
.initialize(initSlideshowWidget, isInDebugMode: kDebugMode);
await Workmanager().registerPeriodicTask(
"slideshow-widget",
"updateSlideshowWidget",
frequency: const Duration(minutes: 15),
);
} catch (_) {}
final savedThemeMode = await AdaptiveTheme.getThemeMode();
await _runInForeground(savedThemeMode);
unawaited(BackgroundFetch.registerHeadlessTask(_headlessTaskHandler));
@ -76,6 +175,7 @@ Future<void> _runInForeground(AdaptiveThemeMode? savedThemeMode) async {
await _init(false, via: 'mainMethod');
final Locale locale = await getLocale();
unawaited(_scheduleFGSync('appStart in FG'));
runApp(
AppLock(
builder: (args) =>

View file

@ -1,21 +1,17 @@
import 'dart:async';
import 'dart:io';
import "dart:math";
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package:flutter/services.dart';
import "package:flutter_animate/flutter_animate.dart";
import "package:flutter_local_notifications/flutter_local_notifications.dart";
import 'package:home_widget/home_widget.dart' as hw;
import 'package:logging/logging.dart';
import 'package:media_extension/media_extension_action_types.dart';
import 'package:modal_bottom_sheet/modal_bottom_sheet.dart';
import 'package:move_to_background/move_to_background.dart';
import 'package:photos/core/configuration.dart';
import "package:photos/core/constants.dart";
import 'package:photos/core/event_bus.dart';
import "package:photos/db/files_db.dart";
import 'package:photos/ente_theme_data.dart';
import 'package:photos/events/account_configured_event.dart';
import 'package:photos/events/backup_folders_updated_event.dart';
@ -33,7 +29,6 @@ import 'package:photos/models/selected_files.dart';
import 'package:photos/services/app_lifecycle_service.dart';
import 'package:photos/services/collections_service.dart';
import "package:photos/services/entity_service.dart";
import "package:photos/services/favorites_service.dart";
import 'package:photos/services/local_sync_service.dart';
import "package:photos/services/notification_service.dart";
import 'package:photos/services/update_service.dart';
@ -61,7 +56,6 @@ import "package:photos/ui/viewer/gallery/collection_page.dart";
import 'package:photos/ui/viewer/search/search_widget.dart';
import 'package:photos/utils/dialog_util.dart';
import "package:photos/utils/navigation_util.dart";
import "package:photos/utils/thumbnail_util.dart";
import 'package:receive_sharing_intent/receive_sharing_intent.dart';
import "package:shared_preferences/shared_preferences.dart";
import 'package:uni_links/uni_links.dart';
@ -115,11 +109,6 @@ class _HomeWidgetState extends State<HomeWidget> {
@override
void initState() {
_logger.info("Building initstate");
if (FavoritesService.instance.hasFavorites()) {
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
initHomeWidget();
});
}
_tabChangedEventSubscription =
Bus.instance.on<TabChangedEvent>().listen((event) {
_selectedTabIndex = event.selectedIndex;
@ -243,70 +232,6 @@ class _HomeWidgetState extends State<HomeWidget> {
super.initState();
}
Future<void> initHomeWidget() async {
await hw.HomeWidget.setAppGroupId("group.io.ente.frame.SlideshowWidget");
final collectionID =
await FavoritesService.instance.getFavoriteCollectionID();
final res = await FilesDB.instance.getFilesInCollection(
collectionID!,
galleryLoadStartTime,
galleryLoadEndTime,
);
final previousGeneratedId =
await hw.HomeWidget.getWidgetData<int>("home_widget_last_img");
final files = res.files
.where((element) => element.generatedID != previousGeneratedId);
final randomNumber = Random().nextInt(files.length);
final randomFile = files.elementAt(randomNumber);
final cachedThumbnail = await getThumbnailFromServer(randomFile);
var img = Image.memory(cachedThumbnail);
var imgProvider = img.image;
await precacheImage(imgProvider, context);
img = Image.memory(cachedThumbnail);
imgProvider = img.image;
final image = await decodeImageFromList(cachedThumbnail);
final width = image.width.toDouble();
final height = image.height.toDouble();
final size = min(width, height);
final widget = ClipRRect(
borderRadius: BorderRadius.circular(32),
child: Container(
width: size,
height: size,
decoration: BoxDecoration(
color: Colors.black,
image: DecorationImage(image: imgProvider, fit: BoxFit.cover),
),
),
);
await hw.HomeWidget.renderFlutterWidget(
widget,
logicalSize: Size(size, size),
key: "slideshow",
);
await hw.HomeWidget.updateWidget(
name: 'SlideshowWidgetProvider',
androidName: 'SlideshowWidgetProvider',
qualifiedAndroidName: 'io.ente.photos.SlideshowWidgetProvider',
iOSName: 'SlideshowWidget',
);
if (randomFile.generatedID != null) {
await hw.HomeWidget.saveWidgetData<int>(
"home_widget_last_img",
randomFile.generatedID!,
);
}
_logger
.info(">>> HomeWidget rendered with size ${img.width}x${img.height}");
}
Future<void> _autoLogoutAlert() async {
final AlertDialog alert = AlertDialog(
title: Text(S.of(context).sessionExpired),

View file

@ -0,0 +1,43 @@
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter/painting.dart';
import 'package:flutter/services.dart';
class PreloadImage {
static Future<void> loadImage(ImageProvider provider) {
final config = ImageConfiguration(
bundle: rootBundle,
devicePixelRatio: 1,
platform: defaultTargetPlatform,
);
final Completer<void> completer = Completer();
final ImageStream stream = provider.resolve(config);
late final ImageStreamListener listener;
listener = ImageStreamListener(
(ImageInfo image, bool sync) {
debugPrint("Image ${image.debugLabel} finished loading");
completer.complete();
stream.removeListener(listener);
},
onError: (dynamic exception, StackTrace? stackTrace) {
completer.complete();
stream.removeListener(listener);
FlutterError.reportError(
FlutterErrorDetails(
context: ErrorDescription('image failed to load'),
library: 'image resource service',
exception: exception,
stack: stackTrace,
silent: true,
),
);
},
);
stream.addListener(listener);
return completer.future;
}
}