react-native-android-media-fetcher 1.0.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.
Files changed (54) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +398 -0
  3. package/android/build.gradle +111 -0
  4. package/android/gradle.properties +3 -0
  5. package/android/src/main/AndroidManifest.xml +3 -0
  6. package/android/src/main/AndroidManifestNew.xml +2 -0
  7. package/android/src/main/java/com/androidmediafetcher/AndroidMediaFetcherPackage.kt +21 -0
  8. package/android/src/main/java/com/androidmediafetcher/models/AudioFile.kt +57 -0
  9. package/android/src/main/java/com/androidmediafetcher/providers/AudioMediaProvider.kt +361 -0
  10. package/android/src/main/java/com/androidmediafetcher/providers/BaseMediaProvider.kt +43 -0
  11. package/android/src/newarch/java/com/androidmediafetcher/AndroidMediaFetcherModule.kt +173 -0
  12. package/android/src/newarch/java/com/androidmediafetcher/NativeAndroidMediaFetcherSpec.kt +21 -0
  13. package/android/src/oldarch/java/com/androidmediafetcher/AndroidMediaFetcherModule.kt +170 -0
  14. package/app.plugin.js +1 -0
  15. package/lib/commonjs/AudioFetcher.js +153 -0
  16. package/lib/commonjs/AudioFetcher.js.map +1 -0
  17. package/lib/commonjs/NativeModule.js +37 -0
  18. package/lib/commonjs/NativeModule.js.map +1 -0
  19. package/lib/commonjs/events.js +73 -0
  20. package/lib/commonjs/events.js.map +1 -0
  21. package/lib/commonjs/index.js +32 -0
  22. package/lib/commonjs/index.js.map +1 -0
  23. package/lib/commonjs/types.js +2 -0
  24. package/lib/commonjs/types.js.map +1 -0
  25. package/lib/module/AudioFetcher.js +147 -0
  26. package/lib/module/AudioFetcher.js.map +1 -0
  27. package/lib/module/NativeModule.js +31 -0
  28. package/lib/module/NativeModule.js.map +1 -0
  29. package/lib/module/events.js +65 -0
  30. package/lib/module/events.js.map +1 -0
  31. package/lib/module/index.js +17 -0
  32. package/lib/module/index.js.map +1 -0
  33. package/lib/module/types.js +2 -0
  34. package/lib/module/types.js.map +1 -0
  35. package/lib/typescript/src/AudioFetcher.d.ts +93 -0
  36. package/lib/typescript/src/AudioFetcher.d.ts.map +1 -0
  37. package/lib/typescript/src/NativeModule.d.ts +21 -0
  38. package/lib/typescript/src/NativeModule.d.ts.map +1 -0
  39. package/lib/typescript/src/events.d.ts +38 -0
  40. package/lib/typescript/src/events.d.ts.map +1 -0
  41. package/lib/typescript/src/index.d.ts +12 -0
  42. package/lib/typescript/src/index.d.ts.map +1 -0
  43. package/lib/typescript/src/types.d.ts +135 -0
  44. package/lib/typescript/src/types.d.ts.map +1 -0
  45. package/package.json +101 -0
  46. package/plugin/build/index.js +48 -0
  47. package/plugin/src/index.ts +63 -0
  48. package/plugin/tsconfig.json +34 -0
  49. package/react-native.config.js +12 -0
  50. package/src/AudioFetcher.ts +163 -0
  51. package/src/NativeModule.ts +44 -0
  52. package/src/events.ts +79 -0
  53. package/src/index.ts +29 -0
  54. package/src/types.ts +171 -0
@@ -0,0 +1,170 @@
1
+ package com.androidmediafetcher
2
+
3
+ import com.facebook.react.bridge.ReactApplicationContext
4
+ import com.facebook.react.bridge.ReactContextBaseJavaModule
5
+ import com.facebook.react.bridge.ReactMethod
6
+ import com.facebook.react.bridge.Promise
7
+ import com.facebook.react.modules.core.DeviceEventManagerModule
8
+ import com.androidmediafetcher.providers.AudioMediaProvider
9
+ import kotlinx.coroutines.CoroutineScope
10
+ import kotlinx.coroutines.Dispatchers
11
+ import kotlinx.coroutines.SupervisorJob
12
+ import kotlinx.coroutines.launch
13
+ import kotlinx.coroutines.cancel
14
+ import org.json.JSONArray
15
+ import org.json.JSONObject
16
+
17
+ /**
18
+ * React Native Native Module for fetching audio files from Android MediaStore
19
+ * This version supports the old architecture (Bridge)
20
+ */
21
+ class AndroidMediaFetcherModule(reactContext: ReactApplicationContext) :
22
+ ReactContextBaseJavaModule(reactContext) {
23
+
24
+ private val scope = CoroutineScope(SupervisorJob() + Dispatchers.Main)
25
+ private val audioProvider by lazy { AudioMediaProvider(reactContext) }
26
+
27
+ override fun getName(): String = NAME
28
+
29
+ /**
30
+ * Send event to JavaScript
31
+ */
32
+ private fun sendEvent(eventName: String, params: Any?) {
33
+ reactApplicationContext
34
+ .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
35
+ .emit(eventName, params)
36
+ }
37
+
38
+ /**
39
+ * Get paginated audio files
40
+ */
41
+ @ReactMethod
42
+ fun getAudioFiles(pageSize: Int, offset: Int, promise: Promise) {
43
+ scope.launch {
44
+ try {
45
+ val totalCount = audioProvider.getTotalCount()
46
+ val files = audioProvider.getItems(pageSize, offset)
47
+
48
+ val filesArray = JSONArray()
49
+ files.forEach { file ->
50
+ filesArray.put(file.toJson())
51
+ }
52
+
53
+ val result = JSONObject().apply {
54
+ put("files", filesArray)
55
+ put("totalCount", totalCount)
56
+ put("hasMore", offset + files.size < totalCount)
57
+ put("nextOffset", offset + files.size)
58
+ }
59
+
60
+ promise.resolve(result.toString())
61
+ } catch (e: Exception) {
62
+ sendErrorEvent(e.message ?: "Unknown error", "FETCH_ERROR")
63
+ promise.reject("FETCH_ERROR", e.message, e)
64
+ }
65
+ }
66
+ }
67
+
68
+ /**
69
+ * Get all audio files with progress events
70
+ */
71
+ @ReactMethod
72
+ fun getAllAudioFiles(promise: Promise) {
73
+ scope.launch {
74
+ try {
75
+ val files = audioProvider.getAllItems { current, total ->
76
+ val progress = JSONObject().apply {
77
+ put("current", current)
78
+ put("total", total)
79
+ put("percentage", if (total > 0) (current * 100 / total) else 0)
80
+ }
81
+ sendEvent(EVENT_PROGRESS, progress.toString())
82
+ }
83
+
84
+ val filesArray = JSONArray()
85
+ files.forEach { file ->
86
+ filesArray.put(file.toJson())
87
+ }
88
+
89
+ promise.resolve(filesArray.toString())
90
+ } catch (e: Exception) {
91
+ sendErrorEvent(e.message ?: "Unknown error", "FETCH_ALL_ERROR")
92
+ promise.reject("FETCH_ALL_ERROR", e.message, e)
93
+ }
94
+ }
95
+ }
96
+
97
+ /**
98
+ * Get single audio file by ID
99
+ */
100
+ @ReactMethod
101
+ fun getAudioFileById(id: String, promise: Promise) {
102
+ scope.launch {
103
+ try {
104
+ val file = audioProvider.getItemById(id)
105
+ if (file != null) {
106
+ promise.resolve(file.toJson().toString())
107
+ } else {
108
+ promise.resolve(null)
109
+ }
110
+ } catch (e: Exception) {
111
+ sendErrorEvent(e.message ?: "Unknown error", "FETCH_BY_ID_ERROR")
112
+ promise.reject("FETCH_BY_ID_ERROR", e.message, e)
113
+ }
114
+ }
115
+ }
116
+
117
+ /**
118
+ * Get total count of audio files
119
+ */
120
+ @ReactMethod
121
+ fun getTotalAudioCount(promise: Promise) {
122
+ scope.launch {
123
+ try {
124
+ val count = audioProvider.getTotalCount()
125
+ promise.resolve(count)
126
+ } catch (e: Exception) {
127
+ sendErrorEvent(e.message ?: "Unknown error", "COUNT_ERROR")
128
+ promise.reject("COUNT_ERROR", e.message, e)
129
+ }
130
+ }
131
+ }
132
+
133
+ /**
134
+ * Required for NativeEventEmitter
135
+ */
136
+ @ReactMethod
137
+ fun addListener(eventName: String) {
138
+ // Keep: Required for NativeEventEmitter
139
+ }
140
+
141
+ /**
142
+ * Required for NativeEventEmitter
143
+ */
144
+ @ReactMethod
145
+ fun removeListeners(count: Int) {
146
+ // Keep: Required for NativeEventEmitter
147
+ }
148
+
149
+ /**
150
+ * Send error event to JavaScript
151
+ */
152
+ private fun sendErrorEvent(message: String, code: String) {
153
+ val error = JSONObject().apply {
154
+ put("message", message)
155
+ put("code", code)
156
+ }
157
+ sendEvent(EVENT_ERROR, error.toString())
158
+ }
159
+
160
+ override fun invalidate() {
161
+ super.invalidate()
162
+ scope.cancel()
163
+ }
164
+
165
+ companion object {
166
+ const val NAME = "AndroidMediaFetcher"
167
+ private const val EVENT_PROGRESS = "MediaFetcher_Progress"
168
+ private const val EVENT_ERROR = "MediaFetcher_Error"
169
+ }
170
+ }
package/app.plugin.js ADDED
@@ -0,0 +1 @@
1
+ module.exports = require('./plugin/build');
@@ -0,0 +1,153 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.AudioFetcher = void 0;
7
+ var _reactNative = require("react-native");
8
+ var _NativeModule = require("./NativeModule");
9
+ /**
10
+ * Default page size for pagination
11
+ */
12
+ const DEFAULT_PAGE_SIZE = 50;
13
+
14
+ /**
15
+ * Maximum page size allowed
16
+ */
17
+ const MAX_PAGE_SIZE = 500;
18
+
19
+ /**
20
+ * Parse JSON response from native module safely
21
+ */
22
+ function parseNativeResponse(json) {
23
+ try {
24
+ return JSON.parse(json);
25
+ } catch {
26
+ throw new Error('Failed to parse response from native module');
27
+ }
28
+ }
29
+
30
+ /**
31
+ * Validate that we're running on Android
32
+ */
33
+ function validatePlatform() {
34
+ if (_reactNative.Platform.OS !== 'android') {
35
+ throw new Error('react-native-android-media-fetcher only supports Android. ' + `Current platform: ${_reactNative.Platform.OS}`);
36
+ }
37
+ }
38
+
39
+ /**
40
+ * AudioFetcher provides methods to fetch audio files from the device
41
+ * with full metadata including album art, artist, duration, etc.
42
+ *
43
+ * @example
44
+ * ```typescript
45
+ * import { AudioFetcher } from 'react-native-android-media-fetcher';
46
+ *
47
+ * // Fetch first page of audio files
48
+ * const result = await AudioFetcher.getAudioFiles({ pageSize: 20 });
49
+ * console.log(result.files);
50
+ *
51
+ * // Fetch all audio files (with progress events)
52
+ * const allFiles = await AudioFetcher.getAllAudioFiles();
53
+ * ```
54
+ */
55
+ const AudioFetcher = exports.AudioFetcher = {
56
+ /**
57
+ * Fetch audio files with pagination
58
+ *
59
+ * @param options - Pagination options
60
+ * @returns Promise resolving to FetchResult with files and pagination info
61
+ *
62
+ * @example
63
+ * ```typescript
64
+ * // Fetch first page
65
+ * const result = await AudioFetcher.getAudioFiles({ pageSize: 20, offset: 0 });
66
+ *
67
+ * // Fetch next page
68
+ * if (result.hasMore) {
69
+ * const nextPage = await AudioFetcher.getAudioFiles({
70
+ * pageSize: 20,
71
+ * offset: result.nextOffset,
72
+ * });
73
+ * }
74
+ * ```
75
+ */
76
+ async getAudioFiles(options) {
77
+ validatePlatform();
78
+ const pageSize = Math.min(Math.max(1, options?.pageSize ?? DEFAULT_PAGE_SIZE), MAX_PAGE_SIZE);
79
+ const offset = Math.max(0, options?.offset ?? 0);
80
+ const json = await _NativeModule.AndroidMediaFetcherModule.getAudioFiles(pageSize, offset);
81
+ return parseNativeResponse(json);
82
+ },
83
+ /**
84
+ * Fetch all audio files from the device
85
+ *
86
+ * For large libraries, use addProgressListener to track progress:
87
+ *
88
+ * @returns Promise resolving to array of all AudioFile objects
89
+ *
90
+ * @example
91
+ * ```typescript
92
+ * import { AudioFetcher, addProgressListener } from 'react-native-android-media-fetcher';
93
+ *
94
+ * const unsubscribe = addProgressListener((progress) => {
95
+ * console.log(`Loading: ${progress.percentage}%`);
96
+ * });
97
+ *
98
+ * const allFiles = await AudioFetcher.getAllAudioFiles();
99
+ * unsubscribe();
100
+ *
101
+ * console.log(`Found ${allFiles.length} audio files`);
102
+ * ```
103
+ */
104
+ async getAllAudioFiles() {
105
+ validatePlatform();
106
+ const json = await _NativeModule.AndroidMediaFetcherModule.getAllAudioFiles();
107
+ return parseNativeResponse(json);
108
+ },
109
+ /**
110
+ * Fetch a single audio file by its ID
111
+ *
112
+ * @param id - The unique ID of the audio file (from AudioFile.id)
113
+ * @returns Promise resolving to AudioFile or null if not found
114
+ *
115
+ * @example
116
+ * ```typescript
117
+ * const file = await AudioFetcher.getAudioFileById('12345');
118
+ * if (file) {
119
+ * console.log(`Found: ${file.title} by ${file.artist}`);
120
+ * }
121
+ * ```
122
+ */
123
+ async getAudioFileById(id) {
124
+ validatePlatform();
125
+ if (!id || typeof id !== 'string') {
126
+ throw new Error('Invalid audio file ID');
127
+ }
128
+ const json = await _NativeModule.AndroidMediaFetcherModule.getAudioFileById(id);
129
+ if (!json) {
130
+ return null;
131
+ }
132
+ return parseNativeResponse(json);
133
+ },
134
+ /**
135
+ * Get the total count of audio files on the device
136
+ *
137
+ * This is a lightweight operation that only counts files without
138
+ * fetching their metadata.
139
+ *
140
+ * @returns Promise resolving to total number of audio files
141
+ *
142
+ * @example
143
+ * ```typescript
144
+ * const count = await AudioFetcher.getTotalAudioCount();
145
+ * console.log(`Device has ${count} audio files`);
146
+ * ```
147
+ */
148
+ async getTotalAudioCount() {
149
+ validatePlatform();
150
+ return _NativeModule.AndroidMediaFetcherModule.getTotalAudioCount();
151
+ }
152
+ };
153
+ //# sourceMappingURL=AudioFetcher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["_reactNative","require","_NativeModule","DEFAULT_PAGE_SIZE","MAX_PAGE_SIZE","parseNativeResponse","json","JSON","parse","Error","validatePlatform","Platform","OS","AudioFetcher","exports","getAudioFiles","options","pageSize","Math","min","max","offset","AndroidMediaFetcherModule","getAllAudioFiles","getAudioFileById","id","getTotalAudioCount"],"sourceRoot":"..\\..\\src","sources":["AudioFetcher.ts"],"mappings":";;;;;;AAAA,IAAAA,YAAA,GAAAC,OAAA;AACA,IAAAC,aAAA,GAAAD,OAAA;AAGA;AACA;AACA;AACA,MAAME,iBAAiB,GAAG,EAAE;;AAE5B;AACA;AACA;AACA,MAAMC,aAAa,GAAG,GAAG;;AAEzB;AACA;AACA;AACA,SAASC,mBAAmBA,CAAIC,IAAY,EAAK;EAC7C,IAAI;IACA,OAAOC,IAAI,CAACC,KAAK,CAACF,IAAI,CAAC;EAC3B,CAAC,CAAC,MAAM;IACJ,MAAM,IAAIG,KAAK,CAAC,6CAA6C,CAAC;EAClE;AACJ;;AAEA;AACA;AACA;AACA,SAASC,gBAAgBA,CAAA,EAAS;EAC9B,IAAIC,qBAAQ,CAACC,EAAE,KAAK,SAAS,EAAE;IAC3B,MAAM,IAAIH,KAAK,CACX,4DAA4D,GAC5D,qBAAqBE,qBAAQ,CAACC,EAAE,EACpC,CAAC;EACL;AACJ;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,MAAMC,YAAY,GAAAC,OAAA,CAAAD,YAAA,GAAG;EACxB;AACJ;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;EACI,MAAME,aAAaA,CAACC,OAAsB,EAAwB;IAC9DN,gBAAgB,CAAC,CAAC;IAElB,MAAMO,QAAQ,GAAGC,IAAI,CAACC,GAAG,CACrBD,IAAI,CAACE,GAAG,CAAC,CAAC,EAAEJ,OAAO,EAAEC,QAAQ,IAAId,iBAAiB,CAAC,EACnDC,aACJ,CAAC;IACD,MAAMiB,MAAM,GAAGH,IAAI,CAACE,GAAG,CAAC,CAAC,EAAEJ,OAAO,EAAEK,MAAM,IAAI,CAAC,CAAC;IAEhD,MAAMf,IAAI,GAAG,MAAMgB,uCAAyB,CAACP,aAAa,CAACE,QAAQ,EAAEI,MAAM,CAAC;IAC5E,OAAOhB,mBAAmB,CAAcC,IAAI,CAAC;EACjD,CAAC;EAED;AACJ;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;EACI,MAAMiB,gBAAgBA,CAAA,EAAyB;IAC3Cb,gBAAgB,CAAC,CAAC;IAElB,MAAMJ,IAAI,GAAG,MAAMgB,uCAAyB,CAACC,gBAAgB,CAAC,CAAC;IAC/D,OAAOlB,mBAAmB,CAAcC,IAAI,CAAC;EACjD,CAAC;EAED;AACJ;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;EACI,MAAMkB,gBAAgBA,CAACC,EAAU,EAA6B;IAC1Df,gBAAgB,CAAC,CAAC;IAElB,IAAI,CAACe,EAAE,IAAI,OAAOA,EAAE,KAAK,QAAQ,EAAE;MAC/B,MAAM,IAAIhB,KAAK,CAAC,uBAAuB,CAAC;IAC5C;IAEA,MAAMH,IAAI,GAAG,MAAMgB,uCAAyB,CAACE,gBAAgB,CAACC,EAAE,CAAC;IACjE,IAAI,CAACnB,IAAI,EAAE;MACP,OAAO,IAAI;IACf;IACA,OAAOD,mBAAmB,CAAYC,IAAI,CAAC;EAC/C,CAAC;EAED;AACJ;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;EACI,MAAMoB,kBAAkBA,CAAA,EAAoB;IACxChB,gBAAgB,CAAC,CAAC;IAElB,OAAOY,uCAAyB,CAACI,kBAAkB,CAAC,CAAC;EACzD;AACJ,CAAC","ignoreList":[]}
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.MediaFetcherEventEmitter = exports.AndroidMediaFetcherModule = void 0;
7
+ var _reactNative = require("react-native");
8
+ const LINKING_ERROR = `The package 'react-native-android-media-fetcher' doesn't seem to be linked. Make sure: \n\n` + _reactNative.Platform.select({
9
+ ios: "- You have run 'pod install'\n",
10
+ default: ''
11
+ }) + '- You rebuilt the app after installing the package\n' + '- You are not using Expo Go (use development builds instead)';
12
+
13
+ /**
14
+ * Native module interface - internal use only
15
+ */
16
+
17
+ /**
18
+ * Get the native module with proper error handling
19
+ */
20
+ function getNativeModule() {
21
+ const module = _reactNative.NativeModules.AndroidMediaFetcher;
22
+ if (!module) {
23
+ throw new Error(LINKING_ERROR);
24
+ }
25
+ return module;
26
+ }
27
+
28
+ /**
29
+ * The native Android Media Fetcher module
30
+ */
31
+ const AndroidMediaFetcherModule = exports.AndroidMediaFetcherModule = getNativeModule();
32
+
33
+ /**
34
+ * Event emitter for native events
35
+ */
36
+ const MediaFetcherEventEmitter = exports.MediaFetcherEventEmitter = new _reactNative.NativeEventEmitter(_reactNative.NativeModules.AndroidMediaFetcher);
37
+ //# sourceMappingURL=NativeModule.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["_reactNative","require","LINKING_ERROR","Platform","select","ios","default","getNativeModule","module","NativeModules","AndroidMediaFetcher","Error","AndroidMediaFetcherModule","exports","MediaFetcherEventEmitter","NativeEventEmitter"],"sourceRoot":"..\\..\\src","sources":["NativeModule.ts"],"mappings":";;;;;;AAAA,IAAAA,YAAA,GAAAC,OAAA;AAEA,MAAMC,aAAa,GACf,6FAA6F,GAC7FC,qBAAQ,CAACC,MAAM,CAAC;EAAEC,GAAG,EAAE,gCAAgC;EAAEC,OAAO,EAAE;AAAG,CAAC,CAAC,GACvE,sDAAsD,GACtD,8DAA8D;;AAElE;AACA;AACA;;AAUA;AACA;AACA;AACA,SAASC,eAAeA,CAAA,EAA6B;EACjD,MAAMC,MAAM,GAAGC,0BAAa,CAACC,mBAAmB;EAEhD,IAAI,CAACF,MAAM,EAAE;IACT,MAAM,IAAIG,KAAK,CAACT,aAAa,CAAC;EAClC;EAEA,OAAOM,MAAM;AACjB;;AAEA;AACA;AACA;AACO,MAAMI,yBAAyB,GAAAC,OAAA,CAAAD,yBAAA,GAAGL,eAAe,CAAC,CAAC;;AAE1D;AACA;AACA;AACO,MAAMO,wBAAwB,GAAAD,OAAA,CAAAC,wBAAA,GAAG,IAAIC,+BAAkB,CAC1DN,0BAAa,CAACC,mBAClB,CAAC","ignoreList":[]}
@@ -0,0 +1,73 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.addErrorListener = addErrorListener;
7
+ exports.addProgressListener = addProgressListener;
8
+ exports.removeAllListeners = removeAllListeners;
9
+ var _NativeModule = require("./NativeModule");
10
+ /**
11
+ * Event names used by the native module
12
+ */
13
+ const EVENTS = {
14
+ PROGRESS: 'MediaFetcher_Progress',
15
+ ERROR: 'MediaFetcher_Error'
16
+ };
17
+
18
+ /**
19
+ * Add a listener for progress updates during getAllAudioFiles operation
20
+ *
21
+ * @param listener - Callback function that receives progress updates
22
+ * @returns Unsubscribe function to remove the listener
23
+ *
24
+ * @example
25
+ * ```typescript
26
+ * const unsubscribe = addProgressListener((progress) => {
27
+ * console.log(`Loading: ${progress.percentage}%`);
28
+ * });
29
+ *
30
+ * // Later, when done:
31
+ * unsubscribe();
32
+ * ```
33
+ */
34
+ function addProgressListener(listener) {
35
+ const subscription = _NativeModule.MediaFetcherEventEmitter.addListener(EVENTS.PROGRESS, event => {
36
+ listener(event);
37
+ });
38
+ return () => {
39
+ subscription.remove();
40
+ };
41
+ }
42
+
43
+ /**
44
+ * Add a listener for error events during fetch operations
45
+ *
46
+ * @param listener - Callback function that receives error information
47
+ * @returns Unsubscribe function to remove the listener
48
+ *
49
+ * @example
50
+ * ```typescript
51
+ * const unsubscribe = addErrorListener((error) => {
52
+ * console.error(`Error: ${error.message} (${error.code})`);
53
+ * });
54
+ * ```
55
+ */
56
+ function addErrorListener(listener) {
57
+ const subscription = _NativeModule.MediaFetcherEventEmitter.addListener(EVENTS.ERROR, event => {
58
+ listener(event);
59
+ });
60
+ return () => {
61
+ subscription.remove();
62
+ };
63
+ }
64
+
65
+ /**
66
+ * Remove all listeners for media fetcher events
67
+ * Useful for cleanup in useEffect
68
+ */
69
+ function removeAllListeners() {
70
+ _NativeModule.MediaFetcherEventEmitter.removeAllListeners(EVENTS.PROGRESS);
71
+ _NativeModule.MediaFetcherEventEmitter.removeAllListeners(EVENTS.ERROR);
72
+ }
73
+ //# sourceMappingURL=events.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["_NativeModule","require","EVENTS","PROGRESS","ERROR","addProgressListener","listener","subscription","MediaFetcherEventEmitter","addListener","event","remove","addErrorListener","removeAllListeners"],"sourceRoot":"..\\..\\src","sources":["events.ts"],"mappings":";;;;;;;;AAAA,IAAAA,aAAA,GAAAC,OAAA;AAQA;AACA;AACA;AACA,MAAMC,MAAM,GAAG;EACXC,QAAQ,EAAE,uBAAuB;EACjCC,KAAK,EAAE;AACX,CAAU;;AAEV;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAASC,mBAAmBA,CAACC,QAA0B,EAAe;EACzE,MAAMC,YAAY,GAAGC,sCAAwB,CAACC,WAAW,CACrDP,MAAM,CAACC,QAAQ,EACdO,KAAoB,IAAK;IACtBJ,QAAQ,CAACI,KAAK,CAAC;EACnB,CACJ,CAAC;EAED,OAAO,MAAM;IACTH,YAAY,CAACI,MAAM,CAAC,CAAC;EACzB,CAAC;AACL;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAASC,gBAAgBA,CAACN,QAAuB,EAAe;EACnE,MAAMC,YAAY,GAAGC,sCAAwB,CAACC,WAAW,CACrDP,MAAM,CAACE,KAAK,EACXM,KAAwC,IAAK;IAC1CJ,QAAQ,CAACI,KAAK,CAAC;EACnB,CACJ,CAAC;EAED,OAAO,MAAM;IACTH,YAAY,CAACI,MAAM,CAAC,CAAC;EACzB,CAAC;AACL;;AAEA;AACA;AACA;AACA;AACO,SAASE,kBAAkBA,CAAA,EAAS;EACvCL,sCAAwB,CAACK,kBAAkB,CAACX,MAAM,CAACC,QAAQ,CAAC;EAC5DK,sCAAwB,CAACK,kBAAkB,CAACX,MAAM,CAACE,KAAK,CAAC;AAC7D","ignoreList":[]}
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ Object.defineProperty(exports, "AudioFetcher", {
7
+ enumerable: true,
8
+ get: function () {
9
+ return _AudioFetcher.AudioFetcher;
10
+ }
11
+ });
12
+ Object.defineProperty(exports, "addErrorListener", {
13
+ enumerable: true,
14
+ get: function () {
15
+ return _events.addErrorListener;
16
+ }
17
+ });
18
+ Object.defineProperty(exports, "addProgressListener", {
19
+ enumerable: true,
20
+ get: function () {
21
+ return _events.addProgressListener;
22
+ }
23
+ });
24
+ Object.defineProperty(exports, "removeAllListeners", {
25
+ enumerable: true,
26
+ get: function () {
27
+ return _events.removeAllListeners;
28
+ }
29
+ });
30
+ var _AudioFetcher = require("./AudioFetcher");
31
+ var _events = require("./events");
32
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["_AudioFetcher","require","_events"],"sourceRoot":"..\\..\\src","sources":["index.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAUA,IAAAA,aAAA,GAAAC,OAAA;AAGA,IAAAC,OAAA,GAAAD,OAAA","ignoreList":[]}
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":[],"sourceRoot":"..\\..\\src","sources":["types.ts"],"mappings":"","ignoreList":[]}
@@ -0,0 +1,147 @@
1
+ import { Platform } from 'react-native';
2
+ import { AndroidMediaFetcherModule } from './NativeModule';
3
+ /**
4
+ * Default page size for pagination
5
+ */
6
+ const DEFAULT_PAGE_SIZE = 50;
7
+
8
+ /**
9
+ * Maximum page size allowed
10
+ */
11
+ const MAX_PAGE_SIZE = 500;
12
+
13
+ /**
14
+ * Parse JSON response from native module safely
15
+ */
16
+ function parseNativeResponse(json) {
17
+ try {
18
+ return JSON.parse(json);
19
+ } catch {
20
+ throw new Error('Failed to parse response from native module');
21
+ }
22
+ }
23
+
24
+ /**
25
+ * Validate that we're running on Android
26
+ */
27
+ function validatePlatform() {
28
+ if (Platform.OS !== 'android') {
29
+ throw new Error('react-native-android-media-fetcher only supports Android. ' + `Current platform: ${Platform.OS}`);
30
+ }
31
+ }
32
+
33
+ /**
34
+ * AudioFetcher provides methods to fetch audio files from the device
35
+ * with full metadata including album art, artist, duration, etc.
36
+ *
37
+ * @example
38
+ * ```typescript
39
+ * import { AudioFetcher } from 'react-native-android-media-fetcher';
40
+ *
41
+ * // Fetch first page of audio files
42
+ * const result = await AudioFetcher.getAudioFiles({ pageSize: 20 });
43
+ * console.log(result.files);
44
+ *
45
+ * // Fetch all audio files (with progress events)
46
+ * const allFiles = await AudioFetcher.getAllAudioFiles();
47
+ * ```
48
+ */
49
+ export const AudioFetcher = {
50
+ /**
51
+ * Fetch audio files with pagination
52
+ *
53
+ * @param options - Pagination options
54
+ * @returns Promise resolving to FetchResult with files and pagination info
55
+ *
56
+ * @example
57
+ * ```typescript
58
+ * // Fetch first page
59
+ * const result = await AudioFetcher.getAudioFiles({ pageSize: 20, offset: 0 });
60
+ *
61
+ * // Fetch next page
62
+ * if (result.hasMore) {
63
+ * const nextPage = await AudioFetcher.getAudioFiles({
64
+ * pageSize: 20,
65
+ * offset: result.nextOffset,
66
+ * });
67
+ * }
68
+ * ```
69
+ */
70
+ async getAudioFiles(options) {
71
+ validatePlatform();
72
+ const pageSize = Math.min(Math.max(1, options?.pageSize ?? DEFAULT_PAGE_SIZE), MAX_PAGE_SIZE);
73
+ const offset = Math.max(0, options?.offset ?? 0);
74
+ const json = await AndroidMediaFetcherModule.getAudioFiles(pageSize, offset);
75
+ return parseNativeResponse(json);
76
+ },
77
+ /**
78
+ * Fetch all audio files from the device
79
+ *
80
+ * For large libraries, use addProgressListener to track progress:
81
+ *
82
+ * @returns Promise resolving to array of all AudioFile objects
83
+ *
84
+ * @example
85
+ * ```typescript
86
+ * import { AudioFetcher, addProgressListener } from 'react-native-android-media-fetcher';
87
+ *
88
+ * const unsubscribe = addProgressListener((progress) => {
89
+ * console.log(`Loading: ${progress.percentage}%`);
90
+ * });
91
+ *
92
+ * const allFiles = await AudioFetcher.getAllAudioFiles();
93
+ * unsubscribe();
94
+ *
95
+ * console.log(`Found ${allFiles.length} audio files`);
96
+ * ```
97
+ */
98
+ async getAllAudioFiles() {
99
+ validatePlatform();
100
+ const json = await AndroidMediaFetcherModule.getAllAudioFiles();
101
+ return parseNativeResponse(json);
102
+ },
103
+ /**
104
+ * Fetch a single audio file by its ID
105
+ *
106
+ * @param id - The unique ID of the audio file (from AudioFile.id)
107
+ * @returns Promise resolving to AudioFile or null if not found
108
+ *
109
+ * @example
110
+ * ```typescript
111
+ * const file = await AudioFetcher.getAudioFileById('12345');
112
+ * if (file) {
113
+ * console.log(`Found: ${file.title} by ${file.artist}`);
114
+ * }
115
+ * ```
116
+ */
117
+ async getAudioFileById(id) {
118
+ validatePlatform();
119
+ if (!id || typeof id !== 'string') {
120
+ throw new Error('Invalid audio file ID');
121
+ }
122
+ const json = await AndroidMediaFetcherModule.getAudioFileById(id);
123
+ if (!json) {
124
+ return null;
125
+ }
126
+ return parseNativeResponse(json);
127
+ },
128
+ /**
129
+ * Get the total count of audio files on the device
130
+ *
131
+ * This is a lightweight operation that only counts files without
132
+ * fetching their metadata.
133
+ *
134
+ * @returns Promise resolving to total number of audio files
135
+ *
136
+ * @example
137
+ * ```typescript
138
+ * const count = await AudioFetcher.getTotalAudioCount();
139
+ * console.log(`Device has ${count} audio files`);
140
+ * ```
141
+ */
142
+ async getTotalAudioCount() {
143
+ validatePlatform();
144
+ return AndroidMediaFetcherModule.getTotalAudioCount();
145
+ }
146
+ };
147
+ //# sourceMappingURL=AudioFetcher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["Platform","AndroidMediaFetcherModule","DEFAULT_PAGE_SIZE","MAX_PAGE_SIZE","parseNativeResponse","json","JSON","parse","Error","validatePlatform","OS","AudioFetcher","getAudioFiles","options","pageSize","Math","min","max","offset","getAllAudioFiles","getAudioFileById","id","getTotalAudioCount"],"sourceRoot":"..\\..\\src","sources":["AudioFetcher.ts"],"mappings":"AAAA,SAASA,QAAQ,QAAQ,cAAc;AACvC,SAASC,yBAAyB,QAAQ,gBAAgB;AAG1D;AACA;AACA;AACA,MAAMC,iBAAiB,GAAG,EAAE;;AAE5B;AACA;AACA;AACA,MAAMC,aAAa,GAAG,GAAG;;AAEzB;AACA;AACA;AACA,SAASC,mBAAmBA,CAAIC,IAAY,EAAK;EAC7C,IAAI;IACA,OAAOC,IAAI,CAACC,KAAK,CAACF,IAAI,CAAC;EAC3B,CAAC,CAAC,MAAM;IACJ,MAAM,IAAIG,KAAK,CAAC,6CAA6C,CAAC;EAClE;AACJ;;AAEA;AACA;AACA;AACA,SAASC,gBAAgBA,CAAA,EAAS;EAC9B,IAAIT,QAAQ,CAACU,EAAE,KAAK,SAAS,EAAE;IAC3B,MAAM,IAAIF,KAAK,CACX,4DAA4D,GAC5D,qBAAqBR,QAAQ,CAACU,EAAE,EACpC,CAAC;EACL;AACJ;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,MAAMC,YAAY,GAAG;EACxB;AACJ;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;EACI,MAAMC,aAAaA,CAACC,OAAsB,EAAwB;IAC9DJ,gBAAgB,CAAC,CAAC;IAElB,MAAMK,QAAQ,GAAGC,IAAI,CAACC,GAAG,CACrBD,IAAI,CAACE,GAAG,CAAC,CAAC,EAAEJ,OAAO,EAAEC,QAAQ,IAAIZ,iBAAiB,CAAC,EACnDC,aACJ,CAAC;IACD,MAAMe,MAAM,GAAGH,IAAI,CAACE,GAAG,CAAC,CAAC,EAAEJ,OAAO,EAAEK,MAAM,IAAI,CAAC,CAAC;IAEhD,MAAMb,IAAI,GAAG,MAAMJ,yBAAyB,CAACW,aAAa,CAACE,QAAQ,EAAEI,MAAM,CAAC;IAC5E,OAAOd,mBAAmB,CAAcC,IAAI,CAAC;EACjD,CAAC;EAED;AACJ;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;EACI,MAAMc,gBAAgBA,CAAA,EAAyB;IAC3CV,gBAAgB,CAAC,CAAC;IAElB,MAAMJ,IAAI,GAAG,MAAMJ,yBAAyB,CAACkB,gBAAgB,CAAC,CAAC;IAC/D,OAAOf,mBAAmB,CAAcC,IAAI,CAAC;EACjD,CAAC;EAED;AACJ;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;EACI,MAAMe,gBAAgBA,CAACC,EAAU,EAA6B;IAC1DZ,gBAAgB,CAAC,CAAC;IAElB,IAAI,CAACY,EAAE,IAAI,OAAOA,EAAE,KAAK,QAAQ,EAAE;MAC/B,MAAM,IAAIb,KAAK,CAAC,uBAAuB,CAAC;IAC5C;IAEA,MAAMH,IAAI,GAAG,MAAMJ,yBAAyB,CAACmB,gBAAgB,CAACC,EAAE,CAAC;IACjE,IAAI,CAAChB,IAAI,EAAE;MACP,OAAO,IAAI;IACf;IACA,OAAOD,mBAAmB,CAAYC,IAAI,CAAC;EAC/C,CAAC;EAED;AACJ;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;EACI,MAAMiB,kBAAkBA,CAAA,EAAoB;IACxCb,gBAAgB,CAAC,CAAC;IAElB,OAAOR,yBAAyB,CAACqB,kBAAkB,CAAC,CAAC;EACzD;AACJ,CAAC","ignoreList":[]}
@@ -0,0 +1,31 @@
1
+ import { NativeModules, Platform, NativeEventEmitter } from 'react-native';
2
+ const LINKING_ERROR = `The package 'react-native-android-media-fetcher' doesn't seem to be linked. Make sure: \n\n` + Platform.select({
3
+ ios: "- You have run 'pod install'\n",
4
+ default: ''
5
+ }) + '- You rebuilt the app after installing the package\n' + '- You are not using Expo Go (use development builds instead)';
6
+
7
+ /**
8
+ * Native module interface - internal use only
9
+ */
10
+
11
+ /**
12
+ * Get the native module with proper error handling
13
+ */
14
+ function getNativeModule() {
15
+ const module = NativeModules.AndroidMediaFetcher;
16
+ if (!module) {
17
+ throw new Error(LINKING_ERROR);
18
+ }
19
+ return module;
20
+ }
21
+
22
+ /**
23
+ * The native Android Media Fetcher module
24
+ */
25
+ export const AndroidMediaFetcherModule = getNativeModule();
26
+
27
+ /**
28
+ * Event emitter for native events
29
+ */
30
+ export const MediaFetcherEventEmitter = new NativeEventEmitter(NativeModules.AndroidMediaFetcher);
31
+ //# sourceMappingURL=NativeModule.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["NativeModules","Platform","NativeEventEmitter","LINKING_ERROR","select","ios","default","getNativeModule","module","AndroidMediaFetcher","Error","AndroidMediaFetcherModule","MediaFetcherEventEmitter"],"sourceRoot":"..\\..\\src","sources":["NativeModule.ts"],"mappings":"AAAA,SAASA,aAAa,EAAEC,QAAQ,EAAEC,kBAAkB,QAAQ,cAAc;AAE1E,MAAMC,aAAa,GACf,6FAA6F,GAC7FF,QAAQ,CAACG,MAAM,CAAC;EAAEC,GAAG,EAAE,gCAAgC;EAAEC,OAAO,EAAE;AAAG,CAAC,CAAC,GACvE,sDAAsD,GACtD,8DAA8D;;AAElE;AACA;AACA;;AAUA;AACA;AACA;AACA,SAASC,eAAeA,CAAA,EAA6B;EACjD,MAAMC,MAAM,GAAGR,aAAa,CAACS,mBAAmB;EAEhD,IAAI,CAACD,MAAM,EAAE;IACT,MAAM,IAAIE,KAAK,CAACP,aAAa,CAAC;EAClC;EAEA,OAAOK,MAAM;AACjB;;AAEA;AACA;AACA;AACA,OAAO,MAAMG,yBAAyB,GAAGJ,eAAe,CAAC,CAAC;;AAE1D;AACA;AACA;AACA,OAAO,MAAMK,wBAAwB,GAAG,IAAIV,kBAAkB,CAC1DF,aAAa,CAACS,mBAClB,CAAC","ignoreList":[]}