From f210b3913269b20d74a5cad2ef7d01dc4f7542bc Mon Sep 17 00:00:00 2001 From: Bartosz Kaszubowski Date: Wed, 11 Aug 2021 19:34:19 +0200 Subject: [PATCH] [media-library] add doc comments in source, correct types (#13936) --- docs/components/plugins/APISection.tsx | 3 +- .../versions/unversioned/sdk/media-library.md | 348 +-------------- .../data/unversioned/expo-media-library.json | 1 + packages/expo-media-library/CHANGELOG.md | 1 + .../build/ExponentMediaLibrary.web.d.ts | 9 +- .../build/ExponentMediaLibrary.web.js.map | 2 +- .../build/MediaLibrary.d.ts | 342 ++++++++++++++- .../expo-media-library/build/MediaLibrary.js | 200 ++++++++- .../build/MediaLibrary.js.map | 2 +- .../src/ExponentMediaLibrary.web.ts | 6 +- .../expo-media-library/src/MediaLibrary.ts | 415 ++++++++++++++++-- tools/src/commands/GenerateDocsAPIData.ts | 1 + 12 files changed, 892 insertions(+), 438 deletions(-) create mode 100644 docs/public/static/data/unversioned/expo-media-library.json diff --git a/docs/components/plugins/APISection.tsx b/docs/components/plugins/APISection.tsx index e87c329d6a6755..1294a658b50a2e 100644 --- a/docs/components/plugins/APISection.tsx +++ b/docs/components/plugins/APISection.tsx @@ -34,7 +34,8 @@ const isHook = ({ name }: GeneratedData) => // note(simek): hardcode this exception until the method will be renamed name !== 'useSystemBrightnessAsync'; -const isListener = ({ name }: GeneratedData) => name.endsWith('Listener'); +const isListener = ({ name }: GeneratedData) => + name.endsWith('Listener') || name.endsWith('Listeners'); const isProp = ({ name }: GeneratedData) => name.includes('Props') && name !== 'ErrorRecoveryProps'; diff --git a/docs/pages/versions/unversioned/sdk/media-library.md b/docs/pages/versions/unversioned/sdk/media-library.md index 82b03dbb0cf40b..17466953a38a1e 100644 --- a/docs/pages/versions/unversioned/sdk/media-library.md +++ b/docs/pages/versions/unversioned/sdk/media-library.md @@ -3,6 +3,7 @@ title: MediaLibrary sourceCodeUrl: 'https://github.com/expo/expo/tree/master/packages/expo-media-library' --- +import APISection from '~/components/plugins/APISection'; import InstallSection from '~/components/plugins/InstallSection'; import PlatformsSection from '~/components/plugins/PlatformsSection'; @@ -26,349 +27,4 @@ In managed apps, the permission to access images or videos is added automaticall import * as MediaLibrary from 'expo-media-library'; ``` -## Methods - -### `MediaLibrary.requestPermissionsAsync()` - -Asks the user to grant permissions for accessing media in user's media library. - -#### Returns - -A promise that resolves to an object of type [CameraRollPermissionResponse](#medialibrarycamerarollpermissionresponse). - -### `MediaLibrary.getPermissionsAsync()` - -Checks user's permissions for accessing media library. - -#### Returns - -A promise that resolves to an object of type [CameraRollPermissionResponse](#medialibrarycamerarollpermissionresponse). - -### `MediaLibrary.isAvailableAsync()` - -Returns whether the Media Library API is enabled on the current device - -#### Returns - -Async `boolean`, indicating whether the Media Library API is available on the current device. - -### `MediaLibrary.presentPermissionsPickerAsync()` - -**Available only on iOS >= 14.** Allows the user to update the assets that your app has access to. The system modal is only displayed if the user originally allowed only `limited` access to their media library, otherwise this method is a no-op. - -#### Returns - -A promise that either rejects if the method is unavailable (meaning the device is not running iOS >= 14), or resolves to void. - -> **Note:** This method doesn't inform you if the user changes which assets your app has access to. For that information, you need to subscribe for updates to the user's media library using [MediaLibrary.addListener(listener)](#medialibraryaddlistenerlistener). If `hasIncrementalChanges` is `false`, the user changed their permissions. - -### `MediaLibrary.createAssetAsync(localUri)` - -Creates an asset from existing file. The most common use case is to save a picture taken by [Camera](camera.md). This method requires `CAMERA_ROLL` permission. - -```js -const { uri } = await Camera.takePictureAsync(); -const asset = await MediaLibrary.createAssetAsync(uri); -``` - -#### Arguments - -- **localUri (_string_)** -- A URI to the image or video file. It must contain an extension. On Android it must be a local path, so it must start with `file:///`. - -#### Returns - -An object representing an [asset](#asset). - -### `MediaLibrary.saveToLibraryAsync(localUri)` - -Saves the file at given `localUri` to the user's media library. Unlike [`createAssetAsync()`](#medialibrarycreateassetasynclocaluri), this method doesn't return created asset. - -On **iOS 11+**, it's possible to use this method without asking for `CAMERA_ROLL` permission, however then yours `Info.plist` should have `NSPhotoLibraryAddUsageDescription` key. - -#### Arguments - -- **localUri (_string_)** -- A URI to the image or video file. It must contain an extension. On Android it must be a local path, so it must start with `file:///`. - -### `MediaLibrary.albumNeedsMigrationAsync(album)` - -Checks if the album should be migrated to a different location. In other words, it checks if the application has the write permission to the album folder. If not, it returns `true`, otherwise `false`. - -For **Android below R**, **web** or **iOS**, this function always returns `false`. - -#### Arguments - -- **album (_string_ | _Album_)** -- [Album](#album) or its ID. - -#### Returns - -Returns a promise resolving to `true` if the album should be migrated. - -### `MediaLibrary.migrateAlbumIfNeededAsync(album)` - -Moves album content to the special media directories on **Android R** or **above** if needed. -Those new locations are in line with the Android `scoped storage` - so your application won't lose write permission to those directories in the future. - -This method does nothing if: - -- app is running on **iOS**, **web** or **Android below R** -- app has **write permission** to the album folder - -The migration is possible when the album contains only compatible files types. -For instance, movies and pictures are compatible with each other, but music and pictures are not. -If automatic migration isn't possible, the function will be rejected. -In that case, you can use methods from the `expo-file-system` to migrate all your files manually. - -#### Why do you need to migrate files? - -**Android R** introduced a lot of changes in the storage system. Now applications can't save anything to the root directory. The only available locations are from the `MediaStore` API. Unfortunately, the media library stored albums in folders for which, because of those changes, the application doesn't have permissions anymore. However, it doesn't mean you need to migrate all your albums. If your application doesn't add assets to albums, you don't have to migrate. Everything will work as it used to. You can read more about scoped storage in [the Android documentation](https://developer.android.com/about/versions/11/privacy/storage). - -#### Arguments - -- **album (_string_ | _Album_)** -- [Album](#album) or its ID to migrate . - -### `MediaLibrary.getAssetsAsync(options)` - -Fetches a page of assets matching the provided criteria. - -#### Arguments - -- **options (_object_)** - - - **first (_number_)** -- The maximum number of items on a single page. Defaults to 20. - - **after (_string_)** -- Asset ID of the last item returned on the previous page. - - **album (_string_ | _Album_)** -- [Album](#album) or its ID to get assets from specific album. - - **sortBy (_array_)** -- An array of [SortBy](#expomedialibrarysortby) keys. By default, all keys are sorted in descending order, however you can also pass a pair `[key, ascending]` where the second item is a `boolean` value that means whether to use ascending order. Note that if the `SortBy.default` key is used, then `ascending` argument will not matter. - Earlier items have higher priority when sorting out the results. - If empty, this method will use the default sorting that is provided by the platform. - - **mediaType (_array_)** -- An array of [MediaType](#expomedialibrarymediatype) types. By default `MediaType.photo` is set. - - **createdAfter (_Date_ | _number_)** -- Date object or Unix timestamp in milliseconds limiting returned assets only to those that were created after this date. - - **createdBefore (_Date_ | _number_)** -- Similarly as `createdAfter`, but limits assets only to those that were created before specified date. - -#### Returns - -A promise that resolves to an object that contains following keys: - -- **assets (_array_)** -- A page of [assets](#asset) fetched by the query. -- **endCursor (_string_)** -- ID of the last fetched asset. It should be passed as `after` option in order to get the next page. -- **hasNextPage (_boolean_)** -- Whether there are more assets to fetch. -- **totalCount (_number_)** -- Estimated total number of assets that match the query. - -### `MediaLibrary.getAssetInfoAsync(asset, options)` - -Provides more information about an asset, including GPS location, local URI and EXIF metadata. - -#### Arguments - -- **asset (_string_ | _Asset_)** -- [Asset](#asset) or its ID. -- **options (_object_)** - - **shouldDownloadFromNetwork (_boolean_)** -- Whether allow the asset to be downloaded from network. Only available in iOS with iCloud assets. Defaults to `true`. - -#### Returns - -Asset object extended by additional fields listed [in the table](#asset). - -### `MediaLibrary.deleteAssetsAsync(assets)` - -Deletes assets from the library. -On iOS it deletes assets from all albums they belong to, while on Android it keeps all copies of them (album is strictly connected to the asset). -Also, there is additional dialog on iOS that requires user to confirm this action. - -#### Arguments - -- **assets (_array_)** -- An array of [assets](#asset) or their IDs. - -#### Returns - -Returns `true` if the assets were successfully deleted. - -### `MediaLibrary.getAlbumsAsync()` - -Queries for user-created albums in media gallery. - -#### Returns - -An array of [albums](#album). Depending on Android version, root directory of your storage may be listed as album titled _"0"_ or unlisted at all. - -### `MediaLibrary.getAlbumAsync(albumName)` - -Queries for an album with a specific name. - -#### Arguments - -- **albumName (_string_)** -- Name of the album to look for. - -#### Returns - -An object representing an [album](#album) if album with given name exists, otherwise returns `null`. - -### `MediaLibrary.createAlbumAsync(albumName, asset, copyAsset)` - -Creates an album with given name and initial asset. -The asset parameter is required on Android, since it's not possible to create empty album on this platform. -On Android, by default it copies given asset from the current album to the new one, however it's also possible to move it by passing `false` as `copyAsset` argument. -In case it's copied you should keep in mind that `getAssetsAsync` will return duplicated asset. - -#### Arguments - -- **albumName (_string_)** -- Name of the album to create. -- **asset (_string_ | _Asset_)** -- [Asset](#asset) or its ID. Required on Android. -- **copyAsset (_boolean_)** -- Whether to copy asset to the new album instead of move it. Defaults to `true`. (**Android only**) - -#### Returns - -Newly created [album](#album). - -### `MediaLibrary.deleteAlbumsAsync(albums, deleteAssets)` - -Deletes given albums from the library. - -On Android by default it deletes assets belonging to given albums from the library. On iOS it doesn't delete these assets, however it's possible to do by passing `true` as `deleteAssets`. - -#### Arguments - -- **albums (_array_)** -- Array of [albums](#album) or their IDs, that will be removed from the library. -- **deleteAssets (_boolean_)** -- Whether to also delete assets belonging to given albums. Defaults to `false`. (**iOS only**) - -#### Returns - -Returns a promise resolving to `true` if the albums were successfully deleted from the library. - -### `MediaLibrary.addAssetsToAlbumAsync(assets, album, copyAssets)` - -Adds array of assets to the album. - -On Android, by default it copies assets from the current album to provided one, however it's also possible to move them by passing `false` as `copyAssets` argument. -In case they're copied you should keep in mind that `getAssetsAsync` will return duplicated assets. - -#### Arguments - -- **assets (_array_)** -- Array of [assets](#assets) to add. -- **album (_string_ | _Album_)** -- [Album](#album) or its ID, to which the assets will be added. -- **copyAssets (_boolean_)** -- Whether to copy assets to the new album instead of move them. Defaults to `true`. (**Android only**) - -#### Returns - -Resolves to `true` if the assets were successfully added to the album. - -### `MediaLibrary.removeAssetsFromAlbumAsync(assets, album)` - -Removes given assets from album. - -On Android, album will be automatically deleted if there are no more assets inside. - -#### Arguments - -- **assets (_array_)** -- Array of [assets](#assets) to remove from album. -- **album (_string_ | _Album_)** -- [Album](#album) or its ID, from which the assets will be removed. - -#### Returns - -Returns `true` if the assets were successfully removed from the album. - -### `MediaLibrary.getMomentsAsync()` - -**Available on iOS only.** Fetches a list of moments, which is a group of assets taken around the same place and time. - -#### Returns - -An array of [albums](#album) whose type is `moment`. - -### `MediaLibrary.addListener(listener)` - -Subscribes for updates in user's media library. - -#### Arguments - -- **listener (_function_)** -- A callback that is fired when any assets have been inserted or deleted from the library, or when the user changes which assets they're allowing access to. **On Android** it's invoked with an empty object. **On iOS** it's invoked with an object containing following keys: - - - **hasIncrementalChanges (_boolean_)** -- Whether the media library's changes could be described as "incremental changes". `true` indicates the changes are described by the `insertedAssets`, `deletedAssets` and `updatedAssets` values. `false` indicates that the scope of changes is too large and you should perform a full assets reload (eg. a user has changed access to individual assets in the media library). - - Available only if `hasIncrementalChanges` is `true`: - - - **insertedAssets (_array_)** -- Array of [assets](#assets) that have been inserted to the library. - - **deletedAssets (_array_)** -- Array of [assets](#assets) that have been deleted from the library. - - **updatedAssets (_array_)** -- Array of [assets](#assets) that have been updated or completed downloading from network storage (iCloud in iOS). - -#### Returns - -An EventSubscription object that you can call `remove()` on when you would like to unsubscribe the listener. - -### `MediaLibrary.removeAllListeners()` - -Removes all listeners. - -## Types - -### `MediaLibrary.CameraRollPermissionResponse` - -`MediaLibrary.CameraRollPermissionResponse` extends [PermissionResponse](permissions.md#permissionresponse) type exported by `unimodules-permission-interface` and contains additional iOS-specific field: - -- `accessPrivileges` **(string)** - Indicates if your app has access to the whole or only part of the photo library. Possible values are: - - `all` if the user granted your app access to the whole photo library - - `limited` if the user granted your app access only to selected photos (only available on **iOS 14.0+**) - - `none` if user denied or hasn't yet granted the permission - -### `Asset` - -| Field name | Type | Platforms | Description | Possible values | -| ------------------- | --------- | --------- | ------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- | -| id | _string_ | both | Internal ID that represents an asset | | -| filename | _string_ | both | Filename of the asset | | -| uri | _string_ | both | URI that points to the asset | `assets://*` (iOS), `file://*` (Android) | -| mediaType | _string_ | both | Media type | `MediaType.audio`, `MediaType.photo`, `MediaType.video`, `MediaType.unknown` | -| width | _number_ | both | Width of the image or video | | -| height | _number_ | both | Height of the image or video | | -| creationTime | _number_ | both | File creation timestamp | | -| modificationTime | _number_ | both | Last modification timestamp | | -| duration | _number_ | both | Duration of the video or audio asset in seconds | | -| mediaSubtypes | _array_ | iOS | An array of media subtypes | `hdr`, `panorama`, `stream`, `timelapse`, `screenshot`, `highFrameRate`, `livePhoto`, `depthEffect` | -| albumId | _string_ | Android | Album ID that the asset belongs to | | -| localUri \* | _string_ | both | Local URI for the asset | | -| location \* | _object_ | both | GPS location if available | `latitude: number, longitude: number` or `null` | -| exif \* | _object_ | both | EXIF metadata associated with the image | | -| orientation \* | _number_ | iOS | Display orientation of the image. Orientation is available only for assets whose mediaType is MediaType.photo | Numbers 1-8, see [EXIF orientation specification](http://sylvana.net/jpegcrop/exif_orientation.html) | -| isFavorite \* | _boolean_ | iOS | Whether the asset is marked as favorite | `true`, `false` | -| isNetworkAsset \*\* | _boolean_ | iOS | Whether the asset is stored on the network (iCloud on iOS) | `true`, `false` | - -> \* These fields can be obtained only by calling `getAssetInfoAsync` method - -> \*\* This field is available only if flag `shouldDownloadFromNetwork` is set to `false` - -### `Album` - -| Field name | Type | Platforms | Description | Possible values | -| ---------------------- | -------- | --------- | ------------------------------------------------------------------------------------------- | ----------------------------------------------- | -| id | _string_ | both | | | -| title | _string_ | both | | | -| assetCount | _number_ | both | Estimated number of assets in the album | | -| folderName | _string_ | iOS | Name of folder that the album belongs to. Can be null if the album is in the root directory | | -| type | _string_ | iOS | The type of the assets album | `album`, `moment`, `smartAlbum` | -| startTime \* | _number_ | iOS | Earliest creation timestamp of all assets in the moment | | -| endTime \* | _number_ | iOS | Latest creation timestamp of all assets in the moment | | -| approximateLocation \* | _object_ | iOS | Approximated location of all assets in the moment | `latitude: number, longitude: number` or `null` | -| locationNames \* | _array_ | iOS | Names of locations grouped in the moment | | - -> \* These fields apply only to albums whose type is `moment` - -## Constants - -### `MediaLibrary.MediaType` - -Possible media types: - -- `MediaType.photo` -- `MediaType.video` -- `MediaType.audio` -- `MediaType.unknown` - -### `MediaLibrary.SortBy` - -Supported keys that can be used to sort `getAssetsAsync` results: - -- `SortBy.default` -- `SortBy.creationTime` -- `SortBy.modificationTime` -- `SortBy.mediaType` -- `SortBy.width` -- `SortBy.height` -- `SortBy.duration` + \ No newline at end of file diff --git a/docs/public/static/data/unversioned/expo-media-library.json b/docs/public/static/data/unversioned/expo-media-library.json new file mode 100644 index 00000000000000..3ae7e931d0dc0c --- /dev/null +++ b/docs/public/static/data/unversioned/expo-media-library.json @@ -0,0 +1 @@ +{"name":"expo-media-library","kind":0,"kindString":"Project","originalName":"","children":[{"name":"PermissionStatus","kind":4,"kindString":"Enumeration","children":[{"name":"DENIED","kind":16,"kindString":"Enumeration member","defaultValue":"\"denied\""},{"name":"GRANTED","kind":16,"kindString":"Enumeration member","defaultValue":"\"granted\""},{"name":"UNDETERMINED","kind":16,"kindString":"Enumeration member","defaultValue":"\"undetermined\""}]},{"name":"EXPermissionResponse","kind":256,"kindString":"Interface","children":[{"name":"canAskAgain","kind":1024,"kindString":"Property","type":{"type":"intrinsic","name":"boolean"}},{"name":"expires","kind":1024,"kindString":"Property","type":{"type":"reference","name":"PermissionExpiration"}},{"name":"granted","kind":1024,"kindString":"Property","type":{"type":"intrinsic","name":"boolean"}},{"name":"status","kind":1024,"kindString":"Property","type":{"type":"reference","name":"PermissionStatus"}}]},{"name":"Album","kind":4194304,"kindString":"Type alias","type":{"type":"reflection","declaration":{"name":"__type","kind":65536,"kindString":"Type literal","children":[{"name":"approximateLocation","kind":1024,"kindString":"Property","flags":{"isOptional":true},"comment":{"shortText":"__iOS Only.__ Apply only to albums whose type is `'moment'`. Approximated location of all assets in the moment."},"type":{"type":"reference","name":"Location"}},{"name":"assetCount","kind":1024,"kindString":"Property","comment":{"shortText":"Estimated number of assets in the album"},"type":{"type":"intrinsic","name":"number"}},{"name":"endTime","kind":1024,"kindString":"Property","comment":{"shortText":"__iOS Only.__ Apply only to albums whose type is `'moment'`. Latest creation timestamp of all assets in the moment."},"type":{"type":"intrinsic","name":"number"}},{"name":"id","kind":1024,"kindString":"Property","type":{"type":"intrinsic","name":"string"}},{"name":"locationNames","kind":1024,"kindString":"Property","flags":{"isOptional":true},"comment":{"shortText":" __iOS Only.__ Apply only to albums whose type is `'moment'`. Names of locations grouped in the moment."},"type":{"type":"array","elementType":{"type":"intrinsic","name":"string"}}},{"name":"startTime","kind":1024,"kindString":"Property","comment":{"shortText":"__iOS Only.__ Apply only to albums whose type is `'moment'`. Earliest creation timestamp of all assets in the moment."},"type":{"type":"intrinsic","name":"number"}},{"name":"title","kind":1024,"kindString":"Property","type":{"type":"intrinsic","name":"string"}},{"name":"type","kind":1024,"kindString":"Property","flags":{"isOptional":true},"comment":{"shortText":"__iOS Only.__ The type of the assets album."},"type":{"type":"reference","name":"AlbumType"}}]}}},{"name":"AlbumRef","kind":4194304,"kindString":"Type alias","type":{"type":"union","types":[{"type":"reference","name":"Album"},{"type":"intrinsic","name":"string"}]}},{"name":"AlbumType","kind":4194304,"kindString":"Type alias","type":{"type":"union","types":[{"type":"literal","value":"album"},{"type":"literal","value":"moment"},{"type":"literal","value":"smartAlbum"}]}},{"name":"AlbumsOptions","kind":4194304,"kindString":"Type alias","type":{"type":"reflection","declaration":{"name":"__type","kind":65536,"kindString":"Type literal","children":[{"name":"includeSmartAlbums","kind":1024,"kindString":"Property","flags":{"isOptional":true},"type":{"type":"intrinsic","name":"boolean"}}]}}},{"name":"Asset","kind":4194304,"kindString":"Type alias","type":{"type":"reflection","declaration":{"name":"__type","kind":65536,"kindString":"Type literal","children":[{"name":"albumId","kind":1024,"kindString":"Property","flags":{"isOptional":true},"comment":{"shortText":"__Android Only.__ Album ID that the asset belongs to."},"type":{"type":"intrinsic","name":"string"}},{"name":"creationTime","kind":1024,"kindString":"Property","comment":{"shortText":"File creation timestamp."},"type":{"type":"intrinsic","name":"number"}},{"name":"duration","kind":1024,"kindString":"Property","comment":{"shortText":"Duration of the video or audio asset in seconds."},"type":{"type":"intrinsic","name":"number"}},{"name":"filename","kind":1024,"kindString":"Property","comment":{"shortText":"Filename of the asset."},"type":{"type":"intrinsic","name":"string"}},{"name":"height","kind":1024,"kindString":"Property","comment":{"shortText":"Height of the image or video."},"type":{"type":"intrinsic","name":"number"}},{"name":"id","kind":1024,"kindString":"Property","comment":{"shortText":"Internal ID that represents an asset."},"type":{"type":"intrinsic","name":"string"}},{"name":"mediaSubtypes","kind":1024,"kindString":"Property","flags":{"isOptional":true},"comment":{"shortText":"__iOS Only.__ An array of media subtypes."},"type":{"type":"array","elementType":{"type":"reference","name":"MediaSubtype"}}},{"name":"mediaType","kind":1024,"kindString":"Property","comment":{"shortText":"Media type."},"type":{"type":"reference","name":"MediaTypeValue"}},{"name":"modificationTime","kind":1024,"kindString":"Property","comment":{"shortText":"Last modification timestamp."},"type":{"type":"intrinsic","name":"number"}},{"name":"uri","kind":1024,"kindString":"Property","comment":{"shortText":"URI that points to the asset. `assets://*` (iOS), `file://*` (Android)"},"type":{"type":"intrinsic","name":"string"}},{"name":"width","kind":1024,"kindString":"Property","comment":{"shortText":"Width of the image or video."},"type":{"type":"intrinsic","name":"number"}}]}}},{"name":"AssetInfo","kind":4194304,"kindString":"Type alias","type":{"type":"intersection","types":[{"type":"reference","name":"Asset"},{"type":"reflection","declaration":{"name":"__type","kind":65536,"kindString":"Type literal","children":[{"name":"exif","kind":1024,"kindString":"Property","flags":{"isOptional":true},"comment":{"shortText":"EXIF metadata associated with the image."},"type":{"type":"intrinsic","name":"object"}},{"name":"isFavorite","kind":1024,"kindString":"Property","flags":{"isOptional":true},"comment":{"shortText":"__iOS Only.__ Whether the asset is marked as favorite."},"type":{"type":"intrinsic","name":"boolean"}},{"name":"isNetworkAsset","kind":1024,"kindString":"Property","flags":{"isOptional":true},"comment":{"shortText":"__iOS Only.__ This field is available only if flag `shouldDownloadFromNetwork` is set to `false`.\nWhether the asset is stored on the network (iCloud on iOS)"},"type":{"type":"intrinsic","name":"boolean"}},{"name":"localUri","kind":1024,"kindString":"Property","flags":{"isOptional":true},"comment":{"shortText":"Local URI for the asset."},"type":{"type":"intrinsic","name":"string"}},{"name":"location","kind":1024,"kindString":"Property","flags":{"isOptional":true},"comment":{"shortText":"GPS location if available."},"type":{"type":"reference","name":"Location"}},{"name":"orientation","kind":1024,"kindString":"Property","flags":{"isOptional":true},"comment":{"shortText":"__iOS Only.__ Display orientation of the image. Orientation is available only for assets whose\n`mediaType` is `MediaType.photo`. Value will range from 1 to 8, see [EXIF orientation specification](http://sylvana.net/jpegcrop/exif_orientation.html)\nfor more details."},"type":{"type":"intrinsic","name":"number"}}]}}]}},{"name":"AssetRef","kind":4194304,"kindString":"Type alias","type":{"type":"union","types":[{"type":"reference","name":"Asset"},{"type":"intrinsic","name":"string"}]}},{"name":"AssetsOptions","kind":4194304,"kindString":"Type alias","type":{"type":"reflection","declaration":{"name":"__type","kind":65536,"kindString":"Type literal","children":[{"name":"after","kind":1024,"kindString":"Property","flags":{"isOptional":true},"comment":{"shortText":"Asset ID of the last item returned on the previous page."},"type":{"type":"reference","name":"AssetRef"}},{"name":"album","kind":1024,"kindString":"Property","flags":{"isOptional":true},"comment":{"shortText":"[Album](#album) or its ID to get assets from specific album."},"type":{"type":"reference","name":"AlbumRef"}},{"name":"createdAfter","kind":1024,"kindString":"Property","flags":{"isOptional":true},"comment":{"shortText":"`Date` object or Unix timestamp in milliseconds limiting returned assets only to those that\nwere created after this date."},"type":{"type":"union","types":[{"type":"reference","name":"Date"},{"type":"intrinsic","name":"number"}]}},{"name":"createdBefore","kind":1024,"kindString":"Property","flags":{"isOptional":true},"comment":{"shortText":"Similarly as `createdAfter`, but limits assets only to those that were created before specified\ndate."},"type":{"type":"union","types":[{"type":"reference","name":"Date"},{"type":"intrinsic","name":"number"}]}},{"name":"first","kind":1024,"kindString":"Property","flags":{"isOptional":true},"comment":{"shortText":"The maximum number of items on a single page.","tags":[{"tag":"default","text":"`20`\n"}]},"type":{"type":"intrinsic","name":"number"}},{"name":"mediaType","kind":1024,"kindString":"Property","flags":{"isOptional":true},"comment":{"shortText":"An array of [MediaTypeValue](#expomedialibrarymediatypevalue)s or a single `MediaTypeValue`.","tags":[{"tag":"default","text":"`MediaType.photo`\n"}]},"type":{"type":"union","types":[{"type":"array","elementType":{"type":"reference","name":"MediaTypeValue"}},{"type":"reference","name":"MediaTypeValue"}]}},{"name":"sortBy","kind":1024,"kindString":"Property","flags":{"isOptional":true},"comment":{"shortText":"An array of [`SortByValue`](#sortbyvalue)s or a single `SortByValue` value. By default, all\nkeys are sorted in descending order, however you can also pass a pair `[key, ascending]` where\nthe second item is a `boolean` value that means whether to use ascending order. Note that if\nthe `SortBy.default` key is used, then `ascending` argument will not matter. Earlier items have\nhigher priority when sorting out the results.\nIf empty, this method will use the default sorting that is provided by the platform."},"type":{"type":"union","types":[{"type":"array","elementType":{"type":"reference","name":"SortByValue"}},{"type":"reference","name":"SortByValue"}]}}]}}},{"name":"Location","kind":4194304,"kindString":"Type alias","type":{"type":"reflection","declaration":{"name":"__type","kind":65536,"kindString":"Type literal","children":[{"name":"latitude","kind":1024,"kindString":"Property","type":{"type":"intrinsic","name":"number"}},{"name":"longitude","kind":1024,"kindString":"Property","type":{"type":"intrinsic","name":"number"}}]}}},{"name":"MediaLibraryAssetInfoQueryOptions","kind":4194304,"kindString":"Type alias","type":{"type":"reflection","declaration":{"name":"__type","kind":65536,"kindString":"Type literal","children":[{"name":"shouldDownloadFromNetwork","kind":1024,"kindString":"Property","flags":{"isOptional":true},"comment":{"shortText":"Whether allow the asset to be downloaded from network. Only available in iOS with iCloud assets.","tags":[{"tag":"default","text":"`true`\n"}]},"type":{"type":"intrinsic","name":"boolean"}}]}}},{"name":"MediaLibraryAssetsChangeEvent","kind":4194304,"kindString":"Type alias","type":{"type":"reflection","declaration":{"name":"__type","kind":65536,"kindString":"Type literal","children":[{"name":"deletedAssets","kind":1024,"kindString":"Property","flags":{"isOptional":true},"comment":{"shortText":"Available only if `hasIncrementalChanges` is `true`.\nArray of [`Asset`](#asset)s that have been deleted from the library."},"type":{"type":"array","elementType":{"type":"reference","name":"Asset"}}},{"name":"hasIncrementalChanges","kind":1024,"kindString":"Property","comment":{"shortText":"Whether the media library's changes could be described as \"incremental changes\".\n`true` indicates the changes are described by the `insertedAssets`, `deletedAssets` and\n`updatedAssets` values. `false` indicates that the scope of changes is too large and you\nshould perform a full assets reload (eg. a user has changed access to individual assets in the\nmedia library)."},"type":{"type":"intrinsic","name":"boolean"}},{"name":"insertedAssets","kind":1024,"kindString":"Property","flags":{"isOptional":true},"comment":{"shortText":"Available only if `hasIncrementalChanges` is `true`.\nArray of [`Asset`](#asset)s that have been inserted to the library."},"type":{"type":"array","elementType":{"type":"reference","name":"Asset"}}},{"name":"updatedAssets","kind":1024,"kindString":"Property","flags":{"isOptional":true},"comment":{"shortText":"Available only if `hasIncrementalChanges` is `true`.\nArray of [`Asset`](#asset)s that have been updated or completed downloading from network\nstorage (iCloud on iOS)."},"type":{"type":"array","elementType":{"type":"reference","name":"Asset"}}}]}}},{"name":"MediaSubtype","kind":4194304,"kindString":"Type alias","type":{"type":"union","types":[{"type":"literal","value":"depthEffect"},{"type":"literal","value":"hdr"},{"type":"literal","value":"highFrameRate"},{"type":"literal","value":"livePhoto"},{"type":"literal","value":"panorama"},{"type":"literal","value":"screenshot"},{"type":"literal","value":"stream"},{"type":"literal","value":"timelapse"}]}},{"name":"MediaTypeObject","kind":4194304,"kindString":"Type alias","type":{"type":"reflection","declaration":{"name":"__type","kind":65536,"kindString":"Type literal","children":[{"name":"audio","kind":1024,"kindString":"Property","type":{"type":"literal","value":"audio"}},{"name":"photo","kind":1024,"kindString":"Property","type":{"type":"literal","value":"photo"}},{"name":"unknown","kind":1024,"kindString":"Property","type":{"type":"literal","value":"unknown"}},{"name":"video","kind":1024,"kindString":"Property","type":{"type":"literal","value":"video"}}]}}},{"name":"MediaTypeValue","kind":4194304,"kindString":"Type alias","type":{"type":"union","types":[{"type":"literal","value":"audio"},{"type":"literal","value":"photo"},{"type":"literal","value":"video"},{"type":"literal","value":"unknown"}]}},{"name":"PagedInfo","kind":4194304,"kindString":"Type alias","typeParameter":[{"name":"T","kind":131072,"kindString":"Type parameter"}],"type":{"type":"reflection","declaration":{"name":"__type","kind":65536,"kindString":"Type literal","children":[{"name":"assets","kind":1024,"kindString":"Property","comment":{"shortText":"A page of [`Asset`](#asset)s fetched by the query."},"type":{"type":"array","elementType":{"type":"reference","name":"T"}}},{"name":"endCursor","kind":1024,"kindString":"Property","comment":{"shortText":"ID of the last fetched asset. It should be passed as `after` option in order to get the\nnext page."},"type":{"type":"intrinsic","name":"string"}},{"name":"hasNextPage","kind":1024,"kindString":"Property","comment":{"shortText":"Whether there are more assets to fetch."},"type":{"type":"intrinsic","name":"boolean"}},{"name":"totalCount","kind":1024,"kindString":"Property","comment":{"shortText":"Estimated total number of assets that match the query."},"type":{"type":"intrinsic","name":"number"}}]}}},{"name":"PermissionExpiration","kind":4194304,"kindString":"Type alias","type":{"type":"union","types":[{"type":"literal","value":"never"},{"type":"intrinsic","name":"number"}]}},{"name":"PermissionResponse","kind":4194304,"kindString":"Type alias","type":{"type":"intersection","types":[{"type":"reference","name":"EXPermissionResponse"},{"type":"reflection","declaration":{"name":"__type","kind":65536,"kindString":"Type literal","children":[{"name":"accessPrivileges","kind":1024,"kindString":"Property","flags":{"isOptional":true},"comment":{"shortText":"Indicates if your app has access to the whole or only part of the photo library. Possible values are:\n- `'all'` if the user granted your app access to the whole photo library\n- `'limited'` if the user granted your app access only to selected photos (only available on iOS 14.0+)\n- `'none'` if user denied or hasn't yet granted the permission"},"type":{"type":"union","types":[{"type":"literal","value":"all"},{"type":"literal","value":"limited"},{"type":"literal","value":"none"}]}}]}}]}},{"name":"SortByKey","kind":4194304,"kindString":"Type alias","type":{"type":"union","types":[{"type":"literal","value":"default"},{"type":"literal","value":"mediaType"},{"type":"literal","value":"width"},{"type":"literal","value":"height"},{"type":"literal","value":"creationTime"},{"type":"literal","value":"modificationTime"},{"type":"literal","value":"duration"}]}},{"name":"SortByObject","kind":4194304,"kindString":"Type alias","type":{"type":"reflection","declaration":{"name":"__type","kind":65536,"kindString":"Type literal","children":[{"name":"creationTime","kind":1024,"kindString":"Property","type":{"type":"literal","value":"creationTime"}},{"name":"default","kind":1024,"kindString":"Property","type":{"type":"literal","value":"default"}},{"name":"duration","kind":1024,"kindString":"Property","type":{"type":"literal","value":"duration"}},{"name":"height","kind":1024,"kindString":"Property","type":{"type":"literal","value":"height"}},{"name":"mediaType","kind":1024,"kindString":"Property","type":{"type":"literal","value":"mediaType"}},{"name":"modificationTime","kind":1024,"kindString":"Property","type":{"type":"literal","value":"modificationTime"}},{"name":"width","kind":1024,"kindString":"Property","type":{"type":"literal","value":"width"}}]}}},{"name":"SortByValue","kind":4194304,"kindString":"Type alias","type":{"type":"union","types":[{"type":"tuple","elements":[{"type":"reference","name":"SortByKey"},{"type":"intrinsic","name":"boolean"}]},{"type":"reference","name":"SortByKey"}]}},{"name":"Subscription","kind":4194304,"kindString":"Type alias","type":{"type":"reflection","declaration":{"name":"__type","kind":65536,"kindString":"Type literal","children":[{"name":"remove","kind":1024,"kindString":"Property","comment":{"shortText":"A method to unsubscribe the listener."},"type":{"type":"reflection","declaration":{"name":"__type","kind":65536,"kindString":"Type literal","signatures":[{"name":"__type","kind":4096,"kindString":"Call signature","type":{"type":"intrinsic","name":"void"}}]}}}]}}},{"name":"MediaType","kind":32,"kindString":"Variable","flags":{"isConst":true},"comment":{"shortText":"Possible media types."},"type":{"type":"reference","name":"MediaTypeObject"},"defaultValue":"..."},{"name":"SortBy","kind":32,"kindString":"Variable","flags":{"isConst":true},"comment":{"shortText":"Supported keys that can be used to sort `getAssetsAsync` results."},"type":{"type":"reference","name":"SortByObject"},"defaultValue":"..."},{"name":"addAssetsToAlbumAsync","kind":64,"kindString":"Function","signatures":[{"name":"addAssetsToAlbumAsync","kind":4096,"kindString":"Call signature","comment":{"shortText":"Adds array of assets to the album.","text":"On Android, by default it copies assets from the current album to provided one, however it's also\npossible to move them by passing `false` as `copyAssets` argument.In case they're copied you\nshould keep in mind that `getAssetsAsync` will return duplicated assets.","returns":"Returns promise which fulfils with `true` if the assets were successfully added to\nthe album.\n"},"parameters":[{"name":"assets","kind":32768,"kindString":"Parameter","comment":{"text":"An array of [Asset](#asset) or their IDs."},"type":{"type":"union","types":[{"type":"array","elementType":{"type":"reference","name":"AssetRef"}},{"type":"reference","name":"AssetRef"}]}},{"name":"album","kind":32768,"kindString":"Parameter","comment":{"text":"An [Album](#album) or its ID."},"type":{"type":"reference","name":"AlbumRef"}},{"name":"copy","kind":32768,"kindString":"Parameter","comment":{"text":"__Android only.__ Whether to copy assets to the new album instead of move them.\nDefaults to `true`."},"type":{"type":"intrinsic","name":"boolean"},"defaultValue":"true"}],"type":{"type":"reference","typeArguments":[{"type":"intrinsic","name":"boolean"}],"name":"Promise"}}]},{"name":"addListener","kind":64,"kindString":"Function","signatures":[{"name":"addListener","kind":4096,"kindString":"Call signature","comment":{"shortText":"Subscribes for updates in user's media library.","returns":"An [`Subscription`](#subscription) object that you can call `remove()` on when you would\nlike to unsubscribe the listener.\n"},"parameters":[{"name":"listener","kind":32768,"kindString":"Parameter","comment":{"text":"A callback that is fired when any assets have been inserted or deleted from the\nlibrary, or when the user changes which assets they're allowing access to. On Android it's\ninvoked with an empty object. On iOS it's invoked with [`MediaLibraryAssetsChangeEvent`](#medialibraryassetschangeevent)\nobject."},"type":{"type":"reflection","declaration":{"name":"__type","kind":65536,"kindString":"Type literal","signatures":[{"name":"__type","kind":4096,"kindString":"Call signature","parameters":[{"name":"event","kind":32768,"kindString":"Parameter","type":{"type":"reference","name":"MediaLibraryAssetsChangeEvent"}}],"type":{"type":"intrinsic","name":"void"}}]}}}],"type":{"type":"reference","name":"Subscription"}}]},{"name":"albumNeedsMigrationAsync","kind":64,"kindString":"Function","signatures":[{"name":"albumNeedsMigrationAsync","kind":4096,"kindString":"Call signature","comment":{"shortText":"Checks if the album should be migrated to a different location. In other words, it checks if the\napplication has the write permission to the album folder. If not, it returns `true`, otherwise `false`.\n> Note: For **Android below R**, **web** or **iOS**, this function always returns `false`.","returns":"Returns a promise which fulfils with `true` if the album should be migrated.\n"},"parameters":[{"name":"album","kind":32768,"kindString":"Parameter","comment":{"text":"An [Album](#album) or its ID."},"type":{"type":"reference","name":"AlbumRef"}}],"type":{"type":"reference","typeArguments":[{"type":"intrinsic","name":"boolean"}],"name":"Promise"}}]},{"name":"createAlbumAsync","kind":64,"kindString":"Function","signatures":[{"name":"createAlbumAsync","kind":4096,"kindString":"Call signature","comment":{"shortText":"Creates an album with given name and initial asset. The asset parameter is required on Android,\nsince it's not possible to create empty album on this platform. On Android, by default it copies\ngiven asset from the current album to the new one, however it's also possible to move it by\npassing `false` as `copyAsset` argument.\nIn case it's copied you should keep in mind that `getAssetsAsync` will return duplicated asset.","returns":"Newly created [`Album`](#album).\n"},"parameters":[{"name":"albumName","kind":32768,"kindString":"Parameter","comment":{"text":"Name of the album to create."},"type":{"type":"intrinsic","name":"string"}},{"name":"asset","kind":32768,"kindString":"Parameter","flags":{"isOptional":true},"comment":{"text":"An [Asset](#asset) or its ID (required on Android)."},"type":{"type":"reference","name":"AssetRef"}},{"name":"copyAsset","kind":32768,"kindString":"Parameter","comment":{"text":"__Android Only.__ Whether to copy asset to the new album instead of move it.\nDefaults to `true`."},"type":{"type":"intrinsic","name":"boolean"},"defaultValue":"true"}],"type":{"type":"reference","typeArguments":[{"type":"reference","name":"Album"}],"name":"Promise"}}]},{"name":"createAssetAsync","kind":64,"kindString":"Function","signatures":[{"name":"createAssetAsync","kind":4096,"kindString":"Call signature","comment":{"shortText":"Creates an asset from existing file. The most common use case is to save a picture taken by [Camera](../camera).\nThis method requires `CAMERA_ROLL` permission.","text":"# Example\n```js\nconst { uri } = await Camera.takePictureAsync();\nconst asset = await MediaLibrary.createAssetAsync(uri);\n```","returns":"A promise which fulfils with an object representing an [`Asset`](#asset).\n"},"parameters":[{"name":"localUri","kind":32768,"kindString":"Parameter","comment":{"text":"A URI to the image or video file. It must contain an extension. On Android it\nmust be a local path, so it must start with `file:///`"},"type":{"type":"intrinsic","name":"string"}}],"type":{"type":"reference","typeArguments":[{"type":"reference","name":"Asset"}],"name":"Promise"}}]},{"name":"deleteAlbumsAsync","kind":64,"kindString":"Function","signatures":[{"name":"deleteAlbumsAsync","kind":4096,"kindString":"Call signature","comment":{"shortText":"Deletes given albums from the library. On Android by default it deletes assets belonging to given\nalbums from the library. On iOS it doesn't delete these assets, however it's possible to do by\npassing `true` as `deleteAssets`.","returns":"Returns a promise which fulfils with `true` if the albums were successfully deleted from\nthe library.\n"},"parameters":[{"name":"albums","kind":32768,"kindString":"Parameter","comment":{"text":"An array of [`Album`](#asset)s or their IDs."},"type":{"type":"union","types":[{"type":"array","elementType":{"type":"reference","name":"AlbumRef"}},{"type":"reference","name":"AlbumRef"}]}},{"name":"assetRemove","kind":32768,"kindString":"Parameter","comment":{"text":"__iOS Only.__ Whether to also delete assets belonging to given albums.\nDefaults to `false`."},"type":{"type":"intrinsic","name":"boolean"},"defaultValue":"false"}],"type":{"type":"reference","typeArguments":[{"type":"intrinsic","name":"boolean"}],"name":"Promise"}}]},{"name":"deleteAssetsAsync","kind":64,"kindString":"Function","signatures":[{"name":"deleteAssetsAsync","kind":4096,"kindString":"Call signature","comment":{"shortText":"Deletes assets from the library. On iOS it deletes assets from all albums they belong to, while\non Android it keeps all copies of them (album is strictly connected to the asset). Also, there is\nadditional dialog on iOS that requires user to confirm this action.","returns":"Returns promise which fulfils with `true` if the assets were successfully deleted.\n"},"parameters":[{"name":"assets","kind":32768,"kindString":"Parameter","comment":{"text":"An array of [Asset](#asset) or their IDs."},"type":{"type":"union","types":[{"type":"array","elementType":{"type":"reference","name":"AssetRef"}},{"type":"reference","name":"AssetRef"}]}}],"type":{"type":"reference","typeArguments":[{"type":"intrinsic","name":"boolean"}],"name":"Promise"}}]},{"name":"getAlbumAsync","kind":64,"kindString":"Function","signatures":[{"name":"getAlbumAsync","kind":4096,"kindString":"Call signature","comment":{"shortText":"Queries for an album with a specific name.","returns":"An object representing an [`Album`](#album), if album with given name exists, otherwise\nreturns `null`.\n"},"parameters":[{"name":"title","kind":32768,"kindString":"Parameter","comment":{"text":"Name of the album to look for."},"type":{"type":"intrinsic","name":"string"}}],"type":{"type":"reference","typeArguments":[{"type":"reference","name":"Album"}],"name":"Promise"}}]},{"name":"getAlbumsAsync","kind":64,"kindString":"Function","signatures":[{"name":"getAlbumsAsync","kind":4096,"kindString":"Call signature","comment":{"shortText":"Queries for user-created albums in media gallery.","returns":"A promise which fulfils with an array of [`Album`](#asset)s. Depending on Android version,\nroot directory of your storage may be listed as album titled `\"0\"` or unlisted at all.\n"},"parameters":[{"name":"__namedParameters","kind":32768,"kindString":"Parameter","type":{"type":"reference","name":"AlbumsOptions"},"defaultValue":"{}"}],"type":{"type":"reference","typeArguments":[{"type":"array","elementType":{"type":"reference","name":"Album"}}],"name":"Promise"}}]},{"name":"getAssetInfoAsync","kind":64,"kindString":"Function","signatures":[{"name":"getAssetInfoAsync","kind":4096,"kindString":"Call signature","comment":{"shortText":"Provides more information about an asset, including GPS location, local URI and EXIF metadata.","returns":"(#assetinfo) object, which is an `Asset` extended by an additional fields.\n"},"parameters":[{"name":"asset","kind":32768,"kindString":"Parameter","comment":{"text":"An [Asset](#asset) or its ID."},"type":{"type":"reference","name":"AssetRef"}},{"name":"options","kind":32768,"kindString":"Parameter","comment":{},"type":{"type":"reference","name":"MediaLibraryAssetInfoQueryOptions"},"defaultValue":"..."}],"type":{"type":"reference","typeArguments":[{"type":"reference","name":"AssetInfo"}],"name":"Promise"}}]},{"name":"getAssetsAsync","kind":64,"kindString":"Function","signatures":[{"name":"getAssetsAsync","kind":4096,"kindString":"Call signature","comment":{"shortText":"Fetches a page of assets matching the provided criteria.","returns":"A promise that fulfils with to [`PagedInfo`](#pagedinfo) object with array of [`Asset`](#asset)s.\n"},"parameters":[{"name":"assetsOptions","kind":32768,"kindString":"Parameter","comment":{},"type":{"type":"reference","name":"AssetsOptions"},"defaultValue":"{}"}],"type":{"type":"reference","typeArguments":[{"type":"reference","typeArguments":[{"type":"reference","name":"Asset"}],"name":"PagedInfo"}],"name":"Promise"}}]},{"name":"getMomentsAsync","kind":64,"kindString":"Function","signatures":[{"name":"getMomentsAsync","kind":4096,"kindString":"Call signature","comment":{"shortText":"__iOS Only.__ Fetches a list of moments, which is a group of assets taken around the same place\nand time.","returns":"An array of [albums](#album) whose type is `moment`.\n"},"type":{"type":"reference","typeArguments":[{"type":"intrinsic","name":"any"}],"name":"Promise"}}]},{"name":"getPermissionsAsync","kind":64,"kindString":"Function","signatures":[{"name":"getPermissionsAsync","kind":4096,"kindString":"Call signature","comment":{"shortText":"Checks user's permissions for accessing media library.","returns":"A promise that fulfils with [`PermissionResponse`](#permissionresponse) object.\n"},"parameters":[{"name":"writeOnly","kind":32768,"kindString":"Parameter","comment":{},"type":{"type":"intrinsic","name":"boolean"},"defaultValue":"false"}],"type":{"type":"reference","typeArguments":[{"type":"reference","name":"PermissionResponse"}],"name":"Promise"}}]},{"name":"isAvailableAsync","kind":64,"kindString":"Function","signatures":[{"name":"isAvailableAsync","kind":4096,"kindString":"Call signature","comment":{"shortText":"Returns whether the Media Library API is enabled on the current device.","returns":"A promise which fulfils with a `boolean`, indicating whether the Media Library API is\navailable on the current device.\n"},"type":{"type":"reference","typeArguments":[{"type":"intrinsic","name":"boolean"}],"name":"Promise"}}]},{"name":"migrateAlbumIfNeededAsync","kind":64,"kindString":"Function","signatures":[{"name":"migrateAlbumIfNeededAsync","kind":4096,"kindString":"Call signature","comment":{"shortText":"Moves album content to the special media directories on **Android R** or **above** if needed.\nThose new locations are in line with the Android `scoped storage` - so your application won't\nlose write permission to those directories in the future.","text":"This method does nothing if:\n- app is running on **iOS**, **web** or **Android below R**\n- app has **write permission** to the album folder\n\nThe migration is possible when the album contains only compatible files types.\nFor instance, movies and pictures are compatible with each other, but music and pictures are not.\nIf automatic migration isn't possible, the function will be rejected.\nIn that case, you can use methods from the `expo-file-system` to migrate all your files manually.\n\n# Why do you need to migrate files?\n__Android R__ introduced a lot of changes in the storage system. Now applications can't save\nanything to the root directory. The only available locations are from the `MediaStore` API.\nUnfortunately, the media library stored albums in folders for which, because of those changes,\nthe application doesn't have permissions anymore. However, it doesn't mean you need to migrate\nall your albums. If your application doesn't add assets to albums, you don't have to migrate.\nEverything will work as it used to. You can read more about scoped storage in [the Android documentation](https://developer.android.com/about/versions/11/privacy/storage).\n","returns":"A promise which fulfils to `void`.\n"},"parameters":[{"name":"album","kind":32768,"kindString":"Parameter","comment":{"text":"An [Album](#album) or its ID."},"type":{"type":"reference","name":"AlbumRef"}}],"type":{"type":"reference","typeArguments":[{"type":"intrinsic","name":"void"}],"name":"Promise"}}]},{"name":"presentPermissionsPickerAsync","kind":64,"kindString":"Function","signatures":[{"name":"presentPermissionsPickerAsync","kind":4096,"kindString":"Call signature","comment":{"shortText":"__Available only on iOS >= 14.__ Allows the user to update the assets that your app has access to.\nThe system modal is only displayed if the user originally allowed only `limited` access to their\nmedia library, otherwise this method is a no-op.","returns":"A promise that either rejects if the method is unavailable (meaning the device is not\nrunning iOS >= 14), or resolves to `void`.\n> __Note:__ This method doesn't inform you if the user changes which assets your app has access to.\nFor that information, you need to subscribe for updates to the user's media library using [addListener(listener)](#medialibraryaddlistenerlistener).\nIf `hasIncrementalChanges` is `false`, the user changed their permissions.\n"},"type":{"type":"reference","typeArguments":[{"type":"intrinsic","name":"void"}],"name":"Promise"}}]},{"name":"removeAllListeners","kind":64,"kindString":"Function","signatures":[{"name":"removeAllListeners","kind":4096,"kindString":"Call signature","comment":{"shortText":"Removes all listeners."},"type":{"type":"intrinsic","name":"void"}}]},{"name":"removeAssetsFromAlbumAsync","kind":64,"kindString":"Function","signatures":[{"name":"removeAssetsFromAlbumAsync","kind":4096,"kindString":"Call signature","comment":{"shortText":"Removes given assets from album.","text":"On Android, album will be automatically deleted if there are no more assets inside.","returns":"Returns promise which fulfils with `true` if the assets were successfully removed from\nthe album.\n"},"parameters":[{"name":"assets","kind":32768,"kindString":"Parameter","comment":{"text":"An array of [Asset](#asset) or their IDs."},"type":{"type":"union","types":[{"type":"array","elementType":{"type":"reference","name":"AssetRef"}},{"type":"reference","name":"AssetRef"}]}},{"name":"album","kind":32768,"kindString":"Parameter","comment":{"text":"An [Album](#album) or its ID."},"type":{"type":"reference","name":"AlbumRef"}}],"type":{"type":"reference","typeArguments":[{"type":"intrinsic","name":"boolean"}],"name":"Promise"}}]},{"name":"removeSubscription","kind":64,"kindString":"Function","signatures":[{"name":"removeSubscription","kind":4096,"kindString":"Call signature","parameters":[{"name":"subscription","kind":32768,"kindString":"Parameter","type":{"type":"reference","name":"Subscription"}}],"type":{"type":"intrinsic","name":"void"}}]},{"name":"requestPermissionsAsync","kind":64,"kindString":"Function","signatures":[{"name":"requestPermissionsAsync","kind":4096,"kindString":"Call signature","comment":{"shortText":"Asks the user to grant permissions for accessing media in user's media library.","returns":"A promise that fulfils with [`PermissionResponse`](#permissionresponse) object.\n"},"parameters":[{"name":"writeOnly","kind":32768,"kindString":"Parameter","comment":{},"type":{"type":"intrinsic","name":"boolean"},"defaultValue":"false"}],"type":{"type":"reference","typeArguments":[{"type":"reference","name":"PermissionResponse"}],"name":"Promise"}}]},{"name":"saveToLibraryAsync","kind":64,"kindString":"Function","signatures":[{"name":"saveToLibraryAsync","kind":4096,"kindString":"Call signature","comment":{"shortText":"Saves the file at given `localUri` to the user's media library. Unlike [`createAssetAsync()`](#medialibrarycreateassetasynclocaluri),\nThis method doesn't return created asset.\nOn __iOS 11+__, it's possible to use this method without asking for `CAMERA_ROLL` permission,\nhowever then yours `Info.plist` should have `NSPhotoLibraryAddUsageDescription` key."},"parameters":[{"name":"localUri","kind":32768,"kindString":"Parameter","comment":{"text":"A URI to the image or video file. It must contain an extension. On Android it\nmust be a local path, so it must start with `file:///`.\n"},"type":{"type":"intrinsic","name":"string"}}],"type":{"type":"reference","typeArguments":[{"type":"intrinsic","name":"void"}],"name":"Promise"}}]}]} \ No newline at end of file diff --git a/packages/expo-media-library/CHANGELOG.md b/packages/expo-media-library/CHANGELOG.md index 50e0f498d76c18..e89daf5899d05c 100644 --- a/packages/expo-media-library/CHANGELOG.md +++ b/packages/expo-media-library/CHANGELOG.md @@ -13,6 +13,7 @@ ### 💡 Others - Migrated from `@unimodules/core` to `expo-modules-core`. ([#13755](https://github.com/expo/expo/pull/13755) by [@tsapeta](https://github.com/tsapeta)) +- Added `AlbumType` and `MediaSubtype` types, added missing `orientation` key to the `Asset` type. ([#13936](https://github.com/expo/expo/pull/13936) by [@Simek](https://github.com/Simek)) ## 12.1.0 — 2021-06-16 diff --git a/packages/expo-media-library/build/ExponentMediaLibrary.web.d.ts b/packages/expo-media-library/build/ExponentMediaLibrary.web.d.ts index 3c31f638b9f2fa..cc66f0d3f119d5 100644 --- a/packages/expo-media-library/build/ExponentMediaLibrary.web.d.ts +++ b/packages/expo-media-library/build/ExponentMediaLibrary.web.d.ts @@ -1,13 +1,10 @@ import { PermissionResponse } from 'expo-modules-core'; +import { MediaTypeObject, SortByObject } from './MediaLibrary'; declare const _default: { readonly name: string; readonly CHANGE_LISTENER_NAME: string; - readonly MediaType: { - [key: string]: string; - }; - readonly SortBy: { - [key: string]: string; - }; + readonly MediaType: MediaTypeObject; + readonly SortBy: SortByObject; getPermissionsAsync(_writeOnly: boolean): Promise; requestPermissionsAsync(_writeOnly: boolean): Promise; }; diff --git a/packages/expo-media-library/build/ExponentMediaLibrary.web.js.map b/packages/expo-media-library/build/ExponentMediaLibrary.web.js.map index d44f9a3642ee20..67e3362bbe3fbc 100644 --- a/packages/expo-media-library/build/ExponentMediaLibrary.web.js.map +++ b/packages/expo-media-library/build/ExponentMediaLibrary.web.js.map @@ -1 +1 @@ -{"version":3,"file":"ExponentMediaLibrary.web.js","sourceRoot":"","sources":["../src/ExponentMediaLibrary.web.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAEzE,MAAM,oBAAoB,GAAuB;IAC/C,MAAM,EAAE,gBAAgB,CAAC,YAAY;IACrC,WAAW,EAAE,IAAI;IACjB,OAAO,EAAE,KAAK;IACd,OAAO,EAAE,OAAO;CACjB,CAAC;AAEF,eAAe;IACb,IAAI,IAAI;QACN,OAAO,sBAAsB,CAAC;IAChC,CAAC;IACD,IAAI,oBAAoB;QACtB,OAAO,uBAAuB,CAAC;IACjC,CAAC;IACD,IAAI,SAAS;QACX,OAAO;YACL,KAAK,EAAE,OAAO;YACd,KAAK,EAAE,OAAO;YACd,KAAK,EAAE,OAAO;YACd,OAAO,EAAE,SAAS;SACnB,CAAC;IACJ,CAAC;IACD,IAAI,MAAM;QACR,OAAO;YACL,OAAO,EAAE,SAAS;YAClB,SAAS,EAAE,WAAW;YACtB,KAAK,EAAE,OAAO;YACd,MAAM,EAAE,QAAQ;YAChB,YAAY,EAAE,cAAc;YAC5B,gBAAgB,EAAE,kBAAkB;YACpC,QAAQ,EAAE,UAAU;SACrB,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,mBAAmB,CAAC,UAAmB;QAC3C,OAAO,oBAAoB,CAAC;IAC9B,CAAC;IACD,KAAK,CAAC,uBAAuB,CAAC,UAAmB;QAC/C,OAAO,oBAAoB,CAAC;IAC9B,CAAC;CACF,CAAC","sourcesContent":["import { PermissionResponse, PermissionStatus } from 'expo-modules-core';\n\nconst noPermissionResponse: PermissionResponse = {\n status: PermissionStatus.UNDETERMINED,\n canAskAgain: true,\n granted: false,\n expires: 'never',\n};\n\nexport default {\n get name(): string {\n return 'ExponentMediaLibrary';\n },\n get CHANGE_LISTENER_NAME(): string {\n return 'mediaLibraryDidChange';\n },\n get MediaType(): { [key: string]: string } {\n return {\n audio: 'audio',\n photo: 'photo',\n video: 'video',\n unknown: 'unknown',\n };\n },\n get SortBy(): { [key: string]: string } {\n return {\n default: 'default',\n mediaType: 'mediaType',\n width: 'width',\n height: 'height',\n creationTime: 'creationTime',\n modificationTime: 'modificationTime',\n duration: 'duration',\n };\n },\n\n async getPermissionsAsync(_writeOnly: boolean): Promise {\n return noPermissionResponse;\n },\n async requestPermissionsAsync(_writeOnly: boolean): Promise {\n return noPermissionResponse;\n },\n};\n"]} \ No newline at end of file +{"version":3,"file":"ExponentMediaLibrary.web.js","sourceRoot":"","sources":["../src/ExponentMediaLibrary.web.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAIzE,MAAM,oBAAoB,GAAuB;IAC/C,MAAM,EAAE,gBAAgB,CAAC,YAAY;IACrC,WAAW,EAAE,IAAI;IACjB,OAAO,EAAE,KAAK;IACd,OAAO,EAAE,OAAO;CACjB,CAAC;AAEF,eAAe;IACb,IAAI,IAAI;QACN,OAAO,sBAAsB,CAAC;IAChC,CAAC;IACD,IAAI,oBAAoB;QACtB,OAAO,uBAAuB,CAAC;IACjC,CAAC;IACD,IAAI,SAAS;QACX,OAAO;YACL,KAAK,EAAE,OAAO;YACd,KAAK,EAAE,OAAO;YACd,KAAK,EAAE,OAAO;YACd,OAAO,EAAE,SAAS;SACnB,CAAC;IACJ,CAAC;IACD,IAAI,MAAM;QACR,OAAO;YACL,OAAO,EAAE,SAAS;YAClB,SAAS,EAAE,WAAW;YACtB,KAAK,EAAE,OAAO;YACd,MAAM,EAAE,QAAQ;YAChB,YAAY,EAAE,cAAc;YAC5B,gBAAgB,EAAE,kBAAkB;YACpC,QAAQ,EAAE,UAAU;SACrB,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,mBAAmB,CAAC,UAAmB;QAC3C,OAAO,oBAAoB,CAAC;IAC9B,CAAC;IACD,KAAK,CAAC,uBAAuB,CAAC,UAAmB;QAC/C,OAAO,oBAAoB,CAAC;IAC9B,CAAC;CACF,CAAC","sourcesContent":["import { PermissionResponse, PermissionStatus } from 'expo-modules-core';\n\nimport { MediaTypeObject, SortByObject } from './MediaLibrary';\n\nconst noPermissionResponse: PermissionResponse = {\n status: PermissionStatus.UNDETERMINED,\n canAskAgain: true,\n granted: false,\n expires: 'never',\n};\n\nexport default {\n get name(): string {\n return 'ExponentMediaLibrary';\n },\n get CHANGE_LISTENER_NAME(): string {\n return 'mediaLibraryDidChange';\n },\n get MediaType(): MediaTypeObject {\n return {\n audio: 'audio',\n photo: 'photo',\n video: 'video',\n unknown: 'unknown',\n };\n },\n get SortBy(): SortByObject {\n return {\n default: 'default',\n mediaType: 'mediaType',\n width: 'width',\n height: 'height',\n creationTime: 'creationTime',\n modificationTime: 'modificationTime',\n duration: 'duration',\n };\n },\n\n async getPermissionsAsync(_writeOnly: boolean): Promise {\n return noPermissionResponse;\n },\n async requestPermissionsAsync(_writeOnly: boolean): Promise {\n return noPermissionResponse;\n },\n};\n"]} \ No newline at end of file diff --git a/packages/expo-media-library/build/MediaLibrary.d.ts b/packages/expo-media-library/build/MediaLibrary.d.ts index 7e8065b986e388..2440d00a32e8af 100644 --- a/packages/expo-media-library/build/MediaLibrary.d.ts +++ b/packages/expo-media-library/build/MediaLibrary.d.ts @@ -1,6 +1,12 @@ import { Subscription } from '@unimodules/core'; import { PermissionResponse as EXPermissionResponse, PermissionStatus, PermissionExpiration } from 'expo-modules-core'; export declare type PermissionResponse = EXPermissionResponse & { + /** + * Indicates if your app has access to the whole or only part of the photo library. Possible values are: + * - `'all'` if the user granted your app access to the whole photo library + * - `'limited'` if the user granted your app access only to selected photos (only available on iOS 14.0+) + * - `'none'` if user denied or hasn't yet granted the permission + */ accessPrivileges?: 'all' | 'limited' | 'none'; }; export declare type MediaTypeValue = 'audio' | 'photo' | 'video' | 'unknown'; @@ -22,35 +28,113 @@ export declare type SortByObject = { duration: 'duration'; }; export declare type Asset = { + /** + * Internal ID that represents an asset. + */ id: string; + /** + * Filename of the asset. + */ filename: string; + /** + * URI that points to the asset. `assets://*` (iOS), `file://*` (Android) + */ uri: string; + /** + * Media type. + */ mediaType: MediaTypeValue; - mediaSubtypes?: string[]; + /** + * __iOS Only.__ An array of media subtypes. + */ + mediaSubtypes?: MediaSubtype[]; + /** + * Width of the image or video. + */ width: number; + /** + * Height of the image or video. + */ height: number; + /** + * File creation timestamp. + */ creationTime: number; + /** + * Last modification timestamp. + */ modificationTime: number; + /** + * Duration of the video or audio asset in seconds. + */ duration: number; + /** + * __Android Only.__ Album ID that the asset belongs to. + */ albumId?: string; }; export declare type AssetInfo = Asset & { + /** + * Local URI for the asset. + */ localUri?: string; + /** + * GPS location if available. + */ location?: Location; + /** + * EXIF metadata associated with the image. + */ exif?: object; + /** + * __iOS Only.__ Whether the asset is marked as favorite. + */ isFavorite?: boolean; + /** + * __iOS Only.__ This field is available only if flag `shouldDownloadFromNetwork` is set to `false`. + * Whether the asset is stored on the network (iCloud on iOS) + */ isNetworkAsset?: boolean; + /** + * __iOS Only.__ Display orientation of the image. Orientation is available only for assets whose + * `mediaType` is `MediaType.photo`. Value will range from 1 to 8, see [EXIF orientation specification](http://sylvana.net/jpegcrop/exif_orientation.html) + * for more details. + */ + orientation?: number; }; +export declare type MediaSubtype = 'depthEffect' | 'hdr' | 'highFrameRate' | 'livePhoto' | 'panorama' | 'screenshot' | 'stream' | 'timelapse'; export declare type MediaLibraryAssetInfoQueryOptions = { + /** + * Whether allow the asset to be downloaded from network. Only available in iOS with iCloud assets. + * @default `true` + */ shouldDownloadFromNetwork?: boolean; }; export declare type MediaLibraryAssetsChangeEvent = { - hasIncrementalChanges: false; -} | { - hasIncrementalChanges: true; - insertedAssets: Asset[]; - deletedAssets: Asset[]; - updatedAssets: Asset[]; + /** + * Whether the media library's changes could be described as "incremental changes". + * `true` indicates the changes are described by the `insertedAssets`, `deletedAssets` and + * `updatedAssets` values. `false` indicates that the scope of changes is too large and you + * should perform a full assets reload (eg. a user has changed access to individual assets in the + * media library). + */ + hasIncrementalChanges: boolean; + /** + * Available only if `hasIncrementalChanges` is `true`. + * Array of [`Asset`](#asset)s that have been inserted to the library. + */ + insertedAssets?: Asset[]; + /** + * Available only if `hasIncrementalChanges` is `true`. + * Array of [`Asset`](#asset)s that have been deleted from the library. + */ + deletedAssets?: Asset[]; + /** + * Available only if `hasIncrementalChanges` is `true`. + * Array of [`Asset`](#asset)s that have been updated or completed downloading from network + * storage (iCloud on iOS). + */ + updatedAssets?: Asset[]; }; export declare type Location = { latitude: number; @@ -59,63 +143,265 @@ export declare type Location = { export declare type Album = { id: string; title: string; + /** + * Estimated number of assets in the album + */ assetCount: number; - type?: string; + /** + * __iOS Only.__ The type of the assets album. + */ + type?: AlbumType; + /** + * __iOS Only.__ Apply only to albums whose type is `'moment'`. Earliest creation timestamp of all assets in the moment. + */ startTime: number; + /** + * __iOS Only.__ Apply only to albums whose type is `'moment'`. Latest creation timestamp of all assets in the moment. + */ endTime: number; + /** + * __iOS Only.__ Apply only to albums whose type is `'moment'`. Approximated location of all assets in the moment. + */ approximateLocation?: Location; + /** + * __iOS Only.__ Apply only to albums whose type is `'moment'`. Names of locations grouped in the moment. + */ locationNames?: string[]; }; +export declare type AlbumType = 'album' | 'moment' | 'smartAlbum'; export declare type AlbumsOptions = { includeSmartAlbums?: boolean; }; export declare type AssetsOptions = { + /** + * The maximum number of items on a single page. + * @default `20` + */ first?: number; + /** + * Asset ID of the last item returned on the previous page. + */ after?: AssetRef; + /** + * [Album](#album) or its ID to get assets from specific album. + */ album?: AlbumRef; + /** + * An array of [`SortByValue`](#sortbyvalue)s or a single `SortByValue` value. By default, all + * keys are sorted in descending order, however you can also pass a pair `[key, ascending]` where + * the second item is a `boolean` value that means whether to use ascending order. Note that if + * the `SortBy.default` key is used, then `ascending` argument will not matter. Earlier items have + * higher priority when sorting out the results. + * If empty, this method will use the default sorting that is provided by the platform. + */ sortBy?: SortByValue[] | SortByValue; + /** + * An array of [MediaTypeValue](#expomedialibrarymediatypevalue)s or a single `MediaTypeValue`. + * @default `MediaType.photo` + */ mediaType?: MediaTypeValue[] | MediaTypeValue; + /** + * `Date` object or Unix timestamp in milliseconds limiting returned assets only to those that + * were created after this date. + */ createdAfter?: Date | number; + /** + * Similarly as `createdAfter`, but limits assets only to those that were created before specified + * date. + */ createdBefore?: Date | number; }; export declare type PagedInfo = { + /** + * A page of [`Asset`](#asset)s fetched by the query. + */ assets: T[]; + /** + * ID of the last fetched asset. It should be passed as `after` option in order to get the + * next page. + */ endCursor: string; + /** + * Whether there are more assets to fetch. + */ hasNextPage: boolean; + /** + * Estimated total number of assets that match the query. + */ totalCount: number; }; export declare type AssetRef = Asset | string; export declare type AlbumRef = Album | string; -export { PermissionStatus, PermissionExpiration }; +export { PermissionStatus, PermissionExpiration, EXPermissionResponse, Subscription }; +/** + * Possible media types. + */ export declare const MediaType: MediaTypeObject; +/** + * Supported keys that can be used to sort `getAssetsAsync` results. + */ export declare const SortBy: SortByObject; +/** + * Returns whether the Media Library API is enabled on the current device. + * @return A promise which fulfils with a `boolean`, indicating whether the Media Library API is + * available on the current device. + */ export declare function isAvailableAsync(): Promise; +/** + * Asks the user to grant permissions for accessing media in user's media library. + * @param writeOnly + * @return A promise that fulfils with [`PermissionResponse`](#permissionresponse) object. + */ export declare function requestPermissionsAsync(writeOnly?: boolean): Promise; +/** + * Checks user's permissions for accessing media library. + * @param writeOnly + * @return A promise that fulfils with [`PermissionResponse`](#permissionresponse) object. + */ export declare function getPermissionsAsync(writeOnly?: boolean): Promise; /** - * @iOS-only - * @throws Will throw an error if called on platform that doesn't support this functionality (eg. iOS < 14, Android, etc.). + * __Available only on iOS >= 14.__ Allows the user to update the assets that your app has access to. + * The system modal is only displayed if the user originally allowed only `limited` access to their + * media library, otherwise this method is a no-op. + * @return A promise that either rejects if the method is unavailable (meaning the device is not + * running iOS >= 14), or resolves to `void`. + * > __Note:__ This method doesn't inform you if the user changes which assets your app has access to. + * For that information, you need to subscribe for updates to the user's media library using [addListener(listener)](#medialibraryaddlistenerlistener). + * If `hasIncrementalChanges` is `false`, the user changed their permissions. */ export declare function presentPermissionsPickerAsync(): Promise; +/** + * Creates an asset from existing file. The most common use case is to save a picture taken by [Camera](../camera). + * This method requires `CAMERA_ROLL` permission. + * + * # Example + * ```js + * const { uri } = await Camera.takePictureAsync(); + * const asset = await MediaLibrary.createAssetAsync(uri); + * ``` + * @param localUri A URI to the image or video file. It must contain an extension. On Android it + * must be a local path, so it must start with `file:///` + * @return A promise which fulfils with an object representing an [`Asset`](#asset). + */ export declare function createAssetAsync(localUri: string): Promise; +/** + * Saves the file at given `localUri` to the user's media library. Unlike [`createAssetAsync()`](#medialibrarycreateassetasynclocaluri), + * This method doesn't return created asset. + * On __iOS 11+__, it's possible to use this method without asking for `CAMERA_ROLL` permission, + * however then yours `Info.plist` should have `NSPhotoLibraryAddUsageDescription` key. + * @param localUri A URI to the image or video file. It must contain an extension. On Android it + * must be a local path, so it must start with `file:///`. + */ export declare function saveToLibraryAsync(localUri: string): Promise; -export declare function addAssetsToAlbumAsync(assets: AssetRef[] | AssetRef, album: AlbumRef, copy?: boolean): Promise; -export declare function removeAssetsFromAlbumAsync(assets: AssetRef[] | AssetRef, album: AlbumRef): Promise; -export declare function deleteAssetsAsync(assets: AssetRef[] | AssetRef): Promise; +/** + * Adds array of assets to the album. + * + * On Android, by default it copies assets from the current album to provided one, however it's also + * possible to move them by passing `false` as `copyAssets` argument.In case they're copied you + * should keep in mind that `getAssetsAsync` will return duplicated assets. + * @param assets An array of [Asset](#asset) or their IDs. + * @param album An [Album](#album) or its ID. + * @param copy __Android only.__ Whether to copy assets to the new album instead of move them. + * Defaults to `true`. + * @return Returns promise which fulfils with `true` if the assets were successfully added to + * the album. + */ +export declare function addAssetsToAlbumAsync(assets: AssetRef[] | AssetRef, album: AlbumRef, copy?: boolean): Promise; +/** + * Removes given assets from album. + * + * On Android, album will be automatically deleted if there are no more assets inside. + * @param assets An array of [Asset](#asset) or their IDs. + * @param album An [Album](#album) or its ID. + * @return Returns promise which fulfils with `true` if the assets were successfully removed from + * the album. + */ +export declare function removeAssetsFromAlbumAsync(assets: AssetRef[] | AssetRef, album: AlbumRef): Promise; +/** + * Deletes assets from the library. On iOS it deletes assets from all albums they belong to, while + * on Android it keeps all copies of them (album is strictly connected to the asset). Also, there is + * additional dialog on iOS that requires user to confirm this action. + * @param assets An array of [Asset](#asset) or their IDs. + * @return Returns promise which fulfils with `true` if the assets were successfully deleted. + */ +export declare function deleteAssetsAsync(assets: AssetRef[] | AssetRef): Promise; +/** + * Provides more information about an asset, including GPS location, local URI and EXIF metadata. + * @param asset An [Asset](#asset) or its ID. + * @param options + * @return [AssetInfo](#assetinfo) object, which is an `Asset` extended by an additional fields. + */ export declare function getAssetInfoAsync(asset: AssetRef, options?: MediaLibraryAssetInfoQueryOptions): Promise; +/** + * Queries for user-created albums in media gallery. + * @return A promise which fulfils with an array of [`Album`](#asset)s. Depending on Android version, + * root directory of your storage may be listed as album titled `"0"` or unlisted at all. + */ export declare function getAlbumsAsync({ includeSmartAlbums }?: AlbumsOptions): Promise; +/** + * Queries for an album with a specific name. + * @param title Name of the album to look for. + * @return An object representing an [`Album`](#album), if album with given name exists, otherwise + * returns `null`. + */ export declare function getAlbumAsync(title: string): Promise; +/** + * Creates an album with given name and initial asset. The asset parameter is required on Android, + * since it's not possible to create empty album on this platform. On Android, by default it copies + * given asset from the current album to the new one, however it's also possible to move it by + * passing `false` as `copyAsset` argument. + * In case it's copied you should keep in mind that `getAssetsAsync` will return duplicated asset. + * @param albumName Name of the album to create. + * @param asset An [Asset](#asset) or its ID (required on Android). + * @param copyAsset __Android Only.__ Whether to copy asset to the new album instead of move it. + * Defaults to `true`. + * @return Newly created [`Album`](#album). + */ export declare function createAlbumAsync(albumName: string, asset?: AssetRef, copyAsset?: boolean): Promise; -export declare function deleteAlbumsAsync(albums: AlbumRef[] | AlbumRef, assetRemove?: boolean): Promise; +/** + * Deletes given albums from the library. On Android by default it deletes assets belonging to given + * albums from the library. On iOS it doesn't delete these assets, however it's possible to do by + * passing `true` as `deleteAssets`. + * @param albums An array of [`Album`](#asset)s or their IDs. + * @param assetRemove __iOS Only.__ Whether to also delete assets belonging to given albums. + * Defaults to `false`. + * @return Returns a promise which fulfils with `true` if the albums were successfully deleted from + * the library. + */ +export declare function deleteAlbumsAsync(albums: AlbumRef[] | AlbumRef, assetRemove?: boolean): Promise; +/** + * Fetches a page of assets matching the provided criteria. + * @param assetsOptions + * @return A promise that fulfils with to [`PagedInfo`](#pagedinfo) object with array of [`Asset`](#asset)s. + */ export declare function getAssetsAsync(assetsOptions?: AssetsOptions): Promise>; +/** + * Subscribes for updates in user's media library. + * @param listener A callback that is fired when any assets have been inserted or deleted from the + * library, or when the user changes which assets they're allowing access to. On Android it's + * invoked with an empty object. On iOS it's invoked with [`MediaLibraryAssetsChangeEvent`](#medialibraryassetschangeevent) + * object. + * @return An [`Subscription`](#subscription) object that you can call `remove()` on when you would + * like to unsubscribe the listener. + */ export declare function addListener(listener: (event: MediaLibraryAssetsChangeEvent) => void): Subscription; export declare function removeSubscription(subscription: Subscription): void; +/** + * Removes all listeners. + */ export declare function removeAllListeners(): void; +/** + * __iOS Only.__ Fetches a list of moments, which is a group of assets taken around the same place + * and time. + * @return An array of [albums](#album) whose type is `moment`. + */ export declare function getMomentsAsync(): Promise; /** - * Moves content of provided album to the special media directories on **Android R** or **above** if needed. + * Moves album content to the special media directories on **Android R** or **above** if needed. + * Those new locations are in line with the Android `scoped storage` - so your application won't + * lose write permission to those directories in the future. * - * This method won't do anything if: + * This method does nothing if: * - app is running on **iOS**, **web** or **Android below R** * - app has **write permission** to the album folder * @@ -124,15 +410,23 @@ export declare function getMomentsAsync(): Promise; * If automatic migration isn't possible, the function will be rejected. * In that case, you can use methods from the `expo-file-system` to migrate all your files manually. * - * @param album + * # Why do you need to migrate files? + * __Android R__ introduced a lot of changes in the storage system. Now applications can't save + * anything to the root directory. The only available locations are from the `MediaStore` API. + * Unfortunately, the media library stored albums in folders for which, because of those changes, + * the application doesn't have permissions anymore. However, it doesn't mean you need to migrate + * all your albums. If your application doesn't add assets to albums, you don't have to migrate. + * Everything will work as it used to. You can read more about scoped storage in [the Android documentation](https://developer.android.com/about/versions/11/privacy/storage). + * + * @param album An [Album](#album) or its ID. + * @return A promise which fulfils to `void`. */ export declare function migrateAlbumIfNeededAsync(album: AlbumRef): Promise; /** - * Checks if provided album should be migrated. - * In other words, it checks if the application has the write permission to the album folder. - * - * This method always returns **false** for all android versions **below Android R**, **iOS** or **web**. - * - * @param album + * Checks if the album should be migrated to a different location. In other words, it checks if the + * application has the write permission to the album folder. If not, it returns `true`, otherwise `false`. + * > Note: For **Android below R**, **web** or **iOS**, this function always returns `false`. + * @param album An [Album](#album) or its ID. + * @return Returns a promise which fulfils with `true` if the album should be migrated. */ export declare function albumNeedsMigrationAsync(album: AlbumRef): Promise; diff --git a/packages/expo-media-library/build/MediaLibrary.js b/packages/expo-media-library/build/MediaLibrary.js index 96b17fe6f09b57..cbbb4536a4c6a8 100644 --- a/packages/expo-media-library/build/MediaLibrary.js +++ b/packages/expo-media-library/build/MediaLibrary.js @@ -50,27 +50,59 @@ function checkSortByKey(sortBy) { function dateToNumber(value) { return value instanceof Date ? value.getTime() : value; } -// export constants +// @needsAudit +/** + * Possible media types. + */ export const MediaType = MediaLibrary.MediaType; +// @needsAudit +/** + * Supported keys that can be used to sort `getAssetsAsync` results. + */ export const SortBy = MediaLibrary.SortBy; +// @needsAudit +/** + * Returns whether the Media Library API is enabled on the current device. + * @return A promise which fulfils with a `boolean`, indicating whether the Media Library API is + * available on the current device. + */ export async function isAvailableAsync() { return !!MediaLibrary && 'getAssetsAsync' in MediaLibrary; } +// @needsAudit @docsMissing +/** + * Asks the user to grant permissions for accessing media in user's media library. + * @param writeOnly + * @return A promise that fulfils with [`PermissionResponse`](#permissionresponse) object. + */ export async function requestPermissionsAsync(writeOnly = false) { if (!MediaLibrary.requestPermissionsAsync) { throw new UnavailabilityError('MediaLibrary', 'requestPermissionsAsync'); } return await MediaLibrary.requestPermissionsAsync(writeOnly); } +// @needsAudit @docsMissing +/** + * Checks user's permissions for accessing media library. + * @param writeOnly + * @return A promise that fulfils with [`PermissionResponse`](#permissionresponse) object. + */ export async function getPermissionsAsync(writeOnly = false) { if (!MediaLibrary.getPermissionsAsync) { throw new UnavailabilityError('MediaLibrary', 'getPermissionsAsync'); } return await MediaLibrary.getPermissionsAsync(writeOnly); } +// @needsAudit /** - * @iOS-only - * @throws Will throw an error if called on platform that doesn't support this functionality (eg. iOS < 14, Android, etc.). + * __Available only on iOS >= 14.__ Allows the user to update the assets that your app has access to. + * The system modal is only displayed if the user originally allowed only `limited` access to their + * media library, otherwise this method is a no-op. + * @return A promise that either rejects if the method is unavailable (meaning the device is not + * running iOS >= 14), or resolves to `void`. + * > __Note:__ This method doesn't inform you if the user changes which assets your app has access to. + * For that information, you need to subscribe for updates to the user's media library using [addListener(listener)](#medialibraryaddlistenerlistener). + * If `hasIncrementalChanges` is `false`, the user changed their permissions. */ export async function presentPermissionsPickerAsync() { if (!MediaLibrary.presentPermissionsPickerAsync) { @@ -78,6 +110,20 @@ export async function presentPermissionsPickerAsync() { } return await MediaLibrary.presentPermissionsPickerAsync(); } +// @needsAudit +/** + * Creates an asset from existing file. The most common use case is to save a picture taken by [Camera](../camera). + * This method requires `CAMERA_ROLL` permission. + * + * # Example + * ```js + * const { uri } = await Camera.takePictureAsync(); + * const asset = await MediaLibrary.createAssetAsync(uri); + * ``` + * @param localUri A URI to the image or video file. It must contain an extension. On Android it + * must be a local path, so it must start with `file:///` + * @return A promise which fulfils with an object representing an [`Asset`](#asset). + */ export async function createAssetAsync(localUri) { if (!MediaLibrary.createAssetAsync) { throw new UnavailabilityError('MediaLibrary', 'createAssetAsync'); @@ -92,12 +138,35 @@ export async function createAssetAsync(localUri) { } return asset; } +// @needsAudit +/** + * Saves the file at given `localUri` to the user's media library. Unlike [`createAssetAsync()`](#medialibrarycreateassetasynclocaluri), + * This method doesn't return created asset. + * On __iOS 11+__, it's possible to use this method without asking for `CAMERA_ROLL` permission, + * however then yours `Info.plist` should have `NSPhotoLibraryAddUsageDescription` key. + * @param localUri A URI to the image or video file. It must contain an extension. On Android it + * must be a local path, so it must start with `file:///`. + */ export async function saveToLibraryAsync(localUri) { if (!MediaLibrary.saveToLibraryAsync) { throw new UnavailabilityError('MediaLibrary', 'saveToLibraryAsync'); } return await MediaLibrary.saveToLibraryAsync(localUri); } +// @needsAudit +/** + * Adds array of assets to the album. + * + * On Android, by default it copies assets from the current album to provided one, however it's also + * possible to move them by passing `false` as `copyAssets` argument.In case they're copied you + * should keep in mind that `getAssetsAsync` will return duplicated assets. + * @param assets An array of [Asset](#asset) or their IDs. + * @param album An [Album](#album) or its ID. + * @param copy __Android only.__ Whether to copy assets to the new album instead of move them. + * Defaults to `true`. + * @return Returns promise which fulfils with `true` if the assets were successfully added to + * the album. + */ export async function addAssetsToAlbumAsync(assets, album, copy = true) { if (!MediaLibrary.addAssetsToAlbumAsync) { throw new UnavailabilityError('MediaLibrary', 'addAssetsToAlbumAsync'); @@ -113,6 +182,16 @@ export async function addAssetsToAlbumAsync(assets, album, copy = true) { } return await MediaLibrary.addAssetsToAlbumAsync(assetIds, albumId, !!copy); } +// @needsAudit +/** + * Removes given assets from album. + * + * On Android, album will be automatically deleted if there are no more assets inside. + * @param assets An array of [Asset](#asset) or their IDs. + * @param album An [Album](#album) or its ID. + * @return Returns promise which fulfils with `true` if the assets were successfully removed from + * the album. + */ export async function removeAssetsFromAlbumAsync(assets, album) { if (!MediaLibrary.removeAssetsFromAlbumAsync) { throw new UnavailabilityError('MediaLibrary', 'removeAssetsFromAlbumAsync'); @@ -122,6 +201,14 @@ export async function removeAssetsFromAlbumAsync(assets, album) { checkAssetIds(assetIds); return await MediaLibrary.removeAssetsFromAlbumAsync(assetIds, albumId); } +// @needsAudit +/** + * Deletes assets from the library. On iOS it deletes assets from all albums they belong to, while + * on Android it keeps all copies of them (album is strictly connected to the asset). Also, there is + * additional dialog on iOS that requires user to confirm this action. + * @param assets An array of [Asset](#asset) or their IDs. + * @return Returns promise which fulfils with `true` if the assets were successfully deleted. + */ export async function deleteAssetsAsync(assets) { if (!MediaLibrary.deleteAssetsAsync) { throw new UnavailabilityError('MediaLibrary', 'deleteAssetsAsync'); @@ -130,6 +217,13 @@ export async function deleteAssetsAsync(assets) { checkAssetIds(assetIds); return await MediaLibrary.deleteAssetsAsync(assetIds); } +// @needsAudit +/** + * Provides more information about an asset, including GPS location, local URI and EXIF metadata. + * @param asset An [Asset](#asset) or its ID. + * @param options + * @return [AssetInfo](#assetinfo) object, which is an `Asset` extended by an additional fields. + */ export async function getAssetInfoAsync(asset, options = { shouldDownloadFromNetwork: true }) { if (!MediaLibrary.getAssetInfoAsync) { throw new UnavailabilityError('MediaLibrary', 'getAssetInfoAsync'); @@ -143,12 +237,25 @@ export async function getAssetInfoAsync(asset, options = { shouldDownloadFromNet } return assetInfo; } +// @needsAudit +/** + * Queries for user-created albums in media gallery. + * @return A promise which fulfils with an array of [`Album`](#asset)s. Depending on Android version, + * root directory of your storage may be listed as album titled `"0"` or unlisted at all. + */ export async function getAlbumsAsync({ includeSmartAlbums = false } = {}) { if (!MediaLibrary.getAlbumsAsync) { throw new UnavailabilityError('MediaLibrary', 'getAlbumsAsync'); } return await MediaLibrary.getAlbumsAsync({ includeSmartAlbums }); } +// @needsAudit +/** + * Queries for an album with a specific name. + * @param title Name of the album to look for. + * @return An object representing an [`Album`](#album), if album with given name exists, otherwise + * returns `null`. + */ export async function getAlbumAsync(title) { if (!MediaLibrary.getAlbumAsync) { throw new UnavailabilityError('MediaLibrary', 'getAlbumAsync'); @@ -158,6 +265,19 @@ export async function getAlbumAsync(title) { } return await MediaLibrary.getAlbumAsync(title); } +// @needsAudit +/** + * Creates an album with given name and initial asset. The asset parameter is required on Android, + * since it's not possible to create empty album on this platform. On Android, by default it copies + * given asset from the current album to the new one, however it's also possible to move it by + * passing `false` as `copyAsset` argument. + * In case it's copied you should keep in mind that `getAssetsAsync` will return duplicated asset. + * @param albumName Name of the album to create. + * @param asset An [Asset](#asset) or its ID (required on Android). + * @param copyAsset __Android Only.__ Whether to copy asset to the new album instead of move it. + * Defaults to `true`. + * @return Newly created [`Album`](#album). + */ export async function createAlbumAsync(albumName, asset, copyAsset = true) { if (!MediaLibrary.createAlbumAsync) { throw new UnavailabilityError('MediaLibrary', 'createAlbumAsync'); @@ -178,6 +298,17 @@ export async function createAlbumAsync(albumName, asset, copyAsset = true) { } return await MediaLibrary.createAlbumAsync(albumName, assetId, !!copyAsset); } +// @needsAudit +/** + * Deletes given albums from the library. On Android by default it deletes assets belonging to given + * albums from the library. On iOS it doesn't delete these assets, however it's possible to do by + * passing `true` as `deleteAssets`. + * @param albums An array of [`Album`](#asset)s or their IDs. + * @param assetRemove __iOS Only.__ Whether to also delete assets belonging to given albums. + * Defaults to `false`. + * @return Returns a promise which fulfils with `true` if the albums were successfully deleted from + * the library. + */ export async function deleteAlbumsAsync(albums, assetRemove = false) { if (!MediaLibrary.deleteAlbumsAsync) { throw new UnavailabilityError('MediaLibrary', 'deleteAlbumsAsync'); @@ -189,6 +320,12 @@ export async function deleteAlbumsAsync(albums, assetRemove = false) { } return await MediaLibrary.deleteAlbumsAsync(albumIds, !!assetRemove); } +// @needsAudit +/** + * Fetches a page of assets matching the provided criteria. + * @param assetsOptions + * @return A promise that fulfils with to [`PagedInfo`](#pagedinfo) object with array of [`Asset`](#asset)s. + */ export async function getAssetsAsync(assetsOptions = {}) { if (!MediaLibrary.getAssetsAsync) { throw new UnavailabilityError('MediaLibrary', 'getAssetsAsync'); @@ -222,28 +359,49 @@ export async function getAssetsAsync(assetsOptions = {}) { options.mediaType.forEach(checkMediaType); return await MediaLibrary.getAssetsAsync(options); } +// @needsAudit +/** + * Subscribes for updates in user's media library. + * @param listener A callback that is fired when any assets have been inserted or deleted from the + * library, or when the user changes which assets they're allowing access to. On Android it's + * invoked with an empty object. On iOS it's invoked with [`MediaLibraryAssetsChangeEvent`](#medialibraryassetschangeevent) + * object. + * @return An [`Subscription`](#subscription) object that you can call `remove()` on when you would + * like to unsubscribe the listener. + */ export function addListener(listener) { - const subscription = eventEmitter.addListener(MediaLibrary.CHANGE_LISTENER_NAME, listener); - return subscription; + return eventEmitter.addListener(MediaLibrary.CHANGE_LISTENER_NAME, listener); } +// @docsMissing export function removeSubscription(subscription) { subscription.remove(); } +// @needsAudit +/** + * Removes all listeners. + */ export function removeAllListeners() { eventEmitter.removeAllListeners(MediaLibrary.CHANGE_LISTENER_NAME); } -// iOS only +// @needsAudit +/** + * __iOS Only.__ Fetches a list of moments, which is a group of assets taken around the same place + * and time. + * @return An array of [albums](#album) whose type is `moment`. + */ export async function getMomentsAsync() { if (!MediaLibrary.getMomentsAsync) { throw new UnavailabilityError('MediaLibrary', 'getMomentsAsync'); } return await MediaLibrary.getMomentsAsync(); } -// Android only +// @needsAudit /** - * Moves content of provided album to the special media directories on **Android R** or **above** if needed. + * Moves album content to the special media directories on **Android R** or **above** if needed. + * Those new locations are in line with the Android `scoped storage` - so your application won't + * lose write permission to those directories in the future. * - * This method won't do anything if: + * This method does nothing if: * - app is running on **iOS**, **web** or **Android below R** * - app has **write permission** to the album folder * @@ -252,7 +410,16 @@ export async function getMomentsAsync() { * If automatic migration isn't possible, the function will be rejected. * In that case, you can use methods from the `expo-file-system` to migrate all your files manually. * - * @param album + * # Why do you need to migrate files? + * __Android R__ introduced a lot of changes in the storage system. Now applications can't save + * anything to the root directory. The only available locations are from the `MediaStore` API. + * Unfortunately, the media library stored albums in folders for which, because of those changes, + * the application doesn't have permissions anymore. However, it doesn't mean you need to migrate + * all your albums. If your application doesn't add assets to albums, you don't have to migrate. + * Everything will work as it used to. You can read more about scoped storage in [the Android documentation](https://developer.android.com/about/versions/11/privacy/storage). + * + * @param album An [Album](#album) or its ID. + * @return A promise which fulfils to `void`. */ export async function migrateAlbumIfNeededAsync(album) { if (!MediaLibrary.migrateAlbumIfNeededAsync) { @@ -260,14 +427,13 @@ export async function migrateAlbumIfNeededAsync(album) { } return await MediaLibrary.migrateAlbumIfNeededAsync(getId(album)); } -// Android only +// @needsAudit /** - * Checks if provided album should be migrated. - * In other words, it checks if the application has the write permission to the album folder. - * - * This method always returns **false** for all android versions **below Android R**, **iOS** or **web**. - * - * @param album + * Checks if the album should be migrated to a different location. In other words, it checks if the + * application has the write permission to the album folder. If not, it returns `true`, otherwise `false`. + * > Note: For **Android below R**, **web** or **iOS**, this function always returns `false`. + * @param album An [Album](#album) or its ID. + * @return Returns a promise which fulfils with `true` if the album should be migrated. */ export async function albumNeedsMigrationAsync(album) { if (!MediaLibrary.albumNeedsMigrationAsync) { diff --git a/packages/expo-media-library/build/MediaLibrary.js.map b/packages/expo-media-library/build/MediaLibrary.js.map index 63537b287cbf81..3f5ce31a0c5792 100644 --- a/packages/expo-media-library/build/MediaLibrary.js.map +++ b/packages/expo-media-library/build/MediaLibrary.js.map @@ -1 +1 @@ -{"version":3,"file":"MediaLibrary.js","sourceRoot":"","sources":["../src/MediaLibrary.ts"],"names":[],"mappings":"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","sourcesContent":["import { EventEmitter, Subscription, UnavailabilityError } from '@unimodules/core';\nimport {\n PermissionResponse as EXPermissionResponse,\n PermissionStatus,\n PermissionExpiration,\n} from 'expo-modules-core';\nimport { Platform } from 'react-native';\n\nimport MediaLibrary from './ExponentMediaLibrary';\n\nconst eventEmitter = new EventEmitter(MediaLibrary);\n\nexport type PermissionResponse = EXPermissionResponse & {\n // iOS only\n accessPrivileges?: 'all' | 'limited' | 'none';\n};\n\nexport type MediaTypeValue = 'audio' | 'photo' | 'video' | 'unknown';\nexport type SortByKey =\n | 'default'\n | 'mediaType'\n | 'width'\n | 'height'\n | 'creationTime'\n | 'modificationTime'\n | 'duration';\nexport type SortByValue = [SortByKey, boolean] | SortByKey;\n\nexport type MediaTypeObject = {\n audio: 'audio';\n photo: 'photo';\n video: 'video';\n unknown: 'unknown';\n};\n\nexport type SortByObject = {\n default: 'default';\n mediaType: 'mediaType';\n width: 'width';\n height: 'height';\n creationTime: 'creationTime';\n modificationTime: 'modificationTime';\n duration: 'duration';\n};\n\nexport type Asset = {\n id: string;\n filename: string;\n uri: string;\n mediaType: MediaTypeValue;\n mediaSubtypes?: string[]; // iOS only\n width: number;\n height: number;\n creationTime: number;\n modificationTime: number;\n duration: number;\n albumId?: string; // Android only\n};\n\nexport type AssetInfo = Asset & {\n localUri?: string;\n location?: Location;\n exif?: object;\n isFavorite?: boolean; //iOS only\n isNetworkAsset?: boolean; //iOS only\n};\n\nexport type MediaLibraryAssetInfoQueryOptions = {\n shouldDownloadFromNetwork?: boolean;\n};\n\nexport type MediaLibraryAssetsChangeEvent =\n | {\n hasIncrementalChanges: false;\n }\n | {\n hasIncrementalChanges: true;\n insertedAssets: Asset[];\n deletedAssets: Asset[];\n updatedAssets: Asset[];\n };\n\nexport type Location = {\n latitude: number;\n longitude: number;\n};\n\nexport type Album = {\n id: string;\n title: string;\n assetCount: number;\n type?: string; // iOS only\n\n // iOS moments only\n startTime: number;\n endTime: number;\n approximateLocation?: Location;\n locationNames?: string[];\n};\n\nexport type AlbumsOptions = {\n // iOS only\n includeSmartAlbums?: boolean;\n};\n\nexport type AssetsOptions = {\n first?: number;\n after?: AssetRef;\n album?: AlbumRef;\n sortBy?: SortByValue[] | SortByValue;\n mediaType?: MediaTypeValue[] | MediaTypeValue;\n createdAfter?: Date | number;\n createdBefore?: Date | number;\n};\n\nexport type PagedInfo = {\n assets: T[];\n endCursor: string;\n hasNextPage: boolean;\n totalCount: number;\n};\n\nexport type AssetRef = Asset | string;\nexport type AlbumRef = Album | string;\n\nexport { PermissionStatus, PermissionExpiration };\n\nfunction arrayize(item: any): any[] {\n if (Array.isArray(item)) {\n return item;\n }\n return item ? [item] : [];\n}\n\nfunction getId(ref: any): string | undefined {\n if (typeof ref === 'string') {\n return ref;\n }\n return ref ? ref.id : undefined;\n}\n\nfunction checkAssetIds(assetIds: any): void {\n if (assetIds.some(id => !id || typeof id !== 'string')) {\n throw new Error('Asset ID must be a string!');\n }\n}\n\nfunction checkAlbumIds(albumIds: any): void {\n if (albumIds.some(id => !id || typeof id !== 'string')) {\n throw new Error('Album ID must be a string!');\n }\n}\n\nfunction checkMediaType(mediaType: any): void {\n if (Object.values(MediaType).indexOf(mediaType) === -1) {\n throw new Error(`Invalid mediaType: ${mediaType}`);\n }\n}\n\nfunction checkSortBy(sortBy: any): void {\n if (Array.isArray(sortBy)) {\n checkSortByKey(sortBy[0]);\n\n if (typeof sortBy[1] !== 'boolean') {\n throw new Error('Invalid sortBy array argument. Second item must be a boolean!');\n }\n } else {\n checkSortByKey(sortBy);\n }\n}\n\nfunction checkSortByKey(sortBy: any): void {\n if (Object.values(SortBy).indexOf(sortBy) === -1) {\n throw new Error(`Invalid sortBy key: ${sortBy}`);\n }\n}\n\nfunction dateToNumber(value?: Date | number): number | undefined {\n return value instanceof Date ? value.getTime() : value;\n}\n\n// export constants\nexport const MediaType: MediaTypeObject = MediaLibrary.MediaType;\nexport const SortBy: SortByObject = MediaLibrary.SortBy;\n\nexport async function isAvailableAsync(): Promise {\n return !!MediaLibrary && 'getAssetsAsync' in MediaLibrary;\n}\n\nexport async function requestPermissionsAsync(\n writeOnly: boolean = false\n): Promise {\n if (!MediaLibrary.requestPermissionsAsync) {\n throw new UnavailabilityError('MediaLibrary', 'requestPermissionsAsync');\n }\n return await MediaLibrary.requestPermissionsAsync(writeOnly);\n}\n\nexport async function getPermissionsAsync(writeOnly: boolean = false): Promise {\n if (!MediaLibrary.getPermissionsAsync) {\n throw new UnavailabilityError('MediaLibrary', 'getPermissionsAsync');\n }\n return await MediaLibrary.getPermissionsAsync(writeOnly);\n}\n\n/**\n * @iOS-only\n * @throws Will throw an error if called on platform that doesn't support this functionality (eg. iOS < 14, Android, etc.).\n */\nexport async function presentPermissionsPickerAsync(): Promise {\n if (!MediaLibrary.presentPermissionsPickerAsync) {\n throw new UnavailabilityError('MediaLibrary', 'presentPermissionsPickerAsync');\n }\n return await MediaLibrary.presentPermissionsPickerAsync();\n}\n\nexport async function createAssetAsync(localUri: string): Promise {\n if (!MediaLibrary.createAssetAsync) {\n throw new UnavailabilityError('MediaLibrary', 'createAssetAsync');\n }\n\n if (!localUri || typeof localUri !== 'string') {\n throw new Error('Invalid argument \"localUri\". It must be a string!');\n }\n const asset = await MediaLibrary.createAssetAsync(localUri);\n\n if (Array.isArray(asset)) {\n // Android returns an array with asset, we need to pick the first item\n return asset[0];\n }\n return asset;\n}\n\nexport async function saveToLibraryAsync(localUri: string): Promise {\n if (!MediaLibrary.saveToLibraryAsync) {\n throw new UnavailabilityError('MediaLibrary', 'saveToLibraryAsync');\n }\n return await MediaLibrary.saveToLibraryAsync(localUri);\n}\n\nexport async function addAssetsToAlbumAsync(\n assets: AssetRef[] | AssetRef,\n album: AlbumRef,\n copy: boolean = true\n) {\n if (!MediaLibrary.addAssetsToAlbumAsync) {\n throw new UnavailabilityError('MediaLibrary', 'addAssetsToAlbumAsync');\n }\n\n const assetIds = arrayize(assets).map(getId);\n const albumId = getId(album);\n\n checkAssetIds(assetIds);\n\n if (!albumId || typeof albumId !== 'string') {\n throw new Error('Invalid album ID. It must be a string!');\n }\n\n if (Platform.OS === 'ios') {\n return await MediaLibrary.addAssetsToAlbumAsync(assetIds, albumId);\n }\n return await MediaLibrary.addAssetsToAlbumAsync(assetIds, albumId, !!copy);\n}\n\nexport async function removeAssetsFromAlbumAsync(assets: AssetRef[] | AssetRef, album: AlbumRef) {\n if (!MediaLibrary.removeAssetsFromAlbumAsync) {\n throw new UnavailabilityError('MediaLibrary', 'removeAssetsFromAlbumAsync');\n }\n\n const assetIds = arrayize(assets).map(getId);\n const albumId = getId(album);\n\n checkAssetIds(assetIds);\n return await MediaLibrary.removeAssetsFromAlbumAsync(assetIds, albumId);\n}\n\nexport async function deleteAssetsAsync(assets: AssetRef[] | AssetRef) {\n if (!MediaLibrary.deleteAssetsAsync) {\n throw new UnavailabilityError('MediaLibrary', 'deleteAssetsAsync');\n }\n\n const assetIds = arrayize(assets).map(getId);\n\n checkAssetIds(assetIds);\n return await MediaLibrary.deleteAssetsAsync(assetIds);\n}\n\nexport async function getAssetInfoAsync(\n asset: AssetRef,\n options: MediaLibraryAssetInfoQueryOptions = { shouldDownloadFromNetwork: true }\n): Promise {\n if (!MediaLibrary.getAssetInfoAsync) {\n throw new UnavailabilityError('MediaLibrary', 'getAssetInfoAsync');\n }\n\n const assetId = getId(asset);\n\n checkAssetIds([assetId]);\n\n const assetInfo = await MediaLibrary.getAssetInfoAsync(assetId, options);\n\n if (Array.isArray(assetInfo)) {\n // Android returns an array with asset info, we need to pick the first item\n return assetInfo[0];\n }\n return assetInfo;\n}\n\nexport async function getAlbumsAsync({ includeSmartAlbums = false }: AlbumsOptions = {}): Promise<\n Album[]\n> {\n if (!MediaLibrary.getAlbumsAsync) {\n throw new UnavailabilityError('MediaLibrary', 'getAlbumsAsync');\n }\n return await MediaLibrary.getAlbumsAsync({ includeSmartAlbums });\n}\n\nexport async function getAlbumAsync(title: string): Promise {\n if (!MediaLibrary.getAlbumAsync) {\n throw new UnavailabilityError('MediaLibrary', 'getAlbumAsync');\n }\n if (typeof title !== 'string') {\n throw new Error('Album title must be a string!');\n }\n return await MediaLibrary.getAlbumAsync(title);\n}\n\nexport async function createAlbumAsync(\n albumName: string,\n asset?: AssetRef,\n copyAsset: boolean = true\n): Promise {\n if (!MediaLibrary.createAlbumAsync) {\n throw new UnavailabilityError('MediaLibrary', 'createAlbumAsync');\n }\n\n const assetId = getId(asset);\n\n if (Platform.OS === 'android' && (typeof assetId !== 'string' || assetId.length === 0)) {\n // it's not possible to create empty album on Android, so initial asset must be provided\n throw new Error('MediaLibrary.createAlbumAsync must be called with an asset on Android.');\n }\n if (!albumName || typeof albumName !== 'string') {\n throw new Error('Invalid argument \"albumName\". It must be a string!');\n }\n if (assetId != null && typeof assetId !== 'string') {\n throw new Error('Asset ID must be a string!');\n }\n\n if (Platform.OS === 'ios') {\n return await MediaLibrary.createAlbumAsync(albumName, assetId);\n }\n return await MediaLibrary.createAlbumAsync(albumName, assetId, !!copyAsset);\n}\n\nexport async function deleteAlbumsAsync(\n albums: AlbumRef[] | AlbumRef,\n assetRemove: boolean = false\n) {\n if (!MediaLibrary.deleteAlbumsAsync) {\n throw new UnavailabilityError('MediaLibrary', 'deleteAlbumsAsync');\n }\n\n const albumIds = arrayize(albums).map(getId);\n\n checkAlbumIds(albumIds);\n if (Platform.OS === 'android') {\n return await MediaLibrary.deleteAlbumsAsync(albumIds);\n }\n return await MediaLibrary.deleteAlbumsAsync(albumIds, !!assetRemove);\n}\n\nexport async function getAssetsAsync(assetsOptions: AssetsOptions = {}): Promise> {\n if (!MediaLibrary.getAssetsAsync) {\n throw new UnavailabilityError('MediaLibrary', 'getAssetsAsync');\n }\n\n const { first, after, album, sortBy, mediaType, createdAfter, createdBefore } = assetsOptions;\n\n const options = {\n first: first == null ? 20 : first,\n after: getId(after),\n album: getId(album),\n sortBy: arrayize(sortBy),\n mediaType: arrayize(mediaType || [MediaType.photo]),\n createdAfter: dateToNumber(createdAfter),\n createdBefore: dateToNumber(createdBefore),\n };\n\n if (first != null && typeof options.first !== 'number') {\n throw new Error('Option \"first\" must be a number!');\n }\n if (after != null && typeof options.after !== 'string') {\n throw new Error('Option \"after\" must be a string!');\n }\n if (album != null && typeof options.album !== 'string') {\n throw new Error('Option \"album\" must be a string!');\n }\n\n if (after != null && Platform.OS === 'android' && isNaN(parseInt(getId(after) as string, 10))) {\n throw new Error('Option \"after\" must be a valid ID!');\n }\n\n if (first != null && first < 0) {\n throw new Error('Option \"first\" must be a positive integer!');\n }\n\n options.sortBy.forEach(checkSortBy);\n options.mediaType.forEach(checkMediaType);\n\n return await MediaLibrary.getAssetsAsync(options);\n}\n\nexport function addListener(\n listener: (event: MediaLibraryAssetsChangeEvent) => void\n): Subscription {\n const subscription = eventEmitter.addListener(MediaLibrary.CHANGE_LISTENER_NAME, listener);\n return subscription;\n}\n\nexport function removeSubscription(subscription: Subscription): void {\n subscription.remove();\n}\n\nexport function removeAllListeners(): void {\n eventEmitter.removeAllListeners(MediaLibrary.CHANGE_LISTENER_NAME);\n}\n\n// iOS only\nexport async function getMomentsAsync() {\n if (!MediaLibrary.getMomentsAsync) {\n throw new UnavailabilityError('MediaLibrary', 'getMomentsAsync');\n }\n\n return await MediaLibrary.getMomentsAsync();\n}\n\n// Android only\n/**\n * Moves content of provided album to the special media directories on **Android R** or **above** if needed.\n *\n * This method won't do anything if:\n * - app is running on **iOS**, **web** or **Android below R**\n * - app has **write permission** to the album folder\n *\n * The migration is possible when the album contains only compatible files types.\n * For instance, movies and pictures are compatible with each other, but music and pictures are not.\n * If automatic migration isn't possible, the function will be rejected.\n * In that case, you can use methods from the `expo-file-system` to migrate all your files manually.\n *\n * @param album\n */\nexport async function migrateAlbumIfNeededAsync(album: AlbumRef): Promise {\n if (!MediaLibrary.migrateAlbumIfNeededAsync) {\n return;\n }\n\n return await MediaLibrary.migrateAlbumIfNeededAsync(getId(album));\n}\n\n// Android only\n/**\n * Checks if provided album should be migrated.\n * In other words, it checks if the application has the write permission to the album folder.\n *\n * This method always returns **false** for all android versions **below Android R**, **iOS** or **web**.\n *\n * @param album\n */\nexport async function albumNeedsMigrationAsync(album: AlbumRef): Promise {\n if (!MediaLibrary.albumNeedsMigrationAsync) {\n return false;\n }\n\n return await MediaLibrary.albumNeedsMigrationAsync(getId(album));\n}\n"]} \ No newline at end of file +{"version":3,"file":"MediaLibrary.js","sourceRoot":"","sources":["../src/MediaLibrary.ts"],"names":[],"mappings":"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","sourcesContent":["import { EventEmitter, Subscription, UnavailabilityError } from '@unimodules/core';\nimport {\n PermissionResponse as EXPermissionResponse,\n PermissionStatus,\n PermissionExpiration,\n} from 'expo-modules-core';\nimport { Platform } from 'react-native';\n\nimport MediaLibrary from './ExponentMediaLibrary';\n\nconst eventEmitter = new EventEmitter(MediaLibrary);\n\n// @needsAudit\nexport type PermissionResponse = EXPermissionResponse & {\n /**\n * Indicates if your app has access to the whole or only part of the photo library. Possible values are:\n * - `'all'` if the user granted your app access to the whole photo library\n * - `'limited'` if the user granted your app access only to selected photos (only available on iOS 14.0+)\n * - `'none'` if user denied or hasn't yet granted the permission\n */\n accessPrivileges?: 'all' | 'limited' | 'none';\n};\n\nexport type MediaTypeValue = 'audio' | 'photo' | 'video' | 'unknown';\nexport type SortByKey =\n | 'default'\n | 'mediaType'\n | 'width'\n | 'height'\n | 'creationTime'\n | 'modificationTime'\n | 'duration';\nexport type SortByValue = [SortByKey, boolean] | SortByKey;\n\nexport type MediaTypeObject = {\n audio: 'audio';\n photo: 'photo';\n video: 'video';\n unknown: 'unknown';\n};\n\nexport type SortByObject = {\n default: 'default';\n mediaType: 'mediaType';\n width: 'width';\n height: 'height';\n creationTime: 'creationTime';\n modificationTime: 'modificationTime';\n duration: 'duration';\n};\n\n// @needsAudit\nexport type Asset = {\n /**\n * Internal ID that represents an asset.\n */\n id: string;\n /**\n * Filename of the asset.\n */\n filename: string;\n /**\n * URI that points to the asset. `assets://*` (iOS), `file://*` (Android)\n */\n uri: string;\n /**\n * Media type.\n */\n mediaType: MediaTypeValue;\n /**\n * __iOS Only.__ An array of media subtypes.\n */\n mediaSubtypes?: MediaSubtype[];\n /**\n * Width of the image or video.\n */\n width: number;\n /**\n * Height of the image or video.\n */\n height: number;\n /**\n * File creation timestamp.\n */\n creationTime: number;\n /**\n * Last modification timestamp.\n */\n modificationTime: number;\n /**\n * Duration of the video or audio asset in seconds.\n */\n duration: number;\n /**\n * __Android Only.__ Album ID that the asset belongs to.\n */\n albumId?: string;\n};\n\n// @needsAudit\nexport type AssetInfo = Asset & {\n /**\n * Local URI for the asset.\n */\n localUri?: string;\n /**\n * GPS location if available.\n */\n location?: Location;\n /**\n * EXIF metadata associated with the image.\n */\n exif?: object;\n /**\n * __iOS Only.__ Whether the asset is marked as favorite.\n */\n isFavorite?: boolean;\n /**\n * __iOS Only.__ This field is available only if flag `shouldDownloadFromNetwork` is set to `false`.\n * Whether the asset is stored on the network (iCloud on iOS)\n */\n isNetworkAsset?: boolean; //iOS only\n /**\n * __iOS Only.__ Display orientation of the image. Orientation is available only for assets whose\n * `mediaType` is `MediaType.photo`. Value will range from 1 to 8, see [EXIF orientation specification](http://sylvana.net/jpegcrop/exif_orientation.html)\n * for more details.\n */\n orientation?: number;\n};\n\n// @docsMissing\nexport type MediaSubtype =\n | 'depthEffect'\n | 'hdr'\n | 'highFrameRate'\n | 'livePhoto'\n | 'panorama'\n | 'screenshot'\n | 'stream'\n | 'timelapse';\n\n// @needsAudit\nexport type MediaLibraryAssetInfoQueryOptions = {\n /**\n * Whether allow the asset to be downloaded from network. Only available in iOS with iCloud assets.\n * @default `true`\n */\n shouldDownloadFromNetwork?: boolean;\n};\n\n// @needsAudit\nexport type MediaLibraryAssetsChangeEvent = {\n /**\n * Whether the media library's changes could be described as \"incremental changes\".\n * `true` indicates the changes are described by the `insertedAssets`, `deletedAssets` and\n * `updatedAssets` values. `false` indicates that the scope of changes is too large and you\n * should perform a full assets reload (eg. a user has changed access to individual assets in the\n * media library).\n */\n hasIncrementalChanges: boolean;\n /**\n * Available only if `hasIncrementalChanges` is `true`.\n * Array of [`Asset`](#asset)s that have been inserted to the library.\n */\n insertedAssets?: Asset[];\n /**\n * Available only if `hasIncrementalChanges` is `true`.\n * Array of [`Asset`](#asset)s that have been deleted from the library.\n */\n deletedAssets?: Asset[];\n /**\n * Available only if `hasIncrementalChanges` is `true`.\n * Array of [`Asset`](#asset)s that have been updated or completed downloading from network\n * storage (iCloud on iOS).\n */\n updatedAssets?: Asset[];\n};\n\n// @docsMissing\nexport type Location = {\n latitude: number;\n longitude: number;\n};\n\n// @needsAudit @docsMissing\nexport type Album = {\n id: string;\n title: string;\n /**\n * Estimated number of assets in the album\n */\n assetCount: number;\n /**\n * __iOS Only.__ The type of the assets album.\n */\n type?: AlbumType;\n /**\n * __iOS Only.__ Apply only to albums whose type is `'moment'`. Earliest creation timestamp of all assets in the moment.\n */\n startTime: number;\n /**\n * __iOS Only.__ Apply only to albums whose type is `'moment'`. Latest creation timestamp of all assets in the moment.\n */\n endTime: number;\n /**\n * __iOS Only.__ Apply only to albums whose type is `'moment'`. Approximated location of all assets in the moment.\n */\n approximateLocation?: Location;\n /**\n * __iOS Only.__ Apply only to albums whose type is `'moment'`. Names of locations grouped in the moment.\n */\n locationNames?: string[];\n};\n\n// @docsMissing\nexport type AlbumType = 'album' | 'moment' | 'smartAlbum';\n\n// @docsMissing\nexport type AlbumsOptions = {\n includeSmartAlbums?: boolean;\n};\n\n// @needsAudit\nexport type AssetsOptions = {\n /**\n * The maximum number of items on a single page.\n * @default `20`\n */\n first?: number;\n /**\n * Asset ID of the last item returned on the previous page.\n */\n after?: AssetRef;\n /**\n * [Album](#album) or its ID to get assets from specific album.\n */\n album?: AlbumRef;\n /**\n * An array of [`SortByValue`](#sortbyvalue)s or a single `SortByValue` value. By default, all\n * keys are sorted in descending order, however you can also pass a pair `[key, ascending]` where\n * the second item is a `boolean` value that means whether to use ascending order. Note that if\n * the `SortBy.default` key is used, then `ascending` argument will not matter. Earlier items have\n * higher priority when sorting out the results.\n * If empty, this method will use the default sorting that is provided by the platform.\n */\n sortBy?: SortByValue[] | SortByValue;\n /**\n * An array of [MediaTypeValue](#expomedialibrarymediatypevalue)s or a single `MediaTypeValue`.\n * @default `MediaType.photo`\n */\n mediaType?: MediaTypeValue[] | MediaTypeValue;\n /**\n * `Date` object or Unix timestamp in milliseconds limiting returned assets only to those that\n * were created after this date.\n */\n createdAfter?: Date | number;\n /**\n * Similarly as `createdAfter`, but limits assets only to those that were created before specified\n * date.\n */\n createdBefore?: Date | number;\n};\n\n// @needsAudit\nexport type PagedInfo = {\n /**\n * A page of [`Asset`](#asset)s fetched by the query.\n */\n assets: T[];\n /**\n * ID of the last fetched asset. It should be passed as `after` option in order to get the\n * next page.\n */\n endCursor: string;\n /**\n * Whether there are more assets to fetch.\n */\n hasNextPage: boolean;\n /**\n * Estimated total number of assets that match the query.\n */\n totalCount: number;\n};\n\n// @docsMissing\nexport type AssetRef = Asset | string;\n\n// @docsMissing\nexport type AlbumRef = Album | string;\n\nexport { PermissionStatus, PermissionExpiration, EXPermissionResponse, Subscription };\n\nfunction arrayize(item: any): any[] {\n if (Array.isArray(item)) {\n return item;\n }\n return item ? [item] : [];\n}\n\nfunction getId(ref: any): string | undefined {\n if (typeof ref === 'string') {\n return ref;\n }\n return ref ? ref.id : undefined;\n}\n\nfunction checkAssetIds(assetIds: any): void {\n if (assetIds.some(id => !id || typeof id !== 'string')) {\n throw new Error('Asset ID must be a string!');\n }\n}\n\nfunction checkAlbumIds(albumIds: any): void {\n if (albumIds.some(id => !id || typeof id !== 'string')) {\n throw new Error('Album ID must be a string!');\n }\n}\n\nfunction checkMediaType(mediaType: any): void {\n if (Object.values(MediaType).indexOf(mediaType) === -1) {\n throw new Error(`Invalid mediaType: ${mediaType}`);\n }\n}\n\nfunction checkSortBy(sortBy: any): void {\n if (Array.isArray(sortBy)) {\n checkSortByKey(sortBy[0]);\n\n if (typeof sortBy[1] !== 'boolean') {\n throw new Error('Invalid sortBy array argument. Second item must be a boolean!');\n }\n } else {\n checkSortByKey(sortBy);\n }\n}\n\nfunction checkSortByKey(sortBy: any): void {\n if (Object.values(SortBy).indexOf(sortBy) === -1) {\n throw new Error(`Invalid sortBy key: ${sortBy}`);\n }\n}\n\nfunction dateToNumber(value?: Date | number): number | undefined {\n return value instanceof Date ? value.getTime() : value;\n}\n\n// @needsAudit\n/**\n * Possible media types.\n */\nexport const MediaType: MediaTypeObject = MediaLibrary.MediaType;\n\n// @needsAudit\n/**\n * Supported keys that can be used to sort `getAssetsAsync` results.\n */\nexport const SortBy: SortByObject = MediaLibrary.SortBy;\n\n// @needsAudit\n/**\n * Returns whether the Media Library API is enabled on the current device.\n * @return A promise which fulfils with a `boolean`, indicating whether the Media Library API is\n * available on the current device.\n */\nexport async function isAvailableAsync(): Promise {\n return !!MediaLibrary && 'getAssetsAsync' in MediaLibrary;\n}\n\n// @needsAudit @docsMissing\n/**\n * Asks the user to grant permissions for accessing media in user's media library.\n * @param writeOnly\n * @return A promise that fulfils with [`PermissionResponse`](#permissionresponse) object.\n */\nexport async function requestPermissionsAsync(\n writeOnly: boolean = false\n): Promise {\n if (!MediaLibrary.requestPermissionsAsync) {\n throw new UnavailabilityError('MediaLibrary', 'requestPermissionsAsync');\n }\n return await MediaLibrary.requestPermissionsAsync(writeOnly);\n}\n\n// @needsAudit @docsMissing\n/**\n * Checks user's permissions for accessing media library.\n * @param writeOnly\n * @return A promise that fulfils with [`PermissionResponse`](#permissionresponse) object.\n */\nexport async function getPermissionsAsync(writeOnly: boolean = false): Promise {\n if (!MediaLibrary.getPermissionsAsync) {\n throw new UnavailabilityError('MediaLibrary', 'getPermissionsAsync');\n }\n return await MediaLibrary.getPermissionsAsync(writeOnly);\n}\n\n// @needsAudit\n/**\n * __Available only on iOS >= 14.__ Allows the user to update the assets that your app has access to.\n * The system modal is only displayed if the user originally allowed only `limited` access to their\n * media library, otherwise this method is a no-op.\n * @return A promise that either rejects if the method is unavailable (meaning the device is not\n * running iOS >= 14), or resolves to `void`.\n * > __Note:__ This method doesn't inform you if the user changes which assets your app has access to.\n * For that information, you need to subscribe for updates to the user's media library using [addListener(listener)](#medialibraryaddlistenerlistener).\n * If `hasIncrementalChanges` is `false`, the user changed their permissions.\n */\nexport async function presentPermissionsPickerAsync(): Promise {\n if (!MediaLibrary.presentPermissionsPickerAsync) {\n throw new UnavailabilityError('MediaLibrary', 'presentPermissionsPickerAsync');\n }\n return await MediaLibrary.presentPermissionsPickerAsync();\n}\n\n// @needsAudit\n/**\n * Creates an asset from existing file. The most common use case is to save a picture taken by [Camera](../camera).\n * This method requires `CAMERA_ROLL` permission.\n *\n * # Example\n * ```js\n * const { uri } = await Camera.takePictureAsync();\n * const asset = await MediaLibrary.createAssetAsync(uri);\n * ```\n * @param localUri A URI to the image or video file. It must contain an extension. On Android it\n * must be a local path, so it must start with `file:///`\n * @return A promise which fulfils with an object representing an [`Asset`](#asset).\n */\nexport async function createAssetAsync(localUri: string): Promise {\n if (!MediaLibrary.createAssetAsync) {\n throw new UnavailabilityError('MediaLibrary', 'createAssetAsync');\n }\n\n if (!localUri || typeof localUri !== 'string') {\n throw new Error('Invalid argument \"localUri\". It must be a string!');\n }\n const asset = await MediaLibrary.createAssetAsync(localUri);\n\n if (Array.isArray(asset)) {\n // Android returns an array with asset, we need to pick the first item\n return asset[0];\n }\n return asset;\n}\n\n// @needsAudit\n/**\n * Saves the file at given `localUri` to the user's media library. Unlike [`createAssetAsync()`](#medialibrarycreateassetasynclocaluri),\n * This method doesn't return created asset.\n * On __iOS 11+__, it's possible to use this method without asking for `CAMERA_ROLL` permission,\n * however then yours `Info.plist` should have `NSPhotoLibraryAddUsageDescription` key.\n * @param localUri A URI to the image or video file. It must contain an extension. On Android it\n * must be a local path, so it must start with `file:///`.\n */\nexport async function saveToLibraryAsync(localUri: string): Promise {\n if (!MediaLibrary.saveToLibraryAsync) {\n throw new UnavailabilityError('MediaLibrary', 'saveToLibraryAsync');\n }\n return await MediaLibrary.saveToLibraryAsync(localUri);\n}\n\n// @needsAudit\n/**\n * Adds array of assets to the album.\n *\n * On Android, by default it copies assets from the current album to provided one, however it's also\n * possible to move them by passing `false` as `copyAssets` argument.In case they're copied you\n * should keep in mind that `getAssetsAsync` will return duplicated assets.\n * @param assets An array of [Asset](#asset) or their IDs.\n * @param album An [Album](#album) or its ID.\n * @param copy __Android only.__ Whether to copy assets to the new album instead of move them.\n * Defaults to `true`.\n * @return Returns promise which fulfils with `true` if the assets were successfully added to\n * the album.\n */\nexport async function addAssetsToAlbumAsync(\n assets: AssetRef[] | AssetRef,\n album: AlbumRef,\n copy: boolean = true\n): Promise {\n if (!MediaLibrary.addAssetsToAlbumAsync) {\n throw new UnavailabilityError('MediaLibrary', 'addAssetsToAlbumAsync');\n }\n\n const assetIds = arrayize(assets).map(getId);\n const albumId = getId(album);\n\n checkAssetIds(assetIds);\n\n if (!albumId || typeof albumId !== 'string') {\n throw new Error('Invalid album ID. It must be a string!');\n }\n\n if (Platform.OS === 'ios') {\n return await MediaLibrary.addAssetsToAlbumAsync(assetIds, albumId);\n }\n return await MediaLibrary.addAssetsToAlbumAsync(assetIds, albumId, !!copy);\n}\n\n// @needsAudit\n/**\n * Removes given assets from album.\n *\n * On Android, album will be automatically deleted if there are no more assets inside.\n * @param assets An array of [Asset](#asset) or their IDs.\n * @param album An [Album](#album) or its ID.\n * @return Returns promise which fulfils with `true` if the assets were successfully removed from\n * the album.\n */\nexport async function removeAssetsFromAlbumAsync(\n assets: AssetRef[] | AssetRef,\n album: AlbumRef\n): Promise {\n if (!MediaLibrary.removeAssetsFromAlbumAsync) {\n throw new UnavailabilityError('MediaLibrary', 'removeAssetsFromAlbumAsync');\n }\n\n const assetIds = arrayize(assets).map(getId);\n const albumId = getId(album);\n\n checkAssetIds(assetIds);\n return await MediaLibrary.removeAssetsFromAlbumAsync(assetIds, albumId);\n}\n\n// @needsAudit\n/**\n * Deletes assets from the library. On iOS it deletes assets from all albums they belong to, while\n * on Android it keeps all copies of them (album is strictly connected to the asset). Also, there is\n * additional dialog on iOS that requires user to confirm this action.\n * @param assets An array of [Asset](#asset) or their IDs.\n * @return Returns promise which fulfils with `true` if the assets were successfully deleted.\n */\nexport async function deleteAssetsAsync(assets: AssetRef[] | AssetRef): Promise {\n if (!MediaLibrary.deleteAssetsAsync) {\n throw new UnavailabilityError('MediaLibrary', 'deleteAssetsAsync');\n }\n\n const assetIds = arrayize(assets).map(getId);\n\n checkAssetIds(assetIds);\n return await MediaLibrary.deleteAssetsAsync(assetIds);\n}\n\n// @needsAudit\n/**\n * Provides more information about an asset, including GPS location, local URI and EXIF metadata.\n * @param asset An [Asset](#asset) or its ID.\n * @param options\n * @return [AssetInfo](#assetinfo) object, which is an `Asset` extended by an additional fields.\n */\nexport async function getAssetInfoAsync(\n asset: AssetRef,\n options: MediaLibraryAssetInfoQueryOptions = { shouldDownloadFromNetwork: true }\n): Promise {\n if (!MediaLibrary.getAssetInfoAsync) {\n throw new UnavailabilityError('MediaLibrary', 'getAssetInfoAsync');\n }\n\n const assetId = getId(asset);\n\n checkAssetIds([assetId]);\n\n const assetInfo = await MediaLibrary.getAssetInfoAsync(assetId, options);\n\n if (Array.isArray(assetInfo)) {\n // Android returns an array with asset info, we need to pick the first item\n return assetInfo[0];\n }\n return assetInfo;\n}\n\n// @needsAudit\n/**\n * Queries for user-created albums in media gallery.\n * @return A promise which fulfils with an array of [`Album`](#asset)s. Depending on Android version,\n * root directory of your storage may be listed as album titled `\"0\"` or unlisted at all.\n */\nexport async function getAlbumsAsync({ includeSmartAlbums = false }: AlbumsOptions = {}): Promise<\n Album[]\n> {\n if (!MediaLibrary.getAlbumsAsync) {\n throw new UnavailabilityError('MediaLibrary', 'getAlbumsAsync');\n }\n return await MediaLibrary.getAlbumsAsync({ includeSmartAlbums });\n}\n\n// @needsAudit\n/**\n * Queries for an album with a specific name.\n * @param title Name of the album to look for.\n * @return An object representing an [`Album`](#album), if album with given name exists, otherwise\n * returns `null`.\n */\nexport async function getAlbumAsync(title: string): Promise {\n if (!MediaLibrary.getAlbumAsync) {\n throw new UnavailabilityError('MediaLibrary', 'getAlbumAsync');\n }\n if (typeof title !== 'string') {\n throw new Error('Album title must be a string!');\n }\n return await MediaLibrary.getAlbumAsync(title);\n}\n\n// @needsAudit\n/**\n * Creates an album with given name and initial asset. The asset parameter is required on Android,\n * since it's not possible to create empty album on this platform. On Android, by default it copies\n * given asset from the current album to the new one, however it's also possible to move it by\n * passing `false` as `copyAsset` argument.\n * In case it's copied you should keep in mind that `getAssetsAsync` will return duplicated asset.\n * @param albumName Name of the album to create.\n * @param asset An [Asset](#asset) or its ID (required on Android).\n * @param copyAsset __Android Only.__ Whether to copy asset to the new album instead of move it.\n * Defaults to `true`.\n * @return Newly created [`Album`](#album).\n */\nexport async function createAlbumAsync(\n albumName: string,\n asset?: AssetRef,\n copyAsset: boolean = true\n): Promise {\n if (!MediaLibrary.createAlbumAsync) {\n throw new UnavailabilityError('MediaLibrary', 'createAlbumAsync');\n }\n\n const assetId = getId(asset);\n\n if (Platform.OS === 'android' && (typeof assetId !== 'string' || assetId.length === 0)) {\n // it's not possible to create empty album on Android, so initial asset must be provided\n throw new Error('MediaLibrary.createAlbumAsync must be called with an asset on Android.');\n }\n if (!albumName || typeof albumName !== 'string') {\n throw new Error('Invalid argument \"albumName\". It must be a string!');\n }\n if (assetId != null && typeof assetId !== 'string') {\n throw new Error('Asset ID must be a string!');\n }\n\n if (Platform.OS === 'ios') {\n return await MediaLibrary.createAlbumAsync(albumName, assetId);\n }\n return await MediaLibrary.createAlbumAsync(albumName, assetId, !!copyAsset);\n}\n\n// @needsAudit\n/**\n * Deletes given albums from the library. On Android by default it deletes assets belonging to given\n * albums from the library. On iOS it doesn't delete these assets, however it's possible to do by\n * passing `true` as `deleteAssets`.\n * @param albums An array of [`Album`](#asset)s or their IDs.\n * @param assetRemove __iOS Only.__ Whether to also delete assets belonging to given albums.\n * Defaults to `false`.\n * @return Returns a promise which fulfils with `true` if the albums were successfully deleted from\n * the library.\n */\nexport async function deleteAlbumsAsync(\n albums: AlbumRef[] | AlbumRef,\n assetRemove: boolean = false\n): Promise {\n if (!MediaLibrary.deleteAlbumsAsync) {\n throw new UnavailabilityError('MediaLibrary', 'deleteAlbumsAsync');\n }\n\n const albumIds = arrayize(albums).map(getId);\n\n checkAlbumIds(albumIds);\n if (Platform.OS === 'android') {\n return await MediaLibrary.deleteAlbumsAsync(albumIds);\n }\n return await MediaLibrary.deleteAlbumsAsync(albumIds, !!assetRemove);\n}\n\n// @needsAudit\n/**\n * Fetches a page of assets matching the provided criteria.\n * @param assetsOptions\n * @return A promise that fulfils with to [`PagedInfo`](#pagedinfo) object with array of [`Asset`](#asset)s.\n */\nexport async function getAssetsAsync(assetsOptions: AssetsOptions = {}): Promise> {\n if (!MediaLibrary.getAssetsAsync) {\n throw new UnavailabilityError('MediaLibrary', 'getAssetsAsync');\n }\n\n const { first, after, album, sortBy, mediaType, createdAfter, createdBefore } = assetsOptions;\n\n const options = {\n first: first == null ? 20 : first,\n after: getId(after),\n album: getId(album),\n sortBy: arrayize(sortBy),\n mediaType: arrayize(mediaType || [MediaType.photo]),\n createdAfter: dateToNumber(createdAfter),\n createdBefore: dateToNumber(createdBefore),\n };\n\n if (first != null && typeof options.first !== 'number') {\n throw new Error('Option \"first\" must be a number!');\n }\n if (after != null && typeof options.after !== 'string') {\n throw new Error('Option \"after\" must be a string!');\n }\n if (album != null && typeof options.album !== 'string') {\n throw new Error('Option \"album\" must be a string!');\n }\n\n if (after != null && Platform.OS === 'android' && isNaN(parseInt(getId(after) as string, 10))) {\n throw new Error('Option \"after\" must be a valid ID!');\n }\n\n if (first != null && first < 0) {\n throw new Error('Option \"first\" must be a positive integer!');\n }\n\n options.sortBy.forEach(checkSortBy);\n options.mediaType.forEach(checkMediaType);\n\n return await MediaLibrary.getAssetsAsync(options);\n}\n\n// @needsAudit\n/**\n * Subscribes for updates in user's media library.\n * @param listener A callback that is fired when any assets have been inserted or deleted from the\n * library, or when the user changes which assets they're allowing access to. On Android it's\n * invoked with an empty object. On iOS it's invoked with [`MediaLibraryAssetsChangeEvent`](#medialibraryassetschangeevent)\n * object.\n * @return An [`Subscription`](#subscription) object that you can call `remove()` on when you would\n * like to unsubscribe the listener.\n */\nexport function addListener(\n listener: (event: MediaLibraryAssetsChangeEvent) => void\n): Subscription {\n return eventEmitter.addListener(MediaLibrary.CHANGE_LISTENER_NAME, listener);\n}\n\n// @docsMissing\nexport function removeSubscription(subscription: Subscription): void {\n subscription.remove();\n}\n\n// @needsAudit\n/**\n * Removes all listeners.\n */\nexport function removeAllListeners(): void {\n eventEmitter.removeAllListeners(MediaLibrary.CHANGE_LISTENER_NAME);\n}\n\n// @needsAudit\n/**\n * __iOS Only.__ Fetches a list of moments, which is a group of assets taken around the same place\n * and time.\n * @return An array of [albums](#album) whose type is `moment`.\n */\nexport async function getMomentsAsync() {\n if (!MediaLibrary.getMomentsAsync) {\n throw new UnavailabilityError('MediaLibrary', 'getMomentsAsync');\n }\n\n return await MediaLibrary.getMomentsAsync();\n}\n\n// @needsAudit\n/**\n * Moves album content to the special media directories on **Android R** or **above** if needed.\n * Those new locations are in line with the Android `scoped storage` - so your application won't\n * lose write permission to those directories in the future.\n *\n * This method does nothing if:\n * - app is running on **iOS**, **web** or **Android below R**\n * - app has **write permission** to the album folder\n *\n * The migration is possible when the album contains only compatible files types.\n * For instance, movies and pictures are compatible with each other, but music and pictures are not.\n * If automatic migration isn't possible, the function will be rejected.\n * In that case, you can use methods from the `expo-file-system` to migrate all your files manually.\n *\n * # Why do you need to migrate files?\n * __Android R__ introduced a lot of changes in the storage system. Now applications can't save\n * anything to the root directory. The only available locations are from the `MediaStore` API.\n * Unfortunately, the media library stored albums in folders for which, because of those changes,\n * the application doesn't have permissions anymore. However, it doesn't mean you need to migrate\n * all your albums. If your application doesn't add assets to albums, you don't have to migrate.\n * Everything will work as it used to. You can read more about scoped storage in [the Android documentation](https://developer.android.com/about/versions/11/privacy/storage).\n *\n * @param album An [Album](#album) or its ID.\n * @return A promise which fulfils to `void`.\n */\nexport async function migrateAlbumIfNeededAsync(album: AlbumRef): Promise {\n if (!MediaLibrary.migrateAlbumIfNeededAsync) {\n return;\n }\n\n return await MediaLibrary.migrateAlbumIfNeededAsync(getId(album));\n}\n\n// @needsAudit\n/**\n * Checks if the album should be migrated to a different location. In other words, it checks if the\n * application has the write permission to the album folder. If not, it returns `true`, otherwise `false`.\n * > Note: For **Android below R**, **web** or **iOS**, this function always returns `false`.\n * @param album An [Album](#album) or its ID.\n * @return Returns a promise which fulfils with `true` if the album should be migrated.\n */\nexport async function albumNeedsMigrationAsync(album: AlbumRef): Promise {\n if (!MediaLibrary.albumNeedsMigrationAsync) {\n return false;\n }\n\n return await MediaLibrary.albumNeedsMigrationAsync(getId(album));\n}\n"]} \ No newline at end of file diff --git a/packages/expo-media-library/src/ExponentMediaLibrary.web.ts b/packages/expo-media-library/src/ExponentMediaLibrary.web.ts index d0ef82ada39c0a..ef09f9de2a88f1 100644 --- a/packages/expo-media-library/src/ExponentMediaLibrary.web.ts +++ b/packages/expo-media-library/src/ExponentMediaLibrary.web.ts @@ -1,5 +1,7 @@ import { PermissionResponse, PermissionStatus } from 'expo-modules-core'; +import { MediaTypeObject, SortByObject } from './MediaLibrary'; + const noPermissionResponse: PermissionResponse = { status: PermissionStatus.UNDETERMINED, canAskAgain: true, @@ -14,7 +16,7 @@ export default { get CHANGE_LISTENER_NAME(): string { return 'mediaLibraryDidChange'; }, - get MediaType(): { [key: string]: string } { + get MediaType(): MediaTypeObject { return { audio: 'audio', photo: 'photo', @@ -22,7 +24,7 @@ export default { unknown: 'unknown', }; }, - get SortBy(): { [key: string]: string } { + get SortBy(): SortByObject { return { default: 'default', mediaType: 'mediaType', diff --git a/packages/expo-media-library/src/MediaLibrary.ts b/packages/expo-media-library/src/MediaLibrary.ts index b3fe851d94c07d..c3191c6a04b882 100644 --- a/packages/expo-media-library/src/MediaLibrary.ts +++ b/packages/expo-media-library/src/MediaLibrary.ts @@ -10,8 +10,14 @@ import MediaLibrary from './ExponentMediaLibrary'; const eventEmitter = new EventEmitter(MediaLibrary); +// @needsAudit export type PermissionResponse = EXPermissionResponse & { - // iOS only + /** + * Indicates if your app has access to the whole or only part of the photo library. Possible values are: + * - `'all'` if the user granted your app access to the whole photo library + * - `'limited'` if the user granted your app access only to selected photos (only available on iOS 14.0+) + * - `'none'` if user denied or hasn't yet granted the permission + */ accessPrivileges?: 'all' | 'limited' | 'none'; }; @@ -43,87 +49,246 @@ export type SortByObject = { duration: 'duration'; }; +// @needsAudit export type Asset = { + /** + * Internal ID that represents an asset. + */ id: string; + /** + * Filename of the asset. + */ filename: string; + /** + * URI that points to the asset. `assets://*` (iOS), `file://*` (Android) + */ uri: string; + /** + * Media type. + */ mediaType: MediaTypeValue; - mediaSubtypes?: string[]; // iOS only + /** + * __iOS Only.__ An array of media subtypes. + */ + mediaSubtypes?: MediaSubtype[]; + /** + * Width of the image or video. + */ width: number; + /** + * Height of the image or video. + */ height: number; + /** + * File creation timestamp. + */ creationTime: number; + /** + * Last modification timestamp. + */ modificationTime: number; + /** + * Duration of the video or audio asset in seconds. + */ duration: number; - albumId?: string; // Android only + /** + * __Android Only.__ Album ID that the asset belongs to. + */ + albumId?: string; }; +// @needsAudit export type AssetInfo = Asset & { + /** + * Local URI for the asset. + */ localUri?: string; + /** + * GPS location if available. + */ location?: Location; + /** + * EXIF metadata associated with the image. + */ exif?: object; - isFavorite?: boolean; //iOS only + /** + * __iOS Only.__ Whether the asset is marked as favorite. + */ + isFavorite?: boolean; + /** + * __iOS Only.__ This field is available only if flag `shouldDownloadFromNetwork` is set to `false`. + * Whether the asset is stored on the network (iCloud on iOS) + */ isNetworkAsset?: boolean; //iOS only + /** + * __iOS Only.__ Display orientation of the image. Orientation is available only for assets whose + * `mediaType` is `MediaType.photo`. Value will range from 1 to 8, see [EXIF orientation specification](http://sylvana.net/jpegcrop/exif_orientation.html) + * for more details. + */ + orientation?: number; }; +// @docsMissing +export type MediaSubtype = + | 'depthEffect' + | 'hdr' + | 'highFrameRate' + | 'livePhoto' + | 'panorama' + | 'screenshot' + | 'stream' + | 'timelapse'; + +// @needsAudit export type MediaLibraryAssetInfoQueryOptions = { + /** + * Whether allow the asset to be downloaded from network. Only available in iOS with iCloud assets. + * @default `true` + */ shouldDownloadFromNetwork?: boolean; }; -export type MediaLibraryAssetsChangeEvent = - | { - hasIncrementalChanges: false; - } - | { - hasIncrementalChanges: true; - insertedAssets: Asset[]; - deletedAssets: Asset[]; - updatedAssets: Asset[]; - }; +// @needsAudit +export type MediaLibraryAssetsChangeEvent = { + /** + * Whether the media library's changes could be described as "incremental changes". + * `true` indicates the changes are described by the `insertedAssets`, `deletedAssets` and + * `updatedAssets` values. `false` indicates that the scope of changes is too large and you + * should perform a full assets reload (eg. a user has changed access to individual assets in the + * media library). + */ + hasIncrementalChanges: boolean; + /** + * Available only if `hasIncrementalChanges` is `true`. + * Array of [`Asset`](#asset)s that have been inserted to the library. + */ + insertedAssets?: Asset[]; + /** + * Available only if `hasIncrementalChanges` is `true`. + * Array of [`Asset`](#asset)s that have been deleted from the library. + */ + deletedAssets?: Asset[]; + /** + * Available only if `hasIncrementalChanges` is `true`. + * Array of [`Asset`](#asset)s that have been updated or completed downloading from network + * storage (iCloud on iOS). + */ + updatedAssets?: Asset[]; +}; +// @docsMissing export type Location = { latitude: number; longitude: number; }; +// @needsAudit @docsMissing export type Album = { id: string; title: string; + /** + * Estimated number of assets in the album + */ assetCount: number; - type?: string; // iOS only - - // iOS moments only + /** + * __iOS Only.__ The type of the assets album. + */ + type?: AlbumType; + /** + * __iOS Only.__ Apply only to albums whose type is `'moment'`. Earliest creation timestamp of all assets in the moment. + */ startTime: number; + /** + * __iOS Only.__ Apply only to albums whose type is `'moment'`. Latest creation timestamp of all assets in the moment. + */ endTime: number; + /** + * __iOS Only.__ Apply only to albums whose type is `'moment'`. Approximated location of all assets in the moment. + */ approximateLocation?: Location; + /** + * __iOS Only.__ Apply only to albums whose type is `'moment'`. Names of locations grouped in the moment. + */ locationNames?: string[]; }; +// @docsMissing +export type AlbumType = 'album' | 'moment' | 'smartAlbum'; + +// @docsMissing export type AlbumsOptions = { - // iOS only includeSmartAlbums?: boolean; }; +// @needsAudit export type AssetsOptions = { + /** + * The maximum number of items on a single page. + * @default `20` + */ first?: number; + /** + * Asset ID of the last item returned on the previous page. + */ after?: AssetRef; + /** + * [Album](#album) or its ID to get assets from specific album. + */ album?: AlbumRef; + /** + * An array of [`SortByValue`](#sortbyvalue)s or a single `SortByValue` value. By default, all + * keys are sorted in descending order, however you can also pass a pair `[key, ascending]` where + * the second item is a `boolean` value that means whether to use ascending order. Note that if + * the `SortBy.default` key is used, then `ascending` argument will not matter. Earlier items have + * higher priority when sorting out the results. + * If empty, this method will use the default sorting that is provided by the platform. + */ sortBy?: SortByValue[] | SortByValue; + /** + * An array of [MediaTypeValue](#expomedialibrarymediatypevalue)s or a single `MediaTypeValue`. + * @default `MediaType.photo` + */ mediaType?: MediaTypeValue[] | MediaTypeValue; + /** + * `Date` object or Unix timestamp in milliseconds limiting returned assets only to those that + * were created after this date. + */ createdAfter?: Date | number; + /** + * Similarly as `createdAfter`, but limits assets only to those that were created before specified + * date. + */ createdBefore?: Date | number; }; +// @needsAudit export type PagedInfo = { + /** + * A page of [`Asset`](#asset)s fetched by the query. + */ assets: T[]; + /** + * ID of the last fetched asset. It should be passed as `after` option in order to get the + * next page. + */ endCursor: string; + /** + * Whether there are more assets to fetch. + */ hasNextPage: boolean; + /** + * Estimated total number of assets that match the query. + */ totalCount: number; }; +// @docsMissing export type AssetRef = Asset | string; + +// @docsMissing export type AlbumRef = Album | string; -export { PermissionStatus, PermissionExpiration }; +export { PermissionStatus, PermissionExpiration, EXPermissionResponse, Subscription }; function arrayize(item: any): any[] { if (Array.isArray(item)) { @@ -179,14 +344,34 @@ function dateToNumber(value?: Date | number): number | undefined { return value instanceof Date ? value.getTime() : value; } -// export constants +// @needsAudit +/** + * Possible media types. + */ export const MediaType: MediaTypeObject = MediaLibrary.MediaType; + +// @needsAudit +/** + * Supported keys that can be used to sort `getAssetsAsync` results. + */ export const SortBy: SortByObject = MediaLibrary.SortBy; +// @needsAudit +/** + * Returns whether the Media Library API is enabled on the current device. + * @return A promise which fulfils with a `boolean`, indicating whether the Media Library API is + * available on the current device. + */ export async function isAvailableAsync(): Promise { return !!MediaLibrary && 'getAssetsAsync' in MediaLibrary; } +// @needsAudit @docsMissing +/** + * Asks the user to grant permissions for accessing media in user's media library. + * @param writeOnly + * @return A promise that fulfils with [`PermissionResponse`](#permissionresponse) object. + */ export async function requestPermissionsAsync( writeOnly: boolean = false ): Promise { @@ -196,6 +381,12 @@ export async function requestPermissionsAsync( return await MediaLibrary.requestPermissionsAsync(writeOnly); } +// @needsAudit @docsMissing +/** + * Checks user's permissions for accessing media library. + * @param writeOnly + * @return A promise that fulfils with [`PermissionResponse`](#permissionresponse) object. + */ export async function getPermissionsAsync(writeOnly: boolean = false): Promise { if (!MediaLibrary.getPermissionsAsync) { throw new UnavailabilityError('MediaLibrary', 'getPermissionsAsync'); @@ -203,9 +394,16 @@ export async function getPermissionsAsync(writeOnly: boolean = false): Promise

= 14.__ Allows the user to update the assets that your app has access to. + * The system modal is only displayed if the user originally allowed only `limited` access to their + * media library, otherwise this method is a no-op. + * @return A promise that either rejects if the method is unavailable (meaning the device is not + * running iOS >= 14), or resolves to `void`. + * > __Note:__ This method doesn't inform you if the user changes which assets your app has access to. + * For that information, you need to subscribe for updates to the user's media library using [addListener(listener)](#medialibraryaddlistenerlistener). + * If `hasIncrementalChanges` is `false`, the user changed their permissions. */ export async function presentPermissionsPickerAsync(): Promise { if (!MediaLibrary.presentPermissionsPickerAsync) { @@ -214,6 +412,20 @@ export async function presentPermissionsPickerAsync(): Promise { return await MediaLibrary.presentPermissionsPickerAsync(); } +// @needsAudit +/** + * Creates an asset from existing file. The most common use case is to save a picture taken by [Camera](../camera). + * This method requires `CAMERA_ROLL` permission. + * + * # Example + * ```js + * const { uri } = await Camera.takePictureAsync(); + * const asset = await MediaLibrary.createAssetAsync(uri); + * ``` + * @param localUri A URI to the image or video file. It must contain an extension. On Android it + * must be a local path, so it must start with `file:///` + * @return A promise which fulfils with an object representing an [`Asset`](#asset). + */ export async function createAssetAsync(localUri: string): Promise { if (!MediaLibrary.createAssetAsync) { throw new UnavailabilityError('MediaLibrary', 'createAssetAsync'); @@ -231,6 +443,15 @@ export async function createAssetAsync(localUri: string): Promise { return asset; } +// @needsAudit +/** + * Saves the file at given `localUri` to the user's media library. Unlike [`createAssetAsync()`](#medialibrarycreateassetasynclocaluri), + * This method doesn't return created asset. + * On __iOS 11+__, it's possible to use this method without asking for `CAMERA_ROLL` permission, + * however then yours `Info.plist` should have `NSPhotoLibraryAddUsageDescription` key. + * @param localUri A URI to the image or video file. It must contain an extension. On Android it + * must be a local path, so it must start with `file:///`. + */ export async function saveToLibraryAsync(localUri: string): Promise { if (!MediaLibrary.saveToLibraryAsync) { throw new UnavailabilityError('MediaLibrary', 'saveToLibraryAsync'); @@ -238,11 +459,25 @@ export async function saveToLibraryAsync(localUri: string): Promise { return await MediaLibrary.saveToLibraryAsync(localUri); } +// @needsAudit +/** + * Adds array of assets to the album. + * + * On Android, by default it copies assets from the current album to provided one, however it's also + * possible to move them by passing `false` as `copyAssets` argument.In case they're copied you + * should keep in mind that `getAssetsAsync` will return duplicated assets. + * @param assets An array of [Asset](#asset) or their IDs. + * @param album An [Album](#album) or its ID. + * @param copy __Android only.__ Whether to copy assets to the new album instead of move them. + * Defaults to `true`. + * @return Returns promise which fulfils with `true` if the assets were successfully added to + * the album. + */ export async function addAssetsToAlbumAsync( assets: AssetRef[] | AssetRef, album: AlbumRef, copy: boolean = true -) { +): Promise { if (!MediaLibrary.addAssetsToAlbumAsync) { throw new UnavailabilityError('MediaLibrary', 'addAssetsToAlbumAsync'); } @@ -262,7 +497,20 @@ export async function addAssetsToAlbumAsync( return await MediaLibrary.addAssetsToAlbumAsync(assetIds, albumId, !!copy); } -export async function removeAssetsFromAlbumAsync(assets: AssetRef[] | AssetRef, album: AlbumRef) { +// @needsAudit +/** + * Removes given assets from album. + * + * On Android, album will be automatically deleted if there are no more assets inside. + * @param assets An array of [Asset](#asset) or their IDs. + * @param album An [Album](#album) or its ID. + * @return Returns promise which fulfils with `true` if the assets were successfully removed from + * the album. + */ +export async function removeAssetsFromAlbumAsync( + assets: AssetRef[] | AssetRef, + album: AlbumRef +): Promise { if (!MediaLibrary.removeAssetsFromAlbumAsync) { throw new UnavailabilityError('MediaLibrary', 'removeAssetsFromAlbumAsync'); } @@ -274,7 +522,15 @@ export async function removeAssetsFromAlbumAsync(assets: AssetRef[] | AssetRef, return await MediaLibrary.removeAssetsFromAlbumAsync(assetIds, albumId); } -export async function deleteAssetsAsync(assets: AssetRef[] | AssetRef) { +// @needsAudit +/** + * Deletes assets from the library. On iOS it deletes assets from all albums they belong to, while + * on Android it keeps all copies of them (album is strictly connected to the asset). Also, there is + * additional dialog on iOS that requires user to confirm this action. + * @param assets An array of [Asset](#asset) or their IDs. + * @return Returns promise which fulfils with `true` if the assets were successfully deleted. + */ +export async function deleteAssetsAsync(assets: AssetRef[] | AssetRef): Promise { if (!MediaLibrary.deleteAssetsAsync) { throw new UnavailabilityError('MediaLibrary', 'deleteAssetsAsync'); } @@ -285,6 +541,13 @@ export async function deleteAssetsAsync(assets: AssetRef[] | AssetRef) { return await MediaLibrary.deleteAssetsAsync(assetIds); } +// @needsAudit +/** + * Provides more information about an asset, including GPS location, local URI and EXIF metadata. + * @param asset An [Asset](#asset) or its ID. + * @param options + * @return [AssetInfo](#assetinfo) object, which is an `Asset` extended by an additional fields. + */ export async function getAssetInfoAsync( asset: AssetRef, options: MediaLibraryAssetInfoQueryOptions = { shouldDownloadFromNetwork: true } @@ -306,6 +569,12 @@ export async function getAssetInfoAsync( return assetInfo; } +// @needsAudit +/** + * Queries for user-created albums in media gallery. + * @return A promise which fulfils with an array of [`Album`](#asset)s. Depending on Android version, + * root directory of your storage may be listed as album titled `"0"` or unlisted at all. + */ export async function getAlbumsAsync({ includeSmartAlbums = false }: AlbumsOptions = {}): Promise< Album[] > { @@ -315,6 +584,13 @@ export async function getAlbumsAsync({ includeSmartAlbums = false }: AlbumsOptio return await MediaLibrary.getAlbumsAsync({ includeSmartAlbums }); } +// @needsAudit +/** + * Queries for an album with a specific name. + * @param title Name of the album to look for. + * @return An object representing an [`Album`](#album), if album with given name exists, otherwise + * returns `null`. + */ export async function getAlbumAsync(title: string): Promise { if (!MediaLibrary.getAlbumAsync) { throw new UnavailabilityError('MediaLibrary', 'getAlbumAsync'); @@ -325,6 +601,19 @@ export async function getAlbumAsync(title: string): Promise { return await MediaLibrary.getAlbumAsync(title); } +// @needsAudit +/** + * Creates an album with given name and initial asset. The asset parameter is required on Android, + * since it's not possible to create empty album on this platform. On Android, by default it copies + * given asset from the current album to the new one, however it's also possible to move it by + * passing `false` as `copyAsset` argument. + * In case it's copied you should keep in mind that `getAssetsAsync` will return duplicated asset. + * @param albumName Name of the album to create. + * @param asset An [Asset](#asset) or its ID (required on Android). + * @param copyAsset __Android Only.__ Whether to copy asset to the new album instead of move it. + * Defaults to `true`. + * @return Newly created [`Album`](#album). + */ export async function createAlbumAsync( albumName: string, asset?: AssetRef, @@ -353,10 +642,21 @@ export async function createAlbumAsync( return await MediaLibrary.createAlbumAsync(albumName, assetId, !!copyAsset); } +// @needsAudit +/** + * Deletes given albums from the library. On Android by default it deletes assets belonging to given + * albums from the library. On iOS it doesn't delete these assets, however it's possible to do by + * passing `true` as `deleteAssets`. + * @param albums An array of [`Album`](#asset)s or their IDs. + * @param assetRemove __iOS Only.__ Whether to also delete assets belonging to given albums. + * Defaults to `false`. + * @return Returns a promise which fulfils with `true` if the albums were successfully deleted from + * the library. + */ export async function deleteAlbumsAsync( albums: AlbumRef[] | AlbumRef, assetRemove: boolean = false -) { +): Promise { if (!MediaLibrary.deleteAlbumsAsync) { throw new UnavailabilityError('MediaLibrary', 'deleteAlbumsAsync'); } @@ -370,6 +670,12 @@ export async function deleteAlbumsAsync( return await MediaLibrary.deleteAlbumsAsync(albumIds, !!assetRemove); } +// @needsAudit +/** + * Fetches a page of assets matching the provided criteria. + * @param assetsOptions + * @return A promise that fulfils with to [`PagedInfo`](#pagedinfo) object with array of [`Asset`](#asset)s. + */ export async function getAssetsAsync(assetsOptions: AssetsOptions = {}): Promise> { if (!MediaLibrary.getAssetsAsync) { throw new UnavailabilityError('MediaLibrary', 'getAssetsAsync'); @@ -411,22 +717,41 @@ export async function getAssetsAsync(assetsOptions: AssetsOptions = {}): Promise return await MediaLibrary.getAssetsAsync(options); } +// @needsAudit +/** + * Subscribes for updates in user's media library. + * @param listener A callback that is fired when any assets have been inserted or deleted from the + * library, or when the user changes which assets they're allowing access to. On Android it's + * invoked with an empty object. On iOS it's invoked with [`MediaLibraryAssetsChangeEvent`](#medialibraryassetschangeevent) + * object. + * @return An [`Subscription`](#subscription) object that you can call `remove()` on when you would + * like to unsubscribe the listener. + */ export function addListener( listener: (event: MediaLibraryAssetsChangeEvent) => void ): Subscription { - const subscription = eventEmitter.addListener(MediaLibrary.CHANGE_LISTENER_NAME, listener); - return subscription; + return eventEmitter.addListener(MediaLibrary.CHANGE_LISTENER_NAME, listener); } +// @docsMissing export function removeSubscription(subscription: Subscription): void { subscription.remove(); } +// @needsAudit +/** + * Removes all listeners. + */ export function removeAllListeners(): void { eventEmitter.removeAllListeners(MediaLibrary.CHANGE_LISTENER_NAME); } -// iOS only +// @needsAudit +/** + * __iOS Only.__ Fetches a list of moments, which is a group of assets taken around the same place + * and time. + * @return An array of [albums](#album) whose type is `moment`. + */ export async function getMomentsAsync() { if (!MediaLibrary.getMomentsAsync) { throw new UnavailabilityError('MediaLibrary', 'getMomentsAsync'); @@ -435,11 +760,13 @@ export async function getMomentsAsync() { return await MediaLibrary.getMomentsAsync(); } -// Android only +// @needsAudit /** - * Moves content of provided album to the special media directories on **Android R** or **above** if needed. + * Moves album content to the special media directories on **Android R** or **above** if needed. + * Those new locations are in line with the Android `scoped storage` - so your application won't + * lose write permission to those directories in the future. * - * This method won't do anything if: + * This method does nothing if: * - app is running on **iOS**, **web** or **Android below R** * - app has **write permission** to the album folder * @@ -448,7 +775,16 @@ export async function getMomentsAsync() { * If automatic migration isn't possible, the function will be rejected. * In that case, you can use methods from the `expo-file-system` to migrate all your files manually. * - * @param album + * # Why do you need to migrate files? + * __Android R__ introduced a lot of changes in the storage system. Now applications can't save + * anything to the root directory. The only available locations are from the `MediaStore` API. + * Unfortunately, the media library stored albums in folders for which, because of those changes, + * the application doesn't have permissions anymore. However, it doesn't mean you need to migrate + * all your albums. If your application doesn't add assets to albums, you don't have to migrate. + * Everything will work as it used to. You can read more about scoped storage in [the Android documentation](https://developer.android.com/about/versions/11/privacy/storage). + * + * @param album An [Album](#album) or its ID. + * @return A promise which fulfils to `void`. */ export async function migrateAlbumIfNeededAsync(album: AlbumRef): Promise { if (!MediaLibrary.migrateAlbumIfNeededAsync) { @@ -458,14 +794,13 @@ export async function migrateAlbumIfNeededAsync(album: AlbumRef): Promise return await MediaLibrary.migrateAlbumIfNeededAsync(getId(album)); } -// Android only +// @needsAudit /** - * Checks if provided album should be migrated. - * In other words, it checks if the application has the write permission to the album folder. - * - * This method always returns **false** for all android versions **below Android R**, **iOS** or **web**. - * - * @param album + * Checks if the album should be migrated to a different location. In other words, it checks if the + * application has the write permission to the album folder. If not, it returns `true`, otherwise `false`. + * > Note: For **Android below R**, **web** or **iOS**, this function always returns `false`. + * @param album An [Album](#album) or its ID. + * @return Returns a promise which fulfils with `true` if the album should be migrated. */ export async function albumNeedsMigrationAsync(album: AlbumRef): Promise { if (!MediaLibrary.albumNeedsMigrationAsync) { diff --git a/tools/src/commands/GenerateDocsAPIData.ts b/tools/src/commands/GenerateDocsAPIData.ts index b34046590777f3..df6dc29471fa66 100644 --- a/tools/src/commands/GenerateDocsAPIData.ts +++ b/tools/src/commands/GenerateDocsAPIData.ts @@ -109,6 +109,7 @@ async function action({packageName, version = 'unversioned'}: ActionOptions) { 'expo-localization': ['Localization.ts'], 'expo-location': ['Location.ts'], 'expo-mail-composer': ['MailComposer.ts'], + 'expo-media-library': ['MediaLibrary.ts'], 'expo-network': ['Network.ts'], 'expo-pedometer': ['Pedometer.ts', 'expo-sensors'], 'expo-print': ['Print.ts'],