iOS background widget fetching

This commit is contained in:
2024-12-21 11:56:18 +08:00
parent 8db6513eef
commit 73468c5c6d
13 changed files with 202 additions and 94 deletions

View File

@ -37,6 +37,24 @@ import 'package:surface/types/realm.dart';
import 'package:flutter_web_plugins/url_strategy.dart' show usePathUrlStrategy;
import 'package:surface/widgets/dialog.dart';
import 'package:surface/widgets/version_label.dart';
import 'package:workmanager/workmanager.dart';
@pragma('vm:entry-point')
void appBackgroundDispatcher() {
Workmanager().executeTask((task, inputData) async {
print("Native called background task: $task");
switch (task) {
case Workmanager.iOSBackgroundTask:
await Future.wait([widgetUpdateRandomPost()]);
return true;
case "WidgetUpdateRandomPost":
await widgetUpdateRandomPost();
return true;
default:
return true;
}
});
}
void main() async {
WidgetsFlutterBinding.ensureInitialized();
@ -64,6 +82,20 @@ void main() async {
});
}
if (!kIsWeb && (Platform.isAndroid || Platform.isIOS)) {
Workmanager().initialize(
appBackgroundDispatcher,
isInDebugMode: kDebugMode,
);
Workmanager().registerPeriodicTask(
"widget-update-random-post",
"WidgetUpdateRandomPost",
frequency: Duration(minutes: 1),
constraints: Constraints(networkType: NetworkType.connected),
tag: "widget-update",
);
}
runApp(const SolianApp());
}
@ -193,10 +225,14 @@ class _AppSplashScreenState extends State<_AppSplashScreen> {
}
}
Future<void> _postInitialization() async {
await widgetUpdateRandomPost();
}
@override
void initState() {
super.initState();
_initialize();
_initialize().then((_) => _postInitialization());
}
@override

View File

@ -71,7 +71,36 @@ class SnNetworkProvider {
});
}
Future<void> initializeUserAgent() async {
static Future<Dio> createOffContextClient() async {
final prefs = await SharedPreferences.getInstance();
final client = Dio();
client.interceptors.add(RetryInterceptor(
dio: client,
retries: 3,
retryDelays: const [
Duration(milliseconds: 300),
Duration(milliseconds: 1000),
Duration(milliseconds: 3000),
],
));
final ua = await _getUserAgent();
client.interceptors.add(
InterceptorsWrapper(
onRequest: (
RequestOptions options,
RequestInterceptorHandler handler,
) async {
options.headers['User-Agent'] = ua;
return handler.next(options);
},
),
);
client.options.baseUrl = prefs.getString(kNetworkServerStoreKey) ?? kNetworkServerDefault;
return client;
}
static Future<String> _getUserAgent() async {
final String platformInfo;
if (kIsWeb) {
final deviceInfo = await DeviceInfoPlugin().webBrowserInfo;
@ -97,7 +126,11 @@ class SnNetworkProvider {
final packageInfo = await PackageInfo.fromPlatform();
_userAgent = 'Solian/${packageInfo.version}+${packageInfo.buildNumber} ($platformInfo)';
return 'Solian/${packageInfo.version}+${packageInfo.buildNumber} ($platformInfo)';
}
Future<void> initializeUserAgent() async {
_userAgent = await _getUserAgent();
}
final tkLock = Lock();

View File

@ -4,6 +4,8 @@ import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:home_widget/home_widget.dart';
import 'package:surface/providers/sn_network.dart';
import 'package:surface/types/post.dart';
class HomeWidgetProvider {
HomeWidgetProvider(BuildContext context);
@ -15,8 +17,7 @@ class HomeWidgetProvider {
}
}
Future<void> saveWidgetData(String id, dynamic data,
{bool update = true}) async {
Future<void> saveWidgetData(String id, dynamic data, {bool update = true}) async {
if (kIsWeb || !(Platform.isAndroid || Platform.isIOS)) return;
await HomeWidget.saveWidgetData(id, jsonEncode(data));
if (update) await updateWidget();
@ -25,7 +26,7 @@ class HomeWidgetProvider {
Future<void> updateWidget() async {
if (kIsWeb || !(Platform.isAndroid || Platform.isIOS)) return;
if (Platform.isIOS) {
const widgets = ["SolarFeaturedPostWidget", "SolarCheckInWidget"];
const widgets = ["SolarRandomPostWidget", "SolarCheckInWidget"];
for (final widget in widgets) {
await HomeWidget.updateWidget(
name: widget,
@ -43,3 +44,16 @@ class HomeWidgetProvider {
}
}
}
Future<void> widgetUpdateRandomPost() async {
final snc = await SnNetworkProvider.createOffContextClient();
final resp = await snc.get('/cgi/co/recommendations/shuffle?take=1');
final post = SnPost.fromJson(resp.data['data'][0]);
await HomeWidget.saveWidgetData("int_random_post", jsonEncode(post.toJson()));
await HomeWidget.updateWidget(
name: "SolarRandomPostWidget",
iOSName: "SolarRandomPostWidget",
androidName: "FeaturedPostWidgetReceiver",
qualifiedAndroidName: "dev.solsynth.solian.widgets.FeaturedPostWidgetReceiver",
);
}

View File

@ -151,7 +151,7 @@ class _HomeDashCheckInWidgetState extends State<_HomeDashCheckInWidget> {
final home = context.read<HomeWidgetProvider>();
final resp = await sn.client.get('/cgi/id/check-in/today');
_todayRecord = SnCheckInRecord.fromJson(resp.data);
home.saveWidgetData('today_check_in', _todayRecord!.toJson());
home.saveWidgetData('pas_check_in_record', _todayRecord!.toJson());
} finally {
setState(() => _isBusy = false);
}
@ -164,7 +164,7 @@ class _HomeDashCheckInWidgetState extends State<_HomeDashCheckInWidget> {
final home = context.read<HomeWidgetProvider>();
final resp = await sn.client.post('/cgi/id/check-in');
_todayRecord = SnCheckInRecord.fromJson(resp.data);
home.saveWidgetData('today_check_in', _todayRecord!.toJson());
home.saveWidgetData('pas_check_in_record', _todayRecord!.toJson());
} catch (err) {
if (!mounted) return;
context.showErrorDialog(err);