mirror of
https://github.com/kiwix/kiwix-apple.git
synced 2025-08-03 20:47:22 -04:00
Generate widgets
This commit is contained in:
parent
ca833c31a4
commit
fd3e5daa3f
@ -25,14 +25,23 @@ struct Kiwix: App {
|
||||
@UIApplicationDelegateAdaptor(AppDelegate.self) private var appDelegate
|
||||
|
||||
private let fileMonitor: DirectoryMonitor
|
||||
private let activityService: ActivityService?
|
||||
|
||||
init() {
|
||||
fileMonitor = DirectoryMonitor(url: URL.documentDirectory) { LibraryOperations.scanDirectory($0) }
|
||||
// MARK: - live activities
|
||||
switch AppType.current {
|
||||
case .kiwix:
|
||||
activityService = nil //ActivityService()
|
||||
case .custom:
|
||||
activityService = nil
|
||||
}
|
||||
UNUserNotificationCenter.current().delegate = appDelegate
|
||||
// MARK: - migrations
|
||||
if !ProcessInfo.processInfo.arguments.contains("testing") {
|
||||
_ = MigrationService().migrateAll()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
var body: some Scene {
|
||||
|
59
Common/DownloadActivityAttributes.swift
Normal file
59
Common/DownloadActivityAttributes.swift
Normal file
@ -0,0 +1,59 @@
|
||||
// This file is part of Kiwix for iOS & macOS.
|
||||
//
|
||||
// Kiwix is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// Kiwix is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Kiwix; If not, see https://www.gnu.org/licenses/.
|
||||
#if os(iOS)
|
||||
|
||||
import ActivityKit
|
||||
|
||||
public struct DownloadActivityAttributes: ActivityAttributes {
|
||||
|
||||
public let title: String
|
||||
|
||||
public init(title: String) {
|
||||
self.title = title
|
||||
}
|
||||
|
||||
public struct ContentState: Codable & Hashable {
|
||||
public let items: [DownloadItem]
|
||||
public var totalProgress: Double {
|
||||
let sum = items.reduce(Double(0.0), { partialResult, item in
|
||||
partialResult + item.progress
|
||||
})
|
||||
return sum / Double(items.count)
|
||||
}
|
||||
|
||||
public init(items: [DownloadItem]) {
|
||||
self.items = items
|
||||
}
|
||||
}
|
||||
|
||||
public struct DownloadItem: Codable & Hashable {
|
||||
public let uuid: UUID
|
||||
public let description: String
|
||||
public let progress: Double
|
||||
|
||||
public init(uuid: UUID, description: String, progress: Double) {
|
||||
self.uuid = uuid
|
||||
self.description = description
|
||||
self.progress = progress
|
||||
}
|
||||
|
||||
public init(completedFor uuid: UUID) {
|
||||
self.uuid = uuid
|
||||
self.progress = 1.0
|
||||
self.description = "Completed!" //TODO: update
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@ -63,5 +63,9 @@
|
||||
</dict>
|
||||
</dict>
|
||||
</array>
|
||||
<key>NSSupportsLiveActivities</key>
|
||||
<true/>
|
||||
<key>NSSupportsLiveActivitiesFrequentUpdates</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
|
@ -12,39 +12,39 @@
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Kiwix; If not, see https://www.gnu.org/licenses/.
|
||||
//
|
||||
//#if os(iOS)
|
||||
//
|
||||
|
||||
#if os(iOS)
|
||||
|
||||
import Combine
|
||||
import ActivityKit
|
||||
import KiwixWidgetsExtension
|
||||
//
|
||||
//@MainActor
|
||||
//final class ActivityService {
|
||||
//
|
||||
// private var cancellables = Set<AnyCancellable>()
|
||||
// private var activity: Activity<DownloadActivityAttributes>?
|
||||
//
|
||||
// init(
|
||||
// publisher: @MainActor () -> CurrentValueSubject<[UUID: DownloadState], Never> = { DownloadService.shared.progress.publisher
|
||||
// }
|
||||
// ) {
|
||||
// publisher().sink { [weak self] (state: [UUID : DownloadState]) in
|
||||
// guard let self else { return }
|
||||
// if state.isEmpty {
|
||||
// stop()
|
||||
// } else {
|
||||
// update(state: state)
|
||||
// }
|
||||
// }.store(in: &cancellables)
|
||||
// }
|
||||
//
|
||||
// private func start(with state: [UUID: DownloadState]) {
|
||||
// let content = ActivityContent(
|
||||
// state: activityState(from: state),
|
||||
// staleDate: nil,
|
||||
// relevanceScore: 0.0
|
||||
// )
|
||||
|
||||
@MainActor
|
||||
final class ActivityService {
|
||||
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
private var activity: Activity<DownloadActivityAttributes>?
|
||||
|
||||
init(
|
||||
publisher: @MainActor () -> CurrentValueSubject<[UUID: DownloadState], Never> = { DownloadService.shared.progress.publisher
|
||||
}
|
||||
) {
|
||||
publisher().sink { [weak self] (state: [UUID : DownloadState]) in
|
||||
guard let self else { return }
|
||||
if state.isEmpty {
|
||||
stop()
|
||||
} else {
|
||||
update(state: state)
|
||||
}
|
||||
}.store(in: &cancellables)
|
||||
}
|
||||
|
||||
private func start(with state: [UUID: DownloadState]) {
|
||||
let content = ActivityContent(
|
||||
state: activityState(from: state),
|
||||
staleDate: nil,
|
||||
relevanceScore: 0.0
|
||||
)
|
||||
debugPrint("start with: \(state)")
|
||||
// if let activity = try? Activity
|
||||
// .request(
|
||||
// attributes: DownloadActivityAttributes(title: "Downloads"),
|
||||
@ -59,9 +59,10 @@ import KiwixWidgetsExtension
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private func update(state: [UUID: DownloadState]) {
|
||||
}
|
||||
|
||||
private func update(state: [UUID: DownloadState]) {
|
||||
debugPrint("update state: \(state)")
|
||||
// guard let activity else {
|
||||
// start(with: state)
|
||||
// return
|
||||
@ -74,9 +75,10 @@ import KiwixWidgetsExtension
|
||||
// )
|
||||
// )
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private func stop() {
|
||||
}
|
||||
|
||||
private func stop() {
|
||||
debugPrint("stop")
|
||||
// if let activity {
|
||||
// let previousState = activity.content.state
|
||||
// Task {
|
||||
@ -86,21 +88,21 @@ import KiwixWidgetsExtension
|
||||
// self.activity = nil
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private func activityState(from state: [UUID: DownloadState]) -> DownloadActivityAttributes.ContentState {
|
||||
// DownloadActivityAttributes.ContentState(
|
||||
// items: state.map { (key: UUID, download: DownloadState)-> DownloadActivityAttributes.DownloadItem in
|
||||
// DownloadActivityAttributes.DownloadItem(uuid: key, description: key.uuidString, progress: Double(download.downloaded/download.total))
|
||||
// })
|
||||
// }
|
||||
//
|
||||
// private func completeState(for previousState: DownloadActivityAttributes.ContentState) -> DownloadActivityAttributes.ContentState {
|
||||
// DownloadActivityAttributes
|
||||
// .ContentState(items: previousState.items.map { item in
|
||||
// DownloadActivityAttributes.DownloadItem(completedFor: item.uuid)
|
||||
// })
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//#endif
|
||||
}
|
||||
|
||||
private func activityState(from state: [UUID: DownloadState]) -> DownloadActivityAttributes.ContentState {
|
||||
DownloadActivityAttributes.ContentState(
|
||||
items: state.map { (key: UUID, download: DownloadState)-> DownloadActivityAttributes.DownloadItem in
|
||||
DownloadActivityAttributes.DownloadItem(uuid: key, description: key.uuidString, progress: Double(download.downloaded/download.total))
|
||||
})
|
||||
}
|
||||
|
||||
private func completeState(for previousState: DownloadActivityAttributes.ContentState) -> DownloadActivityAttributes.ContentState {
|
||||
DownloadActivityAttributes
|
||||
.ContentState(items: previousState.items.map { item in
|
||||
DownloadActivityAttributes.DownloadItem(completedFor: item.uuid)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
11
Widgets/Assets.xcassets/AccentColor.colorset/Contents.json
Normal file
11
Widgets/Assets.xcassets/AccentColor.colorset/Contents.json
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
35
Widgets/Assets.xcassets/AppIcon.appiconset/Contents.json
Normal file
35
Widgets/Assets.xcassets/AppIcon.appiconset/Contents.json
Normal file
@ -0,0 +1,35 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"platform" : "ios",
|
||||
"size" : "1024x1024"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"idiom" : "universal",
|
||||
"platform" : "ios",
|
||||
"size" : "1024x1024"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "tinted"
|
||||
}
|
||||
],
|
||||
"idiom" : "universal",
|
||||
"platform" : "ios",
|
||||
"size" : "1024x1024"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
6
Widgets/Assets.xcassets/Contents.json
Normal file
6
Widgets/Assets.xcassets/Contents.json
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
94
Widgets/DownloadsLiveActivity.swift
Normal file
94
Widgets/DownloadsLiveActivity.swift
Normal file
@ -0,0 +1,94 @@
|
||||
// This file is part of Kiwix for iOS & macOS.
|
||||
//
|
||||
// Kiwix is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// Kiwix is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Kiwix; If not, see https://www.gnu.org/licenses/.
|
||||
|
||||
import ActivityKit
|
||||
import WidgetKit
|
||||
import SwiftUI
|
||||
|
||||
struct DownloadsLiveActivity: Widget {
|
||||
var body: some WidgetConfiguration {
|
||||
ActivityConfiguration(for: DownloadActivityAttributes.self) { context in
|
||||
// Lock screen/banner UI goes here
|
||||
VStack {
|
||||
ForEach(context.state.items, id: \.uuid) { item in
|
||||
HStack {
|
||||
Text(item.description)
|
||||
ProgressView(value: item.progress)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
.activityBackgroundTint(Color.cyan)
|
||||
.activitySystemActionForegroundColor(Color.black)
|
||||
|
||||
} dynamicIsland: { context in
|
||||
DynamicIsland {
|
||||
// Expanded UI goes here. Compose the expanded UI through
|
||||
// various regions, like leading/trailing/center/bottom
|
||||
DynamicIslandExpandedRegion(.leading) {
|
||||
VStack {
|
||||
ForEach(context.state.items, id: \.uuid) { item in
|
||||
Text(item.description)
|
||||
}
|
||||
}
|
||||
}
|
||||
DynamicIslandExpandedRegion(.trailing) {
|
||||
ProgressView(value: context.state.totalProgress)
|
||||
}
|
||||
DynamicIslandExpandedRegion(.bottom) {
|
||||
ProgressView(value: context.state.totalProgress)
|
||||
}
|
||||
} compactLeading: {
|
||||
ProgressView(value: context.state.totalProgress)
|
||||
} compactTrailing: {
|
||||
ProgressView(value: context.state.totalProgress)
|
||||
} minimal: {
|
||||
ProgressView(value: context.state.totalProgress)
|
||||
}
|
||||
.widgetURL(URL(string: "https://www.kiwix.org"))
|
||||
.keylineTint(Color.red)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension DownloadActivityAttributes {
|
||||
fileprivate static var preview: DownloadActivityAttributes {
|
||||
DownloadActivityAttributes(title: "Downloads")
|
||||
}
|
||||
}
|
||||
|
||||
extension DownloadActivityAttributes.ContentState {
|
||||
fileprivate static var midProgress: DownloadActivityAttributes.ContentState {
|
||||
DownloadActivityAttributes.ContentState(items: [
|
||||
DownloadActivityAttributes.DownloadItem(uuid: UUID(), description: "First item", progress: 0.5),
|
||||
DownloadActivityAttributes.DownloadItem(uuid: UUID(), description: "2nd item", progress: 0.9)
|
||||
])
|
||||
}
|
||||
|
||||
fileprivate static var completed: DownloadActivityAttributes.ContentState {
|
||||
DownloadActivityAttributes.ContentState(items: [
|
||||
DownloadActivityAttributes.DownloadItem(uuid: UUID(), description: "First item", progress: 1.0),
|
||||
DownloadActivityAttributes.DownloadItem(uuid: UUID(), description: "2nd item", progress: 0.8)
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
@available(iOS 18.0, *)
|
||||
#Preview("Notification", as: .content, using: DownloadActivityAttributes.preview) {
|
||||
DownloadsLiveActivity()
|
||||
} contentStates: {
|
||||
DownloadActivityAttributes.ContentState.midProgress
|
||||
DownloadActivityAttributes.ContentState.completed
|
||||
}
|
11
Widgets/Info.plist
Normal file
11
Widgets/Info.plist
Normal file
@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-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>NSExtension</key>
|
||||
<dict>
|
||||
<key>NSExtensionPointIdentifier</key>
|
||||
<string>com.apple.widgetkit-extension</string>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
25
Widgets/WidgetsBundle.swift
Normal file
25
Widgets/WidgetsBundle.swift
Normal file
@ -0,0 +1,25 @@
|
||||
// This file is part of Kiwix for iOS & macOS.
|
||||
//
|
||||
// Kiwix is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// Kiwix is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Kiwix; If not, see https://www.gnu.org/licenses/.
|
||||
|
||||
import WidgetKit
|
||||
import SwiftUI
|
||||
|
||||
@available(iOS 16.6, *)
|
||||
@main
|
||||
struct WidgetsBundle: WidgetBundle {
|
||||
var body: some Widget {
|
||||
DownloadsLiveActivity()
|
||||
}
|
||||
}
|
15
project.yml
15
project.yml
@ -2,7 +2,7 @@ name: Kiwix
|
||||
options:
|
||||
xcodeVersion: 15.2
|
||||
deploymentTarget: # the three latest major versions should be supported
|
||||
iOS: 16.0
|
||||
iOS: 16.6
|
||||
macOS: 13.0
|
||||
generateEmptyDirectories: true
|
||||
useTabs: false
|
||||
@ -87,6 +87,9 @@ targetTemplates:
|
||||
- path: PrivacyInfo.xcprivacy
|
||||
destinationFilters:
|
||||
- iOS
|
||||
- path: Common
|
||||
destinationFilters:
|
||||
- iOS
|
||||
- path: Contents
|
||||
includes:
|
||||
- Resources
|
||||
@ -136,6 +139,16 @@ targets:
|
||||
- path: Tests
|
||||
dependencies:
|
||||
- target: Kiwix
|
||||
Widgets:
|
||||
type: app-extension
|
||||
supportedDestinations: [iOS]
|
||||
settings:
|
||||
base:
|
||||
PRODUCT_BUNDLE_IDENTIFIER: self.Kiwix.widgets
|
||||
INFOPLIST_FILE: Widgets/Info.plist
|
||||
sources:
|
||||
- path: Common
|
||||
- path: Widgets
|
||||
|
||||
schemes:
|
||||
Kiwix:
|
||||
|
Loading…
x
Reference in New Issue
Block a user