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.
- package/LICENSE +21 -0
- package/README.md +398 -0
- package/android/build.gradle +111 -0
- package/android/gradle.properties +3 -0
- package/android/src/main/AndroidManifest.xml +3 -0
- package/android/src/main/AndroidManifestNew.xml +2 -0
- package/android/src/main/java/com/androidmediafetcher/AndroidMediaFetcherPackage.kt +21 -0
- package/android/src/main/java/com/androidmediafetcher/models/AudioFile.kt +57 -0
- package/android/src/main/java/com/androidmediafetcher/providers/AudioMediaProvider.kt +361 -0
- package/android/src/main/java/com/androidmediafetcher/providers/BaseMediaProvider.kt +43 -0
- package/android/src/newarch/java/com/androidmediafetcher/AndroidMediaFetcherModule.kt +173 -0
- package/android/src/newarch/java/com/androidmediafetcher/NativeAndroidMediaFetcherSpec.kt +21 -0
- package/android/src/oldarch/java/com/androidmediafetcher/AndroidMediaFetcherModule.kt +170 -0
- package/app.plugin.js +1 -0
- package/lib/commonjs/AudioFetcher.js +153 -0
- package/lib/commonjs/AudioFetcher.js.map +1 -0
- package/lib/commonjs/NativeModule.js +37 -0
- package/lib/commonjs/NativeModule.js.map +1 -0
- package/lib/commonjs/events.js +73 -0
- package/lib/commonjs/events.js.map +1 -0
- package/lib/commonjs/index.js +32 -0
- package/lib/commonjs/index.js.map +1 -0
- package/lib/commonjs/types.js +2 -0
- package/lib/commonjs/types.js.map +1 -0
- package/lib/module/AudioFetcher.js +147 -0
- package/lib/module/AudioFetcher.js.map +1 -0
- package/lib/module/NativeModule.js +31 -0
- package/lib/module/NativeModule.js.map +1 -0
- package/lib/module/events.js +65 -0
- package/lib/module/events.js.map +1 -0
- package/lib/module/index.js +17 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/types.js +2 -0
- package/lib/module/types.js.map +1 -0
- package/lib/typescript/src/AudioFetcher.d.ts +93 -0
- package/lib/typescript/src/AudioFetcher.d.ts.map +1 -0
- package/lib/typescript/src/NativeModule.d.ts +21 -0
- package/lib/typescript/src/NativeModule.d.ts.map +1 -0
- package/lib/typescript/src/events.d.ts +38 -0
- package/lib/typescript/src/events.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +12 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/lib/typescript/src/types.d.ts +135 -0
- package/lib/typescript/src/types.d.ts.map +1 -0
- package/package.json +101 -0
- package/plugin/build/index.js +48 -0
- package/plugin/src/index.ts +63 -0
- package/plugin/tsconfig.json +34 -0
- package/react-native.config.js +12 -0
- package/src/AudioFetcher.ts +163 -0
- package/src/NativeModule.ts +44 -0
- package/src/events.ts +79 -0
- package/src/index.ts +29 -0
- package/src/types.ts +171 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Rakesh Prasad
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,398 @@
|
|
|
1
|
+
# react-native-android-media-fetcher
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/react-native-android-media-fetcher)
|
|
4
|
+
[](https://github.com/rakeshprasad28/react-native-android-media-fetcher/blob/main/LICENSE)
|
|
5
|
+
[](https://developer.android.com/)
|
|
6
|
+
|
|
7
|
+
A React Native package for fetching audio files with complete metadata from Android devices. Built with TypeScript, supports pagination, and provides progress events for large music libraries.
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
- 🎵 **Fetch all audio files** from device storage
|
|
12
|
+
- 📊 **Rich metadata**: title, artist, album, duration, size, bitrate, genre, and more
|
|
13
|
+
- 🎨 **Album art extraction** as base64 for easy display
|
|
14
|
+
- 📑 **Efficient pagination** for large libraries
|
|
15
|
+
- 📡 **Progress events** for real-time loading feedback
|
|
16
|
+
- âš¡ **New Architecture ready** (TurboModules support)
|
|
17
|
+
- 📱 **Expo compatible** (with config plugin)
|
|
18
|
+
|
|
19
|
+
## Requirements
|
|
20
|
+
|
|
21
|
+
- React Native >= 0.60.0
|
|
22
|
+
- Android API 29+ (Android 10+)
|
|
23
|
+
- Expo SDK 47+ (for Expo projects, requires development builds)
|
|
24
|
+
|
|
25
|
+
## Installation
|
|
26
|
+
|
|
27
|
+
### React Native CLI
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
npm install react-native-android-media-fetcher
|
|
31
|
+
# or
|
|
32
|
+
yarn add react-native-android-media-fetcher
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
The package uses auto-linking, so no manual linking is required for React Native 0.60+.
|
|
36
|
+
|
|
37
|
+
### Expo
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
npx expo install react-native-android-media-fetcher
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Add the plugin to your `app.json` or `app.config.js`:
|
|
44
|
+
|
|
45
|
+
```json
|
|
46
|
+
{
|
|
47
|
+
"expo": {
|
|
48
|
+
"plugins": ["react-native-android-media-fetcher"]
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
> **Note:** This package requires a development build. It will not work with Expo Go.
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
npx expo prebuild
|
|
57
|
+
npx expo run:android
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Setup
|
|
61
|
+
|
|
62
|
+
### Add Permissions (React Native CLI)
|
|
63
|
+
|
|
64
|
+
Add the following permissions to your `android/app/src/main/AndroidManifest.xml`:
|
|
65
|
+
|
|
66
|
+
```xml
|
|
67
|
+
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
|
68
|
+
|
|
69
|
+
<!-- For Android 13+ (API 33+) -->
|
|
70
|
+
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
|
|
71
|
+
|
|
72
|
+
<!-- For Android 10-12 (API 29-32) -->
|
|
73
|
+
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"
|
|
74
|
+
android:maxSdkVersion="32" />
|
|
75
|
+
|
|
76
|
+
<application>
|
|
77
|
+
...
|
|
78
|
+
</application>
|
|
79
|
+
</manifest>
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Request Runtime Permissions
|
|
83
|
+
|
|
84
|
+
You must request runtime permissions before using the library. Here's an example using React Native's built-in `PermissionsAndroid`:
|
|
85
|
+
|
|
86
|
+
```typescript
|
|
87
|
+
import { PermissionsAndroid, Platform } from 'react-native';
|
|
88
|
+
|
|
89
|
+
async function requestAudioPermission(): Promise<boolean> {
|
|
90
|
+
if (Platform.OS !== 'android') return false;
|
|
91
|
+
|
|
92
|
+
try {
|
|
93
|
+
if (Platform.Version >= 33) {
|
|
94
|
+
const granted = await PermissionsAndroid.request(
|
|
95
|
+
PermissionsAndroid.PERMISSIONS.READ_MEDIA_AUDIO,
|
|
96
|
+
{
|
|
97
|
+
title: 'Audio Library Permission',
|
|
98
|
+
message: 'This app needs access to your audio library.',
|
|
99
|
+
buttonNeutral: 'Ask Me Later',
|
|
100
|
+
buttonNegative: 'Cancel',
|
|
101
|
+
buttonPositive: 'OK',
|
|
102
|
+
}
|
|
103
|
+
);
|
|
104
|
+
return granted === PermissionsAndroid.RESULTS.GRANTED;
|
|
105
|
+
} else {
|
|
106
|
+
const granted = await PermissionsAndroid.request(
|
|
107
|
+
PermissionsAndroid.PERMISSIONS.READ_EXTERNAL_STORAGE,
|
|
108
|
+
{
|
|
109
|
+
title: 'Storage Permission',
|
|
110
|
+
message: 'This app needs access to your storage to read audio files.',
|
|
111
|
+
buttonNeutral: 'Ask Me Later',
|
|
112
|
+
buttonNegative: 'Cancel',
|
|
113
|
+
buttonPositive: 'OK',
|
|
114
|
+
}
|
|
115
|
+
);
|
|
116
|
+
return granted === PermissionsAndroid.RESULTS.GRANTED;
|
|
117
|
+
}
|
|
118
|
+
} catch (err) {
|
|
119
|
+
console.warn(err);
|
|
120
|
+
return false;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Usage
|
|
126
|
+
|
|
127
|
+
### Basic Usage
|
|
128
|
+
|
|
129
|
+
```typescript
|
|
130
|
+
import { AudioFetcher } from 'react-native-android-media-fetcher';
|
|
131
|
+
|
|
132
|
+
// Fetch first 20 audio files
|
|
133
|
+
const result = await AudioFetcher.getAudioFiles({ pageSize: 20, offset: 0 });
|
|
134
|
+
|
|
135
|
+
console.log(`Total files: ${result.totalCount}`);
|
|
136
|
+
console.log(`Has more: ${result.hasMore}`);
|
|
137
|
+
|
|
138
|
+
result.files.forEach((file) => {
|
|
139
|
+
console.log(`${file.title} - ${file.artist}`);
|
|
140
|
+
});
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### Pagination
|
|
144
|
+
|
|
145
|
+
```typescript
|
|
146
|
+
import { AudioFetcher } from 'react-native-android-media-fetcher';
|
|
147
|
+
|
|
148
|
+
async function loadAudioFiles() {
|
|
149
|
+
const allFiles = [];
|
|
150
|
+
let offset = 0;
|
|
151
|
+
const pageSize = 50;
|
|
152
|
+
let hasMore = true;
|
|
153
|
+
|
|
154
|
+
while (hasMore) {
|
|
155
|
+
const result = await AudioFetcher.getAudioFiles({ pageSize, offset });
|
|
156
|
+
allFiles.push(...result.files);
|
|
157
|
+
offset = result.nextOffset;
|
|
158
|
+
hasMore = result.hasMore;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return allFiles;
|
|
162
|
+
}
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### Fetch All with Progress
|
|
166
|
+
|
|
167
|
+
```typescript
|
|
168
|
+
import {
|
|
169
|
+
AudioFetcher,
|
|
170
|
+
addProgressListener
|
|
171
|
+
} from 'react-native-android-media-fetcher';
|
|
172
|
+
|
|
173
|
+
// Subscribe to progress updates
|
|
174
|
+
const unsubscribe = addProgressListener((progress) => {
|
|
175
|
+
console.log(`Loading: ${progress.percentage}% (${progress.current}/${progress.total})`);
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
// Fetch all files
|
|
179
|
+
const allFiles = await AudioFetcher.getAllAudioFiles();
|
|
180
|
+
|
|
181
|
+
// Clean up listener
|
|
182
|
+
unsubscribe();
|
|
183
|
+
|
|
184
|
+
console.log(`Loaded ${allFiles.length} audio files`);
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
### Get Single File by ID
|
|
188
|
+
|
|
189
|
+
```typescript
|
|
190
|
+
import { AudioFetcher } from 'react-native-android-media-fetcher';
|
|
191
|
+
|
|
192
|
+
const file = await AudioFetcher.getAudioFileById('12345');
|
|
193
|
+
|
|
194
|
+
if (file) {
|
|
195
|
+
console.log(`Title: ${file.title}`);
|
|
196
|
+
console.log(`Artist: ${file.artist}`);
|
|
197
|
+
console.log(`Duration: ${file.duration}ms`);
|
|
198
|
+
}
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
### Get Total Count
|
|
202
|
+
|
|
203
|
+
```typescript
|
|
204
|
+
import { AudioFetcher } from 'react-native-android-media-fetcher';
|
|
205
|
+
|
|
206
|
+
const count = await AudioFetcher.getTotalAudioCount();
|
|
207
|
+
console.log(`Device has ${count} audio files`);
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### Display Album Art
|
|
211
|
+
|
|
212
|
+
```typescript
|
|
213
|
+
import { Image } from 'react-native';
|
|
214
|
+
|
|
215
|
+
// file.albumArt is a base64 string
|
|
216
|
+
{file.albumArt && (
|
|
217
|
+
<Image
|
|
218
|
+
source={{ uri: `data:image/jpeg;base64,${file.albumArt}` }}
|
|
219
|
+
style={{ width: 100, height: 100 }}
|
|
220
|
+
/>
|
|
221
|
+
)}
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
### React Hook Example
|
|
225
|
+
|
|
226
|
+
```typescript
|
|
227
|
+
import { useState, useEffect, useCallback } from 'react';
|
|
228
|
+
import {
|
|
229
|
+
AudioFetcher,
|
|
230
|
+
addProgressListener,
|
|
231
|
+
type AudioFile,
|
|
232
|
+
type FetchProgress
|
|
233
|
+
} from 'react-native-android-media-fetcher';
|
|
234
|
+
|
|
235
|
+
function useAudioLibrary() {
|
|
236
|
+
const [files, setFiles] = useState<AudioFile[]>([]);
|
|
237
|
+
const [loading, setLoading] = useState(false);
|
|
238
|
+
const [progress, setProgress] = useState<FetchProgress | null>(null);
|
|
239
|
+
const [error, setError] = useState<string | null>(null);
|
|
240
|
+
|
|
241
|
+
const loadFiles = useCallback(async () => {
|
|
242
|
+
setLoading(true);
|
|
243
|
+
setError(null);
|
|
244
|
+
|
|
245
|
+
const unsubscribe = addProgressListener(setProgress);
|
|
246
|
+
|
|
247
|
+
try {
|
|
248
|
+
const allFiles = await AudioFetcher.getAllAudioFiles();
|
|
249
|
+
setFiles(allFiles);
|
|
250
|
+
} catch (err) {
|
|
251
|
+
setError(err instanceof Error ? err.message : 'Failed to load files');
|
|
252
|
+
} finally {
|
|
253
|
+
setLoading(false);
|
|
254
|
+
setProgress(null);
|
|
255
|
+
unsubscribe();
|
|
256
|
+
}
|
|
257
|
+
}, []);
|
|
258
|
+
|
|
259
|
+
return { files, loading, progress, error, loadFiles };
|
|
260
|
+
}
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
## API Reference
|
|
264
|
+
|
|
265
|
+
### AudioFetcher
|
|
266
|
+
|
|
267
|
+
#### `getAudioFiles(options?: FetchOptions): Promise<FetchResult>`
|
|
268
|
+
|
|
269
|
+
Fetches audio files with pagination.
|
|
270
|
+
|
|
271
|
+
| Option | Type | Default | Description |
|
|
272
|
+
|--------|------|---------|-------------|
|
|
273
|
+
| `pageSize` | `number` | 50 | Number of files per page (max: 500) |
|
|
274
|
+
| `offset` | `number` | 0 | Starting position |
|
|
275
|
+
|
|
276
|
+
Returns `FetchResult`:
|
|
277
|
+
|
|
278
|
+
```typescript
|
|
279
|
+
interface FetchResult {
|
|
280
|
+
files: AudioFile[]; // Array of audio files
|
|
281
|
+
totalCount: number; // Total files on device
|
|
282
|
+
hasMore: boolean; // Whether more files exist
|
|
283
|
+
nextOffset: number; // Offset for next page
|
|
284
|
+
}
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
#### `getAllAudioFiles(): Promise<AudioFile[]>`
|
|
288
|
+
|
|
289
|
+
Fetches all audio files. Use with `addProgressListener` for progress updates.
|
|
290
|
+
|
|
291
|
+
#### `getAudioFileById(id: string): Promise<AudioFile | null>`
|
|
292
|
+
|
|
293
|
+
Fetches a single audio file by its ID.
|
|
294
|
+
|
|
295
|
+
#### `getTotalAudioCount(): Promise<number>`
|
|
296
|
+
|
|
297
|
+
Returns the total count of audio files on the device.
|
|
298
|
+
|
|
299
|
+
### Event Functions
|
|
300
|
+
|
|
301
|
+
#### `addProgressListener(listener: ProgressListener): Unsubscribe`
|
|
302
|
+
|
|
303
|
+
Subscribes to progress updates during `getAllAudioFiles()`.
|
|
304
|
+
|
|
305
|
+
```typescript
|
|
306
|
+
type ProgressListener = (progress: FetchProgress) => void;
|
|
307
|
+
|
|
308
|
+
interface FetchProgress {
|
|
309
|
+
current: number; // Files processed
|
|
310
|
+
total: number; // Total files
|
|
311
|
+
percentage: number; // Progress 0-100
|
|
312
|
+
}
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
#### `addErrorListener(listener: ErrorListener): Unsubscribe`
|
|
316
|
+
|
|
317
|
+
Subscribes to error events.
|
|
318
|
+
|
|
319
|
+
#### `removeAllListeners(): void`
|
|
320
|
+
|
|
321
|
+
Removes all event listeners.
|
|
322
|
+
|
|
323
|
+
### AudioFile Interface
|
|
324
|
+
|
|
325
|
+
```typescript
|
|
326
|
+
interface AudioFile {
|
|
327
|
+
id: string; // Unique identifier
|
|
328
|
+
uri: string; // Content URI
|
|
329
|
+
displayName: string; // Filename with extension
|
|
330
|
+
title: string; // Track title
|
|
331
|
+
artist: string; // Artist name
|
|
332
|
+
album: string; // Album name
|
|
333
|
+
duration: number; // Duration in milliseconds
|
|
334
|
+
size: number; // File size in bytes
|
|
335
|
+
mimeType: string; // MIME type (e.g., "audio/mpeg")
|
|
336
|
+
dateAdded: number; // Unix timestamp (seconds)
|
|
337
|
+
dateModified: number; // Unix timestamp (seconds)
|
|
338
|
+
albumArt: string | null; // Base64 encoded album art
|
|
339
|
+
path: string; // Absolute file path
|
|
340
|
+
bitrate: number; // Bitrate in bps
|
|
341
|
+
sampleRate: number; // Sample rate in Hz
|
|
342
|
+
albumArtist: string; // Album artist
|
|
343
|
+
composer: string; // Composer
|
|
344
|
+
genre: string; // Genre
|
|
345
|
+
year: number; // Release year
|
|
346
|
+
trackNumber: number; // Track number
|
|
347
|
+
}
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
### Supported Audio Formats
|
|
351
|
+
|
|
352
|
+
All audio formats recognized by Android MediaStore:
|
|
353
|
+
|
|
354
|
+
- MP3, M4A, AAC, FLAC, WAV
|
|
355
|
+
- OGG, OPUS, WMA, AMR
|
|
356
|
+
- MIDI, and more
|
|
357
|
+
|
|
358
|
+
## Troubleshooting
|
|
359
|
+
|
|
360
|
+
### "The package doesn't seem to be linked"
|
|
361
|
+
|
|
362
|
+
1. Make sure you've rebuilt the app after installing:
|
|
363
|
+
```bash
|
|
364
|
+
cd android && ./gradlew clean && cd ..
|
|
365
|
+
npx react-native run-android
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
2. For Expo, ensure you're using a development build, not Expo Go.
|
|
369
|
+
|
|
370
|
+
### No audio files returned
|
|
371
|
+
|
|
372
|
+
1. Verify permissions are granted:
|
|
373
|
+
```typescript
|
|
374
|
+
const granted = await PermissionsAndroid.check(
|
|
375
|
+
Platform.Version >= 33
|
|
376
|
+
? PermissionsAndroid.PERMISSIONS.READ_MEDIA_AUDIO
|
|
377
|
+
: PermissionsAndroid.PERMISSIONS.READ_EXTERNAL_STORAGE
|
|
378
|
+
);
|
|
379
|
+
console.log('Permission granted:', granted);
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
2. Check if audio files exist on the device/emulator.
|
|
383
|
+
|
|
384
|
+
### Album art not showing
|
|
385
|
+
|
|
386
|
+
Album art extraction depends on:
|
|
387
|
+
- Having embedded artwork in the audio file
|
|
388
|
+
- Sufficient permissions
|
|
389
|
+
|
|
390
|
+
Some files may not have embedded album art. In those cases, `albumArt` will be `null`.
|
|
391
|
+
|
|
392
|
+
## License
|
|
393
|
+
|
|
394
|
+
MIT © [Rakesh Prasad](https://github.com/rakeshprasad28)
|
|
395
|
+
|
|
396
|
+
## Contributing
|
|
397
|
+
|
|
398
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
buildscript {
|
|
2
|
+
repositories {
|
|
3
|
+
google()
|
|
4
|
+
mavenCentral()
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
dependencies {
|
|
8
|
+
classpath "com.android.tools.build:gradle:8.1.0"
|
|
9
|
+
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.0"
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
def isNewArchitectureEnabled() {
|
|
14
|
+
return rootProject.hasProperty("newArchEnabled") && rootProject.getProperty("newArchEnabled") == "true"
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
apply plugin: "com.android.library"
|
|
18
|
+
apply plugin: "kotlin-android"
|
|
19
|
+
|
|
20
|
+
if (isNewArchitectureEnabled()) {
|
|
21
|
+
apply plugin: "com.facebook.react"
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
def getExtOrDefault(name) {
|
|
25
|
+
return rootProject.ext.has(name) ? rootProject.ext.get(name) : project.properties["AndroidMediaFetcher_" + name]
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
def getExtOrIntegerDefault(name) {
|
|
29
|
+
return rootProject.ext.has(name) ? rootProject.ext.get(name) : (project.properties["AndroidMediaFetcher_" + name]).toInteger()
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
def supportsNamespace() {
|
|
33
|
+
def parsed = com.android.Version.ANDROID_GRADLE_PLUGIN_VERSION.tokenize('.')
|
|
34
|
+
def major = parsed[0].toInteger()
|
|
35
|
+
def minor = parsed[1].toInteger()
|
|
36
|
+
return (major == 7 && minor >= 3) || major >= 8
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
android {
|
|
40
|
+
if (supportsNamespace()) {
|
|
41
|
+
namespace "com.androidmediafetcher"
|
|
42
|
+
|
|
43
|
+
sourceSets {
|
|
44
|
+
main {
|
|
45
|
+
manifest.srcFile "src/main/AndroidManifestNew.xml"
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
compileSdkVersion getExtOrIntegerDefault("compileSdkVersion")
|
|
51
|
+
|
|
52
|
+
defaultConfig {
|
|
53
|
+
minSdkVersion 29
|
|
54
|
+
targetSdkVersion getExtOrIntegerDefault("targetSdkVersion")
|
|
55
|
+
buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString()
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
buildFeatures {
|
|
59
|
+
buildConfig true
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
buildTypes {
|
|
63
|
+
release {
|
|
64
|
+
minifyEnabled false
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
lintOptions {
|
|
69
|
+
disable "GradleCompatible"
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
compileOptions {
|
|
73
|
+
sourceCompatibility JavaVersion.VERSION_1_8
|
|
74
|
+
targetCompatibility JavaVersion.VERSION_1_8
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
kotlinOptions {
|
|
78
|
+
jvmTarget = "1.8"
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
sourceSets {
|
|
82
|
+
main {
|
|
83
|
+
if (isNewArchitectureEnabled()) {
|
|
84
|
+
java.srcDirs += ["src/newarch"]
|
|
85
|
+
} else {
|
|
86
|
+
java.srcDirs += ["src/oldarch"]
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
repositories {
|
|
93
|
+
mavenCentral()
|
|
94
|
+
google()
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
def kotlin_version = getExtOrDefault("kotlinVersion")
|
|
98
|
+
|
|
99
|
+
dependencies {
|
|
100
|
+
implementation "com.facebook.react:react-native:+"
|
|
101
|
+
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
|
102
|
+
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3"
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (isNewArchitectureEnabled()) {
|
|
106
|
+
react {
|
|
107
|
+
jsRootDir = file("../src/")
|
|
108
|
+
libraryName = "AndroidMediaFetcher"
|
|
109
|
+
codegenJavaPackageName = "com.androidmediafetcher"
|
|
110
|
+
}
|
|
111
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
package com.androidmediafetcher
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.ReactPackage
|
|
4
|
+
import com.facebook.react.bridge.NativeModule
|
|
5
|
+
import com.facebook.react.bridge.ReactApplicationContext
|
|
6
|
+
import com.facebook.react.uimanager.ViewManager
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* React Native Package registration for AndroidMediaFetcher
|
|
10
|
+
* This registers the native module with React Native
|
|
11
|
+
*/
|
|
12
|
+
class AndroidMediaFetcherPackage : ReactPackage {
|
|
13
|
+
|
|
14
|
+
override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
|
|
15
|
+
return listOf(AndroidMediaFetcherModule(reactContext))
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
|
|
19
|
+
return emptyList()
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
package com.androidmediafetcher.models
|
|
2
|
+
|
|
3
|
+
import org.json.JSONObject
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Data class representing an audio file with all its metadata
|
|
7
|
+
*/
|
|
8
|
+
data class AudioFile(
|
|
9
|
+
val id: String,
|
|
10
|
+
val uri: String,
|
|
11
|
+
val displayName: String,
|
|
12
|
+
val title: String,
|
|
13
|
+
val artist: String,
|
|
14
|
+
val album: String,
|
|
15
|
+
val duration: Long,
|
|
16
|
+
val size: Long,
|
|
17
|
+
val mimeType: String,
|
|
18
|
+
val dateAdded: Long,
|
|
19
|
+
val dateModified: Long,
|
|
20
|
+
val albumArt: String?,
|
|
21
|
+
val path: String,
|
|
22
|
+
val bitrate: Int,
|
|
23
|
+
val sampleRate: Int,
|
|
24
|
+
val albumArtist: String,
|
|
25
|
+
val composer: String,
|
|
26
|
+
val genre: String,
|
|
27
|
+
val year: Int,
|
|
28
|
+
val trackNumber: Int
|
|
29
|
+
) {
|
|
30
|
+
/**
|
|
31
|
+
* Convert AudioFile to JSON object for passing to JavaScript
|
|
32
|
+
*/
|
|
33
|
+
fun toJson(): JSONObject {
|
|
34
|
+
return JSONObject().apply {
|
|
35
|
+
put("id", id)
|
|
36
|
+
put("uri", uri)
|
|
37
|
+
put("displayName", displayName)
|
|
38
|
+
put("title", title)
|
|
39
|
+
put("artist", artist)
|
|
40
|
+
put("album", album)
|
|
41
|
+
put("duration", duration)
|
|
42
|
+
put("size", size)
|
|
43
|
+
put("mimeType", mimeType)
|
|
44
|
+
put("dateAdded", dateAdded)
|
|
45
|
+
put("dateModified", dateModified)
|
|
46
|
+
put("albumArt", albumArt ?: JSONObject.NULL)
|
|
47
|
+
put("path", path)
|
|
48
|
+
put("bitrate", bitrate)
|
|
49
|
+
put("sampleRate", sampleRate)
|
|
50
|
+
put("albumArtist", albumArtist)
|
|
51
|
+
put("composer", composer)
|
|
52
|
+
put("genre", genre)
|
|
53
|
+
put("year", year)
|
|
54
|
+
put("trackNumber", trackNumber)
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|