expo-live-activity 0.4.3-alpha1 → 0.5.0-alpha1
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/README.md +59 -6
- package/build/index.d.ts +27 -2
- package/build/index.d.ts.map +1 -1
- package/build/index.js +42 -29
- package/build/index.js.map +1 -1
- package/ios/ExpoLiveActivityModule.swift +87 -6
- package/ios/LiveActivityAttributes.swift +10 -0
- package/ios-files/LiveActivityHelpers.swift +58 -0
- package/ios-files/LiveActivityMediumView.swift +127 -0
- package/ios-files/LiveActivitySmallView.swift +178 -0
- package/ios-files/LiveActivityView.swift +130 -104
- package/ios-files/LiveActivityWidget.swift +95 -6
- package/ios-files/ViewHelpers.swift +41 -0
- package/package.json +2 -3
- package/plugin/build/index.js +2 -0
- package/plugin/build/types.d.ts +1 -0
- package/plugin/build/withUnsupportedOS.d.ts +4 -0
- package/plugin/build/withUnsupportedOS.js +9 -0
- package/plugin/src/index.ts +3 -0
- package/plugin/src/types.ts +1 -0
- package/plugin/src/withUnsupportedOS.ts +10 -0
- package/plugin/tsconfig.tsbuildinfo +1 -1
- package/src/index.ts +70 -28
- package/.prettierignore +0 -5
package/README.md
CHANGED
|
@@ -132,9 +132,12 @@ The `state` object should include:
|
|
|
132
132
|
{
|
|
133
133
|
title: string;
|
|
134
134
|
subtitle?: string;
|
|
135
|
-
progressBar: { // Only one property
|
|
136
|
-
date?: number; // Set as epoch time in milliseconds. This is used as an end date in a timer.
|
|
137
|
-
progress?: number; //Set amount of progress in the progress bar
|
|
135
|
+
progressBar: { // Only one property (date, progress, or elapsedTimer) is available at a time
|
|
136
|
+
date?: number; // Set as epoch time in milliseconds. This is used as an end date in a countdown timer.
|
|
137
|
+
progress?: number; // Set amount of progress in the progress bar (0-1)
|
|
138
|
+
elapsedTimer?: { // Count up timer (elapsed time from start)
|
|
139
|
+
startDate: number; // Epoch time in milliseconds when the timer started
|
|
140
|
+
};
|
|
138
141
|
};
|
|
139
142
|
imageName?: string; // Matches the name of the image in 'assets/liveActivity'
|
|
140
143
|
dynamicIslandImageName?: string; // Matches the name of the image in 'assets/liveActivity'
|
|
@@ -157,7 +160,7 @@ The `config` object should include:
|
|
|
157
160
|
padding?: Padding // number | {top?: number bottom?: number ...}
|
|
158
161
|
imagePosition?: ImagePosition; // 'left' | 'right';
|
|
159
162
|
imageAlign?: ImageAlign; // 'top' | 'center' | 'bottom'
|
|
160
|
-
imageSize?: ImageSize // { width
|
|
163
|
+
imageSize?: ImageSize // { width?: number|`${number}%`, height?: number|`${number}%` } | undefined (defaults to 64pt)
|
|
161
164
|
contentFit?: ImageContentFit; // 'cover' | 'contain' | 'fill' | 'none' | 'scale-down'
|
|
162
165
|
};
|
|
163
166
|
```
|
|
@@ -202,6 +205,26 @@ const activityId = LiveActivity.startActivity(state, config)
|
|
|
202
205
|
|
|
203
206
|
This will initiate a Live Activity with the specified title, subtitle, image from your configured assets folder and a time to which there will be a countdown in a progress view.
|
|
204
207
|
|
|
208
|
+
Using an elapsed timer:
|
|
209
|
+
|
|
210
|
+
```typescript
|
|
211
|
+
const elapsedTimerState: LiveActivity.LiveActivityState = {
|
|
212
|
+
title: 'Walk in Progress',
|
|
213
|
+
subtitle: 'With Max the Dog',
|
|
214
|
+
progressBar: {
|
|
215
|
+
elapsedTimer: {
|
|
216
|
+
startDate: Date.now() - 5 * 60 * 1000, // Started 5 minutes ago
|
|
217
|
+
},
|
|
218
|
+
},
|
|
219
|
+
imageName: 'dog_walking',
|
|
220
|
+
dynamicIslandImageName: 'dog_icon',
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const activityId = LiveActivity.startActivity(elapsedTimerState, config)
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
The elapsed timer will automatically update every second based on the `startDate` you provide.
|
|
227
|
+
|
|
205
228
|
Subscribing to push token changes:
|
|
206
229
|
|
|
207
230
|
```typescript
|
|
@@ -246,7 +269,8 @@ Example payload for starting Live Activity:
|
|
|
246
269
|
"timerEndDateInMilliseconds": 1754410997000,
|
|
247
270
|
"progress": 0.5,
|
|
248
271
|
"imageName": "live_activity_image",
|
|
249
|
-
"dynamicIslandImageName": "dynamic_island_image"
|
|
272
|
+
"dynamicIslandImageName": "dynamic_island_image",
|
|
273
|
+
"elapsedTimerStartDateInMilliseconds": null
|
|
250
274
|
},
|
|
251
275
|
"timestamp": 1754491435000, // timestamp of when the push notification was sent
|
|
252
276
|
"attributes-type": "LiveActivityAttributes",
|
|
@@ -290,7 +314,36 @@ Example payload for updating Live Activity:
|
|
|
290
314
|
}
|
|
291
315
|
```
|
|
292
316
|
|
|
293
|
-
Where `timerEndDateInMilliseconds` value is a timestamp in milliseconds corresponding to the target point of the
|
|
317
|
+
Where `timerEndDateInMilliseconds` value is a timestamp in milliseconds corresponding to the target point of the countdown displayed in Live Activity view.
|
|
318
|
+
|
|
319
|
+
Example payload for starting Live Activity with elapsed timer:
|
|
320
|
+
|
|
321
|
+
```json
|
|
322
|
+
{
|
|
323
|
+
"aps": {
|
|
324
|
+
"event": "start",
|
|
325
|
+
"content-state": {
|
|
326
|
+
"title": "Walk in Progress",
|
|
327
|
+
"subtitle": "With Max",
|
|
328
|
+
"timerEndDateInMilliseconds": null,
|
|
329
|
+
"progress": null,
|
|
330
|
+
"imageName": "dog_walking",
|
|
331
|
+
"dynamicIslandImageName": "dog_icon",
|
|
332
|
+
"elapsedTimerStartDateInMilliseconds": 1754410997000
|
|
333
|
+
},
|
|
334
|
+
"timestamp": 1754491435000,
|
|
335
|
+
"attributes-type": "LiveActivityAttributes",
|
|
336
|
+
"attributes": {
|
|
337
|
+
"name": "WalkActivity",
|
|
338
|
+
"backgroundColor": "001A72",
|
|
339
|
+
"titleColor": "EBEBF0",
|
|
340
|
+
"progressViewLabelColor": "FFFFFF"
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
Where `elapsedTimerStartDateInMilliseconds` is the timestamp (in milliseconds) when the elapsed timer started counting up.
|
|
294
347
|
|
|
295
348
|
## Image support
|
|
296
349
|
|
package/build/index.d.ts
CHANGED
|
@@ -1,12 +1,33 @@
|
|
|
1
1
|
import { EventSubscription } from 'expo-modules-core';
|
|
2
2
|
type Voidable<T> = T | void;
|
|
3
3
|
export type DynamicIslandTimerType = 'circular' | 'digital';
|
|
4
|
+
export type ElapsedTimer = {
|
|
5
|
+
startDate: number;
|
|
6
|
+
};
|
|
4
7
|
type ProgressBarType = {
|
|
5
8
|
date?: number;
|
|
6
9
|
progress?: undefined;
|
|
10
|
+
elapsedTimer?: undefined;
|
|
11
|
+
currentStep?: undefined;
|
|
12
|
+
totalSteps?: undefined;
|
|
7
13
|
} | {
|
|
8
14
|
date?: undefined;
|
|
9
15
|
progress?: number;
|
|
16
|
+
elapsedTimer?: undefined;
|
|
17
|
+
currentStep?: undefined;
|
|
18
|
+
totalSteps?: undefined;
|
|
19
|
+
} | {
|
|
20
|
+
date?: undefined;
|
|
21
|
+
progress?: undefined;
|
|
22
|
+
elapsedTimer?: ElapsedTimer;
|
|
23
|
+
currentStep?: undefined;
|
|
24
|
+
totalSteps?: undefined;
|
|
25
|
+
} | {
|
|
26
|
+
date?: undefined;
|
|
27
|
+
progress?: undefined;
|
|
28
|
+
elapsedTimer?: undefined;
|
|
29
|
+
currentStep?: number;
|
|
30
|
+
totalSteps?: number;
|
|
10
31
|
};
|
|
11
32
|
export type LiveActivityState = {
|
|
12
33
|
title: string;
|
|
@@ -14,6 +35,7 @@ export type LiveActivityState = {
|
|
|
14
35
|
progressBar?: ProgressBarType;
|
|
15
36
|
imageName?: string;
|
|
16
37
|
dynamicIslandImageName?: string;
|
|
38
|
+
smallImageName?: string;
|
|
17
39
|
};
|
|
18
40
|
export type NativeLiveActivityState = {
|
|
19
41
|
title: string;
|
|
@@ -35,8 +57,8 @@ export type ImagePosition = 'left' | 'right' | 'leftStretch' | 'rightStretch';
|
|
|
35
57
|
export type ImageAlign = 'top' | 'center' | 'bottom';
|
|
36
58
|
export type ImageDimension = number | `${number}%`;
|
|
37
59
|
export type ImageSize = {
|
|
38
|
-
width
|
|
39
|
-
height
|
|
60
|
+
width?: ImageDimension;
|
|
61
|
+
height?: ImageDimension;
|
|
40
62
|
};
|
|
41
63
|
export type ImageContentFit = 'cover' | 'contain' | 'fill' | 'none' | 'scale-down';
|
|
42
64
|
export type LiveActivityConfig = {
|
|
@@ -51,7 +73,10 @@ export type LiveActivityConfig = {
|
|
|
51
73
|
imagePosition?: ImagePosition;
|
|
52
74
|
imageAlign?: ImageAlign;
|
|
53
75
|
imageSize?: ImageSize;
|
|
76
|
+
smallImageSize?: ImageSize;
|
|
54
77
|
contentFit?: ImageContentFit;
|
|
78
|
+
progressSegmentActiveColor?: string;
|
|
79
|
+
progressSegmentInactiveColor?: string;
|
|
55
80
|
};
|
|
56
81
|
export type ActivityTokenReceivedEvent = {
|
|
57
82
|
activityID: string;
|
package/build/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAA;AAKrD,KAAK,QAAQ,CAAC,CAAC,IAAI,CAAC,GAAG,IAAI,CAAA;AAE3B,MAAM,MAAM,sBAAsB,GAAG,UAAU,GAAG,SAAS,CAAA;AAE3D,KAAK,eAAe,GAChB;IACE,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,SAAS,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/build/index.js
CHANGED
|
@@ -9,8 +9,12 @@ function assertIOS(name) {
|
|
|
9
9
|
function normalizeConfig(config) {
|
|
10
10
|
if (config === undefined)
|
|
11
11
|
return config;
|
|
12
|
-
const { padding, imageSize, ...base } = config;
|
|
13
|
-
const normalized = {
|
|
12
|
+
const { padding, imageSize, smallImageSize, progressSegmentActiveColor, progressSegmentInactiveColor, ...base } = config;
|
|
13
|
+
const normalized = {
|
|
14
|
+
...base,
|
|
15
|
+
progressSegmentActiveColor,
|
|
16
|
+
progressSegmentInactiveColor,
|
|
17
|
+
};
|
|
14
18
|
// Normalize padding: keep number in padding, object in paddingDetails
|
|
15
19
|
if (typeof padding === 'number') {
|
|
16
20
|
normalized.padding = padding;
|
|
@@ -18,34 +22,43 @@ function normalizeConfig(config) {
|
|
|
18
22
|
else if (typeof padding === 'object') {
|
|
19
23
|
normalized.paddingDetails = padding;
|
|
20
24
|
}
|
|
21
|
-
//
|
|
22
|
-
|
|
25
|
+
// Helper to parse a dimension value (number or percent string like '50%')
|
|
26
|
+
const parseDimension = (value, fieldName) => {
|
|
27
|
+
if (value === undefined)
|
|
28
|
+
return {};
|
|
29
|
+
if (typeof value === 'number')
|
|
30
|
+
return { absolute: value };
|
|
23
31
|
const regExp = /^(100(?:\.0+)?|\d{1,2}(?:\.\d+)?)%$/; // Matches 0.0% to 100.0%
|
|
24
|
-
const
|
|
25
|
-
if (
|
|
26
|
-
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
if (
|
|
38
|
-
normalized.imageHeight =
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
32
|
+
const match = value.trim().match(regExp);
|
|
33
|
+
if (match)
|
|
34
|
+
return { percent: Number(match[1]) };
|
|
35
|
+
throw new Error(`${fieldName} percent string must be in format "0%" to "100%"`);
|
|
36
|
+
};
|
|
37
|
+
// Normalize imageSize
|
|
38
|
+
if (imageSize) {
|
|
39
|
+
const w = parseDimension(imageSize.width, 'imageSize.width');
|
|
40
|
+
const h = parseDimension(imageSize.height, 'imageSize.height');
|
|
41
|
+
if (w.absolute !== undefined)
|
|
42
|
+
normalized.imageWidth = w.absolute;
|
|
43
|
+
if (w.percent !== undefined)
|
|
44
|
+
normalized.imageWidthPercent = w.percent;
|
|
45
|
+
if (h.absolute !== undefined)
|
|
46
|
+
normalized.imageHeight = h.absolute;
|
|
47
|
+
if (h.percent !== undefined)
|
|
48
|
+
normalized.imageHeightPercent = h.percent;
|
|
49
|
+
}
|
|
50
|
+
// Normalize smallImageSize
|
|
51
|
+
if (smallImageSize) {
|
|
52
|
+
const w = parseDimension(smallImageSize.width, 'smallImageSize.width');
|
|
53
|
+
const h = parseDimension(smallImageSize.height, 'smallImageSize.height');
|
|
54
|
+
if (w.absolute !== undefined)
|
|
55
|
+
normalized.smallImageWidth = w.absolute;
|
|
56
|
+
if (w.percent !== undefined)
|
|
57
|
+
normalized.smallImageWidthPercent = w.percent;
|
|
58
|
+
if (h.absolute !== undefined)
|
|
59
|
+
normalized.smallImageHeight = h.absolute;
|
|
60
|
+
if (h.percent !== undefined)
|
|
61
|
+
normalized.smallImageHeightPercent = h.percent;
|
|
49
62
|
}
|
|
50
63
|
return normalized;
|
|
51
64
|
}
|
package/build/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["import { EventSubscription } from 'expo-modules-core'\nimport { Platform } from 'react-native'\n\nimport ExpoLiveActivityModule from './ExpoLiveActivityModule'\n\ntype Voidable<T> = T | void\n\nexport type DynamicIslandTimerType = 'circular' | 'digital'\n\ntype ProgressBarType =\n | {\n date?: number\n progress?: undefined\n }\n | {\n date?: undefined\n progress?: number\n }\n\nexport type LiveActivityState = {\n title: string\n subtitle?: string\n progressBar?: ProgressBarType\n imageName?: string\n dynamicIslandImageName?: string\n}\n\nexport type NativeLiveActivityState = {\n title: string\n subtitle?: string\n date?: number\n progress?: number\n imageName?: string\n dynamicIslandImageName?: string\n}\n\nexport type Padding =\n | {\n top?: number\n bottom?: number\n left?: number\n right?: number\n vertical?: number\n horizontal?: number\n }\n | number\n\nexport type ImagePosition = 'left' | 'right' | 'leftStretch' | 'rightStretch'\n\nexport type ImageAlign = 'top' | 'center' | 'bottom'\n\nexport type ImageDimension = number | `${number}%`\nexport type ImageSize = {\n width: ImageDimension\n height: ImageDimension\n}\n\nexport type ImageContentFit = 'cover' | 'contain' | 'fill' | 'none' | 'scale-down'\n\nexport type LiveActivityConfig = {\n backgroundColor?: string\n titleColor?: string\n subtitleColor?: string\n progressViewTint?: string\n progressViewLabelColor?: string\n deepLinkUrl?: string\n timerType?: DynamicIslandTimerType\n padding?: Padding\n imagePosition?: ImagePosition\n imageAlign?: ImageAlign\n imageSize?: ImageSize\n contentFit?: ImageContentFit\n}\n\nexport type ActivityTokenReceivedEvent = {\n activityID: string\n activityName: string\n activityPushToken: string\n}\n\nexport type ActivityPushToStartTokenReceivedEvent = {\n activityPushToStartToken: string | null\n}\n\ntype ActivityState = 'active' | 'dismissed' | 'pending' | 'stale' | 'ended'\n\nexport type ActivityUpdateEvent = {\n activityID: string\n activityName: string\n activityState: ActivityState\n}\n\nexport type LiveActivityModuleEvents = {\n onTokenReceived: (params: ActivityTokenReceivedEvent) => void\n onPushToStartTokenReceived: (params: ActivityPushToStartTokenReceivedEvent) => void\n onStateChange: (params: ActivityUpdateEvent) => void\n}\n\nfunction assertIOS(name: string) {\n const isIOS = Platform.OS === 'ios'\n\n if (!isIOS) console.error(`${name} is only available on iOS`)\n\n return isIOS\n}\n\nfunction normalizeConfig(config?: LiveActivityConfig) {\n if (config === undefined) return config\n\n const { padding, imageSize, ...base } = config\n type NormalizedConfig = LiveActivityConfig & {\n paddingDetails?: Padding\n imageWidth?: number\n imageHeight?: number\n imageWidthPercent?: number\n imageHeightPercent?: number\n }\n const normalized: NormalizedConfig = { ...base }\n\n // Normalize padding: keep number in padding, object in paddingDetails\n if (typeof padding === 'number') {\n normalized.padding = padding\n } else if (typeof padding === 'object') {\n normalized.paddingDetails = padding\n }\n\n // Normalize imageSize: object with width/height each a number (points) or percent string like '50%'\n if (imageSize) {\n const regExp = /^(100(?:\\.0+)?|\\d{1,2}(?:\\.\\d+)?)%$/ // Matches 0.0% to 100.0%\n\n const { width, height } = imageSize\n\n if (typeof width === 'number') {\n normalized.imageWidth = width\n } else if (typeof width === 'string') {\n const match = width.trim().match(regExp)\n if (match) {\n normalized.imageWidthPercent = Number(match[1])\n } else {\n throw new Error('imageSize.width percent string must be in format \"0%\" to \"100%\"')\n }\n }\n\n if (typeof height === 'number') {\n normalized.imageHeight = height\n } else if (typeof height === 'string') {\n const match = height.trim().match(regExp)\n if (match) {\n normalized.imageHeightPercent = Number(match[1])\n } else {\n throw new Error('imageSize.height percent string must be in format \"0%\" to \"100%\"')\n }\n }\n }\n\n return normalized\n}\n\n/**\n * @param {LiveActivityState} state The state for the live activity.\n * @param {LiveActivityConfig} config Live activity config object.\n * @returns {string} The identifier of the started activity or undefined if creating live activity failed.\n */\nexport function startActivity(state: LiveActivityState, config?: LiveActivityConfig): Voidable<string> {\n if (assertIOS('startActivity')) return ExpoLiveActivityModule.startActivity(state, normalizeConfig(config))\n}\n\n/**\n * @param {string} id The identifier of the activity to stop.\n * @param {LiveActivityState} state The updated state for the live activity.\n */\nexport function stopActivity(id: string, state: LiveActivityState) {\n if (assertIOS('stopActivity')) return ExpoLiveActivityModule.stopActivity(id, state)\n}\n\n/**\n * @param {string} id The identifier of the activity to update.\n * @param {LiveActivityState} state The updated state for the live activity.\n */\nexport function updateActivity(id: string, state: LiveActivityState) {\n if (assertIOS('updateActivity')) return ExpoLiveActivityModule.updateActivity(id, state)\n}\n\n/**\n * @param {function} updateTokenListener The listener function that will be called when an update token is received.\n */\nexport function addActivityTokenListener(\n updateTokenListener: (event: ActivityTokenReceivedEvent) => void\n): Voidable<EventSubscription> {\n if (assertIOS('addActivityTokenListener'))\n return ExpoLiveActivityModule.addListener('onTokenReceived', updateTokenListener)\n}\n\n/**\n * Adds a listener that is called when a push-to-start token is received. Supported only on iOS > 17.2.\n * On earlier iOS versions, the listener will return null as a token.\n * @param {function} listener The listener function that will be called when the observer starts and then when a push-to-start token is received.\n */\nexport function addActivityPushToStartTokenListener(\n listener: (event: ActivityPushToStartTokenReceivedEvent) => void\n): Voidable<EventSubscription> {\n if (assertIOS('addActivityPushToStartTokenListener'))\n return ExpoLiveActivityModule.addListener('onPushToStartTokenReceived', listener)\n}\n\n/**\n * @param {function} statusListener The listener function that will be called when an activity status changes.\n */\nexport function addActivityUpdatesListener(\n statusListener: (event: ActivityUpdateEvent) => void\n): Voidable<EventSubscription> {\n if (assertIOS('addActivityUpdatesListener'))\n return ExpoLiveActivityModule.addListener('onStateChange', statusListener)\n}\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["import { EventSubscription } from 'expo-modules-core'\nimport { Platform } from 'react-native'\n\nimport ExpoLiveActivityModule from './ExpoLiveActivityModule'\n\ntype Voidable<T> = T | void\n\nexport type DynamicIslandTimerType = 'circular' | 'digital'\n\nexport type ElapsedTimer = {\n startDate: number // milliseconds timestamp (past time when timer started)\n}\n\ntype ProgressBarType =\n | {\n date?: number\n progress?: undefined\n elapsedTimer?: undefined\n currentStep?: undefined\n totalSteps?: undefined\n }\n | {\n date?: undefined\n progress?: number\n elapsedTimer?: undefined\n currentStep?: undefined\n totalSteps?: undefined\n }\n | {\n date?: undefined\n progress?: undefined\n elapsedTimer?: ElapsedTimer\n currentStep?: undefined\n totalSteps?: undefined\n }\n | {\n date?: undefined\n progress?: undefined\n elapsedTimer?: undefined\n currentStep?: number\n totalSteps?: number\n }\n\nexport type LiveActivityState = {\n title: string\n subtitle?: string\n progressBar?: ProgressBarType\n imageName?: string\n dynamicIslandImageName?: string\n smallImageName?: string\n}\n\nexport type NativeLiveActivityState = {\n title: string\n subtitle?: string\n date?: number\n progress?: number\n imageName?: string\n dynamicIslandImageName?: string\n}\n\nexport type Padding =\n | {\n top?: number\n bottom?: number\n left?: number\n right?: number\n vertical?: number\n horizontal?: number\n }\n | number\n\nexport type ImagePosition = 'left' | 'right' | 'leftStretch' | 'rightStretch'\n\nexport type ImageAlign = 'top' | 'center' | 'bottom'\n\nexport type ImageDimension = number | `${number}%`\nexport type ImageSize = {\n width?: ImageDimension\n height?: ImageDimension\n}\n\nexport type ImageContentFit = 'cover' | 'contain' | 'fill' | 'none' | 'scale-down'\n\nexport type LiveActivityConfig = {\n backgroundColor?: string\n titleColor?: string\n subtitleColor?: string\n progressViewTint?: string\n progressViewLabelColor?: string\n deepLinkUrl?: string\n timerType?: DynamicIslandTimerType\n padding?: Padding\n imagePosition?: ImagePosition\n imageAlign?: ImageAlign\n imageSize?: ImageSize\n smallImageSize?: ImageSize\n contentFit?: ImageContentFit\n progressSegmentActiveColor?: string\n progressSegmentInactiveColor?: string\n}\n\nexport type ActivityTokenReceivedEvent = {\n activityID: string\n activityName: string\n activityPushToken: string\n}\n\nexport type ActivityPushToStartTokenReceivedEvent = {\n activityPushToStartToken: string | null\n}\n\ntype ActivityState = 'active' | 'dismissed' | 'pending' | 'stale' | 'ended'\n\nexport type ActivityUpdateEvent = {\n activityID: string\n activityName: string\n activityState: ActivityState\n}\n\nexport type LiveActivityModuleEvents = {\n onTokenReceived: (params: ActivityTokenReceivedEvent) => void\n onPushToStartTokenReceived: (params: ActivityPushToStartTokenReceivedEvent) => void\n onStateChange: (params: ActivityUpdateEvent) => void\n}\n\nfunction assertIOS(name: string) {\n const isIOS = Platform.OS === 'ios'\n\n if (!isIOS) console.error(`${name} is only available on iOS`)\n\n return isIOS\n}\n\nfunction normalizeConfig(config?: LiveActivityConfig) {\n if (config === undefined) return config\n\n const { padding, imageSize, smallImageSize, progressSegmentActiveColor, progressSegmentInactiveColor, ...base } =\n config\n type NormalizedConfig = LiveActivityConfig & {\n paddingDetails?: Padding\n imageWidth?: number\n imageHeight?: number\n imageWidthPercent?: number\n imageHeightPercent?: number\n smallImageWidth?: number\n smallImageHeight?: number\n smallImageWidthPercent?: number\n smallImageHeightPercent?: number\n }\n const normalized: NormalizedConfig = {\n ...base,\n progressSegmentActiveColor,\n progressSegmentInactiveColor,\n }\n\n // Normalize padding: keep number in padding, object in paddingDetails\n if (typeof padding === 'number') {\n normalized.padding = padding\n } else if (typeof padding === 'object') {\n normalized.paddingDetails = padding\n }\n\n // Helper to parse a dimension value (number or percent string like '50%')\n const parseDimension = (\n value: ImageDimension | undefined,\n fieldName: string\n ): { absolute?: number; percent?: number } => {\n if (value === undefined) return {}\n\n if (typeof value === 'number') return { absolute: value }\n const regExp = /^(100(?:\\.0+)?|\\d{1,2}(?:\\.\\d+)?)%$/ // Matches 0.0% to 100.0%\n const match = value.trim().match(regExp)\n if (match) return { percent: Number(match[1]) }\n throw new Error(`${fieldName} percent string must be in format \"0%\" to \"100%\"`)\n }\n\n // Normalize imageSize\n if (imageSize) {\n const w = parseDimension(imageSize.width, 'imageSize.width')\n const h = parseDimension(imageSize.height, 'imageSize.height')\n if (w.absolute !== undefined) normalized.imageWidth = w.absolute\n if (w.percent !== undefined) normalized.imageWidthPercent = w.percent\n if (h.absolute !== undefined) normalized.imageHeight = h.absolute\n if (h.percent !== undefined) normalized.imageHeightPercent = h.percent\n }\n\n // Normalize smallImageSize\n if (smallImageSize) {\n const w = parseDimension(smallImageSize.width, 'smallImageSize.width')\n const h = parseDimension(smallImageSize.height, 'smallImageSize.height')\n if (w.absolute !== undefined) normalized.smallImageWidth = w.absolute\n if (w.percent !== undefined) normalized.smallImageWidthPercent = w.percent\n if (h.absolute !== undefined) normalized.smallImageHeight = h.absolute\n if (h.percent !== undefined) normalized.smallImageHeightPercent = h.percent\n }\n\n return normalized\n}\n\n/**\n * @param {LiveActivityState} state The state for the live activity.\n * @param {LiveActivityConfig} config Live activity config object.\n * @returns {string} The identifier of the started activity or undefined if creating live activity failed.\n */\nexport function startActivity(state: LiveActivityState, config?: LiveActivityConfig): Voidable<string> {\n if (assertIOS('startActivity')) return ExpoLiveActivityModule.startActivity(state, normalizeConfig(config))\n}\n\n/**\n * @param {string} id The identifier of the activity to stop.\n * @param {LiveActivityState} state The updated state for the live activity.\n */\nexport function stopActivity(id: string, state: LiveActivityState) {\n if (assertIOS('stopActivity')) return ExpoLiveActivityModule.stopActivity(id, state)\n}\n\n/**\n * @param {string} id The identifier of the activity to update.\n * @param {LiveActivityState} state The updated state for the live activity.\n */\nexport function updateActivity(id: string, state: LiveActivityState) {\n if (assertIOS('updateActivity')) return ExpoLiveActivityModule.updateActivity(id, state)\n}\n\n/**\n * @param {function} updateTokenListener The listener function that will be called when an update token is received.\n */\nexport function addActivityTokenListener(\n updateTokenListener: (event: ActivityTokenReceivedEvent) => void\n): Voidable<EventSubscription> {\n if (assertIOS('addActivityTokenListener'))\n return ExpoLiveActivityModule.addListener('onTokenReceived', updateTokenListener)\n}\n\n/**\n * Adds a listener that is called when a push-to-start token is received. Supported only on iOS > 17.2.\n * On earlier iOS versions, the listener will return null as a token.\n * @param {function} listener The listener function that will be called when the observer starts and then when a push-to-start token is received.\n */\nexport function addActivityPushToStartTokenListener(\n listener: (event: ActivityPushToStartTokenReceivedEvent) => void\n): Voidable<EventSubscription> {\n if (assertIOS('addActivityPushToStartTokenListener'))\n return ExpoLiveActivityModule.addListener('onPushToStartTokenReceived', listener)\n}\n\n/**\n * @param {function} statusListener The listener function that will be called when an activity status changes.\n */\nexport function addActivityUpdatesListener(\n statusListener: (event: ActivityUpdateEvent) => void\n): Voidable<EventSubscription> {\n if (assertIOS('addActivityUpdatesListener'))\n return ExpoLiveActivityModule.addListener('onStateChange', statusListener)\n}\n"]}
|
|
@@ -18,6 +18,20 @@ public class ExpoLiveActivityModule: Module {
|
|
|
18
18
|
|
|
19
19
|
@Field
|
|
20
20
|
var progress: Double?
|
|
21
|
+
|
|
22
|
+
@Field
|
|
23
|
+
var elapsedTimer: ElapsedTimer?
|
|
24
|
+
|
|
25
|
+
@Field
|
|
26
|
+
var currentStep: Int?
|
|
27
|
+
|
|
28
|
+
@Field
|
|
29
|
+
var totalSteps: Int?
|
|
30
|
+
|
|
31
|
+
struct ElapsedTimer: Record {
|
|
32
|
+
@Field
|
|
33
|
+
var startDate: Double?
|
|
34
|
+
}
|
|
21
35
|
}
|
|
22
36
|
|
|
23
37
|
@Field
|
|
@@ -25,6 +39,9 @@ public class ExpoLiveActivityModule: Module {
|
|
|
25
39
|
|
|
26
40
|
@Field
|
|
27
41
|
var dynamicIslandImageName: String?
|
|
42
|
+
|
|
43
|
+
@Field
|
|
44
|
+
var smallImageName: String?
|
|
28
45
|
}
|
|
29
46
|
|
|
30
47
|
struct LiveActivityConfig: Record {
|
|
@@ -70,12 +87,30 @@ public class ExpoLiveActivityModule: Module {
|
|
|
70
87
|
@Field
|
|
71
88
|
var imageHeightPercent: Double?
|
|
72
89
|
|
|
90
|
+
@Field
|
|
91
|
+
var smallImageWidth: Int?
|
|
92
|
+
|
|
93
|
+
@Field
|
|
94
|
+
var smallImageHeight: Int?
|
|
95
|
+
|
|
96
|
+
@Field
|
|
97
|
+
var smallImageWidthPercent: Double?
|
|
98
|
+
|
|
99
|
+
@Field
|
|
100
|
+
var smallImageHeightPercent: Double?
|
|
101
|
+
|
|
73
102
|
@Field
|
|
74
103
|
var imageAlign: String?
|
|
75
104
|
|
|
76
105
|
@Field
|
|
77
106
|
var contentFit: String?
|
|
78
107
|
|
|
108
|
+
@Field
|
|
109
|
+
var progressSegmentActiveColor: String?
|
|
110
|
+
|
|
111
|
+
@Field
|
|
112
|
+
var progressSegmentInactiveColor: String?
|
|
113
|
+
|
|
79
114
|
struct PaddingDetails: Record {
|
|
80
115
|
@Field var top: Int?
|
|
81
116
|
@Field var bottom: Int?
|
|
@@ -136,6 +171,10 @@ public class ExpoLiveActivityModule: Module {
|
|
|
136
171
|
if let name = state.dynamicIslandImageName {
|
|
137
172
|
newState.dynamicIslandImageName = try await resolveImage(from: name)
|
|
138
173
|
}
|
|
174
|
+
|
|
175
|
+
if let name = state.smallImageName {
|
|
176
|
+
newState.smallImageName = try await resolveImage(from: name)
|
|
177
|
+
}
|
|
139
178
|
}
|
|
140
179
|
|
|
141
180
|
private func observePushToStartToken() {
|
|
@@ -197,6 +236,11 @@ public class ExpoLiveActivityModule: Module {
|
|
|
197
236
|
?? false
|
|
198
237
|
}
|
|
199
238
|
|
|
239
|
+
private var silentOnUnsupportedOS: Bool {
|
|
240
|
+
Bundle.main.object(forInfoDictionaryKey: "ExpoLiveActivity_SilentOnUnsupportedOS") as? Bool
|
|
241
|
+
?? false
|
|
242
|
+
}
|
|
243
|
+
|
|
200
244
|
public func definition() -> ModuleDefinition {
|
|
201
245
|
Name("ExpoLiveActivity")
|
|
202
246
|
|
|
@@ -214,7 +258,12 @@ public class ExpoLiveActivityModule: Module {
|
|
|
214
258
|
|
|
215
259
|
Function("startActivity") {
|
|
216
260
|
(state: LiveActivityState, maybeConfig: LiveActivityConfig?) -> String in
|
|
217
|
-
guard #available(iOS 16.2, *) else {
|
|
261
|
+
guard #available(iOS 16.2, *) else {
|
|
262
|
+
if silentOnUnsupportedOS {
|
|
263
|
+
return ""
|
|
264
|
+
}
|
|
265
|
+
throw UnsupportedOSException("16.2")
|
|
266
|
+
}
|
|
218
267
|
|
|
219
268
|
guard ActivityAuthorizationInfo().areActivitiesEnabled else {
|
|
220
269
|
throw LiveActivitiesNotEnabledException()
|
|
@@ -248,15 +297,27 @@ public class ExpoLiveActivityModule: Module {
|
|
|
248
297
|
imageHeight: config.imageHeight,
|
|
249
298
|
imageWidthPercent: config.imageWidthPercent,
|
|
250
299
|
imageHeightPercent: config.imageHeightPercent,
|
|
300
|
+
smallImageWidth: config.smallImageWidth,
|
|
301
|
+
smallImageHeight: config.smallImageHeight,
|
|
302
|
+
smallImageWidthPercent: config.smallImageWidthPercent,
|
|
303
|
+
smallImageHeightPercent: config.smallImageHeightPercent,
|
|
251
304
|
imageAlign: config.imageAlign,
|
|
252
|
-
contentFit: config.contentFit
|
|
305
|
+
contentFit: config.contentFit,
|
|
306
|
+
progressSegmentActiveColor: config.progressSegmentActiveColor,
|
|
307
|
+
progressSegmentInactiveColor: config.progressSegmentInactiveColor
|
|
253
308
|
)
|
|
254
309
|
|
|
255
310
|
let initialState = LiveActivityAttributes.ContentState(
|
|
256
311
|
title: state.title,
|
|
257
312
|
subtitle: state.subtitle,
|
|
258
313
|
timerEndDateInMilliseconds: state.progressBar?.date,
|
|
259
|
-
progress: state.progressBar?.progress
|
|
314
|
+
progress: state.progressBar?.progress,
|
|
315
|
+
imageName: state.imageName,
|
|
316
|
+
dynamicIslandImageName: state.dynamicIslandImageName,
|
|
317
|
+
smallImageName: state.smallImageName,
|
|
318
|
+
elapsedTimerStartDateInMilliseconds: state.progressBar?.elapsedTimer?.startDate,
|
|
319
|
+
currentStep: state.progressBar?.currentStep,
|
|
320
|
+
totalSteps: state.progressBar?.totalSteps
|
|
260
321
|
)
|
|
261
322
|
|
|
262
323
|
let activity = try Activity.request(
|
|
@@ -278,7 +339,12 @@ public class ExpoLiveActivityModule: Module {
|
|
|
278
339
|
}
|
|
279
340
|
|
|
280
341
|
Function("stopActivity") { (activityId: String, state: LiveActivityState) in
|
|
281
|
-
guard #available(iOS 16.2, *) else {
|
|
342
|
+
guard #available(iOS 16.2, *) else {
|
|
343
|
+
if silentOnUnsupportedOS {
|
|
344
|
+
return
|
|
345
|
+
}
|
|
346
|
+
throw UnsupportedOSException("16.2")
|
|
347
|
+
}
|
|
282
348
|
|
|
283
349
|
guard
|
|
284
350
|
let activity = Activity<LiveActivityAttributes>.activities.first(where: {
|
|
@@ -292,7 +358,13 @@ public class ExpoLiveActivityModule: Module {
|
|
|
292
358
|
title: state.title,
|
|
293
359
|
subtitle: state.subtitle,
|
|
294
360
|
timerEndDateInMilliseconds: state.progressBar?.date,
|
|
295
|
-
progress: state.progressBar?.progress
|
|
361
|
+
progress: state.progressBar?.progress,
|
|
362
|
+
imageName: state.imageName,
|
|
363
|
+
dynamicIslandImageName: state.dynamicIslandImageName,
|
|
364
|
+
smallImageName: state.smallImageName,
|
|
365
|
+
elapsedTimerStartDateInMilliseconds: state.progressBar?.elapsedTimer?.startDate,
|
|
366
|
+
currentStep: state.progressBar?.currentStep,
|
|
367
|
+
totalSteps: state.progressBar?.totalSteps
|
|
296
368
|
)
|
|
297
369
|
try await updateImages(state: state, newState: &newState)
|
|
298
370
|
await activity.end(
|
|
@@ -304,6 +376,9 @@ public class ExpoLiveActivityModule: Module {
|
|
|
304
376
|
|
|
305
377
|
Function("updateActivity") { (activityId: String, state: LiveActivityState) in
|
|
306
378
|
guard #available(iOS 16.2, *) else {
|
|
379
|
+
if silentOnUnsupportedOS {
|
|
380
|
+
return
|
|
381
|
+
}
|
|
307
382
|
throw UnsupportedOSException("16.2")
|
|
308
383
|
}
|
|
309
384
|
|
|
@@ -319,7 +394,13 @@ public class ExpoLiveActivityModule: Module {
|
|
|
319
394
|
title: state.title,
|
|
320
395
|
subtitle: state.subtitle,
|
|
321
396
|
timerEndDateInMilliseconds: state.progressBar?.date,
|
|
322
|
-
progress: state.progressBar?.progress
|
|
397
|
+
progress: state.progressBar?.progress,
|
|
398
|
+
imageName: state.imageName,
|
|
399
|
+
dynamicIslandImageName: state.dynamicIslandImageName,
|
|
400
|
+
smallImageName: state.smallImageName,
|
|
401
|
+
elapsedTimerStartDateInMilliseconds: state.progressBar?.elapsedTimer?.startDate,
|
|
402
|
+
currentStep: state.progressBar?.currentStep,
|
|
403
|
+
totalSteps: state.progressBar?.totalSteps
|
|
323
404
|
)
|
|
324
405
|
try await updateImages(state: state, newState: &newState)
|
|
325
406
|
await activity.update(ActivityContent(state: newState, staleDate: nil))
|
|
@@ -9,6 +9,10 @@ struct LiveActivityAttributes: ActivityAttributes {
|
|
|
9
9
|
var progress: Double?
|
|
10
10
|
var imageName: String?
|
|
11
11
|
var dynamicIslandImageName: String?
|
|
12
|
+
var smallImageName: String?
|
|
13
|
+
var elapsedTimerStartDateInMilliseconds: Double?
|
|
14
|
+
var currentStep: Int?
|
|
15
|
+
var totalSteps: Int?
|
|
12
16
|
}
|
|
13
17
|
|
|
14
18
|
var name: String
|
|
@@ -26,8 +30,14 @@ struct LiveActivityAttributes: ActivityAttributes {
|
|
|
26
30
|
var imageHeight: Int?
|
|
27
31
|
var imageWidthPercent: Double?
|
|
28
32
|
var imageHeightPercent: Double?
|
|
33
|
+
var smallImageWidth: Int?
|
|
34
|
+
var smallImageHeight: Int?
|
|
35
|
+
var smallImageWidthPercent: Double?
|
|
36
|
+
var smallImageHeightPercent: Double?
|
|
29
37
|
var imageAlign: String?
|
|
30
38
|
var contentFit: String?
|
|
39
|
+
var progressSegmentActiveColor: String?
|
|
40
|
+
var progressSegmentInactiveColor: String?
|
|
31
41
|
|
|
32
42
|
enum DynamicIslandTimerType: String, Codable {
|
|
33
43
|
case circular
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import SwiftUI
|
|
2
|
+
import WidgetKit
|
|
3
|
+
|
|
4
|
+
extension LiveActivityAttributes.ContentState {
|
|
5
|
+
var hasSegmentedProgress: Bool {
|
|
6
|
+
currentStep != nil && (totalSteps ?? 0) > 0
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
func logSegmentedProgressWarningIfNeeded() {
|
|
10
|
+
#if DEBUG
|
|
11
|
+
if hasSegmentedProgress,
|
|
12
|
+
elapsedTimerStartDateInMilliseconds != nil
|
|
13
|
+
|| timerEndDateInMilliseconds != nil
|
|
14
|
+
|| progress != nil
|
|
15
|
+
{
|
|
16
|
+
DebugLog("⚠️[ExpoLiveActivity] Both segmented and regular progress provided; showing segmented")
|
|
17
|
+
}
|
|
18
|
+
#endif
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
extension LiveActivityAttributes {
|
|
23
|
+
var segmentActiveColor: Color {
|
|
24
|
+
progressSegmentActiveColor.map { Color(hex: $0) } ?? Color.blue
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
var segmentInactiveColor: Color {
|
|
28
|
+
progressSegmentInactiveColor.map { Color(hex: $0) } ?? Color.gray.opacity(0.3)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
func resolvedPadding(defaultPadding: Int) -> EdgeInsets {
|
|
32
|
+
let top = CGFloat(
|
|
33
|
+
paddingDetails?.top
|
|
34
|
+
?? paddingDetails?.vertical
|
|
35
|
+
?? padding
|
|
36
|
+
?? defaultPadding
|
|
37
|
+
)
|
|
38
|
+
let bottom = CGFloat(
|
|
39
|
+
paddingDetails?.bottom
|
|
40
|
+
?? paddingDetails?.vertical
|
|
41
|
+
?? padding
|
|
42
|
+
?? defaultPadding
|
|
43
|
+
)
|
|
44
|
+
let leading = CGFloat(
|
|
45
|
+
paddingDetails?.left
|
|
46
|
+
?? paddingDetails?.horizontal
|
|
47
|
+
?? padding
|
|
48
|
+
?? defaultPadding
|
|
49
|
+
)
|
|
50
|
+
let trailing = CGFloat(
|
|
51
|
+
paddingDetails?.right
|
|
52
|
+
?? paddingDetails?.horizontal
|
|
53
|
+
?? padding
|
|
54
|
+
?? defaultPadding
|
|
55
|
+
)
|
|
56
|
+
return EdgeInsets(top: top, leading: leading, bottom: bottom, trailing: trailing)
|
|
57
|
+
}
|
|
58
|
+
}
|