expo-live-activity 0.4.2 → 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/Package.swift ADDED
@@ -0,0 +1,16 @@
1
+ // swift-tools-version: 5.7
2
+
3
+ import PackageDescription
4
+
5
+ let package = Package(
6
+ name: "ExpoLiveActivity",
7
+ platforms: [.iOS(.v16)],
8
+ products: [.library(name: "ExpoLiveActivity", targets: ["ExpoLiveActivity"])],
9
+ targets: [
10
+ .target(
11
+ name: "ExpoLiveActivity",
12
+ path: "ios-files",
13
+ exclude: ["LiveActivityWidgetBundle.swift"]
14
+ ),
15
+ ]
16
+ )
package/README.md CHANGED
@@ -44,7 +44,7 @@ The module comes with a built-in config plugin that creates a target in iOS with
44
44
  }
45
45
  }
46
46
  ```
47
- If you want to update Live Acitivity with push notifications you can add option `"enablePushNotifications": true`:
47
+ If you want to update Live Activity with push notifications you can add option `"enablePushNotifications": true`:
48
48
  ```json
49
49
  {
50
50
  "expo": {
@@ -132,9 +132,12 @@ The `state` object should include:
132
132
  {
133
133
  title: string;
134
134
  subtitle?: string;
135
- progressBar: { // Only one property, either date or progress, is available at a time as they share a single progress bar component
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: number|`${number}%`, height: number|`${number}%` } | undefined (defaults to 64pt)
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 counter displayed in Live Activity view.
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: ImageDimension;
39
- height: ImageDimension;
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;
@@ -59,7 +84,7 @@ export type ActivityTokenReceivedEvent = {
59
84
  activityPushToken: string;
60
85
  };
61
86
  export type ActivityPushToStartTokenReceivedEvent = {
62
- activityPushToStartToken: string;
87
+ activityPushToStartToken: string | null;
63
88
  };
64
89
  type ActivityState = 'active' | 'dismissed' | 'pending' | 'stale' | 'ended';
65
90
  export type ActivityUpdateEvent = {
@@ -88,8 +113,19 @@ export declare function stopActivity(id: string, state: LiveActivityState): any;
88
113
  * @param {LiveActivityState} state The updated state for the live activity.
89
114
  */
90
115
  export declare function updateActivity(id: string, state: LiveActivityState): any;
91
- export declare function addActivityTokenListener(listener: (event: ActivityTokenReceivedEvent) => void): Voidable<EventSubscription>;
116
+ /**
117
+ * @param {function} updateTokenListener The listener function that will be called when an update token is received.
118
+ */
119
+ export declare function addActivityTokenListener(updateTokenListener: (event: ActivityTokenReceivedEvent) => void): Voidable<EventSubscription>;
120
+ /**
121
+ * Adds a listener that is called when a push-to-start token is received. Supported only on iOS > 17.2.
122
+ * On earlier iOS versions, the listener will return null as a token.
123
+ * @param {function} listener The listener function that will be called when the observer starts and then when a push-to-start token is received.
124
+ */
92
125
  export declare function addActivityPushToStartTokenListener(listener: (event: ActivityPushToStartTokenReceivedEvent) => void): Voidable<EventSubscription>;
93
- export declare function addActivityUpdatesListener(listener: (event: ActivityUpdateEvent) => void): Voidable<EventSubscription>;
126
+ /**
127
+ * @param {function} statusListener The listener function that will be called when an activity status changes.
128
+ */
129
+ export declare function addActivityUpdatesListener(statusListener: (event: ActivityUpdateEvent) => void): Voidable<EventSubscription>;
94
130
  export {};
95
131
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
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 = { ...base };
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
- // Normalize imageSize: object with width/height each a number (points) or percent string like '50%'
22
- if (imageSize) {
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 { width, height } = imageSize;
25
- if (typeof width === 'number') {
26
- normalized.imageWidth = width;
27
- }
28
- else if (typeof width === 'string') {
29
- const match = width.trim().match(regExp);
30
- if (match) {
31
- normalized.imageWidthPercent = Number(match[1]);
32
- }
33
- else {
34
- throw new Error('imageSize.width percent string must be in format "0%" to "100%"');
35
- }
36
- }
37
- if (typeof height === 'number') {
38
- normalized.imageHeight = height;
39
- }
40
- else if (typeof height === 'string') {
41
- const match = height.trim().match(regExp);
42
- if (match) {
43
- normalized.imageHeightPercent = Number(match[1]);
44
- }
45
- else {
46
- throw new Error('imageSize.height percent string must be in format "0%" to "100%"');
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
  }
@@ -74,16 +87,27 @@ export function updateActivity(id, state) {
74
87
  if (assertIOS('updateActivity'))
75
88
  return ExpoLiveActivityModule.updateActivity(id, state);
76
89
  }
77
- export function addActivityTokenListener(listener) {
90
+ /**
91
+ * @param {function} updateTokenListener The listener function that will be called when an update token is received.
92
+ */
93
+ export function addActivityTokenListener(updateTokenListener) {
78
94
  if (assertIOS('addActivityTokenListener'))
79
- return ExpoLiveActivityModule.addListener('onTokenReceived', listener);
95
+ return ExpoLiveActivityModule.addListener('onTokenReceived', updateTokenListener);
80
96
  }
97
+ /**
98
+ * Adds a listener that is called when a push-to-start token is received. Supported only on iOS > 17.2.
99
+ * On earlier iOS versions, the listener will return null as a token.
100
+ * @param {function} listener The listener function that will be called when the observer starts and then when a push-to-start token is received.
101
+ */
81
102
  export function addActivityPushToStartTokenListener(listener) {
82
103
  if (assertIOS('addActivityPushToStartTokenListener'))
83
104
  return ExpoLiveActivityModule.addListener('onPushToStartTokenReceived', listener);
84
105
  }
85
- export function addActivityUpdatesListener(listener) {
106
+ /**
107
+ * @param {function} statusListener The listener function that will be called when an activity status changes.
108
+ */
109
+ export function addActivityUpdatesListener(statusListener) {
86
110
  if (assertIOS('addActivityUpdatesListener'))
87
- return ExpoLiveActivityModule.addListener('onStateChange', listener);
111
+ return ExpoLiveActivityModule.addListener('onStateChange', statusListener);
88
112
  }
89
113
  //# sourceMappingURL=index.js.map
@@ -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\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\nexport function addActivityTokenListener(\n listener: (event: ActivityTokenReceivedEvent) => void\n): Voidable<EventSubscription> {\n if (assertIOS('addActivityTokenListener')) return ExpoLiveActivityModule.addListener('onTokenReceived', listener)\n}\n\nexport function addActivityPushToStartTokenListener(\n listener: (event: ActivityPushToStartTokenReceivedEvent) => void\n): Voidable<EventSubscription> {\n if (assertIOS('addActivityPushToStartTokenListener'))\n return ExpoLiveActivityModule.addListener('onPushToStartTokenReceived', listener)\n}\n\nexport function addActivityUpdatesListener(\n listener: (event: ActivityUpdateEvent) => void\n): Voidable<EventSubscription> {\n if (assertIOS('addActivityUpdatesListener')) return ExpoLiveActivityModule.addListener('onStateChange', listener)\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,11 +171,19 @@ 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() {
142
181
  guard #available(iOS 17.2, *), ActivityAuthorizationInfo().areActivitiesEnabled else { return }
143
182
 
183
+ if let initialToken = (Activity<LiveActivityAttributes>.pushToStartToken?.reduce("") { $0 + String(format: "%02x", $1) }) {
184
+ sendPushToStartToken(activityPushToStartToken: initialToken)
185
+ }
186
+
144
187
  print("Observing push to start token updates...")
145
188
  Task {
146
189
  for await data in Activity<LiveActivityAttributes>.pushToStartTokenUpdates {
@@ -193,21 +236,34 @@ public class ExpoLiveActivityModule: Module {
193
236
  ?? false
194
237
  }
195
238
 
239
+ private var silentOnUnsupportedOS: Bool {
240
+ Bundle.main.object(forInfoDictionaryKey: "ExpoLiveActivity_SilentOnUnsupportedOS") as? Bool
241
+ ?? false
242
+ }
243
+
196
244
  public func definition() -> ModuleDefinition {
197
245
  Name("ExpoLiveActivity")
198
246
 
199
- OnCreate {
247
+ OnStartObserving("onTokenReceived") {
248
+ observeLiveActivityUpdates()
249
+ }
250
+
251
+ OnStartObserving("onPushToStartTokenReceived") {
200
252
  if pushNotificationsEnabled {
201
253
  observePushToStartToken()
202
254
  }
203
- observeLiveActivityUpdates()
204
255
  }
205
256
 
206
257
  Events("onTokenReceived", "onPushToStartTokenReceived", "onStateChange")
207
258
 
208
259
  Function("startActivity") {
209
260
  (state: LiveActivityState, maybeConfig: LiveActivityConfig?) -> String in
210
- guard #available(iOS 16.2, *) else { throw UnsupportedOSException("16.2") }
261
+ guard #available(iOS 16.2, *) else {
262
+ if silentOnUnsupportedOS {
263
+ return ""
264
+ }
265
+ throw UnsupportedOSException("16.2")
266
+ }
211
267
 
212
268
  guard ActivityAuthorizationInfo().areActivitiesEnabled else {
213
269
  throw LiveActivitiesNotEnabledException()
@@ -241,15 +297,27 @@ public class ExpoLiveActivityModule: Module {
241
297
  imageHeight: config.imageHeight,
242
298
  imageWidthPercent: config.imageWidthPercent,
243
299
  imageHeightPercent: config.imageHeightPercent,
300
+ smallImageWidth: config.smallImageWidth,
301
+ smallImageHeight: config.smallImageHeight,
302
+ smallImageWidthPercent: config.smallImageWidthPercent,
303
+ smallImageHeightPercent: config.smallImageHeightPercent,
244
304
  imageAlign: config.imageAlign,
245
- contentFit: config.contentFit
305
+ contentFit: config.contentFit,
306
+ progressSegmentActiveColor: config.progressSegmentActiveColor,
307
+ progressSegmentInactiveColor: config.progressSegmentInactiveColor
246
308
  )
247
309
 
248
310
  let initialState = LiveActivityAttributes.ContentState(
249
311
  title: state.title,
250
312
  subtitle: state.subtitle,
251
313
  timerEndDateInMilliseconds: state.progressBar?.date,
252
- 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
253
321
  )
254
322
 
255
323
  let activity = try Activity.request(
@@ -271,7 +339,12 @@ public class ExpoLiveActivityModule: Module {
271
339
  }
272
340
 
273
341
  Function("stopActivity") { (activityId: String, state: LiveActivityState) in
274
- guard #available(iOS 16.2, *) else { throw UnsupportedOSException("16.2") }
342
+ guard #available(iOS 16.2, *) else {
343
+ if silentOnUnsupportedOS {
344
+ return
345
+ }
346
+ throw UnsupportedOSException("16.2")
347
+ }
275
348
 
276
349
  guard
277
350
  let activity = Activity<LiveActivityAttributes>.activities.first(where: {
@@ -285,7 +358,13 @@ public class ExpoLiveActivityModule: Module {
285
358
  title: state.title,
286
359
  subtitle: state.subtitle,
287
360
  timerEndDateInMilliseconds: state.progressBar?.date,
288
- 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
289
368
  )
290
369
  try await updateImages(state: state, newState: &newState)
291
370
  await activity.end(
@@ -297,6 +376,9 @@ public class ExpoLiveActivityModule: Module {
297
376
 
298
377
  Function("updateActivity") { (activityId: String, state: LiveActivityState) in
299
378
  guard #available(iOS 16.2, *) else {
379
+ if silentOnUnsupportedOS {
380
+ return
381
+ }
300
382
  throw UnsupportedOSException("16.2")
301
383
  }
302
384
 
@@ -312,7 +394,13 @@ public class ExpoLiveActivityModule: Module {
312
394
  title: state.title,
313
395
  subtitle: state.subtitle,
314
396
  timerEndDateInMilliseconds: state.progressBar?.date,
315
- 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
316
404
  )
317
405
  try await updateImages(state: state, newState: &newState)
318
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