Skip to content

Commit e97b4e7

Browse files
feat: Update version to 1.7.11+12, enhance model installation with progress notifications, and add Live Activities support for download status
1 parent d977d99 commit e97b4e7

6 files changed

Lines changed: 530 additions & 23 deletions

File tree

Lines changed: 257 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,257 @@
1+
# Live Activities & Dynamic Island Feature
2+
3+
## Overview
4+
5+
Add iOS Live Activities and Dynamic Island support for model download progress, allowing users to see download status on the lock screen and Dynamic Island without opening the app.
6+
7+
## Visual Mockups
8+
9+
```
10+
┌──────────────────────────────────────────────┐
11+
│ Dynamic Island (Compact) │
12+
│ ┌──────┐ ┌────────┐ │
13+
│ │ 💎 │ ▓▓▓▓▓▓▓░░░ 68% │ │ │
14+
│ └──────┘ └────────┘ │
15+
└──────────────────────────────────────────────┘
16+
17+
┌──────────────────────────────────────────────┐
18+
│ Dynamic Island (Expanded - on tap/hold) │
19+
│ ┌────────────────────────────────────────┐ │
20+
│ │ 💎 Downloading Gemma 3 1B │ │
21+
│ │ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓░░░░░░ 68% │ │
22+
│ │ 458 MB / 689 MB • 2.3 MB/s │ │
23+
│ └────────────────────────────────────────┘ │
24+
└──────────────────────────────────────────────┘
25+
26+
┌──────────────────────────────────────────────┐
27+
│ Lock Screen Live Activity │
28+
│ ┌────────────────────────────────────────┐ │
29+
│ │ 💎 Gemma 3 1B │ │
30+
│ │ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░░░░░░░░ 68% │ │
31+
│ │ 458 MB / 689 MB │ │
32+
│ └────────────────────────────────────────┘ │
33+
└──────────────────────────────────────────────┘
34+
```
35+
36+
## Architecture
37+
38+
```
39+
┌─────────────────────────────────────────────────────────────────┐
40+
│ LIVE ACTIVITIES DATA FLOW │
41+
├─────────────────────────────────────────────────────────────────┤
42+
│ │
43+
│ ┌─────────────────────┐ ┌─────────────────────────────┐ │
44+
│ │ Flutter App │ │ iOS Widget Extension │ │
45+
│ │ (Dart) │ ──→ │ (Swift/SwiftUI) │ │
46+
│ │ │ │ │ │
47+
│ │ • live_activities │ │ • LiveActivitiesAppAttributes│ │
48+
│ │ plugin │ │ • Lock Screen UI │ │
49+
│ │ • createActivity() │ │ • Dynamic Island UI │ │
50+
│ │ • updateActivity() │ │ - Compact view │ │
51+
│ │ │ │ - Expanded view │ │
52+
│ └─────────────────────┘ │ - Minimal view │ │
53+
│ │ └─────────────────────────────┘ │
54+
│ │ ↑ │
55+
│ └──── UserDefaults (App Group) ┘ │
56+
│ │
57+
│ Data passed via UserDefaults: │
58+
│ • modelName: String (e.g., "Gemma 3 1B") │
59+
│ • progress: Double (0.0 - 1.0) │
60+
│ • downloadedMB: Int │
61+
│ • totalMB: Int │
62+
│ • downloadSpeed: String (optional) │
63+
│ │
64+
└─────────────────────────────────────────────────────────────────┘
65+
```
66+
67+
## Requirements
68+
69+
- iOS 16.1+ (Live Activities)
70+
- iPhone 14 Pro+ (Dynamic Island)
71+
- Plugin: `live_activities: ^2.4.3`
72+
73+
## Implementation Tasks
74+
75+
### Phase 1: iOS Setup (Xcode)
76+
77+
- [ ] Create Widget Extension target in Xcode
78+
- File → New → Target → Widget Extension
79+
- Name: `WingManWidgetExtension`
80+
- Embed in: Runner
81+
- [ ] Add `NSSupportsLiveActivities: true` to Info.plist (Runner + Extension)
82+
- [ ] Enable App Groups capability for both Runner and Extension
83+
- Group ID: `group.com.poly186.wingman`
84+
- [ ] Enable Push Notifications capability for Runner
85+
86+
### Phase 2: Native SwiftUI Views
87+
88+
- [ ] Create `LiveActivitiesAppAttributes` struct (MUST use this exact name)
89+
- [ ] Design Lock Screen view
90+
- [ ] Design Dynamic Island compact view (leading/trailing)
91+
- [ ] Design Dynamic Island expanded view
92+
- [ ] Design Dynamic Island minimal view (for multiple activities)
93+
94+
### Phase 3: Flutter Integration
95+
96+
- [ ] Add `live_activities` to pubspec.yaml
97+
- [ ] Initialize plugin with App Group ID
98+
- [ ] Create `LiveActivityService` in Dart
99+
- [ ] Integrate with `LlmService.installModel()` progress callback
100+
- [ ] Start activity when download begins
101+
- [ ] Update activity on progress changes (throttle to every 2-5%)
102+
- [ ] End activity when download completes or fails
103+
104+
### Phase 4: Polish
105+
106+
- [ ] Add WingMan branding/colors to native views
107+
- [ ] Handle edge cases (app killed, multiple downloads)
108+
- [ ] Test on real devices
109+
- [ ] Add Android RemoteViews support (optional)
110+
111+
## Code Snippets
112+
113+
### Flutter Side (Dart)
114+
115+
```dart
116+
// lib/services/live_activity_service.dart
117+
import 'package:live_activities/live_activities.dart';
118+
119+
class LiveActivityService {
120+
final _plugin = LiveActivities();
121+
String? _currentActivityId;
122+
123+
Future<void> init() async {
124+
await _plugin.init(appGroupId: 'group.com.poly186.wingman');
125+
}
126+
127+
Future<void> startDownloadActivity({
128+
required String modelName,
129+
required int totalMB,
130+
}) async {
131+
_currentActivityId = await _plugin.createActivity({
132+
'modelName': modelName,
133+
'progress': 0.0,
134+
'downloadedMB': 0,
135+
'totalMB': totalMB,
136+
});
137+
}
138+
139+
Future<void> updateProgress(double progress, int downloadedMB) async {
140+
if (_currentActivityId == null) return;
141+
await _plugin.updateActivity(_currentActivityId!, {
142+
'progress': progress,
143+
'downloadedMB': downloadedMB,
144+
});
145+
}
146+
147+
Future<void> endActivity() async {
148+
if (_currentActivityId == null) return;
149+
await _plugin.endActivity(_currentActivityId!);
150+
_currentActivityId = null;
151+
}
152+
}
153+
```
154+
155+
### iOS Side (Swift)
156+
157+
```swift
158+
// WingManWidgetExtension/WingManLiveActivity.swift
159+
import ActivityKit
160+
import SwiftUI
161+
import WidgetKit
162+
163+
struct LiveActivitiesAppAttributes: ActivityAttributes, Identifiable {
164+
public typealias LiveDeliveryData = ContentState
165+
public struct ContentState: Codable, Hashable { }
166+
var id = UUID()
167+
}
168+
169+
extension LiveActivitiesAppAttributes {
170+
func prefixedKey(_ key: String) -> String {
171+
return "\(id)_\(key)"
172+
}
173+
}
174+
175+
struct WingManLiveActivity: Widget {
176+
let sharedDefault = UserDefaults(suiteName: "group.com.poly186.wingman")!
177+
178+
var body: some WidgetConfiguration {
179+
ActivityConfiguration(for: LiveActivitiesAppAttributes.self) { context in
180+
// Lock Screen UI
181+
let modelName = sharedDefault.string(forKey: context.attributes.prefixedKey("modelName")) ?? "Model"
182+
let progress = sharedDefault.double(forKey: context.attributes.prefixedKey("progress"))
183+
let downloadedMB = sharedDefault.integer(forKey: context.attributes.prefixedKey("downloadedMB"))
184+
let totalMB = sharedDefault.integer(forKey: context.attributes.prefixedKey("totalMB"))
185+
186+
VStack(alignment: .leading, spacing: 8) {
187+
HStack {
188+
Text("💎")
189+
Text(modelName)
190+
.font(.headline)
191+
.foregroundColor(.white)
192+
}
193+
194+
ProgressView(value: progress)
195+
.tint(.blue)
196+
197+
Text("\(downloadedMB) MB / \(totalMB) MB")
198+
.font(.caption)
199+
.foregroundColor(.gray)
200+
}
201+
.padding()
202+
.activityBackgroundTint(.black)
203+
204+
} dynamicIsland: { context in
205+
let progress = sharedDefault.double(forKey: context.attributes.prefixedKey("progress"))
206+
let percent = Int(progress * 100)
207+
208+
DynamicIsland {
209+
// Expanded view
210+
DynamicIslandExpandedRegion(.leading) {
211+
Text("💎")
212+
}
213+
DynamicIslandExpandedRegion(.trailing) {
214+
Text("\(percent)%")
215+
.font(.headline)
216+
}
217+
DynamicIslandExpandedRegion(.bottom) {
218+
ProgressView(value: progress)
219+
.tint(.blue)
220+
}
221+
} compactLeading: {
222+
Text("💎")
223+
} compactTrailing: {
224+
Text("\(percent)%")
225+
.font(.caption)
226+
} minimal: {
227+
// Circular progress for minimal view
228+
ProgressView(value: progress)
229+
.progressViewStyle(.circular)
230+
.scaleEffect(0.5)
231+
}
232+
}
233+
}
234+
}
235+
```
236+
237+
## Estimated Effort
238+
239+
| Task | Time |
240+
|------|------|
241+
| Xcode setup | 30 min |
242+
| SwiftUI views | 2-3 hours |
243+
| Flutter integration | 1 hour |
244+
| Testing & polish | 1-2 hours |
245+
| **Total** | **~5-6 hours** |
246+
247+
## References
248+
249+
- [live_activities Flutter plugin](https://pub.dev/packages/live_activities)
250+
- [Apple ActivityKit docs](https://developer.apple.com/documentation/activitykit)
251+
- [Canopas tutorial](https://canopas.com/integrating-live-activity-and-dynamic-island-in-i-os-a-complete-guide)
252+
253+
## Status
254+
255+
**Priority**: Nice-to-have (v2.0)
256+
**Status**: Planning
257+
**Blocked by**: None

ios/version.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
11
1+
12

0 commit comments

Comments
 (0)