react-native-media-notification 0.3.4 → 0.3.5

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 CHANGED
@@ -149,6 +149,8 @@ MediaControls.addEventListener('duck', () => {}); // reduce volume for interrupt
149
149
  MediaControls.addEventListener('unduck', () => {}); // restore volume after interruption
150
150
  ```
151
151
 
152
+ **Note**: On iOS, seekForward and seekBackward are not fired, even when technically triggered. Instead, a seek event with the corresponding timestamp is fired.
153
+
152
154
  ### Stop Media Notification
153
155
 
154
156
  ```typescript
@@ -58,11 +58,11 @@ class MediaControlsModule(reactContext: ReactApplicationContext) :
58
58
  ensureServiceStarted()
59
59
 
60
60
  val trackMetadata = MediaTrackMetadata(
61
- title = metadata.getString("title") ?: "",
62
- artist = metadata.getString("artist") ?: "",
63
- album = metadata.getString("album"),
61
+ title = if (metadata.hasKey("title")) metadata.getString("title") else null,
62
+ artist = if (metadata.hasKey("artist")) metadata.getString("artist") else null,
63
+ album = if (metadata.hasKey("album")) metadata.getString("album") else null,
64
64
  duration = if (metadata.hasKey("duration")) metadata.getDouble("duration") else null,
65
- artwork = metadata.getString("artwork"),
65
+ artwork = if (metadata.hasKey("artwork")) metadata.getString("artwork") else null,
66
66
  position = if (metadata.hasKey("position")) metadata.getDouble("position") else null,
67
67
  isPlaying = if (metadata.hasKey("isPlaying")) metadata.getBoolean("isPlaying") else null,
68
68
  shuffleMode = if (metadata.hasKey("shuffle")) metadata.getBoolean("shuffle") else null,
@@ -143,19 +143,19 @@ class MediaControlsPlayer(
143
143
  audioFocusListener.requestAudioFocus()
144
144
  }
145
145
 
146
- this.currentMetadata = metadata
146
+ this.currentMetadata = this.currentMetadata?.merge(metadata) ?: metadata
147
147
 
148
148
  val mediaMetadata = MediaMetadata.Builder()
149
- .setTitle(metadata.title)
150
- .setArtist(metadata.artist)
151
- .setAlbumTitle(metadata.album)
152
- .setDurationMs(metadata.duration?.times(1000)?.toLong())
149
+ .setTitle(this.currentMetadata!!.title)
150
+ .setArtist(this.currentMetadata!!.artist)
151
+ .setAlbumTitle(this.currentMetadata!!.album)
152
+ .setDurationMs(this.currentMetadata!!.duration?.times(1000)?.toLong())
153
153
  .setIsPlayable(true)
154
154
  .setIsBrowsable(false)
155
155
  .setMediaType(MediaMetadata.MEDIA_TYPE_MUSIC)
156
- .apply {
157
- metadata.artwork?.let { artworkUrl ->
158
- setArtworkUri(artworkUrl.toUri())
156
+ .also {
157
+ this.currentMetadata!!.artwork?.let { artworkUrl ->
158
+ it.setArtworkUri(artworkUrl.toUri())
159
159
  }
160
160
  }
161
161
  .build()
@@ -171,23 +171,23 @@ class MediaControlsPlayer(
171
171
 
172
172
  val mediaItemData = MediaItemData.Builder(mediaId)
173
173
  .setMediaItem(mediaItem)
174
- .setDefaultPositionUs(metadata.position?.times(1_000_000)?.toLong() ?: 0)
175
- .setDurationUs(metadata.duration?.times(1_000_000)?.toLong() ?: androidx.media3.common.C.TIME_UNSET)
174
+ .setDefaultPositionUs(this.currentMetadata!!.position?.times(1_000_000)?.toLong() ?: 0)
175
+ .setDurationUs(this.currentMetadata!!.duration?.times(1_000_000)?.toLong() ?: androidx.media3.common.C.TIME_UNSET)
176
176
  .setIsSeekable(true)
177
177
  .build()
178
178
 
179
179
  updateState { builder ->
180
180
  builder.setPlaylist(listOf(mediaItemData))
181
181
  .setCurrentMediaItemIndex(0)
182
- .setContentPositionMs(metadata.position?.times(1000)?.toLong() ?: 0)
182
+ .setContentPositionMs(this.currentMetadata!!.position?.times(1000)?.toLong() ?: 0)
183
183
  .setPlayWhenReady(
184
- metadata.isPlaying ?: false,
184
+ this.currentMetadata!!.isPlaying ?: false,
185
185
  Player.PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST
186
186
  )
187
187
  .setPlaybackState(Player.STATE_READY)
188
188
  .setAvailableCommands(state.availableCommands)
189
- .setRepeatMode(metadata.repeatMode)
190
- .setShuffleModeEnabled(metadata.shuffleMode)
189
+ .setRepeatMode(this.currentMetadata!!.repeatMode)
190
+ .setShuffleModeEnabled(this.currentMetadata!!.shuffleMode)
191
191
  }
192
192
  }
193
193
 
@@ -313,8 +313,8 @@ class MediaControlsPlayer(
313
313
 
314
314
  // Data class for metadata
315
315
  data class MediaTrackMetadata(
316
- val title: String,
317
- val artist: String,
316
+ val title: String? = null,
317
+ val artist: String? = null,
318
318
  val album: String? = null,
319
319
  val duration: Double? = null,
320
320
  val artwork: String? = null,
@@ -322,4 +322,17 @@ data class MediaTrackMetadata(
322
322
  val isPlaying: Boolean? = null,
323
323
  val repeatMode: String? = null,
324
324
  val shuffleMode: Boolean? = null
325
- )
325
+ ) {
326
+
327
+ fun merge(other: MediaTrackMetadata): MediaTrackMetadata = MediaTrackMetadata(
328
+ title = other.title ?: this.title,
329
+ artist = other.artist ?: this.artist,
330
+ album = other.album ?: this.album,
331
+ duration = other.duration ?: this.duration,
332
+ artwork = other.artwork ?: this.artwork,
333
+ position = other.position ?: this.position,
334
+ isPlaying = other.isPlaying ?: this.isPlaying,
335
+ repeatMode = other.repeatMode ?: this.repeatMode,
336
+ shuffleMode = other.shuffleMode ?: this.shuffleMode
337
+ )
338
+ }
@@ -87,17 +87,17 @@ RCT_EXPORT_METHOD(updateMetadata:(JS::NativeMediaControls::NativeMediaTrackMetad
87
87
  MPNowPlayingInfoCenter *_nowPlayingCenter = [MPNowPlayingInfoCenter defaultCenter];
88
88
 
89
89
  @try {
90
- NSMutableDictionary *nowPlayingInfo = [NSMutableDictionary dictionary];
90
+ NSMutableDictionary *nowPlayingInfo = [[NSMutableDictionary alloc] initWithDictionary: _nowPlayingCenter.nowPlayingInfo];
91
91
 
92
- if (metadata.title().length > 0) {
92
+ if (metadata.title() != nil && metadata.title().length > 0) {
93
93
  nowPlayingInfo[MPMediaItemPropertyTitle] = metadata.title();
94
94
  }
95
95
 
96
- if (metadata.artist().length > 0) {
96
+ if (metadata.artist() != nil && metadata.artist().length > 0) {
97
97
  nowPlayingInfo[MPMediaItemPropertyArtist] = metadata.artist();
98
98
  }
99
99
 
100
- if (metadata.album().length > 0) {
100
+ if (metadata.album() != nil && metadata.album().length > 0) {
101
101
  nowPlayingInfo[MPMediaItemPropertyAlbumTitle] = metadata.album();
102
102
  }
103
103
 
@@ -110,17 +110,20 @@ RCT_EXPORT_METHOD(updateMetadata:(JS::NativeMediaControls::NativeMediaTrackMetad
110
110
  double position = metadata.position().value();
111
111
  nowPlayingInfo[MPNowPlayingInfoPropertyElapsedPlaybackTime] = [NSNumber numberWithDouble:position];
112
112
  }
113
-
114
- nowPlayingInfo[MPNowPlayingInfoPropertyPlaybackRate] = metadata.isPlaying() ? [NSNumber numberWithDouble:1] : [NSNumber numberWithDouble:0];
113
+
114
+ if (metadata.isPlaying().has_value()) {
115
+ nowPlayingInfo[MPNowPlayingInfoPropertyPlaybackRate] = metadata.isPlaying().value() ? [NSNumber numberWithDouble:1] : [NSNumber numberWithDouble:0];
116
+ }
117
+
115
118
 
116
119
  _nowPlayingCenter.nowPlayingInfo = nowPlayingInfo;
117
120
 
118
- if (@available(iOS 11.0, *)) {
121
+ if (@available(iOS 11.0, *) && metadata.isPlaying().has_value()) {
119
122
  if (!self.audioInterrupted) {
120
123
  self.explictlyPaused = false;
121
124
  }
122
125
 
123
- if (metadata.isPlaying()) {
126
+ if (metadata.isPlaying().value()) {
124
127
  _nowPlayingCenter.playbackState = MPNowPlayingPlaybackStatePlaying;
125
128
  } else {
126
129
  _nowPlayingCenter.playbackState = MPNowPlayingPlaybackStatePaused;
@@ -133,7 +136,7 @@ RCT_EXPORT_METHOD(updateMetadata:(JS::NativeMediaControls::NativeMediaTrackMetad
133
136
 
134
137
 
135
138
  // Load artwork if provided
136
- if (metadata.artwork().length > 0) {
139
+ if (metadata.artwork() != nil && metadata.artwork().length > 0) {
137
140
  NSString *artworkURL = metadata.artwork();
138
141
  [self loadArtworkFromURL:artworkURL completion:^(UIImage *image) {
139
142
  if (!image) {
@@ -4,8 +4,8 @@ export declare const ALL_MEDIA_EVENTS: readonly ["play", "pause", "stop", "skipT
4
4
  export type MediaControl = (typeof ALL_MEDIA_EVENTS)[number];
5
5
  export type MediaControlEvent = MediaControl | 'duck' | 'unDuck';
6
6
  export interface NativeMediaTrackMetadata {
7
- title: string;
8
- artist: string;
7
+ title?: string;
8
+ artist?: string;
9
9
  album?: string;
10
10
  duration?: number;
11
11
  artwork?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"NativeMediaControls.d.ts","sourceRoot":"","sources":["../../../src/NativeMediaControls.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAEhD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2CAA2C,CAAC;AAG9E,eAAO,MAAM,gBAAgB,oIAWnB,CAAC;AACX,MAAM,MAAM,YAAY,GAAG,CAAC,OAAO,gBAAgB,CAAC,CAAC,MAAM,CAAC,CAAC;AAE7D,MAAM,MAAM,iBAAiB,GAAG,YAAY,GAAG,MAAM,GAAG,QAAQ,CAAC;AAEjE,MAAM,WAAW,wBAAwB;IACvC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,UAAU,CAAC,EAAE,KAAK,GAAG,KAAK,GAAG,KAAK,CAAC;IACnC,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,IAAK,SAAQ,WAAW;IACvC,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,IAAI,CAAC;IACxD,cAAc,CAAC,QAAQ,EAAE,wBAAwB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAClE,qBAAqB,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAGvC,uBAAuB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAGzD,oBAAoB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAAC;IAE7C,QAAQ,IAAI,IAAI,CAAC;IAGjB,QAAQ,CAAC,OAAO,EAAE,YAAY,CAAC,WAAW,CAAC,CAAC;CAC7C;;AAED,wBAAuE"}
1
+ {"version":3,"file":"NativeMediaControls.d.ts","sourceRoot":"","sources":["../../../src/NativeMediaControls.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAEhD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2CAA2C,CAAC;AAG9E,eAAO,MAAM,gBAAgB,oIAWnB,CAAC;AACX,MAAM,MAAM,YAAY,GAAG,CAAC,OAAO,gBAAgB,CAAC,CAAC,MAAM,CAAC,CAAC;AAE7D,MAAM,MAAM,iBAAiB,GAAG,YAAY,GAAG,MAAM,GAAG,QAAQ,CAAC;AAEjE,MAAM,WAAW,wBAAwB;IACvC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,UAAU,CAAC,EAAE,KAAK,GAAG,KAAK,GAAG,KAAK,CAAC;IACnC,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,IAAK,SAAQ,WAAW;IACvC,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,IAAI,CAAC;IACxD,cAAc,CAAC,QAAQ,EAAE,wBAAwB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAClE,qBAAqB,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAGvC,uBAAuB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAGzD,oBAAoB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAAC;IAE7C,QAAQ,IAAI,IAAI,CAAC;IAGjB,QAAQ,CAAC,OAAO,EAAE,YAAY,CAAC,WAAW,CAAC,CAAC;CAC7C;;AAED,wBAAuE"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-media-notification",
3
- "version": "0.3.4",
3
+ "version": "0.3.5",
4
4
  "description": "Display and manage media style notifications based on react-native-music-control",
5
5
  "main": "./lib/module/index.js",
6
6
  "types": "./lib/typescript/src/index.d.ts",
@@ -1,56 +1,56 @@
1
- import type { TurboModule } from 'react-native';
2
- import { TurboModuleRegistry } from 'react-native';
3
- import type { EventEmitter } from 'react-native/Libraries/Types/CodegenTypes';
4
-
5
- // Event types
6
- export const ALL_MEDIA_EVENTS = [
7
- 'play',
8
- 'pause',
9
- 'stop',
10
- 'skipToNext',
11
- 'skipToPrevious',
12
- 'seekForward',
13
- 'seekBackward',
14
- 'seek',
15
- 'shuffle',
16
- 'repeatMode',
17
- ] as const;
18
- export type MediaControl = (typeof ALL_MEDIA_EVENTS)[number];
19
-
20
- export type MediaControlEvent = MediaControl | 'duck' | 'unDuck';
21
-
22
- export interface NativeMediaTrackMetadata {
23
- title: string;
24
- artist: string;
25
- album?: string;
26
- duration?: number;
27
- artwork?: string;
28
- position?: number;
29
- isPlaying?: boolean;
30
- repeatMode?: 'off' | 'one' | 'all';
31
- shuffle?: boolean;
32
- }
33
-
34
- export interface NativeEvent {
35
- command: string;
36
- seekPosition?: number; // Position in seconds for seek events
37
- }
38
-
39
- export interface Spec extends TurboModule {
40
- setControlEnabled(name: string, enabled: boolean): void;
41
- updateMetadata(metadata: NativeMediaTrackMetadata): Promise<void>;
42
- stopMediaNotification(): Promise<void>;
43
-
44
- // Audio interruption handling
45
- enableAudioInterruption(enabled: boolean): Promise<void>;
46
-
47
- // Audio session activation
48
- enableBackgroundMode(enabled: boolean): void;
49
-
50
- shutdown(): void;
51
-
52
- // Event listeners (native events will be emitted)
53
- readonly onEvent: EventEmitter<NativeEvent>;
54
- }
55
-
56
- export default TurboModuleRegistry.getEnforcing<Spec>('MediaControls');
1
+ import type { TurboModule } from 'react-native';
2
+ import { TurboModuleRegistry } from 'react-native';
3
+ import type { EventEmitter } from 'react-native/Libraries/Types/CodegenTypes';
4
+
5
+ // Event types
6
+ export const ALL_MEDIA_EVENTS = [
7
+ 'play',
8
+ 'pause',
9
+ 'stop',
10
+ 'skipToNext',
11
+ 'skipToPrevious',
12
+ 'seekForward',
13
+ 'seekBackward',
14
+ 'seek',
15
+ 'shuffle',
16
+ 'repeatMode',
17
+ ] as const;
18
+ export type MediaControl = (typeof ALL_MEDIA_EVENTS)[number];
19
+
20
+ export type MediaControlEvent = MediaControl | 'duck' | 'unDuck';
21
+
22
+ export interface NativeMediaTrackMetadata {
23
+ title?: string;
24
+ artist?: string;
25
+ album?: string;
26
+ duration?: number;
27
+ artwork?: string;
28
+ position?: number;
29
+ isPlaying?: boolean;
30
+ repeatMode?: 'off' | 'one' | 'all';
31
+ shuffle?: boolean;
32
+ }
33
+
34
+ export interface NativeEvent {
35
+ command: string;
36
+ seekPosition?: number; // Position in seconds for seek events
37
+ }
38
+
39
+ export interface Spec extends TurboModule {
40
+ setControlEnabled(name: string, enabled: boolean): void;
41
+ updateMetadata(metadata: NativeMediaTrackMetadata): Promise<void>;
42
+ stopMediaNotification(): Promise<void>;
43
+
44
+ // Audio interruption handling
45
+ enableAudioInterruption(enabled: boolean): Promise<void>;
46
+
47
+ // Audio session activation
48
+ enableBackgroundMode(enabled: boolean): void;
49
+
50
+ shutdown(): void;
51
+
52
+ // Event listeners (native events will be emitted)
53
+ readonly onEvent: EventEmitter<NativeEvent>;
54
+ }
55
+
56
+ export default TurboModuleRegistry.getEnforcing<Spec>('MediaControls');