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

@ -56,7 +56,7 @@ PODS:
- Firebase/Analytics (= 11.4.0)
- firebase_core
- Flutter
- firebase_core (3.8.1):
- firebase_core (3.9.0):
- Firebase/CoreOnly (= 11.4.0)
- Flutter
- firebase_messaging (15.1.6):
@ -216,6 +216,8 @@ PODS:
- wakelock_plus (0.0.1):
- Flutter
- WebRTC-SDK (125.6422.06)
- workmanager (0.0.1):
- Flutter
DEPENDENCIES:
- connectivity_plus (from `.symlinks/plugins/connectivity_plus/darwin`)
@ -249,6 +251,7 @@ DEPENDENCIES:
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
- volume_controller (from `.symlinks/plugins/volume_controller/ios`)
- wakelock_plus (from `.symlinks/plugins/wakelock_plus/ios`)
- workmanager (from `.symlinks/plugins/workmanager/ios`)
SPEC REPOS:
trunk:
@ -333,6 +336,8 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/volume_controller/ios"
wakelock_plus:
:path: ".symlinks/plugins/wakelock_plus/ios"
workmanager:
:path: ".symlinks/plugins/workmanager/ios"
SPEC CHECKSUMS:
connectivity_plus: 18382e7311ba19efcaee94442b23b32507b20695
@ -344,7 +349,7 @@ SPEC CHECKSUMS:
file_saver: 503e386464dbe118f630e17b4c2e1190fa0cf808
Firebase: cf1b19f21410b029b6786a54e9764a0cacad3c99
firebase_analytics: 2815af29d49c1a994652abd37a5b001a88bc7b75
firebase_core: 418aed674e9a0b8b6088aec16cde82a811f6261f
firebase_core: b62a5080210edad3f2934314a8b2c6f5124e8e10
firebase_messaging: 98619a0572d82cfb3668e78859ba9f1110e268c9
FirebaseAnalytics: 3feef9ae8733c567866342a1000691baaa7cad49
FirebaseCore: e0510f1523bc0eb21653cac00792e1e2bd6f1771
@ -381,8 +386,9 @@ SPEC CHECKSUMS:
SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4
url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe
volume_controller: 531ddf792994285c9b17f9d8a7e4dcdd29b3eae9
wakelock_plus: 78ec7c5b202cab7761af8e2b2b3d0671be6c4ae1
wakelock_plus: 373cfe59b235a6dd5837d0fb88791d2f13a90d56
WebRTC-SDK: 79942c006ea64f6fb48d7da8a4786dfc820bc1db
workmanager: 0afdcf5628bbde6924c21af7836fed07b42e30e6
PODFILE CHECKSUM: 23d35ad686cacf9103d1e85035ee4f3e9750630d

View File

@ -3,7 +3,7 @@
archiveVersion = 1;
classes = {
};
objectVersion = 54;
objectVersion = 77;
objects = {
/* Begin PBXBuildFile section */
@ -879,7 +879,7 @@
INFOPLIST_FILE = Runner/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = Solian;
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.social-networking";
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@ -1433,7 +1433,7 @@
INFOPLIST_FILE = Runner/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = Solian;
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.social-networking";
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@ -1461,7 +1461,7 @@
INFOPLIST_FILE = Runner/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = Solian;
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.social-networking";
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",

View File

@ -1,6 +1,8 @@
import Flutter
import UIKit
import workmanager
@main
@objc class AppDelegate: FlutterAppDelegate {
override func application(
@ -9,6 +11,12 @@ import UIKit
) -> Bool {
GeneratedPluginRegistrant.register(with: self)
WorkmanagerPlugin.setPluginRegistrantCallback { registry in
GeneratedPluginRegistrant.register(with: registry)
}
UIApplication.shared.setMinimumBackgroundFetchInterval(TimeInterval(60*5))
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}

View File

@ -2,6 +2,8 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>AppGroupId</key>
<string>group.solsynth.solian</string>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
<key>CFBundleDevelopmentRegion</key>
@ -27,6 +29,17 @@
<string>$(FLUTTER_BUILD_NAME)</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLSchemes</key>
<array>
<string>ShareMedia-$(PRODUCT_BUNDLE_IDENTIFIER)</string>
</array>
</dict>
</array>
<key>CFBundleVersion</key>
<string>$(FLUTTER_BUILD_NUMBER)</string>
<key>ITSAppUsesNonExemptEncryption</key>
@ -66,8 +79,6 @@
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>AppGroupId</key>
<string>group.solsynth.solian</string>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
@ -75,16 +86,5 @@
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLSchemes</key>
<array>
<string>ShareMedia-$(PRODUCT_BUNDLE_IDENTIFIER)</string>
</array>
</dict>
</array>
</dict>
</plist>

View File

@ -29,10 +29,13 @@ struct CheckInProvider: TimelineProvider {
user = try! jsonDecoder.decode(SolarUser.self, from: userRaw.data(using: .utf8)!)
}
let checkInRaw = prefs?.string(forKey: "today_check_in")
let checkInRaw = prefs?.string(forKey: "pas_check_in_record")
var checkIn: SolarCheckInRecord?
if let checkInRaw = checkInRaw {
checkIn = try! jsonDecoder.decode(SolarCheckInRecord.self, from: checkInRaw.data(using: .utf8)!)
if checkIn != nil && Calendar.current.isDate(checkIn!.createdAt, inSameDayAs: Date()) {
checkIn = nil
}
}
let entry = CheckInEntry(

View File

@ -1,5 +1,5 @@
//
// FeaturedPostWidget.swift
// RandomPostWidget.swift
// Runner
//
// Created by LittleSheep on 2024/12/14.
@ -8,12 +8,12 @@
import SwiftUI
import WidgetKit
struct FeaturedPostProvider: TimelineProvider {
func placeholder(in context: Context) -> FeaturedPostEntry {
FeaturedPostEntry(date: Date(), user: nil, featuredPost: nil, family: .systemMedium)
struct RandomPostProvider: TimelineProvider {
func placeholder(in context: Context) -> RandomPostEntry {
RandomPostEntry(date: Date(), user: nil, randomPost: nil, family: .systemMedium)
}
func getSnapshot(in context: Context, completion: @escaping (FeaturedPostEntry) -> ()) {
func getSnapshot(in context: Context, completion: @escaping (RandomPostEntry) -> ()) {
let prefs = UserDefaults(suiteName: "group.solsynth.solian")
let dateFormatter = DateFormatter()
@ -29,16 +29,17 @@ struct FeaturedPostProvider: TimelineProvider {
user = try! jsonDecoder.decode(SolarUser.self, from: userRaw.data(using: .utf8)!)
}
let featuredPostRaw = prefs?.string(forKey: "post_featured")
var featuredPosts: [SolarPost]?
if let featuredPostRaw = featuredPostRaw {
featuredPosts = try! jsonDecoder.decode([SolarPost].self, from: featuredPostRaw.data(using: .utf8)!)
let randomPostRaw = prefs?.string(forKey: "int_random_post")
var randomPost: SolarPost?
if let randomPostRaw = randomPostRaw {
randomPost = try! jsonDecoder.decode(SolarPost.self, from: randomPostRaw.data(using: .utf8)!)
}
let entry = FeaturedPostEntry(
let entry = RandomPostEntry(
date: Date(),
user: user,
featuredPost: featuredPosts?.first,
randomPost: randomPost,
family: context.family
)
completion(entry)
@ -52,24 +53,22 @@ struct FeaturedPostProvider: TimelineProvider {
}
}
struct FeaturedPostEntry: TimelineEntry {
struct RandomPostEntry: TimelineEntry {
let date: Date
let user: SolarUser?
let featuredPost: SolarPost?
let randomPost: SolarPost?
let family: WidgetFamily
}
struct FeaturedPostWidgetEntryView : View {
var entry: FeaturedPostProvider.Entry
private let resultTierSymbols: [String] = ["大凶", "", "中平", "大吉", ""]
struct RandomPostWidgetEntryView : View {
var entry: RandomPostProvider.Entry
var body: some View {
VStack(alignment: .leading, spacing: 0) {
if let featuredPost = entry.featuredPost {
if let randomPost = entry.randomPost {
HStack(alignment: .center) {
if let avatar = featuredPost.publisher.avatar {
if let avatar = randomPost.publisher.avatar {
let avatarUrl = getAttachmentUrl(for: avatar)
let size: CGFloat = 24
@ -90,28 +89,28 @@ struct FeaturedPostWidgetEntryView : View {
}
}
Text("@\(featuredPost.publisher.name)")
Text("@\(randomPost.publisher.name)")
.font(.system(size: 13, design: .monospaced))
.opacity(0.9)
Spacer()
}.frame(maxWidth: .infinity).padding(.bottom, 12)
if featuredPost.body.title != nil || featuredPost.body.description != nil {
if randomPost.body.title != nil || randomPost.body.description != nil {
VStack(alignment: .leading) {
if let title = featuredPost.body.title {
if let title = randomPost.body.title {
Text(title)
.font(.system(size: 17))
}
if let description = featuredPost.body.description {
if let description = randomPost.body.description {
Text(description)
.font(.system(size: 15))
}
}.padding(.bottom, 8)
}
if let content = featuredPost.body.content {
if (featuredPost.body.title == nil && featuredPost.body.description == nil) || entry.family == .systemLarge || entry.family == .systemExtraLarge {
if let content = randomPost.body.content {
if (randomPost.body.title == nil && randomPost.body.description == nil) || entry.family == .systemLarge || entry.family == .systemExtraLarge {
Text(
(entry.family == .systemLarge || entry.family == .systemExtraLarge) ? content : content.replacingOccurrences(of: "\n", with: " ")
)
@ -124,7 +123,7 @@ struct FeaturedPostWidgetEntryView : View {
}
}
if let attachment = featuredPost.body.attachments {
if let attachment = randomPost.body.attachments {
if attachment.count == 1 {
Text("\(Image(systemName: "document.fill")) \(attachment.count) attachment")
.font(.system(size: 11, design: .monospaced))
@ -140,14 +139,14 @@ struct FeaturedPostWidgetEntryView : View {
Spacer()
Text(featuredPost.publishedAt!, format: .dateTime)
Text(randomPost.publishedAt!, format: .dateTime)
.font(.system(size: 11))
Text("Solar Network Featured Posts")
Text("#\(randomPost.id)")
.font(.system(size: 9))
} else {
VStack(alignment: .center) {
Text("No Recommendations").font(.system(size: 19, weight: .bold))
Text("Click the widget to open the app to load featured posts")
Text("Open the app to load some random post")
.font(.system(size: 15))
.multilineTextAlignment(.center)
}.frame(alignment: .center)
@ -156,34 +155,34 @@ struct FeaturedPostWidgetEntryView : View {
}
}
struct FeaturedPostWidget: Widget {
let kind: String = "SolarFeaturedPostWidget"
struct RandomPostWidget: Widget {
let kind: String = "SolarRandomPostWidget"
var body: some WidgetConfiguration {
StaticConfiguration(kind: kind, provider: FeaturedPostProvider()) { entry in
StaticConfiguration(kind: kind, provider: RandomPostProvider()) { entry in
if #available(iOS 17.0, *) {
FeaturedPostWidgetEntryView(entry: entry)
RandomPostWidgetEntryView(entry: entry)
.containerBackground(.fill.tertiary, for: .widget)
} else {
FeaturedPostWidgetEntryView(entry: entry)
RandomPostWidgetEntryView(entry: entry)
.padding()
.background()
}
}
.configurationDisplayName("Featured Posts")
.description("View the featured posts on the Solar Network")
.configurationDisplayName("Random Post")
.description("View the random post on the Solar Network")
.supportedFamilies([.systemSmall, .systemMedium, .systemLarge, .systemExtraLarge])
}
}
#Preview(as: .systemSmall) {
FeaturedPostWidget()
RandomPostWidget()
} timeline: {
FeaturedPostEntry(date: Date.now, user: nil, featuredPost: nil, family: .systemLarge)
FeaturedPostEntry(
RandomPostEntry(date: Date.now, user: nil, randomPost: nil, family: .systemLarge)
RandomPostEntry(
date: .now,
user: SolarUser(id: 1, name: "demo", nick: "Deemo"),
featuredPost: SolarPost(
randomPost: SolarPost(
id: 1,
body: SolarPostBody(
content: "Hello, World",
@ -209,10 +208,10 @@ struct FeaturedPostWidget: Widget {
),
family: .systemSmall
)
FeaturedPostEntry(
RandomPostEntry(
date: .now,
user: SolarUser(id: 1, name: "demo", nick: "Deemo"),
featuredPost: SolarPost(
randomPost: SolarPost(
id: 1,
body: SolarPostBody(
content: "Hello, World\nOh wow",

View File

@ -12,6 +12,6 @@ import SwiftUI
struct SolarWidgetBundle: WidgetBundle {
var body: some Widget {
CheckInWidget()
FeaturedPostWidget()
RandomPostWidget()
}
}