@jesushr0013/native-timer 8.0.10 → 8.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Jesushr0013NativeTimer.podspec +3 -14
- package/NativeTimerKit.podspec +3 -5
- package/Package.swift +16 -6
- package/README.md +308 -0
- package/android/.gradle/8.9/dependencies-accessors/gc.properties +0 -0
- package/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock +0 -0
- package/android/.gradle/buildOutputCleanup/cache.properties +2 -2
- package/android/.gradle/checksums/checksums.lock +0 -0
- package/android/.gradle/checksums/md5-checksums.bin +0 -0
- package/android/.gradle/checksums/sha1-checksums.bin +0 -0
- package/ios/{LiveActivitiesKit → Core}/NativeTimerManager.swift +0 -25
- package/ios/Core/WorkSessionTimerAttributes.swift +32 -0
- package/ios/{LiveActivitiesKit → LiveActivities}/NativeTimerWidget.swift +8 -2
- package/ios/Plugin/NativeTimerPlugin.swift +4 -0
- package/package.json +1 -1
|
@@ -11,23 +11,12 @@ Pod::Spec.new do |s|
|
|
|
11
11
|
s.homepage = package['repository']['url'] rescue 'https://github.com/jesherram/native-timer'
|
|
12
12
|
s.author = package['author']
|
|
13
13
|
s.source = { :git => 'https://github.com/jesherram/native-timer.git', :tag => s.version.to_s }
|
|
14
|
-
s.source_files = 'ios/
|
|
15
|
-
s.public_header_files = 'ios/LiveActivitiesKit/*.h'
|
|
14
|
+
s.source_files = 'ios/Core/**/*.{swift,h,m,c,cc,mm,cpp}', 'ios/Plugin/**/*.{swift,h,m,c,cc,mm,cpp}'
|
|
16
15
|
|
|
17
16
|
s.ios.deployment_target = '15.0'
|
|
18
17
|
s.dependency 'Capacitor'
|
|
19
18
|
s.swift_version = '5.1'
|
|
20
19
|
|
|
21
|
-
#
|
|
22
|
-
|
|
23
|
-
# y en iOS 16.x no existe como framework independiente
|
|
24
|
-
s.weak_frameworks = 'WidgetKit', 'SwiftUI', 'ActivityKit', 'SwiftUICore'
|
|
25
|
-
|
|
26
|
-
# Linker flags para asegurar weak linking de SwiftUICore
|
|
27
|
-
s.pod_target_xcconfig = {
|
|
28
|
-
'OTHER_LDFLAGS' => '$(inherited) -weak_framework SwiftUICore'
|
|
29
|
-
}
|
|
30
|
-
s.user_target_xcconfig = {
|
|
31
|
-
'OTHER_LDFLAGS' => '$(inherited) -weak_framework SwiftUICore'
|
|
32
|
-
}
|
|
20
|
+
# Solo ActivityKit como weak_framework - SwiftUI ya NO se linkea en este target
|
|
21
|
+
s.weak_frameworks = 'ActivityKit'
|
|
33
22
|
end
|
package/NativeTimerKit.podspec
CHANGED
|
@@ -6,15 +6,13 @@ Pod::Spec.new do |s|
|
|
|
6
6
|
s.homepage = 'https://github.com/jesherram/native-timer'
|
|
7
7
|
s.author = { 'Meycagesal' => 'info@meycagesal.com' }
|
|
8
8
|
s.source = { :git => 'https://github.com/jesherram/native-timer.git', :tag => s.version.to_s }
|
|
9
|
-
s.source_files = 'ios/
|
|
10
|
-
s.public_header_files = 'ios/LiveActivitiesKit/*.h'
|
|
9
|
+
s.source_files = 'ios/LiveActivities/**/*.{swift,h,m,c,cc,mm,cpp}'
|
|
11
10
|
|
|
12
11
|
s.ios.deployment_target = '15.0'
|
|
12
|
+
s.dependency 'Jesushr0013NativeTimer'
|
|
13
13
|
s.swift_version = '5.1'
|
|
14
14
|
|
|
15
|
-
#
|
|
16
|
-
# SwiftUICore es necesario como weak_framework porque Xcode 16+ lo separa de SwiftUI
|
|
17
|
-
# y en iOS 16.x no existe como framework independiente
|
|
15
|
+
# Live Activities requiere SwiftUI - weak linking para compatibilidad con iOS < 16.1
|
|
18
16
|
s.weak_frameworks = 'WidgetKit', 'SwiftUI', 'ActivityKit', 'SwiftUICore'
|
|
19
17
|
|
|
20
18
|
# Linker flags para asegurar weak linking de SwiftUICore
|
package/Package.swift
CHANGED
|
@@ -7,22 +7,32 @@ let package = Package(
|
|
|
7
7
|
products: [
|
|
8
8
|
.library(
|
|
9
9
|
name: "Jesushr0013NativeTimer",
|
|
10
|
-
targets: ["Jesushr0013NativeTimer"])
|
|
10
|
+
targets: ["Jesushr0013NativeTimer"]),
|
|
11
|
+
.library(
|
|
12
|
+
name: "NativeTimerLiveActivities",
|
|
13
|
+
targets: ["NativeTimerLiveActivities"])
|
|
11
14
|
],
|
|
12
15
|
dependencies: [
|
|
13
16
|
.package(url: "https://github.com/ionic-team/capacitor-swift-pm.git", from: "8.0.0")
|
|
14
17
|
],
|
|
15
18
|
targets: [
|
|
19
|
+
.target(
|
|
20
|
+
name: "NativeTimerCore",
|
|
21
|
+
dependencies: [],
|
|
22
|
+
path: "ios/Core"),
|
|
16
23
|
.target(
|
|
17
24
|
name: "Jesushr0013NativeTimer",
|
|
18
25
|
dependencies: [
|
|
26
|
+
"NativeTimerCore",
|
|
19
27
|
.product(name: "Capacitor", package: "capacitor-swift-pm"),
|
|
20
28
|
.product(name: "Cordova", package: "capacitor-swift-pm")
|
|
21
29
|
],
|
|
22
|
-
path: "ios",
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
30
|
+
path: "ios/Plugin"),
|
|
31
|
+
.target(
|
|
32
|
+
name: "NativeTimerLiveActivities",
|
|
33
|
+
dependencies: [
|
|
34
|
+
"NativeTimerCore"
|
|
35
|
+
],
|
|
36
|
+
path: "ios/LiveActivities")
|
|
27
37
|
]
|
|
28
38
|
)
|
package/README.md
ADDED
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
# @jesushr0013/native-timer
|
|
2
|
+
|
|
3
|
+
A Capacitor 8+ plugin for **work shift time tracking** (jornada laboral) with **Android Foreground Service** and **iOS Live Activities** (Dynamic Island + Lock Screen).
|
|
4
|
+
|
|
5
|
+
The widget displays a live "Jornada Activa" (Active Shift) timer designed specifically for tracking work hours.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- **Android**: Persistent foreground service with notification for shift tracking
|
|
10
|
+
- **iOS**: Live Activities with Dynamic Island and Lock Screen widget showing "Jornada Activa" (iOS 16.2+)
|
|
11
|
+
- **iOS**: Compatible with iOS 16.x and 17+ (SwiftUICore weak-linked)
|
|
12
|
+
- Background-safe timer that survives app suspension — ideal for long work shifts
|
|
13
|
+
- Customizable primary color for notifications and widgets
|
|
14
|
+
- Smart notification management (foreground/background aware)
|
|
15
|
+
|
|
16
|
+
## Requirements
|
|
17
|
+
|
|
18
|
+
| Platform | Minimum Version |
|
|
19
|
+
|----------|----------------|
|
|
20
|
+
| Capacitor | 8.0.0+ |
|
|
21
|
+
| iOS | 16.0+ (Live Activities require 16.2+) |
|
|
22
|
+
| Android | API 26+ (Android 8.0) |
|
|
23
|
+
| Xcode | 16+ |
|
|
24
|
+
|
|
25
|
+
## Installation
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npm install @jesushr0013/native-timer
|
|
29
|
+
npx cap sync
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## iOS Setup
|
|
33
|
+
|
|
34
|
+
### 1. Add Widget Extension
|
|
35
|
+
|
|
36
|
+
In Xcode, add a **Widget Extension** target to your project for Live Activities support.
|
|
37
|
+
|
|
38
|
+
### 2. Fix SwiftUICore for iOS 16.x
|
|
39
|
+
|
|
40
|
+
If you compile with Xcode 16+, you **must** add this linker flag to both your App target and Widget Extension target in Xcode:
|
|
41
|
+
|
|
42
|
+
**Build Settings → Other Linker Flags:**
|
|
43
|
+
```
|
|
44
|
+
-weak_framework SwiftUICore
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
This prevents a crash on iOS 16.x where `SwiftUICore.framework` doesn't exist as a standalone framework.
|
|
48
|
+
|
|
49
|
+
### 3. Entitlements
|
|
50
|
+
|
|
51
|
+
Add the `Push Notifications` capability and enable `Supports Live Activities` in your `Info.plist`:
|
|
52
|
+
|
|
53
|
+
```xml
|
|
54
|
+
<key>NSSupportsLiveActivities</key>
|
|
55
|
+
<true/>
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Android Setup
|
|
59
|
+
|
|
60
|
+
The plugin automatically manages a Foreground Service. Make sure your `AndroidManifest.xml` includes:
|
|
61
|
+
|
|
62
|
+
```xml
|
|
63
|
+
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
|
64
|
+
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Usage
|
|
68
|
+
|
|
69
|
+
```typescript
|
|
70
|
+
import { NativeTimer } from '@jesushr0013/native-timer';
|
|
71
|
+
|
|
72
|
+
// Start a timer
|
|
73
|
+
// Note: title/body are for the Android notification only.
|
|
74
|
+
// The iOS widget always shows "⚡ Jornada Activa" as the title.
|
|
75
|
+
await NativeTimer.startTimer({
|
|
76
|
+
startTime: Date.now(),
|
|
77
|
+
title: 'Jornada Activa',
|
|
78
|
+
body: 'Timer en marcha...',
|
|
79
|
+
primaryColor: '#0045a5', // optional — widget & notification accent color
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
// Check if timer is running
|
|
83
|
+
const { isRunning } = await NativeTimer.isTimerRunning();
|
|
84
|
+
|
|
85
|
+
// Get elapsed time (ms)
|
|
86
|
+
const { elapsedTime } = await NativeTimer.getElapsedTime();
|
|
87
|
+
|
|
88
|
+
// Update notification text
|
|
89
|
+
await NativeTimer.updateNotification({
|
|
90
|
+
title: 'Still working',
|
|
91
|
+
body: '2h 30min elapsed',
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
// Stop the timer
|
|
95
|
+
await NativeTimer.stopTimer();
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Live Activities (iOS only)
|
|
99
|
+
|
|
100
|
+
```typescript
|
|
101
|
+
// Check availability
|
|
102
|
+
const { available } = await NativeTimer.areLiveActivitiesAvailable();
|
|
103
|
+
|
|
104
|
+
if (available) {
|
|
105
|
+
// Start Live Activity
|
|
106
|
+
const { activityId } = await NativeTimer.startLiveActivity({
|
|
107
|
+
title: 'Work Session',
|
|
108
|
+
startTime: new Date().toISOString(),
|
|
109
|
+
elapsedTime: '0 h 0 min',
|
|
110
|
+
status: 'active',
|
|
111
|
+
primaryColor: '#0045a5',
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
// Update Live Activity
|
|
115
|
+
await NativeTimer.updateLiveActivity({
|
|
116
|
+
activityId: activityId!,
|
|
117
|
+
elapsedTime: '1 h 30 min',
|
|
118
|
+
status: 'active',
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
// Stop Live Activity
|
|
122
|
+
await NativeTimer.stopLiveActivity({ activityId: activityId! });
|
|
123
|
+
|
|
124
|
+
// Or stop all at once
|
|
125
|
+
await NativeTimer.stopAllLiveActivities();
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### Foreground State Management
|
|
130
|
+
|
|
131
|
+
```typescript
|
|
132
|
+
// Tell the plugin when your app goes to background/foreground
|
|
133
|
+
// to manage notifications intelligently
|
|
134
|
+
App.addListener('appStateChange', ({ isActive }) => {
|
|
135
|
+
NativeTimer.setAppForegroundState({ inForeground: isActive });
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
// Reset notification dismissed state
|
|
139
|
+
await NativeTimer.resetNotificationState();
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### Event Listener
|
|
143
|
+
|
|
144
|
+
```typescript
|
|
145
|
+
// Listen for periodic timer updates
|
|
146
|
+
const listener = await NativeTimer.addListener('timerUpdate', (data) => {
|
|
147
|
+
console.log('Elapsed:', data.elapsedTime, 'ms');
|
|
148
|
+
console.log('Formatted:', data.formattedTime);
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
// Remove all listeners
|
|
152
|
+
await NativeTimer.removeAllListeners();
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## API
|
|
156
|
+
|
|
157
|
+
### `startTimer(options)`
|
|
158
|
+
|
|
159
|
+
Inicia el timer nativo. En Android crea un Foreground Service con notificación persistente. En iOS prepara el timer interno para Live Activities.
|
|
160
|
+
|
|
161
|
+
| Param | Type | Required | Description |
|
|
162
|
+
|-------|------|----------|-------------|
|
|
163
|
+
| `startTime` | `number` | Yes | Timestamp en milisegundos (ej: `Date.now()`) del inicio de la jornada |
|
|
164
|
+
| `title` | `string` | Yes | Título de la notificación (solo Android) |
|
|
165
|
+
| `body` | `string` | Yes | Cuerpo de la notificación (solo Android) |
|
|
166
|
+
| `primaryColor` | `string` | No | Color hex (ej: `#0045a5`) para la notificación Android y el widget iOS |
|
|
167
|
+
|
|
168
|
+
**Returns:** `Promise<{ success: boolean }>`
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
### `stopTimer()`
|
|
173
|
+
|
|
174
|
+
Detiene el timer, cancela el Foreground Service (Android) y elimina todas las notificaciones pendientes.
|
|
175
|
+
|
|
176
|
+
**Returns:** `Promise<{ success: boolean }>`
|
|
177
|
+
|
|
178
|
+
---
|
|
179
|
+
|
|
180
|
+
### `updateNotification(options)`
|
|
181
|
+
|
|
182
|
+
Actualiza el texto de la notificación del timer en Android. En iOS no tiene efecto visible (el widget se actualiza via `updateLiveActivity`).
|
|
183
|
+
|
|
184
|
+
| Param | Type | Required | Description |
|
|
185
|
+
|-------|------|----------|-------------|
|
|
186
|
+
| `title` | `string` | Yes | Nuevo título de la notificación |
|
|
187
|
+
| `body` | `string` | Yes | Nuevo cuerpo de la notificación |
|
|
188
|
+
|
|
189
|
+
**Returns:** `Promise<{ success: boolean }>`
|
|
190
|
+
|
|
191
|
+
---
|
|
192
|
+
|
|
193
|
+
### `isTimerRunning()`
|
|
194
|
+
|
|
195
|
+
Comprueba si hay un timer activo en ejecución.
|
|
196
|
+
|
|
197
|
+
**Returns:** `Promise<{ isRunning: boolean }>`
|
|
198
|
+
|
|
199
|
+
---
|
|
200
|
+
|
|
201
|
+
### `getElapsedTime()`
|
|
202
|
+
|
|
203
|
+
Obtiene el tiempo transcurrido desde que se inició el timer.
|
|
204
|
+
|
|
205
|
+
**Returns:** `Promise<{ elapsedTime: number }>` — tiempo en milisegundos
|
|
206
|
+
|
|
207
|
+
---
|
|
208
|
+
|
|
209
|
+
### `setAppForegroundState(options)`
|
|
210
|
+
|
|
211
|
+
Indica al plugin si la app está en primer o segundo plano. Esto controla si las notificaciones locales se muestran (solo en segundo plano) para no molestar al usuario mientras usa la app.
|
|
212
|
+
|
|
213
|
+
| Param | Type | Required | Description |
|
|
214
|
+
|-------|------|----------|-------------|
|
|
215
|
+
| `inForeground` | `boolean` | Yes | `true` si la app está visible, `false` si está en segundo plano |
|
|
216
|
+
|
|
217
|
+
**Returns:** `Promise<{ success: boolean }>`
|
|
218
|
+
|
|
219
|
+
---
|
|
220
|
+
|
|
221
|
+
### `resetNotificationState()`
|
|
222
|
+
|
|
223
|
+
Resetea el estado interno de "notificación descartada". Útil al volver a abrir la app para que las notificaciones puedan mostrarse de nuevo en segundo plano.
|
|
224
|
+
|
|
225
|
+
**Returns:** `Promise<{ success: boolean }>`
|
|
226
|
+
|
|
227
|
+
---
|
|
228
|
+
|
|
229
|
+
### `areLiveActivitiesAvailable()` *(iOS only)*
|
|
230
|
+
|
|
231
|
+
Verifica si el dispositivo soporta Live Activities (requiere iOS 16.2+ y que el usuario las tenga habilitadas).
|
|
232
|
+
|
|
233
|
+
**Returns:** `Promise<{ available: boolean }>`
|
|
234
|
+
|
|
235
|
+
---
|
|
236
|
+
|
|
237
|
+
### `startLiveActivity(options)` *(iOS only)*
|
|
238
|
+
|
|
239
|
+
Inicia una Live Activity que muestra el timer de jornada en la Dynamic Island (iPhone 14 Pro+) y en la pantalla de bloqueo. El widget muestra "⚡ Jornada Activa" con un contador en tiempo real.
|
|
240
|
+
|
|
241
|
+
| Param | Type | Required | Description |
|
|
242
|
+
|-------|------|----------|-------------|
|
|
243
|
+
| `title` | `string` | Yes | Nombre de la sesión (usado internamente) |
|
|
244
|
+
| `startTime` | `string` | Yes | Fecha/hora de inicio en formato ISO 8601 |
|
|
245
|
+
| `elapsedTime` | `string` | Yes | Tiempo transcurrido formateado (ej: `"1 h 30 min"`) |
|
|
246
|
+
| `status` | `string` | Yes | Estado de la jornada (ej: `"active"`, `"paused"`) |
|
|
247
|
+
| `primaryColor` | `string` | No | Color hex para el widget (default: `#0045a5`) |
|
|
248
|
+
|
|
249
|
+
**Returns:** `Promise<{ success: boolean; activityId?: string }>` — el `activityId` se usa para actualizar o detener la actividad
|
|
250
|
+
|
|
251
|
+
---
|
|
252
|
+
|
|
253
|
+
### `updateLiveActivity(options)` *(iOS only)*
|
|
254
|
+
|
|
255
|
+
Actualiza una Live Activity existente con nuevo tiempo transcurrido y estado.
|
|
256
|
+
|
|
257
|
+
| Param | Type | Required | Description |
|
|
258
|
+
|-------|------|----------|-------------|
|
|
259
|
+
| `activityId` | `string` | Yes | ID devuelto por `startLiveActivity` |
|
|
260
|
+
| `elapsedTime` | `string` | Yes | Tiempo transcurrido actualizado (ej: `"2 h 15 min"`) |
|
|
261
|
+
| `status` | `string` | Yes | Estado actual (ej: `"active"`) |
|
|
262
|
+
|
|
263
|
+
**Returns:** `Promise<{ success: boolean }>`
|
|
264
|
+
|
|
265
|
+
---
|
|
266
|
+
|
|
267
|
+
### `stopLiveActivity(options)` *(iOS only)*
|
|
268
|
+
|
|
269
|
+
Detiene una Live Activity específica y la elimina de la Dynamic Island y pantalla de bloqueo.
|
|
270
|
+
|
|
271
|
+
| Param | Type | Required | Description |
|
|
272
|
+
|-------|------|----------|-------------|
|
|
273
|
+
| `activityId` | `string` | Yes | ID devuelto por `startLiveActivity` |
|
|
274
|
+
|
|
275
|
+
**Returns:** `Promise<{ success: boolean }>`
|
|
276
|
+
|
|
277
|
+
---
|
|
278
|
+
|
|
279
|
+
### `stopAllLiveActivities()` *(iOS only)*
|
|
280
|
+
|
|
281
|
+
Detiene **todas** las Live Activities activas del plugin. Útil para limpieza al cerrar sesión o al detener la jornada.
|
|
282
|
+
|
|
283
|
+
**Returns:** `Promise<{ success: boolean }>`
|
|
284
|
+
|
|
285
|
+
---
|
|
286
|
+
|
|
287
|
+
### `addListener('timerUpdate', callback)`
|
|
288
|
+
|
|
289
|
+
Escucha actualizaciones periódicas del timer (cada ~30 segundos).
|
|
290
|
+
|
|
291
|
+
```typescript
|
|
292
|
+
const listener = await NativeTimer.addListener('timerUpdate', (data) => {
|
|
293
|
+
console.log(data.elapsedTime); // number (ms)
|
|
294
|
+
console.log(data.formattedTime); // string
|
|
295
|
+
});
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
---
|
|
299
|
+
|
|
300
|
+
### `removeAllListeners()`
|
|
301
|
+
|
|
302
|
+
Elimina todos los listeners registrados.
|
|
303
|
+
|
|
304
|
+
**Returns:** `Promise<void>`
|
|
305
|
+
|
|
306
|
+
## License
|
|
307
|
+
|
|
308
|
+
MIT
|
|
File without changes
|
|
Binary file
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
#
|
|
2
|
-
gradle.version=
|
|
1
|
+
#Thu Apr 16 07:13:06 CEST 2026
|
|
2
|
+
gradle.version=8.9
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -6,31 +6,6 @@ import UIKit
|
|
|
6
6
|
import ActivityKit
|
|
7
7
|
#endif
|
|
8
8
|
|
|
9
|
-
@available(iOS 16.2, *)
|
|
10
|
-
public struct WorkSessionTimerAttributes: ActivityAttributes {
|
|
11
|
-
public struct ContentState: Codable, Hashable {
|
|
12
|
-
public let title: String
|
|
13
|
-
public let elapsedTime: String
|
|
14
|
-
public let status: String
|
|
15
|
-
public let startTime: String
|
|
16
|
-
public let primaryColor: String
|
|
17
|
-
|
|
18
|
-
public init(title: String, elapsedTime: String, status: String, startTime: String, primaryColor: String = "#0045a5") {
|
|
19
|
-
self.title = title
|
|
20
|
-
self.elapsedTime = elapsedTime
|
|
21
|
-
self.status = status
|
|
22
|
-
self.startTime = startTime
|
|
23
|
-
self.primaryColor = primaryColor
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
public let sessionName: String
|
|
28
|
-
|
|
29
|
-
public init(sessionName: String) {
|
|
30
|
-
self.sessionName = sessionName
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
9
|
public class NativeTimerManager {
|
|
35
10
|
private var startTime: Date?
|
|
36
11
|
private var timer: Timer?
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
|
|
3
|
+
#if canImport(ActivityKit)
|
|
4
|
+
import ActivityKit
|
|
5
|
+
#endif
|
|
6
|
+
|
|
7
|
+
#if canImport(ActivityKit)
|
|
8
|
+
@available(iOS 16.1, *)
|
|
9
|
+
public struct WorkSessionTimerAttributes: ActivityAttributes {
|
|
10
|
+
public struct ContentState: Codable, Hashable {
|
|
11
|
+
public let title: String
|
|
12
|
+
public let elapsedTime: String
|
|
13
|
+
public let status: String
|
|
14
|
+
public let startTime: String
|
|
15
|
+
public let primaryColor: String
|
|
16
|
+
|
|
17
|
+
public init(title: String, elapsedTime: String, status: String, startTime: String, primaryColor: String = "#0045a5") {
|
|
18
|
+
self.title = title
|
|
19
|
+
self.elapsedTime = elapsedTime
|
|
20
|
+
self.status = status
|
|
21
|
+
self.startTime = startTime
|
|
22
|
+
self.primaryColor = primaryColor
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
public let sessionName: String
|
|
27
|
+
|
|
28
|
+
public init(sessionName: String) {
|
|
29
|
+
self.sessionName = sessionName
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
#endif
|
|
@@ -3,7 +3,13 @@ import SwiftUI
|
|
|
3
3
|
import WidgetKit
|
|
4
4
|
import ActivityKit
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
#if canImport(NativeTimerCore)
|
|
7
|
+
import NativeTimerCore
|
|
8
|
+
#elseif canImport(MeycagesalNativeTimer)
|
|
9
|
+
import MeycagesalNativeTimer
|
|
10
|
+
#endif
|
|
11
|
+
|
|
12
|
+
@available(iOS 16.1, *)
|
|
7
13
|
public struct NativeTimerWidget: Widget {
|
|
8
14
|
public var body: some WidgetConfiguration {
|
|
9
15
|
ActivityConfiguration(for: WorkSessionTimerAttributes.self) { context in
|
|
@@ -467,7 +473,7 @@ extension Color {
|
|
|
467
473
|
}
|
|
468
474
|
}
|
|
469
475
|
|
|
470
|
-
@available(iOS 16.
|
|
476
|
+
@available(iOS 16.1, *)
|
|
471
477
|
public struct NativeTimerWidgetBundle: WidgetBundle {
|
|
472
478
|
public var body: some Widget {
|
|
473
479
|
NativeTimerWidget()
|