Prateek Sunal 1 anno fa
parent
commit
a18dc7feb0

+ 1 - 1
android/app/src/main/res/xml/slideshow_widget.xml

@@ -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">

+ 11 - 8
ios/Runner/AppDelegate.swift

@@ -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)
+  }
+}

+ 1 - 0
ios/Runner/Info.plist

@@ -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>

+ 2 - 0
lib/core/constants.dart

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

+ 100 - 0
lib/main.dart

@@ -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) =>

+ 0 - 75
lib/ui/tabs/home_widget.dart

@@ -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),

+ 43 - 0
lib/utils/preload_util.dart

@@ -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;
+  }
+}