Compare commits

..

146 Commits

Author SHA1 Message Date
marcosrdz
3e343152a6 Merge remote-tracking branch 'upstream/master' 2020-08-10 19:28:32 -04:00
Janic Duplessis
b7a89e3d3b 2.3.3 2020-07-13 13:44:38 -04:00
Jafar Jabr
be1a4980e3
remove deprecated gradle option (#1390)
remove The option 'android.enableUnitTestBinaryResources' is deprecated
which removed from gradle 4, The current default is 'false'
to allow the build using gradle 4
2020-07-13 13:42:09 -04:00
Marcos Rodriguez Vélez
da9bd7db9d
Update ImagePickerManager.m 2020-07-11 23:28:19 -04:00
Marcos Rodriguez Vélez
5bcf00fd6a
Use Catalyst if Target 2020-07-11 00:21:04 -04:00
Marcos Rodriguez Vélez
be4bca9225
Remove Code that blocks from compiling with Catalyst. 2020-07-10 20:35:41 -04:00
Janic Duplessis
e99b8bb5be 2.3.2 2020-07-06 13:46:13 -04:00
Andrei Tofan
18ee85b339
Make sure the calls are execute on the main thread (#1265)
* Make sure the calls are execute on the main thread

* Fix formatting

* Wrap method calls

Co-authored-by: Andrei Tofan <andrei@optinify.io>
2020-07-06 13:43:30 -04:00
Akhmad Syaikhul Hadi
38db13aeb6 Fix failure delivering result info #579 2020-05-07 08:13:08 +02:00
Evan Bacon
3b9cf0347b Update README.md 2020-05-06 14:59:26 +02:00
Ulf Andersson
d3a7ed2ca0 test: Make tests work with >=Java9 by upgrading libraries
- Upgrade RoboElectric from 3.3.2 => 4.3.1
- Upgrade PowerMock from 1.6.2 => 1.6.6
- Removed unused fest-assert-core
- Remove explicit mockito, junit and assertj dependencies, correct
versions are pulled in as transitive depedencies by RoboElectric
2020-05-04 07:33:54 +02:00
Johan du Toit
a2626b3760
Add help wanted notification 2020-04-30 13:03:32 +02:00
Janic Duplessis
a40e85bfc9 2.3.1 2020-02-26 10:28:12 -06:00
Marcus Bergholm
54d62aca89
Add write permission check when accessing camera android (#1295) 2020-02-26 10:26:40 -06:00
Johan Du toit
5ec8f8d9fe 2.3.0 2020-02-10 09:29:12 +02:00
Levi
5368e2acc8 fix documentation 2020-02-10 09:27:16 +02:00
Levi
14be039814 clean up 2020-02-10 09:27:16 +02:00
Levi
6005f36dd7 allow android to save photos to private directory 2020-02-10 09:27:16 +02:00
Johan Du toit
8213cff3dd 2.2.1 2020-01-28 08:31:39 +02:00
Jeff Algera
2ad856a1a7 Code review
Copy the file only if we cannot move it.
2020-01-28 08:11:37 +02:00
Jeff Algera
49e6a775cc Copy file instead of move
iOS 13 beta is not allowing the `moveItemAtURL` command to execute which may cause trimmed clips to fail based on permission.

Instead of moving the file, copy it.
2020-01-28 08:11:37 +02:00
Johan Du toit
d1423768c7 2.2.0 2020-01-22 09:24:51 +02:00
Nathan Greene
eab19caa19 fix: Having a space in the filename on android no longer causes the filetype to be null 2020-01-22 09:21:02 +02:00
Johan-dutoit
bcbde0fa1f 📗 2020-01-21 06:27:41 +02:00
Chris
5d1feb500b fix: issue#1252 - if target is running iOS 11 or greater, do not reference deprecated PhotoKit api 2020-01-21 06:22:09 +02:00
aschnapp
4e699d3a59 use system default picker text color 2020-01-10 10:49:09 +02:00
lizraeli
cd69e948d0 remove commented code 2020-01-09 11:39:53 +02:00
lizraeli
ac142cf9cd Add break statements in switch 2020-01-09 11:39:53 +02:00
Lev Izraelit
997c272f78 Split permission requests for Camera & Gallery 2020-01-09 11:39:53 +02:00
Erik Haider Forsén
085d0b7d08 docs: adds tintColor to API Reference 2019-09-23 15:45:38 +01:00
Johan-dutoit
341e95491b docs (readme): improved linking details 2019-09-05 11:42:50 +01:00
Johan-dutoit
5d1ff8566b docs (readme): improved linking details 2019-09-05 11:23:05 +01:00
Johan-dutoit
5281955b2c 📖 2019-08-30 13:57:42 +01:00
Johan-dutoit
90e7004668 1.1.0 2019-08-30 13:52:33 +01:00
Johan-dutoit
134c5b485b chore(cleanup): Apply prettier 2019-08-30 13:38:24 +01:00
Ryan Nickel
0028ebf64d feat(ios): add support for tintColor 2019-08-30 13:34:29 +01:00
Johan Forslund
aacf1b2d5b fix(android): Support devices with multiple gallery apps 2019-08-30 13:33:06 +01:00
Tomas Harkema
e9e0f87a2a fix(permissions): iOS 11+ doesn't need permissions for picking a single image 2019-08-30 13:32:06 +01:00
hossamnasser938
454fff34df feat(mixed): add support for android 2019-08-29 16:29:05 +01:00
Dmitry Alekseev
677373675a Update react-native-image-picker.podspec (#1161)
fix pod install
2019-08-21 09:25:48 +01:00
Janic Duplessis
7261f868a4 1.0.2 2019-08-02 02:18:24 -04:00
Janic Duplessis
707a9e4222 Move default theme to ImagePickerModule 2019-08-02 00:27:39 -04:00
Janic Duplessis
48869a0e08 Add @NativeModule annotation 2019-08-02 00:21:20 -04:00
Johan-dutoit
4130cb2f70 📖 2019-07-11 16:54:39 +01:00
Johan-dutoit
470ad96945 📖 2019-07-11 16:53:16 +01:00
Johan-dutoit
dffb5624b0 📖 2019-07-10 15:55:32 +01:00
Johan
11dcf311b7
V1.0.1 (#1120)
refactor: Replace JavaScript with TypeScript and AndroidX support
2019-07-09 10:09:00 +01:00
Jonathan Boellke
8de2847bd4 fix(types): Add ImagePickerPermissionDeniedOptions 2019-07-01 10:12:15 +01:00
Johan
8c6d45d562
Merge pull request #1101 from nabati/patch-1
Change `compile` to `implementation`
2019-06-17 09:39:22 +01:00
Poyan Nabati
337b6fe757
Change compile to implementation
`react-native link` does this already, but the steps in the manual installation steps still uses the old keyword.
2019-06-17 09:34:44 +03:00
Johan-dutoit
53316af684 chore(github): Add stale.yml to manage issues 2019-06-11 11:03:41 +01:00
Johan
53a9932d9c
Merge pull request #1094 from farskid/patch-1
Fix typo
2019-06-10 09:36:56 +01:00
Farzad YZ
2feda1e99a
Fix typo 2019-06-09 18:02:21 +03:00
Johan-dutoit
fd9300d196 Update flow types to match typescript 2019-06-02 12:40:45 +01:00
Johan
9ed071a69f
Merge pull request #1072 from miyabi/fix-flow-error
Update index.js.flow
2019-06-02 12:23:32 +01:00
Masayuki Iwai
9a834025d5
Update index.js.flow
Fix flow error on launchImageLibrary.
2019-04-22 16:17:07 +09:00
Janic Duplessis
c78cb2c987 0.28.1 2019-03-29 01:06:18 -04:00
Matt Oakes
35724d9257 Export the interfaces in TypeScript (#1042) 2019-03-29 00:36:58 -04:00
Janic Duplessis
6569a311bf 0.28.0 2019-01-06 00:15:55 -05:00
Janic Duplessis
6735364402 Revert "[iOS] Update permissions flow for iOS 11 UIImagePickerController (#819)"
This reverts commit ab4d858bf1.
2019-01-05 23:59:51 -05:00
Janic Duplessis
a5d2938cd1
Update Reference.md 2019-01-05 22:54:12 -05:00
Samuel Duursma
8c3e1f46f5 Set value for key "type" in picker response on iOS (#710)
I discovered that UIImagePickerControllerReferenceURL was not a good source to derive the picked media item's MIME type from, because UIImagePickerControllerReferenceURL will be nil for for newly captured images. I also discovered that react-native-image-picker always creates either a PNG or JPEG representation of picked still images, so I decided to just set the MIME types on the response object for these data presentations when they are created.
2019-01-05 22:53:06 -05:00
Casey Li
619902cc16 #972: change ImageConfig.withOriginalFile() to address testOnImmutable failure (#973) 2019-01-05 22:47:48 -05:00
Alex Figueroa
ab4d858bf1 [iOS] Update permissions flow for iOS 11 UIImagePickerController (#819)
* [iOS] Update permissions flow for iOS 11 UIImagePickerController

* Update macro value for pre-Xcode 9

* PR Feedback

* Simplify things a bit and remove #if check
2019-01-05 22:45:45 -05:00
Salvatore Randazzo
c3fa45b576 Resolve memory leak in iOS image picker (#835)
* Move all alert code to main thread
Eliminiate strong reference to UIAlertController which causes a memory leak

* Delete contents.xcworkspacedata

* Delete IDEWorkspaceChecks.plist
2019-01-05 22:34:04 -05:00
Wyatt Greenway
d0683f464d Fix root path exception for older Android devices (#951)
* Fix root path exception for older Android devices

* Updates for further issues on other older devices
2019-01-05 22:23:47 -05:00
Antoine Grandchamp
c72de27595 Remove useless “stringValue” (#977) 2019-01-05 22:02:24 -05:00
Sergey Zolotarev
3e9ef5f3fb Fix iOS build warning (#982) 2019-01-03 18:54:28 -05:00
Phạm Văn Quân
54f1042716 Upgrade Android dependencies, added Google maven repository (#1000)
* Upgrade Android dependencies, added Google maven repository

* Removed redundant google()

* Revert google() repo change

* Sync gradle settings with latest version of React Native
2019-01-03 18:51:09 -05:00
Hyo Chan Jang
072e389ec8 Replace deprecated 'compile' gradle configuration with 'implementation' (#1010)
According to [ReactNative@0.57](https://github.com/react-native-community/react-native-releases/blob/master/CHANGELOG.md#057) default gradle version is now `4.4`.
2019-01-03 18:48:26 -05:00
Alex Bakh
da77c92e9b fixed lib fileName in doc (#985) 2018-12-11 13:22:37 -05:00
Nicolas Charpentier
b9985618d7 0.27.2 2018-12-10 14:42:13 -05:00
Nicolas Charpentier
fa3c624602 Fix Android build 2018-12-10 14:41:44 -05:00
Nicolas Charpentier
7e4a3aa5a3 Update example project 2018-12-10 14:41:30 -05:00
Nicolas Charpentier
979b9a8097
Update example with RN 0.57 (#980)
* Remove Example folder

* Init RN example project

* Add an example with a freshly created RN project
2018-11-21 12:44:46 -05:00
Martin Sandström
0ed6afff61 Add note for NSCameraUsageDescription (#953)
Used the example in the install.md but got rejected in app store due to Guideline 5.1.1 - Legal - Privacy - Data Collection and Storage.

The permission request alert should specify how your app will use this feature to help users understand why your app is requesting access to their personal data.

To hinder other people from getting their binary rejected, a note has been added to this section.
2018-11-19 11:08:17 -05:00
Andrew Marmion
b85aa9744c Update Reference.md (#954)
Sometimes the fileName property comes back undefined when taking a picture on iOS.
2018-11-19 11:07:37 -05:00
Nicolas Charpentier
f0f2ee143c 0.27.1 2018-10-05 14:04:30 -04:00
Nicolas Charpentier
94c6590777 Update yarn.lock 2018-10-05 14:04:08 -04:00
Nicolas Charpentier
cb22b90a83
Update podspec (#948) 2018-10-05 14:01:20 -04:00
Nicolas Charpentier
3d6813c5f6
Fix flow (#947)
* Fix Flow

* Add CircleCI config
2018-10-05 13:49:28 -04:00
Nicolas Charpentier
99b3802b99 0.27.0 2018-10-05 09:02:34 -04:00
ian
6a83fe4e4b 修复Android6.0,开通权限后未调用打开相机的问题 (#826) 2018-10-04 16:33:02 -04:00
Nicolas Charpentier
d36107b4c4
Configure Prettier (#945) 2018-10-04 09:41:13 -04:00
Nicolas Charpentier
ac0c31a094
Refactor docs (#944) 2018-10-04 09:30:28 -04:00
Jamie Curtis
e6c64ea1b5 Set this.callback before permissionsCheck so that permissionDenied dialog doesn't crash when trying to call the callback (#808) 2018-10-03 15:15:01 -04:00
Himanshu Kushwah
6d24702205 Update ImagePickerModule.java to fix multiple tap on upload image button crash (#782)
* Update ImagePickerModule.java

when multiple time image picker open is clicked doOnCancel() function cause app crash.
updated to handle case when callback is null.

* Update ImagePickerModule.java
2018-10-02 15:50:24 -04:00
Nicolas Charpentier
816b107b93
Fix spreaded export (#942) 2018-10-02 15:32:27 -04:00
Rudy
7fc3e3b4c1 Fix Android to bypass GIF image compression (#725) 2018-10-02 13:46:22 -04:00
龚江鹏
943a690171 Update README.md (#940)
If MainActivity is not instance of ReactActivity, you will need to let ReactInstanceManager handle onActivityResult
2018-10-01 09:14:55 -04:00
Benjamin Reid
abcd829828 Add declaration for default export (#851) 2018-09-28 10:23:06 -04:00
Alex Figueroa
cc216e87dd [iOS] Fix Xcode value conversion warnings (#817) 2018-09-28 10:22:29 -04:00
Taylor Kline
3293e349bc Specify full path to AndroidManifest.xml (#852)
Especially considering this is a file that must be edited manually, good idea to have the full path.
2018-09-28 10:21:01 -04:00
nathan-K-
7b9ff643c7 Use filestorage.path on android #885 (#886)
* Add support for fileStorage.path on Android

* Update README.md
2018-09-28 10:19:27 -04:00
paweljaneczek
97ea8fa9e7 Fixed wrong image orientation when saved to camera roll (#887) 2018-09-28 10:18:47 -04:00
satinos
78bf08cd7b Fix runtime crashe for Android 7. (#918)
Crash message:
        Tried to read an int but got non-integral double.
2018-09-28 10:17:43 -04:00
Nicolas Charpentier
32961d4297 Modernize index.js 2018-09-28 10:13:19 -04:00
Nicolas Charpentier
8187eaf1ca Check for presence of project-wide gradle configuration properties 2018-09-28 10:10:51 -04:00
Nicolas Charpentier
f579f929e9
Merge pull request #939 from webtaculars/patch-1
Update README.md
2018-09-28 09:45:09 -04:00
abhishek gupta
54202c3a9c
Update README.md
Update README.md as `cancelButtonTitle` works for both android and iOS
2018-09-28 12:12:41 +05:30
Nicolas Charpentier
8a92ccd91b
Merge pull request #938 from react-community/develop
Merge develop into master
2018-09-27 15:05:30 -04:00
Nicolas Charpentier
4a5fcdc236
Merge pull request #937 from react-community/master
Merge master into develop
2018-09-27 15:03:34 -04:00
Joshua Pinter
46b3c6be18 Move README badges below title. (#880)
They were getting long and wrapping.
2018-09-27 14:59:54 -04:00
Yuanzhong Xia
ab60113c87 Added missing comma in README.md (#922) 2018-09-27 14:59:35 -04:00
Joshua Kappers
5e2b54e3b7 Move non-optional step above the (optional) header (#925)
This step is not an optional step, but was below the optional heading and this tripped me up. Hoping to save someone else a day of unnecessary debugging. :)
2018-09-27 14:59:13 -04:00
Dan Maas
acbd30295c add license to package.json (#935) 2018-09-27 14:58:31 -04:00
Haitao Li
8f24d522da Bump version to 0.26.10 2018-05-13 10:36:58 -07:00
Haitao Li
b1be2e2020 Merge branch 'develop' 2018-05-13 10:36:10 -07:00
David Alexander Bjerremose
694fa1de5c Fix permission issue (#848) 2018-05-13 10:33:49 -07:00
Haitao Li
8a822bd687 bump package version number 2018-05-12 11:30:38 -07:00
Haitao Li
200d276b86 Merge branch 'develop' 2018-05-12 11:27:45 -07:00
Haitao Li
75f2ae87a4 bump package version number 2018-05-12 11:22:00 -07:00
Haitao Li
96ce16d7a1
Merge pull request #775 from hasaxena/patch-1
Error while generating build.
2018-02-27 19:27:26 -08:00
Haitao Li
60169dc5df
Merge pull request #571 from rigdern/rigdern/fileprovider-conflict
Avoid conflicting with other FileProviders
2018-02-20 21:01:43 -08:00
hasaxena
7f62d56aeb
Error while generating build. 2018-01-12 07:43:55 +05:30
Haitao Li
003b8f8b1b
Merge pull request #682 from cheapsteak/patch-1
Link downloads badge to npmcharts.com
2017-12-02 15:02:29 -08:00
Haitao Li
24bb9bfffb
Merge pull request #703 from geirman/pr/tweakSetupInstructions
remove step 3 from important note for android. it was automatically d…
2017-12-02 14:52:05 -08:00
Haitao Li
3f25a73433
Merge pull request #723 from IgorMing/develop
Fixed typo on README.md related to themes/style customization file on…
2017-12-02 14:35:53 -08:00
Haitao Li
05764b4b6a
Merge pull request #733 from ayildirim/develop
Documentation fix on response object
2017-12-02 14:27:48 -08:00
Haitao Li
560176d927
Merge pull request #727 from cpfeiffer/develop
Avoid copying image data when saving and close the stream!
2017-12-02 14:23:53 -08:00
Ahmet YILDIRIM
74f24232f3
Documentation fix on response object
Width & height dimensions are only returned for photos
2017-11-20 16:43:26 +01:00
Carsten Pfeiffer
876c193871 Avoid copying image data when saving and close the stream!
Instead of duplicating the entire image in memory with
ByteArrayOutputStream.toByteArray(), write to the FileOutputStream without copying.
2017-11-13 16:39:26 +01:00
Igor Ming de Mesquita
959bf3eb6b
Update README.md 2017-11-10 14:59:18 -02:00
Igor Ming
d6b9200393 Fixed typo on README.md related to themes/style customization file on android customization settings 2017-11-10 14:45:19 -02:00
Chris Geirman
8ec880abab add NSPhotoLibraryAddUsageDescription to list of permissions along with an example of how the plist would be updated 2017-11-03 13:25:12 -07:00
Chris Geirman
54a9b0bff0 remove step 3 from important note for android. it was automatically done with react-native link 2017-10-23 14:10:39 -07:00
Janic Duplessis
0416a0d17e 0.26.7 2017-09-15 18:39:36 -04:00
Sirui Li
497ca664e1 Fix Flow type for optional parameter (#679)
Per documentation:
> The first arg is the options object for customization (it can also be null or omitted for default options)

Therefore the first parameter should be allow to be null or omitted in type definition.

Also it's not necessary to specify `callback` return type as `void`.
2017-09-15 18:35:59 -04:00
Chang Wang
1f58ea03cb Link downloads badge to npmcharts.com
https://npmcharts.com/compare/react-native-image-picker?minimal=true
2017-09-12 20:44:34 -04:00
Marc Shilling
862a6c8118 0.26.6 2017-09-05 17:32:18 -04:00
Marc Shilling
cc598fb0e4 0.26.5 2017-09-05 17:32:09 -04:00
Aaron Trank
d25c17f3ed Add support for KitKat (API level 19) (#583) 2017-09-05 17:29:34 -04:00
cani1see
f37d925085 null checker for some old phones (#621) 2017-09-05 10:10:37 -04:00
Feng999
55051321b2 Update ImagePickerManager.m (#573)
modify the way to get root view controller.
2017-09-05 10:09:55 -04:00
Dmitry Patsura
3b3c12c558 Add Flow support (#654) 2017-09-05 10:07:46 -04:00
Brent Chuang
d7ffd2a38c Update documentation for storageOptions.waitUntilSaved (#658) 2017-09-05 10:07:00 -04:00
Eugene Sokovikov
a95cbe6504 disable permission dialog when permissionDenied=null (#622) 2017-09-05 09:58:35 -04:00
Haitao Li
fd4a3d18f8 Merge pull request #565 from react-community/android-image-sampling
[Android] Subsample image to save memory.
2017-08-17 20:52:26 -07:00
Marc Shilling
be9bdd97b5 0.26.4 2017-08-14 08:47:32 -04:00
Marc Shilling
835809c7fd 0.26.4 2017-08-14 08:44:16 -04:00
Marc Shilling
b75b701d42 0.26.3 2017-08-14 08:43:46 -04:00
Radek Czemerys
15c4e2a718 Remove createJSModules @overide marker - RN 0.47 compatibility (#653) 2017-08-13 05:17:12 -04:00
Adam Comella
48e8683245 Avoid conflicting with other FileProviders
If multiple AndroidManifest.xml files specify FileProviders (e.g. the app, a library like react-native-image-picker), you may receive compilation errors when these AndroidManifest.xml files are merged due to conflicts in fields such as `android:authorities` and `android:resource`.

This occurs when multiple AndroidManifest.xml files specify a `android.support.v4.content.FileProvider`. To avoid the conflict, we subclass `android.support.v4.content.FileProvider` and then we refer to our subclass in the AndroidManifest.xml file to avoid the conflict.

This solution is described here: http://stackoverflow.com/a/41550634
2017-05-03 15:50:03 -07:00
Adam Comella
e71b802d6e Add .vscode/ to .gitignore 2017-05-03 15:49:56 -07:00
Haitao Li
2eb0ebca4d [Android] Subsample image to save memory. 2017-04-30 12:04:54 -07:00
Haitao Li
4c0984c6f6 Add gitter badge 2017-04-23 10:53:31 -07:00
122 changed files with 25805 additions and 2346 deletions

27
.circleci/config.yml Normal file
View File

@ -0,0 +1,27 @@
aliases:
- &save-cache-yarn
key: yarn-packages-{{ checksum "yarn.lock" }}
paths:
- ~/.cache/yarn
- &restore-cache-yarn
name: Restore Yarn Package Cache
keys:
- yarn-packages-{{ checksum "yarn.lock" }}
defaults: &defaults
working_directory: ~/react-native-image-picker
docker:
- image: circleci/node:10
version: 2
jobs:
build:
<<: *defaults
steps:
- checkout
- restore_cache: *restore-cache-yarn
- run:
name: Yarn Install
command: |
yarn install --frozen-lockfile --no-progress --non-interactive --cache-folder ~/.cache/yarn
- save_cache: *save-cache-yarn

7
.eslintignore Normal file
View File

@ -0,0 +1,7 @@
typings
node_modules
example/android-bundle.js
example/ios-bundle.js
# generated by bob
lib

52
.eslintrc.js Normal file
View File

@ -0,0 +1,52 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
*/
const typescriptEslintRecommended = require('@typescript-eslint/eslint-plugin/dist/configs/recommended.json');
const typescriptEslintPrettier = require('eslint-config-prettier/@typescript-eslint');
module.exports = {
extends: ['@react-native-community'],
overrides: [
{
files: ['*.ts', '*.tsx'],
// Apply the recommended Typescript defaults and the prettier overrides to all Typescript files
rules: Object.assign(
typescriptEslintRecommended.rules,
typescriptEslintPrettier.rules,
{
'@typescript-eslint/explicit-member-accessibility': 'off',
},
),
},
{
files: ['example/**/*.ts', 'example/**/*.tsx'],
rules: {
// Turn off rules which are useless and annoying for the example files files
'@typescript-eslint/explicit-function-return-type': 'off',
'react-native/no-inline-styles': 'off',
},
},
{
files: ['**/__tests__/**/*.ts', '**/*.spec.ts'],
env: {
jest: true,
},
rules: {
// Turn off rules which are useless and annoying for unit test files
'@typescript-eslint/explicit-function-return-type': 'off',
},
},
{
files: ['*.ts', '*.tsx'],
rules: {
'no-dupe-class-members': 'off',
},
},
],
};

1
.gitattributes vendored Normal file
View File

@ -0,0 +1 @@
*.pbxproj -text

41
.github/stale.yml vendored Normal file
View File

@ -0,0 +1,41 @@
# Configuration for probot-stale based on: https://github.com/facebook/react-native/blob/master/.github/stale.yml
# Number of days of inactivity before an Issue or Pull Request becomes stale
daysUntilStale: 60
# Number of days of inactivity before an Issue or Pull Request with the stale label is closed.
daysUntilClose: 7
# Issues or Pull Requests.
exemptLabels:
- pinned
- security
- discussion
# Set to true to ignore issues in a project (defaults to false)
exemptProjects: false
# Set to true to ignore issues in a milestone (defaults to false)
exemptMilestones: false
# Set to true to ignore issues with an assignee (defaults to false)
exemptAssignees: false
# Label to use when marking as stale
staleLabel: stale
# Comment to post when marking as stale. Set to `false` to disable
markComment: >
This issue has been automatically marked as stale because it has not had
recent activity. It will be closed if no further activity occurs. Thank you
for your contributions. You may also mark this issue as a "discussion" and I
will leave this open.
# Comment to post when closing a stale Issue or Pull Request.
closeComment: >
Closing this issue after a prolonged period of inactivity. Fell free to reopen
this issue, if this still affecting you.
# Limit the number of actions per hour, from 1-30. Default is 30
limitPerRun: 30
# Limit to only `issues` or `pulls`
only: issues

70
.gitignore vendored
View File

@ -1,15 +1,17 @@
### Android Studio ###
.idea/
.gradle/
local.properties
# OSX
#
.DS_Store
### Xcode ###
# node.js
#
node_modules/
npm-debug.log
yarn-error.log
## Build generated
# Xcode
#
build/
DerivedData/
## Various settings
*.pbxuser
!default.pbxuser
*.mode1v3
@ -18,22 +20,42 @@ DerivedData/
!default.mode2v3
*.perspectivev3
!default.perspectivev3
xcuserdata/
## Other
*.moved-aside
xcuserdata
*.xccheckout
*.xcscmblueprint
*.moved-aside
DerivedData
*.hmap
*.ipa
*.xcuserstate
project.xcworkspace
### /Xcode ###
# CocoaPods
/ios/Pods/
### OS X
.DS_Store
### Node
node_modules
*.log
yarn.lock
## Android iml
# Android/IntelliJ
#
build/
.idea
.gradle
local.properties
*.iml
# BUCK
buck-out/
\.buckd/
debug.keystore
# Editor config
.vscode
# Outputs
coverage
.tmp
example/android-bundle.js
example/ios-bundle.js
index.android.bundle
index.ios.bundle
# generated by bob
lib/

View File

@ -1,3 +1,4 @@
Example/
example/
images/
node_modules/
yarn.lock

7
.prettierrc Normal file
View File

@ -0,0 +1,7 @@
{
"requirePragma": true,
"singleQuote": true,
"trailingComma": "all",
"bracketSpacing": false,
"jsxBracketSameLine": true
}

3
.watchmanconfig Normal file
View File

@ -0,0 +1,3 @@
{
"ignore_dirs": [".git", "node_modules", "example"]
}

2
CODEOWNERS Normal file
View File

@ -0,0 +1,2 @@
# Global owners
* @johan-dutoit @janicduplessis

49
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,49 @@
# Contributing to React Native ImagePicker
## Development Process
All work on React Native ImagePicker happens directly on GitHub. Contributors send pull requests which go through a review process.
> **Working on your first pull request?** You can learn how from this *free* series: [How to Contribute to an Open Source Project on GitHub](https://egghead.io/series/how-to-contribute-to-an-open-source-project-on-github).
1. Fork the repo and create your branch from `master` (a guide on [how to fork a repository](https://help.github.com/articles/fork-a-repo/)).
2. Run `yarn` or `npm install` to install all required dependencies.
3. Now you are ready to make your changes!
## Tests & Verifications
Currently we use `TypeScript` for typechecking, `eslint` with `prettier` for linting and formatting the code, and `jest` for unit testing.
* `yarn test`: Run all tests and validations.
* `yarn validate:android`: Run Spotless style checker on the Java code.
* `yarn validate:eslint`: Run `eslint`.
* `yarn validate:eslint --fix`: Run `eslint` and automatically fix issues. This is useful for correcting code formatting.
* `yarn validate:typescript`: Run `typescript` typechecking.
* `yarn test:jest`: Run unit tests with `jest`.
## Sending a pull request
When you're sending a pull request:
* Prefer small pull requests focused on one change.
* Verify that all tests and validations are passing.
* Follow the pull request template when opening a pull request.
## Commit message convention
We prefix our commit messages with one of the following to signify the kind of change:
* **build**: Changes that affect the build system or external dependencies.
* **ci**, **chore**: Changes to our CI configuration files and scripts.
* **docs**: Documentation only changes.
* **feat**: A new feature.
* **fix**: A bug fix.
* **perf**: A code change that improves performance.
* **refactor**: A code change that neither fixes a bug nor adds a feature.
* **style**: Changes that do not affect the meaning of the code.
* **test**: Adding missing tests or correcting existing tests.
## Release process
We use [Semantic Release](http://semantic-release.org) to automatically release new versions of the library when changes are merged into master. Using the commit message convention described above, it will detect if we need to release a patch, minor, or major version of the library.
## Reporting issues
You can report issues on our [bug tracker](https://github.com/react-native-community/react-native-ImagePicker/issues). Please search for existing issues and follow the issue template when opening an issue.
## License
By contributing to React Native ImagePicker, you agree that your contributions will be licensed under the **MIT** license.

View File

@ -1,65 +0,0 @@
[ignore]
# We fork some components by platform.
.*/*.web.js
.*/*.android.js
# Some modules have their own node_modules with overlap
.*/node_modules/node-haste/.*
# Ugh
.*/node_modules/babel.*
.*/node_modules/babylon.*
.*/node_modules/invariant.*
# Ignore react and fbjs where there are overlaps, but don't ignore
# anything that react-native relies on
.*/node_modules/fbjs/lib/Map.js
.*/node_modules/fbjs/lib/Promise.js
.*/node_modules/fbjs/lib/fetch.js
.*/node_modules/fbjs/lib/ExecutionEnvironment.js
.*/node_modules/fbjs/lib/isEmpty.js
.*/node_modules/fbjs/lib/crc32.js
.*/node_modules/fbjs/lib/ErrorUtils.js
# Flow has a built-in definition for the 'react' module which we prefer to use
# over the currently-untyped source
.*/node_modules/react/react.js
.*/node_modules/react/lib/React.js
.*/node_modules/react/lib/ReactDOM.js
# Ignore commoner tests
.*/node_modules/commoner/test/.*
# See https://github.com/facebook/flow/issues/442
.*/react-tools/node_modules/commoner/lib/reader.js
# Ignore jest
.*/node_modules/jest-cli/.*
# Ignore Website
.*/website/.*
[include]
[libs]
node_modules/react-native/Libraries/react-native/react-native-interface.js
[options]
module.system=haste
munge_underscores=true
module.name_mapper='^image![a-zA-Z0-9$_-]+$' -> 'GlobalImageStub'
module.name_mapper='^[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\)$' -> 'RelativeImageStub'
suppress_type=$FlowIssue
suppress_type=$FlowFixMe
suppress_type=$FixMe
suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(2[0-1]\\|1[0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)
suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(2[0-1]\\|1[0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)?:? #[0-9]+
suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy
[version]
0.21.0

34
Example/.gitignore vendored
View File

@ -1,34 +0,0 @@
# OSX
#
.DS_Store
# Xcode
#
build/
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
xcuserdata
*.xccheckout
*.moved-aside
DerivedData
*.hmap
*.ipa
*.xcuserstate
project.xcworkspace
# Android/IJ
#
.idea
.gradle
local.properties
# node.js
#
node_modules/
npm-debug.log

View File

@ -1,18 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module external.linked.project.id="Example" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="unspecified" type="JAVA_MODULE" version="4">
<component name="FacetManager">
<facet type="java-gradle" name="Java-Gradle">
<configuration>
<option name="BUILD_FOLDER_PATH" value="$MODULE_DIR$/build" />
<option name="BUILDABLE" value="false" />
</configuration>
</facet>
</component>
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_7" inherit-compiler-output="true">
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/.gradle" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

View File

@ -1,67 +0,0 @@
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Disabling obfuscation is useful if you collect stack traces from production crashes
# (unless you are using a system that supports de-obfuscate the stack traces).
-dontobfuscate
# React Native
# Keep our interfaces so they can be used by other ProGuard rules.
# See http://sourceforge.net/p/proguard/bugs/466/
-keep,allowobfuscation @interface com.facebook.proguard.annotations.DoNotStrip
-keep,allowobfuscation @interface com.facebook.proguard.annotations.KeepGettersAndSetters
# Do not strip any method/class that is annotated with @DoNotStrip
-keep @com.facebook.proguard.annotations.DoNotStrip class *
-keepclassmembers class * {
@com.facebook.proguard.annotations.DoNotStrip *;
}
-keepclassmembers @com.facebook.proguard.annotations.KeepGettersAndSetters class * {
void set*(***);
*** get*();
}
-keep class * extends com.facebook.react.bridge.JavaScriptModule { *; }
-keep class * extends com.facebook.react.bridge.NativeModule { *; }
-keepclassmembers,includedescriptorclasses class * { native <methods>; }
-keepclassmembers class * { @com.facebook.react.uimanager.UIProp <fields>; }
-keepclassmembers class * { @com.facebook.react.uimanager.annotations.ReactProp <methods>; }
-keepclassmembers class * { @com.facebook.react.uimanager.annotations.ReactPropGroup <methods>; }
-dontwarn com.facebook.react.**
# okhttp
-keepattributes Signature
-keepattributes *Annotation*
-keep class com.squareup.okhttp.** { *; }
-keep interface com.squareup.okhttp.** { *; }
-dontwarn com.squareup.okhttp.**
# okio
-keep class sun.misc.Unsafe { *; }
-dontwarn java.nio.file.*
-dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement
-dontwarn okio.**
# stetho
-dontwarn com.facebook.stetho.**

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

View File

@ -1,6 +0,0 @@
rootProject.name = 'Example'
include ':app'
include ':react-native-image-picker'
project(':react-native-image-picker').projectDir = new File(settingsDir, '../../android')

View File

@ -1,15 +0,0 @@
import React from 'react';
import {
AppRegistry
} from 'react-native';
import App from './App';
class Example extends React.Component {
render() {
return (
<App />
);
}
}
AppRegistry.registerComponent('Example', () => Example);

View File

@ -1,15 +0,0 @@
import React from 'react';
import {
AppRegistry
} from 'react-native';
import App from './App';
class Example extends React.Component {
render() {
return (
<App />
);
}
}
AppRegistry.registerComponent('Example', () => Example);

View File

@ -1,833 +0,0 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
00C302E51ABCBA2D00DB3ED1 /* libRCTActionSheet.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302AC1ABCB8CE00DB3ED1 /* libRCTActionSheet.a */; };
00C302E71ABCBA2D00DB3ED1 /* libRCTGeolocation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302BA1ABCB90400DB3ED1 /* libRCTGeolocation.a */; };
00C302E81ABCBA2D00DB3ED1 /* libRCTImage.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302C01ABCB91800DB3ED1 /* libRCTImage.a */; };
00C302E91ABCBA2D00DB3ED1 /* libRCTNetwork.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302DC1ABCB9D200DB3ED1 /* libRCTNetwork.a */; };
00C302EA1ABCBA2D00DB3ED1 /* libRCTVibration.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302E41ABCB9EE00DB3ED1 /* libRCTVibration.a */; };
019E52E21DEE2CD7000A5FCC /* libRCTAnimation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 019E52C01DEE2CC7000A5FCC /* libRCTAnimation.a */; };
01BBD31C1C9077A5000A3935 /* libRNImagePicker.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 01BBD31B1C907630000A3935 /* libRNImagePicker.a */; };
133E29F31AD74F7200F7D852 /* libRCTLinking.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 78C398B91ACF4ADC00677621 /* libRCTLinking.a */; };
139105C61AF99C1200B5F7CC /* libRCTSettings.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 139105C11AF99BAD00B5F7CC /* libRCTSettings.a */; };
139FDEF61B0652A700C62182 /* libRCTWebSocket.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 139FDEF41B06529B00C62182 /* libRCTWebSocket.a */; };
13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.m */; };
13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB11A68108700A75B9A /* LaunchScreen.xib */; };
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; };
146834051AC3E58100842450 /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 146834041AC3E56700842450 /* libReact.a */; };
832341BD1AAA6AB300B99B32 /* libRCTText.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 832341B51AAA6A8300B99B32 /* libRCTText.a */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
00C302AB1ABCB8CE00DB3ED1 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 134814201AA4EA6300B7C361;
remoteInfo = RCTActionSheet;
};
00C302B91ABCB90400DB3ED1 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 134814201AA4EA6300B7C361;
remoteInfo = RCTGeolocation;
};
00C302BF1ABCB91800DB3ED1 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 58B5115D1A9E6B3D00147676;
remoteInfo = RCTImage;
};
00C302DB1ABCB9D200DB3ED1 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 58B511DB1A9E6C8500147676;
remoteInfo = RCTNetwork;
};
00C302E31ABCB9EE00DB3ED1 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 832C81801AAF6DEF007FA2F7;
remoteInfo = RCTVibration;
};
019E52BF1DEE2CC7000A5FCC /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 019E52B91DEE2CC7000A5FCC /* RCTAnimation.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 134814201AA4EA6300B7C361;
remoteInfo = RCTAnimation;
};
019E52C11DEE2CC7000A5FCC /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 019E52B91DEE2CC7000A5FCC /* RCTAnimation.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 2D2A28201D9B03D100D4039D;
remoteInfo = "RCTAnimation-tvOS";
};
019E52C61DEE2CC7000A5FCC /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 2D2A283A1D9B042B00D4039D;
remoteInfo = "RCTImage-tvOS";
};
019E52CA1DEE2CC7000A5FCC /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 2D2A28471D9B043800D4039D;
remoteInfo = "RCTLinking-tvOS";
};
019E52CE1DEE2CC7000A5FCC /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 2D2A28541D9B044C00D4039D;
remoteInfo = "RCTNetwork-tvOS";
};
019E52D21DEE2CC7000A5FCC /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 2D2A28611D9B046600D4039D;
remoteInfo = "RCTSettings-tvOS";
};
019E52D61DEE2CC7000A5FCC /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 2D2A287B1D9B048500D4039D;
remoteInfo = "RCTText-tvOS";
};
019E52DB1DEE2CC7000A5FCC /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 2D2A28881D9B049200D4039D;
remoteInfo = "RCTWebSocket-tvOS";
};
019E52DF1DEE2CC7000A5FCC /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 2D2A28131D9B038B00D4039D;
remoteInfo = "React-tvOS";
};
01BBD31A1C907630000A3935 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 01BBD30C1C90762F000A3935 /* RNImagePicker.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 014A3B5C1C6CF33500B6D375;
remoteInfo = RNImagePicker;
};
139105C01AF99BAD00B5F7CC /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 134814201AA4EA6300B7C361;
remoteInfo = RCTSettings;
};
139FDEF31B06529B00C62182 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 3C86DF461ADF2C930047B81A;
remoteInfo = RCTWebSocket;
};
146834031AC3E56700842450 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 83CBBA2E1A601D0E00E9B192;
remoteInfo = React;
};
78C398B81ACF4ADC00677621 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 134814201AA4EA6300B7C361;
remoteInfo = RCTLinking;
};
832341B41AAA6A8300B99B32 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 58B5119B1A9E6C1200147676;
remoteInfo = RCTText;
};
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTActionSheet.xcodeproj; path = "../node_modules/react-native/Libraries/ActionSheetIOS/RCTActionSheet.xcodeproj"; sourceTree = "<group>"; };
00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTGeolocation.xcodeproj; path = "../node_modules/react-native/Libraries/Geolocation/RCTGeolocation.xcodeproj"; sourceTree = "<group>"; };
00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTImage.xcodeproj; path = "../node_modules/react-native/Libraries/Image/RCTImage.xcodeproj"; sourceTree = "<group>"; };
00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTNetwork.xcodeproj; path = "../node_modules/react-native/Libraries/Network/RCTNetwork.xcodeproj"; sourceTree = "<group>"; };
00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTVibration.xcodeproj; path = "../node_modules/react-native/Libraries/Vibration/RCTVibration.xcodeproj"; sourceTree = "<group>"; };
019E52B91DEE2CC7000A5FCC /* RCTAnimation.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTAnimation.xcodeproj; path = "../node_modules/react-native/Libraries/NativeAnimation/RCTAnimation.xcodeproj"; sourceTree = "<group>"; };
01BBD30C1C90762F000A3935 /* RNImagePicker.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RNImagePicker.xcodeproj; path = ../../ios/RNImagePicker.xcodeproj; sourceTree = "<group>"; };
139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTSettings.xcodeproj; path = "../node_modules/react-native/Libraries/Settings/RCTSettings.xcodeproj"; sourceTree = "<group>"; };
139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTWebSocket.xcodeproj; path = "../node_modules/react-native/Libraries/WebSocket/RCTWebSocket.xcodeproj"; sourceTree = "<group>"; };
13B07F961A680F5B00A75B9A /* Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Example.app; sourceTree = BUILT_PRODUCTS_DIR; };
13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = Example/AppDelegate.h; sourceTree = "<group>"; tabWidth = 4; };
13B07FB01A68108700A75B9A /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.objc; name = AppDelegate.m; path = Example/AppDelegate.m; sourceTree = "<group>"; tabWidth = 4; };
13B07FB21A68108700A75B9A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = "<group>"; };
13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = Example/Images.xcassets; sourceTree = "<group>"; };
13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = Example/Info.plist; sourceTree = "<group>"; };
13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = Example/main.m; sourceTree = "<group>"; };
146833FF1AC3E56700842450 /* React.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = React.xcodeproj; path = "../node_modules/react-native/React/React.xcodeproj"; sourceTree = "<group>"; };
78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTLinking.xcodeproj; path = "../node_modules/react-native/Libraries/LinkingIOS/RCTLinking.xcodeproj"; sourceTree = "<group>"; };
832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTText.xcodeproj; path = "../node_modules/react-native/Libraries/Text/RCTText.xcodeproj"; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
13B07F8C1A680F5B00A75B9A /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
146834051AC3E58100842450 /* libReact.a in Frameworks */,
00C302E51ABCBA2D00DB3ED1 /* libRCTActionSheet.a in Frameworks */,
019E52E21DEE2CD7000A5FCC /* libRCTAnimation.a in Frameworks */,
00C302E71ABCBA2D00DB3ED1 /* libRCTGeolocation.a in Frameworks */,
00C302E81ABCBA2D00DB3ED1 /* libRCTImage.a in Frameworks */,
133E29F31AD74F7200F7D852 /* libRCTLinking.a in Frameworks */,
00C302E91ABCBA2D00DB3ED1 /* libRCTNetwork.a in Frameworks */,
139105C61AF99C1200B5F7CC /* libRCTSettings.a in Frameworks */,
832341BD1AAA6AB300B99B32 /* libRCTText.a in Frameworks */,
00C302EA1ABCBA2D00DB3ED1 /* libRCTVibration.a in Frameworks */,
139FDEF61B0652A700C62182 /* libRCTWebSocket.a in Frameworks */,
01BBD31C1C9077A5000A3935 /* libRNImagePicker.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
00C302A81ABCB8CE00DB3ED1 /* Products */ = {
isa = PBXGroup;
children = (
00C302AC1ABCB8CE00DB3ED1 /* libRCTActionSheet.a */,
);
name = Products;
sourceTree = "<group>";
};
00C302B61ABCB90400DB3ED1 /* Products */ = {
isa = PBXGroup;
children = (
00C302BA1ABCB90400DB3ED1 /* libRCTGeolocation.a */,
);
name = Products;
sourceTree = "<group>";
};
00C302BC1ABCB91800DB3ED1 /* Products */ = {
isa = PBXGroup;
children = (
00C302C01ABCB91800DB3ED1 /* libRCTImage.a */,
019E52C71DEE2CC7000A5FCC /* libRCTImage-tvOS.a */,
);
name = Products;
sourceTree = "<group>";
};
00C302D41ABCB9D200DB3ED1 /* Products */ = {
isa = PBXGroup;
children = (
00C302DC1ABCB9D200DB3ED1 /* libRCTNetwork.a */,
019E52CF1DEE2CC7000A5FCC /* libRCTNetwork-tvOS.a */,
);
name = Products;
sourceTree = "<group>";
};
00C302E01ABCB9EE00DB3ED1 /* Products */ = {
isa = PBXGroup;
children = (
00C302E41ABCB9EE00DB3ED1 /* libRCTVibration.a */,
);
name = Products;
sourceTree = "<group>";
};
019E52BA1DEE2CC7000A5FCC /* Products */ = {
isa = PBXGroup;
children = (
019E52C01DEE2CC7000A5FCC /* libRCTAnimation.a */,
019E52C21DEE2CC7000A5FCC /* libRCTAnimation-tvOS.a */,
);
name = Products;
sourceTree = "<group>";
};
01BBD30D1C90762F000A3935 /* Products */ = {
isa = PBXGroup;
children = (
01BBD31B1C907630000A3935 /* libRNImagePicker.a */,
);
name = Products;
sourceTree = "<group>";
};
139105B71AF99BAD00B5F7CC /* Products */ = {
isa = PBXGroup;
children = (
139105C11AF99BAD00B5F7CC /* libRCTSettings.a */,
019E52D31DEE2CC7000A5FCC /* libRCTSettings-tvOS.a */,
);
name = Products;
sourceTree = "<group>";
};
139FDEE71B06529A00C62182 /* Products */ = {
isa = PBXGroup;
children = (
139FDEF41B06529B00C62182 /* libRCTWebSocket.a */,
019E52DC1DEE2CC7000A5FCC /* libRCTWebSocket-tvOS.a */,
);
name = Products;
sourceTree = "<group>";
};
13B07FAE1A68108700A75B9A /* Example */ = {
isa = PBXGroup;
children = (
13B07FAF1A68108700A75B9A /* AppDelegate.h */,
13B07FB01A68108700A75B9A /* AppDelegate.m */,
13B07FB51A68108700A75B9A /* Images.xcassets */,
13B07FB61A68108700A75B9A /* Info.plist */,
13B07FB11A68108700A75B9A /* LaunchScreen.xib */,
13B07FB71A68108700A75B9A /* main.m */,
);
name = Example;
sourceTree = "<group>";
};
146834001AC3E56700842450 /* Products */ = {
isa = PBXGroup;
children = (
146834041AC3E56700842450 /* libReact.a */,
019E52E01DEE2CC7000A5FCC /* libReact-tvOS.a */,
);
name = Products;
sourceTree = "<group>";
};
78C398B11ACF4ADC00677621 /* Products */ = {
isa = PBXGroup;
children = (
78C398B91ACF4ADC00677621 /* libRCTLinking.a */,
019E52CB1DEE2CC7000A5FCC /* libRCTLinking-tvOS.a */,
);
name = Products;
sourceTree = "<group>";
};
832341AE1AAA6A7D00B99B32 /* Libraries */ = {
isa = PBXGroup;
children = (
01BBD30C1C90762F000A3935 /* RNImagePicker.xcodeproj */,
146833FF1AC3E56700842450 /* React.xcodeproj */,
00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */,
019E52B91DEE2CC7000A5FCC /* RCTAnimation.xcodeproj */,
00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */,
00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */,
78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */,
00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */,
139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */,
832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */,
00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */,
139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */,
);
name = Libraries;
sourceTree = "<group>";
};
832341B11AAA6A8300B99B32 /* Products */ = {
isa = PBXGroup;
children = (
832341B51AAA6A8300B99B32 /* libRCTText.a */,
019E52D71DEE2CC7000A5FCC /* libRCTText-tvOS.a */,
);
name = Products;
sourceTree = "<group>";
};
83CBB9F61A601CBA00E9B192 = {
isa = PBXGroup;
children = (
13B07FAE1A68108700A75B9A /* Example */,
832341AE1AAA6A7D00B99B32 /* Libraries */,
83CBBA001A601CBA00E9B192 /* Products */,
);
indentWidth = 2;
sourceTree = "<group>";
tabWidth = 2;
};
83CBBA001A601CBA00E9B192 /* Products */ = {
isa = PBXGroup;
children = (
13B07F961A680F5B00A75B9A /* Example.app */,
);
name = Products;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
13B07F861A680F5B00A75B9A /* Example */ = {
isa = PBXNativeTarget;
buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "Example" */;
buildPhases = (
13B07F871A680F5B00A75B9A /* Sources */,
13B07F8C1A680F5B00A75B9A /* Frameworks */,
13B07F8E1A680F5B00A75B9A /* Resources */,
00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */,
);
buildRules = (
);
dependencies = (
);
name = Example;
productName = "Hello World";
productReference = 13B07F961A680F5B00A75B9A /* Example.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
83CBB9F71A601CBA00E9B192 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0820;
ORGANIZATIONNAME = Facebook;
TargetAttributes = {
13B07F861A680F5B00A75B9A = {
DevelopmentTeam = X4WMF529W9;
};
};
};
buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "Example" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 83CBB9F61A601CBA00E9B192;
productRefGroup = 83CBBA001A601CBA00E9B192 /* Products */;
projectDirPath = "";
projectReferences = (
{
ProductGroup = 00C302A81ABCB8CE00DB3ED1 /* Products */;
ProjectRef = 00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */;
},
{
ProductGroup = 019E52BA1DEE2CC7000A5FCC /* Products */;
ProjectRef = 019E52B91DEE2CC7000A5FCC /* RCTAnimation.xcodeproj */;
},
{
ProductGroup = 00C302B61ABCB90400DB3ED1 /* Products */;
ProjectRef = 00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */;
},
{
ProductGroup = 00C302BC1ABCB91800DB3ED1 /* Products */;
ProjectRef = 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */;
},
{
ProductGroup = 78C398B11ACF4ADC00677621 /* Products */;
ProjectRef = 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */;
},
{
ProductGroup = 00C302D41ABCB9D200DB3ED1 /* Products */;
ProjectRef = 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */;
},
{
ProductGroup = 139105B71AF99BAD00B5F7CC /* Products */;
ProjectRef = 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */;
},
{
ProductGroup = 832341B11AAA6A8300B99B32 /* Products */;
ProjectRef = 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */;
},
{
ProductGroup = 00C302E01ABCB9EE00DB3ED1 /* Products */;
ProjectRef = 00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */;
},
{
ProductGroup = 139FDEE71B06529A00C62182 /* Products */;
ProjectRef = 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */;
},
{
ProductGroup = 146834001AC3E56700842450 /* Products */;
ProjectRef = 146833FF1AC3E56700842450 /* React.xcodeproj */;
},
{
ProductGroup = 01BBD30D1C90762F000A3935 /* Products */;
ProjectRef = 01BBD30C1C90762F000A3935 /* RNImagePicker.xcodeproj */;
},
);
projectRoot = "";
targets = (
13B07F861A680F5B00A75B9A /* Example */,
);
};
/* End PBXProject section */
/* Begin PBXReferenceProxy section */
00C302AC1ABCB8CE00DB3ED1 /* libRCTActionSheet.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = libRCTActionSheet.a;
remoteRef = 00C302AB1ABCB8CE00DB3ED1 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
00C302BA1ABCB90400DB3ED1 /* libRCTGeolocation.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = libRCTGeolocation.a;
remoteRef = 00C302B91ABCB90400DB3ED1 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
00C302C01ABCB91800DB3ED1 /* libRCTImage.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = libRCTImage.a;
remoteRef = 00C302BF1ABCB91800DB3ED1 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
00C302DC1ABCB9D200DB3ED1 /* libRCTNetwork.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = libRCTNetwork.a;
remoteRef = 00C302DB1ABCB9D200DB3ED1 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
00C302E41ABCB9EE00DB3ED1 /* libRCTVibration.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = libRCTVibration.a;
remoteRef = 00C302E31ABCB9EE00DB3ED1 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
019E52C01DEE2CC7000A5FCC /* libRCTAnimation.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = libRCTAnimation.a;
remoteRef = 019E52BF1DEE2CC7000A5FCC /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
019E52C21DEE2CC7000A5FCC /* libRCTAnimation-tvOS.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = "libRCTAnimation-tvOS.a";
remoteRef = 019E52C11DEE2CC7000A5FCC /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
019E52C71DEE2CC7000A5FCC /* libRCTImage-tvOS.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = "libRCTImage-tvOS.a";
remoteRef = 019E52C61DEE2CC7000A5FCC /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
019E52CB1DEE2CC7000A5FCC /* libRCTLinking-tvOS.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = "libRCTLinking-tvOS.a";
remoteRef = 019E52CA1DEE2CC7000A5FCC /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
019E52CF1DEE2CC7000A5FCC /* libRCTNetwork-tvOS.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = "libRCTNetwork-tvOS.a";
remoteRef = 019E52CE1DEE2CC7000A5FCC /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
019E52D31DEE2CC7000A5FCC /* libRCTSettings-tvOS.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = "libRCTSettings-tvOS.a";
remoteRef = 019E52D21DEE2CC7000A5FCC /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
019E52D71DEE2CC7000A5FCC /* libRCTText-tvOS.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = "libRCTText-tvOS.a";
remoteRef = 019E52D61DEE2CC7000A5FCC /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
019E52DC1DEE2CC7000A5FCC /* libRCTWebSocket-tvOS.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = "libRCTWebSocket-tvOS.a";
remoteRef = 019E52DB1DEE2CC7000A5FCC /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
019E52E01DEE2CC7000A5FCC /* libReact-tvOS.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = "libReact-tvOS.a";
remoteRef = 019E52DF1DEE2CC7000A5FCC /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
01BBD31B1C907630000A3935 /* libRNImagePicker.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = libRNImagePicker.a;
remoteRef = 01BBD31A1C907630000A3935 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
139105C11AF99BAD00B5F7CC /* libRCTSettings.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = libRCTSettings.a;
remoteRef = 139105C01AF99BAD00B5F7CC /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
139FDEF41B06529B00C62182 /* libRCTWebSocket.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = libRCTWebSocket.a;
remoteRef = 139FDEF31B06529B00C62182 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
146834041AC3E56700842450 /* libReact.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = libReact.a;
remoteRef = 146834031AC3E56700842450 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
78C398B91ACF4ADC00677621 /* libRCTLinking.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = libRCTLinking.a;
remoteRef = 78C398B81ACF4ADC00677621 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
832341B51AAA6A8300B99B32 /* libRCTText.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = libRCTText.a;
remoteRef = 832341B41AAA6A8300B99B32 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
/* End PBXReferenceProxy section */
/* Begin PBXResourcesBuildPhase section */
13B07F8E1A680F5B00A75B9A /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */,
13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Bundle React Native code and images";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "export NODE_BINARY=node\n../node_modules/react-native/packager/react-native-xcode.sh";
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
13B07F871A680F5B00A75B9A /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */,
13B07FC11A68108700A75B9A /* main.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXVariantGroup section */
13B07FB11A68108700A75B9A /* LaunchScreen.xib */ = {
isa = PBXVariantGroup;
children = (
13B07FB21A68108700A75B9A /* Base */,
);
name = LaunchScreen.xib;
path = Example;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
13B07F941A680F5B00A75B9A /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
DEAD_CODE_STRIPPING = NO;
DEVELOPMENT_TEAM = X4WMF529W9;
HEADER_SEARCH_PATHS = (
"$(inherited)",
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
"$(SRCROOT)/../node_modules/react-native/React/**",
);
INFOPLIST_FILE = Example/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
OTHER_LDFLAGS = (
"-ObjC",
"-lc++",
);
PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = Example;
};
name = Debug;
};
13B07F951A680F5B00A75B9A /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
DEVELOPMENT_TEAM = X4WMF529W9;
HEADER_SEARCH_PATHS = (
"$(inherited)",
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
"$(SRCROOT)/../node_modules/react-native/React/**",
);
INFOPLIST_FILE = Example/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
OTHER_LDFLAGS = (
"-ObjC",
"-lc++",
);
PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = Example;
};
name = Release;
};
83CBBA201A601CBA00E9B192 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
HEADER_SEARCH_PATHS = (
"$(inherited)",
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
"$(SRCROOT)/../node_modules/react-native/React/**",
);
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
};
name = Debug;
};
83CBBA211A601CBA00E9B192 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = YES;
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
HEADER_SEARCH_PATHS = (
"$(inherited)",
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
"$(SRCROOT)/../node_modules/react-native/React/**",
);
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
VALIDATE_PRODUCT = YES;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "Example" */ = {
isa = XCConfigurationList;
buildConfigurations = (
13B07F941A680F5B00A75B9A /* Debug */,
13B07F951A680F5B00A75B9A /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "Example" */ = {
isa = XCConfigurationList;
buildConfigurations = (
83CBBA201A601CBA00E9B192 /* Debug */,
83CBBA211A601CBA00E9B192 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 83CBB9F71A601CBA00E9B192 /* Project object */;
}

View File

@ -1,16 +0,0 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import <UIKit/UIKit.h>
@interface AppDelegate : UIResponder <UIApplicationDelegate>
@property (nonatomic, strong) UIWindow *window;
@end

View File

@ -1,30 +0,0 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import "AppDelegate.h"
#import "RCTBundleURLProvider.h"
#import "RCTRootView.h"
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
NSURL *jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index.ios" fallbackResource:nil];
RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation moduleName:@"Example" initialProperties:nil launchOptions:launchOptions];
rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1];
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
UIViewController *rootViewController = [UIViewController new];
rootViewController.view = rootView;
self.window.rootViewController = rootViewController;
[self.window makeKeyAndVisible];
return YES;
}
@end

View File

@ -1,14 +0,0 @@
{
"name": "Example",
"version": "0.0.1",
"private": true,
"scripts": {
"start": "node node_modules/react-native/local-cli/cli.js start",
"adb-reverse": "adb reverse tcp:8081 tcp:8081"
},
"dependencies": {
"react": "15.4.1",
"react-native": "^0.40.0",
"react-native-image-picker": "file:../"
}
}

View File

@ -1,6 +1,6 @@
The MIT License (MIT)
MIT License
Copyright (c) 2015 Marc Shilling
Copyright (c) 2015-present, Facebook, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
SOFTWARE.

251
README.md
View File

@ -1,197 +1,94 @@
# React Native Image Picker
# React Native Image Picker [![npm version](https://badge.fury.io/js/react-native-image-picker.svg)](https://badge.fury.io/js/react-native-image-picker) [![npm](https://img.shields.io/npm/dt/react-native-image-picker.svg)](https://www.npmjs.org/package/react-native-image-picker) ![MIT](https://img.shields.io/dub/l/vibe-d.svg) ![Platform - Android and iOS](https://img.shields.io/badge/platform-Android%20%7C%20iOS-yellow.svg)
[![npm version](https://badge.fury.io/js/react-native-image-picker.svg)](https://badge.fury.io/js/react-native-image-picker)
[![npm](https://img.shields.io/npm/dt/react-native-image-picker.svg)](https://npmcharts.com/compare/react-native-image-picker?minimal=true)
![MIT](https://img.shields.io/dub/l/vibe-d.svg)
![Platform - Android and iOS](https://img.shields.io/badge/platform-Android%20%7C%20iOS-yellow.svg)
[![Gitter chat](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/react-native-image-picker/Lobby)
A React Native module that allows you to use native UI to select a photo/video from the device library or directly from the camera, like so:
iOS | Android
------- | ----
<img title="iOS" src="https://github.com/marcshilling/react-native-image-picker/blob/master/images/ios-image.png"> | <img title="Android" src="https://github.com/marcshilling/react-native-image-picker/blob/master/images/android-image.png">
🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧
🚧🚧🚧🚧[Help & Input Wanted](https://github.com/react-native-community/react-native-image-picker/issues/1358) 🚧🚧🚧🚧
🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧
| iOS | Android |
| --------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| <img title="iOS" src="https://github.com/react-community/react-native-image-picker/blob/master/images/ios-image.png"> | <img title="Android" src="https://github.com/react-community/react-native-image-picker/blob/master/images/android-image.png"> |
#### _Before you open an issue_
This library started as a basic bridge of the native iOS image picker, and I want to keep it that way. As such, functionality beyond what the native `UIImagePickerController` supports will not be supported here. **Multiple image selection, more control over the crop tool, and landscape support** are things missing from the native iOS functionality - **not issues with my library**. If you need these things, [react-native-image-crop-picker](https://github.com/ivpusic/react-native-image-crop-picker) might be a better choice for you.
## Table of contents
- [Install](#install)
- [Usage](#usage)
- [Direct launch](#directly-launching-the-camera-or-image-library)
- [Options](#options)
- [Response object](#the-response-object)
This library started as a basic bridge of the native iOS image picker, and I want to keep it that way. As such, functionality beyond what the native `UIImagePickerController` supports will not be supported here. **Multiple image selection, more control over the crop tool, and landscape support** are things missing from the native iOS functionality - **not issues with my library**. If you need these things, [react-native-image-crop-picker](https://github.com/ivpusic/react-native-image-crop-picker) might be a better choice for you.
## Install
## React Native Compatibility
To use this library you need to ensure you match up with the correct version of React Native you are using.
### NOTE: THIS PACKAGE IS NOW BUILT FOR REACT NATIVE 0.40 OR GREATER! IF YOU NEED TO SUPPORT REACT NATIVE < 0.40, YOU SHOULD INSTALL THIS PACKAGE `@0.24`
p.s. React Native introduced AndroidX support in 0.60, which is a **breaking change** for most libraries (incl. this one) using native Android functionality.
`npm install react-native-image-picker@latest --save`
| `@react-native-community/imagepicker` version | Required React Native Version |
| ----------------------------------------- | --------------------------------------------------------------------------------- |
| `1.x.x` | `>= 0.60` or `>= 0.59` if using [Jetifier](https://github.com/mikehardy/jetifier) |
| `0.x.x` | `<= 0.59` |
### Automatic Installation
`react-native link`
## Getting Started
IMPORTANT NOTE: You'll still need to perform step 4 for iOS and steps 2, 3, and 5 for Android of the manual instructions below.
```
yarn add react-native-image-picker
### Manual Installation
# RN >= 0.60
npx pod-install
#### iOS
1. In the XCode's "Project navigator", right click on your project's Libraries folder ➜ `Add Files to <...>`
2. Go to `node_modules``react-native-image-picker``ios` ➜ select `RNImagePicker.xcodeproj`
3. Add `RNImagePicker.a` to `Build Phases -> Link Binary With Libraries`
4. For iOS 10+, Add the `NSPhotoLibraryUsageDescription`, `NSCameraUsageDescription`, and `NSMicrophoneUsageDescription` (if allowing video) keys to your `Info.plist` with strings describing why your app needs these permissions. **Note: You will get a SIGABRT crash if you don't complete this step**
5. Compile and have fun
#### Android
1. Add the following lines to `android/settings.gradle`:
```gradle
include ':react-native-image-picker'
project(':react-native-image-picker').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-image-picker/android')
```
2. Update the android build tools version to `2.2.+` in `android/build.gradle`:
```gradle
buildscript {
...
dependencies {
classpath 'com.android.tools.build:gradle:2.2.+' // <- USE 2.2.+ version
}
...
}
...
```
3. Update the gradle version to `2.14.1` in `android/gradle/wrapper/gradle-wrapper.properties`:
```
...
distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip
```
4. Add the compile line to the dependencies in `android/app/build.gradle`:
```gradle
dependencies {
compile project(':react-native-image-picker')
}
```
5. Add the required permissions in `AndroidManifest.xml`:
```xml
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
```
6. Add the import and link the package in `MainApplication.java`:
```java
import com.imagepicker.ImagePickerPackage; // <-- add this import
public class MainApplication extends Application implements ReactApplication {
@Override
protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList(
new MainReactPackage(),
new ImagePickerPackage() // <-- add this line
// OR if you want to customize dialog style
new ImagePickerPackage(R.style.my_dialog_style)
);
}
}
```
##### Android (Optional)
Customization settings of dialog `android/app/res/values/themes.xml`:
```xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="DefaultExplainingPermissionsTheme" parent="Theme.AppCompat.Light.Dialog.Alert">
<!-- Used for the buttons -->
<item name="colorAccent">@color/your_color</item>
<!-- Used for the title and text -->
<item name="android:textColorPrimary">@color/your_color</item>
<!-- Used for the background -->
<item name="android:background">@color/your_color</item>
</style>
<resources>
# RN < 0.60
react-native link react-native-image-picker
```
If `MainActivity` is not instance of `ReactActivity`, you will need to implement `OnImagePickerPermissionsCallback` to `MainActivity`:
```java
import com.imagepicker.permissions.OnImagePickerPermissionsCallback; // <- add this import
import com.facebook.react.modules.core.PermissionListener; // <- add this import
public class MainActivity extends YourActivity implements OnImagePickerPermissionsCallback {
private PermissionListener listener; // <- add this attribute
// Your methods here
// Copy from here
@Override
public void setPermissionListener(PermissionListener listener)
{
this.listener = listener;
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults)
{
if (listener != null)
{
listener.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
// To here
}
```
This code allows to pass result of request permissions to native part.
You will also need to add `UsageDescription` on iOS and some permissions on Android, refer to the [Install doc](docs/Install.md).
## Usage
```javascript
var ImagePicker = require('react-native-image-picker');
import ImagePicker from 'react-native-image-picker';
// More info on all the options is below in the README...just some common use cases shown here
var options = {
// More info on all the options is below in the API Reference... just some common use cases shown here
const options = {
title: 'Select Avatar',
customButtons: [
{name: 'fb', title: 'Choose Photo from Facebook'},
],
customButtons: [{ name: 'fb', title: 'Choose Photo from Facebook' }],
storageOptions: {
skipBackup: true,
path: 'images'
}
path: 'images',
},
};
/**
* The first arg is the options object for customization (it can also be null or omitted for default options),
* The second arg is the callback which sends object: response (more info below in README)
* The second arg is the callback which sends object: response (more info in the API Reference)
*/
ImagePicker.showImagePicker(options, (response) => {
console.log('Response = ', response);
if (response.didCancel) {
console.log('User cancelled image picker');
}
else if (response.error) {
} else if (response.error) {
console.log('ImagePicker Error: ', response.error);
}
else if (response.customButton) {
} else if (response.customButton) {
console.log('User tapped custom button: ', response.customButton);
}
else {
let source = { uri: response.uri };
} else {
const source = { uri: response.uri };
// You can also display the image using data:
// let source = { uri: 'data:image/jpeg;base64,' + response.data };
// const source = { uri: 'data:image/jpeg;base64,' + response.data };
this.setState({
avatarSource: source
avatarSource: source,
});
}
});
```
Then later, if you want to display this image in your render() method:
```javascript
<Image source={this.state.avatarSource} style={styles.uploadAvatar} />
```
@ -200,69 +97,25 @@ Then later, if you want to display this image in your render() method:
To Launch the Camera or Image Library directly (skipping the alert dialog) you can
do the following:
```javascript
// Launch Camera:
ImagePicker.launchCamera(options, (response) => {
ImagePicker.launchCamera(options, (response) => {
// Same code as in above section!
});
// Open Image Library:
ImagePicker.launchImageLibrary(options, (response) => {
ImagePicker.launchImageLibrary(options, (response) => {
// Same code as in above section!
});
```
#### Notes
#### Note
On iOS, don't assume that the absolute uri returned will persist. See [#107](/../../issues/107)
### Options
For more, read the [API Reference](docs/Reference.md).
option | iOS | Android | Info
------ | ---- | ------- | ----
title | OK | OK | Specify `null` or empty string to remove the title
cancelButtonTitle | OK | OK | Specify `null` or empty string to remove this button (Android only)
takePhotoButtonTitle | OK | OK | Specify `null` or empty string to remove this button
chooseFromLibraryButtonTitle | OK | OK | Specify `null` or empty string to remove this button
customButtons | OK | OK | An array containing objects with the name and title of buttons
cameraType | OK | - | 'front' or 'back'
mediaType | OK | OK | 'photo', 'video', or 'mixed' on iOS, 'photo' or 'video' on Android
maxWidth | OK | OK | Photos only
maxHeight | OK | OK | Photos only
quality | OK | OK | 0 to 1, photos only
videoQuality | OK | OK | 'low', 'medium', or 'high' on iOS, 'low' or 'high' on Android
durationLimit | OK | OK | Max video recording time, in seconds
rotation | - | OK | Photos only, 0 to 360 degrees of rotation
allowsEditing | OK | - | bool - enables built in iOS functionality to resize the image after selection
noData | OK | OK | If true, disables the base64 `data` field from being generated (greatly improves performance on large photos)
storageOptions | OK | OK | If this key is provided, the image will be saved in your app's `Documents` directory on iOS, or your app's `Pictures` directory on Android (rather than a temporary directory)
storageOptions.skipBackup | OK | - | If true, the photo will NOT be backed up to iCloud
storageOptions.path | OK | - | If set, will save the image at `Documents/[path]/` rather than the root `Documents`
storageOptions.cameraRoll | OK | OK | If true, the cropped photo will be saved to the iOS Camera Roll or Android DCIM folder.
storageOptions.waitUntilSaved | OK | - | If true, will delay the response callback until after the photo/video was saved to the Camera Roll. If the photo or video was just taken, then the file name and timestamp fields are only provided in the response object when this is true.
permissionDenied.title | - | OK | Title of explaining permissions dialog. By default `Permission denied`.
permissionDenied.text | - | OK | Message of explaining permissions dialog. By default `To be able to take pictures with your camera and choose images from your library.`.
permissionDenied.reTryTitle | - | OK | Title of re-try button. By default `re-try`
permissionDenied.okTitle | - | OK | Title of ok button. By default `I'm sure`
## License
### The Response Object
key | iOS | Android | Description
------ | ---- | ------- | ----------------------
didCancel | OK | OK | Informs you if the user cancelled the process
error | OK | OK | Contains an error message, if there is one
customButton | OK | OK | If the user tapped one of your custom buttons, contains the name of it
data | OK | OK | The base64 encoded image data (photos only)
uri | OK | OK | The uri to the local file asset on the device (photo or video)
origURL | OK | - | The URL of the original asset in photo library, if it exists
isVertical | OK | OK | Will be true if the image is vertically oriented
width | OK | OK | Image dimensions
height | OK | OK | Image dimensions
fileSize | OK | OK | The file size (photos only)
type | - | OK | The file type (photos only)
fileName | OK (photos and videos) | OK (photos) | The file name
path | - | OK | The file path
latitude | OK | OK | Latitude metadata, if available
longitude | OK | OK | Longitude metadata, if available
timestamp | OK | OK | Timestamp metadata, if available, in ISO8601 UTC format
originalRotation | - | OK | Rotation degrees (photos only) *See [#109](/../../issues/199)*
[MIT](LICENSE.md)

View File

@ -1,62 +1,65 @@
import groovy.json.JsonSlurper
def computeVersionName() {
// dynamically retrieve version from package.json
def slurper = new JsonSlurper()
def json = slurper.parse(file('../package.json'), "utf-8")
return json.version
}
buildscript {
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.2.+'
classpath 'com.android.tools.build:gradle:3.4.1'
}
}
def getExtOrDefault(name) {
return rootProject.ext.has(name) ? rootProject.ext.get(name) : project.properties['ReactNativeImagePicker_' + name]
}
def getExtOrIntegerDefault(name) {
return rootProject.ext.has(name) ? rootProject.ext.get(name) : (project.properties['ReactNativeImagePicker_' + name]).toInteger()
}
apply plugin: 'com.android.library'
android {
compileSdkVersion 25
buildToolsVersion "25.0.2"
compileSdkVersion getExtOrIntegerDefault('compileSdkVersion')
buildToolsVersion getExtOrDefault('buildToolsVersion')
defaultConfig {
minSdkVersion 16
targetSdkVersion 25
versionCode 1
versionName computeVersionName()
minSdkVersion getExtOrIntegerDefault('minSdkVersion')
targetSdkVersion getExtOrIntegerDefault('targetSdkVersion')
}
lintOptions {
abortOnError false
}
testOptions {
unitTests.returnDefaultValues = true
unitTests {
includeAndroidResources = true
}
}
}
repositories {
mavenCentral()
mavenLocal()
maven {
url "$projectDir/../Example/node_modules/react-native/android"
url "$projectDir/../node_modules/react-native/android"
}
maven {
url "$projectDir/../../react-native/android"
url "$projectDir/../node_modules/jsc-android/dist"
}
google()
jcenter()
}
dependencies {
compile "com.facebook.react:react-native:+" // From node_modules
testCompile "junit:junit:4.10"
testCompile "org.assertj:assertj-core:1.7.0"
testCompile "org.robolectric:robolectric:3.3.2"
api "com.facebook.react:react-native:+" // From node_modules
testCompile "org.easytesting:fest-assert-core:${FEST_ASSERT_CORE_VERSION}"
testCompile "org.powermock:powermock-api-mockito:${POWERMOCK_VERSION}"
testCompile "org.powermock:powermock-module-junit4-rule:${POWERMOCK_VERSION}"
testCompile "org.powermock:powermock-classloading-xstream:${POWERMOCK_VERSION}"
testCompile "org.mockito:mockito-core:${MOCKITO_CORE_VERSION}"
testImplementation('org.robolectric:robolectric:4.3.1') {
// https://github.com/robolectric/robolectric/issues/5245
exclude group: 'com.google.auto.service', module: 'auto-service'
}
testImplementation "org.powermock:powermock-api-mockito:${POWERMOCK_VERSION}"
testImplementation "org.powermock:powermock-module-junit4:${POWERMOCK_VERSION}"
testImplementation "org.powermock:powermock-module-junit4-rule:${POWERMOCK_VERSION}"
testImplementation "org.powermock:powermock-classloading-xstream:${POWERMOCK_VERSION}"
}

View File

@ -1,3 +1,9 @@
MOCKITO_CORE_VERSION=1.+
POWERMOCK_VERSION=1.6.2
FEST_ASSERT_CORE_VERSION=2.0M10
POWERMOCK_VERSION=1.6.6
ReactNativeImagePicker_compileSdkVersion=28
ReactNativeImagePicker_buildToolsVersion=28.0.3
ReactNativeImagePicker_targetSdkVersion=27
ReactNativeImagePicker_minSdkVersion=16
android.useAndroidX=true
android.enableJetifier=true

View File

@ -1,6 +1,6 @@
#Mon Dec 28 10:00:20 PST 2015
#Mon Jun 24 15:04:47 BST 2019
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip

View File

@ -5,7 +5,7 @@
>
<application>
<provider
android:name="android.support.v4.content.FileProvider"
android:name="com.imagepicker.FileProvider"
android:authorities="${applicationId}.provider"
android:exported="false"
android:grantUriPermissions="true">

View File

@ -0,0 +1,4 @@
package com.imagepicker;
public class FileProvider extends androidx.core.content.FileProvider {
}

View File

@ -6,15 +6,17 @@ import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.ResolveInfo;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Build;
import android.provider.MediaStore;
import android.provider.Settings;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.StyleRes;
import android.support.v4.app.ActivityCompat;
import android.support.v7.app.AlertDialog;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StyleRes;
import androidx.core.app.ActivityCompat;
import androidx.appcompat.app.AlertDialog;
import android.text.TextUtils;
import android.util.Base64;
import android.util.Patterns;
@ -28,11 +30,12 @@ import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.module.annotations.ReactModule;
import com.imagepicker.media.ImageConfig;
import com.imagepicker.permissions.PermissionUtils;
import com.imagepicker.permissions.OnImagePickerPermissionsCallback;
import com.imagepicker.utils.MediaUtils;
import com.imagepicker.utils.MediaUtils.ReadExifResult;
import com.imagepicker.utils.ReadableMapUtils;
import com.imagepicker.utils.RealPathUtil;
import com.imagepicker.utils.UI;
@ -45,16 +48,22 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.ref.WeakReference;
import java.util.List;
import com.facebook.react.modules.core.PermissionListener;
import com.facebook.react.modules.core.PermissionAwareActivity;
import static com.imagepicker.utils.MediaUtils.*;
import static com.imagepicker.utils.MediaUtils.createNewFile;
import static com.imagepicker.utils.MediaUtils.getResizedImage;
@ReactModule(name = ImagePickerModule.NAME)
public class ImagePickerModule extends ReactContextBaseJavaModule
implements ActivityEventListener
{
public static final String NAME = "ImagePickerManager";
public static final int DEFAULT_EXPLAINING_PERMISSION_DIALIOG_THEME = R.style.DefaultExplainingPermissionsTheme;
public static final int REQUEST_LAUNCH_IMAGE_CAPTURE = 13001;
public static final int REQUEST_LAUNCH_IMAGE_LIBRARY = 13002;
@ -67,10 +76,13 @@ public class ImagePickerModule extends ReactContextBaseJavaModule
private final int dialogThemeId;
protected Callback callback;
private Callback permissionRequestCallback;
private ReadableMap options;
protected Uri cameraCaptureURI;
private Boolean noData = false;
private Boolean pickVideo = false;
private Boolean pickBoth = false;
private ImageConfig imageConfig = new ImageConfig(null, null, 0, 0, 100, 0, false);
@Deprecated
@ -100,18 +112,18 @@ public class ImagePickerModule extends ReactContextBaseJavaModule
if (!permissionsGranted)
{
responseHelper.invokeError(callback, "Permissions weren't granted");
responseHelper.invokeError(permissionRequestCallback, "Permissions weren't granted");
return false;
}
switch (requestCode)
{
case REQUEST_PERMISSIONS_FOR_CAMERA:
launchCamera(options, callback);
launchCamera(options, permissionRequestCallback);
break;
case REQUEST_PERMISSIONS_FOR_LIBRARY:
launchImageLibrary(options, callback);
launchImageLibrary(options, permissionRequestCallback);
break;
}
@ -119,6 +131,11 @@ public class ImagePickerModule extends ReactContextBaseJavaModule
}
};
public ImagePickerModule(ReactApplicationContext reactContext)
{
this(reactContext, DEFAULT_EXPLAINING_PERMISSION_DIALIOG_THEME);
}
public ImagePickerModule(ReactApplicationContext reactContext,
@StyleRes final int dialogThemeId)
{
@ -131,7 +148,7 @@ public class ImagePickerModule extends ReactContextBaseJavaModule
@Override
public String getName() {
return "ImagePickerManager";
return NAME;
}
@ReactMethod
@ -196,7 +213,10 @@ public class ImagePickerModule extends ReactContextBaseJavaModule
public void doOnCancel()
{
responseHelper.invokeCancel(callback);
if (callback != null) {
responseHelper.invokeCancel(callback);
callback = null;
}
}
public void launchCamera()
@ -208,6 +228,8 @@ public class ImagePickerModule extends ReactContextBaseJavaModule
@ReactMethod
public void launchCamera(final ReadableMap options, final Callback callback)
{
permissionRequestCallback = callback;
if (!isCameraAvailable())
{
responseHelper.invokeError(callback, "Camera not available");
@ -221,6 +243,7 @@ public class ImagePickerModule extends ReactContextBaseJavaModule
return;
}
this.callback = callback;
this.options = options;
if (!permissionsCheck(currentActivity, callback, REQUEST_PERMISSIONS_FOR_CAMERA))
@ -248,10 +271,15 @@ public class ImagePickerModule extends ReactContextBaseJavaModule
requestCode = REQUEST_LAUNCH_IMAGE_CAPTURE;
cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
final File original = createNewFile(reactContext);
final File original = createNewFile(reactContext, this.options, false);
imageConfig = imageConfig.withOriginalFile(original);
cameraCaptureURI = RealPathUtil.compatUriFromFile(reactContext, imageConfig.original);
if (imageConfig.original != null) {
cameraCaptureURI = RealPathUtil.compatUriFromFile(reactContext, imageConfig.original);
}else {
responseHelper.invokeError(callback, "Couldn't get file path for photo");
return;
}
if (cameraCaptureURI == null)
{
responseHelper.invokeError(callback, "Couldn't get file path for photo");
@ -266,7 +294,16 @@ public class ImagePickerModule extends ReactContextBaseJavaModule
return;
}
this.callback = callback;
// Workaround for Android bug.
// grantUriPermission also needed for KITKAT,
// see https://code.google.com/p/android/issues/detail?id=76683
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) {
List<ResolveInfo> resInfoList = reactContext.getPackageManager().queryIntentActivities(cameraIntent, PackageManager.MATCH_DEFAULT_ONLY);
for (ResolveInfo resolveInfo : resInfoList) {
String packageName = resolveInfo.activityInfo.packageName;
reactContext.grantUriPermission(packageName, cameraCaptureURI, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
}
}
try
{
@ -287,12 +324,15 @@ public class ImagePickerModule extends ReactContextBaseJavaModule
@ReactMethod
public void launchImageLibrary(final ReadableMap options, final Callback callback)
{
permissionRequestCallback = callback;
final Activity currentActivity = getCurrentActivity();
if (currentActivity == null) {
responseHelper.invokeError(callback, "can't find current Activity");
return;
}
this.callback = callback;
this.options = options;
if (!permissionsCheck(currentActivity, callback, REQUEST_PERMISSIONS_FOR_LIBRARY))
@ -315,6 +355,11 @@ public class ImagePickerModule extends ReactContextBaseJavaModule
requestCode = REQUEST_LAUNCH_IMAGE_LIBRARY;
libraryIntent = new Intent(Intent.ACTION_PICK,
MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
if (pickBoth)
{
libraryIntent.setType("image/* video/*");
}
}
if (libraryIntent.resolveActivity(reactContext.getPackageManager()) == null)
@ -323,11 +368,15 @@ public class ImagePickerModule extends ReactContextBaseJavaModule
return;
}
this.callback = callback;
try
{
currentActivity.startActivityForResult(libraryIntent, requestCode);
String chooseWhichLibraryTitle = null;
if (ReadableMapUtils.hasAndNotEmptyString(options, "chooseWhichLibraryTitle"))
{
chooseWhichLibraryTitle = options.getString("chooseWhichLibraryTitle");
}
currentActivity.startActivityForResult(Intent.createChooser(libraryIntent, chooseWhichLibraryTitle), requestCode);
}
catch (ActivityNotFoundException e)
{
@ -431,7 +480,7 @@ public class ImagePickerModule extends ReactContextBaseJavaModule
}
else
{
imageConfig = getResizedImage(reactContext, imageConfig, initialWidth, initialHeight);
imageConfig = getResizedImage(reactContext, this.options, imageConfig, initialWidth, initialHeight, requestCode);
if (imageConfig.resized == null)
{
removeUselessFiles(requestCode, imageConfig);
@ -446,7 +495,6 @@ public class ImagePickerModule extends ReactContextBaseJavaModule
updatedResultResponse(uri, imageConfig.resized.getAbsolutePath());
fileScan(reactContext, imageConfig.resized.getAbsolutePath());
MediaUtils.removeOriginIfNeeded(imageConfig, requestCode);
}
}
@ -528,10 +576,18 @@ public class ImagePickerModule extends ReactContextBaseJavaModule
final int cameraPermission = ActivityCompat
.checkSelfPermission(activity, Manifest.permission.CAMERA);
final boolean permissionsGrated = writePermission == PackageManager.PERMISSION_GRANTED &&
cameraPermission == PackageManager.PERMISSION_GRANTED;
boolean permissionsGranted = false;
if (!permissionsGrated)
switch (requestCode) {
case REQUEST_PERMISSIONS_FOR_LIBRARY:
permissionsGranted = writePermission == PackageManager.PERMISSION_GRANTED;
break;
case REQUEST_PERMISSIONS_FOR_CAMERA:
permissionsGranted = cameraPermission == PackageManager.PERMISSION_GRANTED && writePermission == PackageManager.PERMISSION_GRANTED;
break;
}
if (!permissionsGranted)
{
final Boolean dontAskAgain = ActivityCompat.shouldShowRequestPermissionRationale(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE) && ActivityCompat.shouldShowRequestPermissionRationale(activity, Manifest.permission.CAMERA);
@ -572,16 +628,33 @@ public class ImagePickerModule extends ReactContextBaseJavaModule
innerActivity.startActivityForResult(intent, 1);
}
});
dialog.show();
if (dialog != null) {
dialog.show();
}
return false;
}
else
{
String[] PERMISSIONS = {Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.CAMERA};
String[] PERMISSIONS;
switch (requestCode) {
case REQUEST_PERMISSIONS_FOR_LIBRARY:
PERMISSIONS = new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE};
break;
case REQUEST_PERMISSIONS_FOR_CAMERA:
PERMISSIONS = new String[]{Manifest.permission.CAMERA,Manifest.permission.WRITE_EXTERNAL_STORAGE};
break;
default:
PERMISSIONS = new String[]{};
break;
}
if (activity instanceof ReactActivity)
{
((ReactActivity) activity).requestPermissions(PERMISSIONS, requestCode, listener);
}
else if (activity instanceof PermissionAwareActivity) {
((PermissionAwareActivity) activity).requestPermissions(PERMISSIONS, requestCode, listener);
}
else if (activity instanceof OnImagePickerPermissionsCallback)
{
((OnImagePickerPermissionsCallback) activity).setPermissionListener(listener);
@ -592,6 +665,8 @@ public class ImagePickerModule extends ReactContextBaseJavaModule
final String errorDescription = new StringBuilder(activity.getClass().getSimpleName())
.append(" must implement ")
.append(OnImagePickerPermissionsCallback.class.getSimpleName())
.append(" or ")
.append(PermissionAwareActivity.class.getSimpleName())
.toString();
throw new UnsupportedOperationException(errorDescription);
}
@ -667,20 +742,26 @@ public class ImagePickerModule extends ReactContextBaseJavaModule
private void putExtraFileInfo(@NonNull final String path,
@NonNull final ResponseHelper responseHelper)
{
// size && filename
try {
// size && filename
File f = new File(path);
responseHelper.putDouble("fileSize", f.length());
responseHelper.putString("fileName", f.getName());
// type
String extension = MimeTypeMap.getFileExtensionFromUrl(path);
String fileName = f.getName();
if (extension != "") {
responseHelper.putString("type", MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension));
} else {
int i = fileName.lastIndexOf('.');
if (i > 0) {
extension = fileName.substring(i+1);
responseHelper.putString("type", MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension));
}
}
} catch (Exception e) {
e.printStackTrace();
}
// type
String extension = MimeTypeMap.getFileExtensionFromUrl(path);
if (extension != null) {
responseHelper.putString("type", MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension));
}
}
private void parseOptions(final ReadableMap options) {
@ -690,6 +771,10 @@ public class ImagePickerModule extends ReactContextBaseJavaModule
}
imageConfig = imageConfig.updateFromOptions(options);
pickVideo = false;
pickBoth = false;
if (options.hasKey("mediaType") && options.getString("mediaType").equals("mixed")) {
pickBoth = true;
}
if (options.hasKey("mediaType") && options.getString("mediaType").equals("video")) {
pickVideo = true;
}

View File

@ -1,6 +1,6 @@
package com.imagepicker;
import android.support.annotation.StyleRes;
import androidx.annotation.StyleRes;
import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.JavaScriptModule;
@ -13,12 +13,11 @@ import java.util.Collections;
import java.util.List;
public class ImagePickerPackage implements ReactPackage {
public static final int DEFAULT_EXPLAINING_PERMISSION_DIALIOG_THEME = R.style.DefaultExplainingPermissionsTheme;
private @StyleRes final int dialogThemeId;
public ImagePickerPackage()
{
this.dialogThemeId = DEFAULT_EXPLAINING_PERMISSION_DIALIOG_THEME;
this.dialogThemeId = ImagePickerModule.DEFAULT_EXPLAINING_PERMISSION_DIALIOG_THEME;
}
public ImagePickerPackage(@StyleRes final int dialogThemeId)
@ -31,7 +30,7 @@ public class ImagePickerPackage implements ReactPackage {
return Arrays.<NativeModule>asList(new ImagePickerModule(reactContext, dialogThemeId));
}
@Override
// Deprecated RN 0.47
public List<Class<? extends JavaScriptModule>> createJSModules() {
return Collections.emptyList();
}
@ -40,4 +39,4 @@ public class ImagePickerPackage implements ReactPackage {
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
return Collections.emptyList();
}
}
}

View File

@ -1,6 +1,6 @@
package com.imagepicker;
import android.support.annotation.NonNull;
import androidx.annotation.NonNull;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.Callback;
@ -73,6 +73,9 @@ public class ResponseHelper
public void invokeResponse(@NonNull final Callback callback)
{
if (callback == null) {
return;
}
callback.invoke(response);
}
}

View File

@ -1,7 +1,8 @@
package com.imagepicker.media;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import android.webkit.MimeTypeMap;
import com.facebook.react.bridge.ReadableMap;
@ -77,9 +78,18 @@ public class ImageConfig
public @NonNull ImageConfig withOriginalFile(@Nullable final File original)
{
if (original != null) {
//if it is a GIF file, always set quality to 100 to prevent compression
String extension = MimeTypeMap.getFileExtensionFromUrl(original.getAbsolutePath());
int quality = this.quality;
if(extension.contains("gif")){
quality = 100;
}
}
return new ImageConfig(
original, this.resized, this.maxWidth,
this.maxHeight, this.quality, this.rotation,
this.maxHeight, quality, this.rotation,
this.saveToCameraRoll
);
}
@ -107,12 +117,12 @@ public class ImageConfig
int maxWidth = 0;
if (options.hasKey("maxWidth"))
{
maxWidth = options.getInt("maxWidth");
maxWidth = (int) options.getDouble("maxWidth");
}
int maxHeight = 0;
if (options.hasKey("maxHeight"))
{
maxHeight = options.getInt("maxHeight");
maxHeight = (int) options.getDouble("maxHeight");
}
int quality = 100;
if (options.hasKey("quality"))
@ -122,7 +132,7 @@ public class ImageConfig
int rotation = 0;
if (options.hasKey("rotation"))
{
rotation = options.getInt("rotation");
rotation = (int) options.getDouble("rotation");
}
boolean saveToCameraRoll = false;
if (options.hasKey("storageOptions"))

View File

@ -1,6 +1,6 @@
package com.imagepicker.permissions;
import android.support.annotation.NonNull;
import androidx.annotation.NonNull;
import com.facebook.react.modules.core.PermissionListener;
/**

View File

@ -2,13 +2,13 @@ package com.imagepicker.permissions;
import android.app.Activity;
import android.content.DialogInterface;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.app.AlertDialog;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.ReadableNativeMap;
import com.imagepicker.ImagePickerModule;
import com.imagepicker.R;
import java.lang.ref.WeakReference;
@ -26,7 +26,16 @@ public class PermissionUtils
{
return null;
}
if (!options.hasKey("permissionDenied"))
{
return null;
}
final ReadableMap permissionDenied = options.getMap("permissionDenied");
if (((ReadableNativeMap) permissionDenied).toHashMap().size() == 0)
{
return null;
}
final String title = permissionDenied.getString("title");
final String text = permissionDenied.getString("text");
final String btnReTryTitle = permissionDenied.getString("reTryTitle");

View File

@ -1,7 +1,5 @@
package com.imagepicker.permissions;
import android.support.annotation.NonNull;
/**
* Created by rusfearuth on 03.03.17.
*/

View File

@ -1,7 +1,7 @@
package com.imagepicker.utils;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;

View File

@ -8,8 +8,8 @@ import android.media.ExifInterface;
import android.media.MediaScannerConnection;
import android.net.Uri;
import android.os.Environment;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import android.util.Log;
import com.facebook.react.bridge.ReadableMap;
@ -20,7 +20,6 @@ import com.imagepicker.media.ImageConfig;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.channels.FileChannel;
@ -37,15 +36,55 @@ import static com.imagepicker.ImagePickerModule.REQUEST_LAUNCH_IMAGE_CAPTURE;
public class MediaUtils
{
public static @NonNull File createNewFile(@NonNull final Context reactContext)
public static @Nullable File createNewFile(@NonNull final Context reactContext,
@NonNull final ReadableMap options,
@NonNull final boolean forceLocal)
{
final String filename = new StringBuilder("image-")
.append(UUID.randomUUID().toString())
.append(".jpg")
.toString();
final File path = reactContext.getExternalFilesDir(Environment.DIRECTORY_PICTURES);
// defaults to Public Pictures Directory
File path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
if (ReadableMapUtils.hasAndNotNullReadableMap(options, "storageOptions"))
{
final ReadableMap storageOptions = options.getMap("storageOptions");
if (storageOptions.hasKey("privateDirectory"))
{
boolean saveToPrivateDirectory = storageOptions.getBoolean("privateDirectory");
if (saveToPrivateDirectory)
{
// if privateDirectory is set then save to app's private files directory
path = reactContext.getExternalFilesDir(Environment.DIRECTORY_PICTURES);
}
}
if (ReadableMapUtils.hasAndNotEmptyString(storageOptions, "path"))
{
path = new File(path, storageOptions.getString("path"));
}
}
else if (forceLocal)
{
path = reactContext.getExternalFilesDir(Environment.DIRECTORY_PICTURES);
}
File result = new File(path, filename);
path.mkdirs();
try
{
path.mkdirs();
result.createNewFile();
}
catch (IOException e)
{
e.printStackTrace();
result = null;
}
return result;
}
@ -53,24 +92,37 @@ public class MediaUtils
* Create a resized image to fulfill the maxWidth/maxHeight, quality and rotation values
*
* @param context
* @param options
* @param imageConfig
* @param initialWidth
* @param initialHeight
* @return updated ImageConfig
*/
public static @NonNull ImageConfig getResizedImage(@NonNull final Context context,
@NonNull final ReadableMap options,
@NonNull final ImageConfig imageConfig,
final int initialWidth,
final int initialHeight)
int initialWidth,
int initialHeight,
final int requestCode)
{
BitmapFactory.Options imageOptions = new BitmapFactory.Options();
imageOptions.inScaled = false;
// FIXME: OOM here
imageOptions.inSampleSize = 1;
if (imageConfig.maxWidth != 0 || imageConfig.maxHeight != 0) {
while ((imageConfig.maxWidth == 0 || initialWidth > 2 * imageConfig.maxWidth) &&
(imageConfig.maxHeight == 0 || initialHeight > 2 * imageConfig.maxHeight)) {
imageOptions.inSampleSize *= 2;
initialHeight /= 2;
initialWidth /= 2;
}
}
Bitmap photo = BitmapFactory.decodeFile(imageConfig.original.getAbsolutePath(), imageOptions);
if (photo == null)
{
return null;
return imageConfig;
}
ImageConfig result = imageConfig;
@ -125,7 +177,8 @@ public class MediaUtils
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
scaledPhoto.compress(Bitmap.CompressFormat.JPEG, result.quality, bytes);
final File resized = createNewFile(context);
final boolean forceLocal = requestCode == REQUEST_LAUNCH_IMAGE_CAPTURE;
final File resized = createNewFile(context, options, !forceLocal);
if (resized == null)
{
@ -144,11 +197,9 @@ public class MediaUtils
result = result.withResizedFile(resized);
FileOutputStream fos;
try
try (FileOutputStream fos = new FileOutputStream(result.resized))
{
fos = new FileOutputStream(result.resized);
fos.write(bytes.toByteArray());
bytes.writeTo(fos);
}
catch (IOException e)
{
@ -216,11 +267,14 @@ public class MediaUtils
ExifInterface exif = new ExifInterface(imageConfig.original.getAbsolutePath());
// extract lat, long, and timestamp and add to the response
float[] latLng = new float[2];
if(exif.getLatLong(latLng))
float[] latlng = new float[2];
exif.getLatLong(latlng);
float latitude = latlng[0];
float longitude = latlng[1];
if(latitude != 0f || longitude != 0f)
{
responseHelper.putDouble("latitude", latLng[0]);
responseHelper.putDouble("longitude", latLng[1]);
responseHelper.putDouble("latitude", latitude);
responseHelper.putDouble("longitude", longitude);
}
final String timestamp = exif.getAttribute(ExifInterface.TAG_DATETIME);
@ -300,35 +354,26 @@ public class MediaUtils
* This is done via copy + deletion, because Android will throw an error
* if you try to move a file across mount points, e.g. to the SD card.
*/
public static void moveFile(@NonNull final File oldFile,
private static void moveFile(@NonNull final File oldFile,
@NonNull final File newFile) throws IOException
{
FileInputStream in = new FileInputStream(oldFile);
FileOutputStream out = new FileOutputStream(newFile);
FileChannel oldChannel = null;
FileChannel newChannel = null;
try
{
byte[] buf = new byte[1024];
int len;
while ((len = in.read(buf)) > 0) {
out.write(buf, 0, len);
}
in.close();
out.close();
oldChannel = new FileInputStream(oldFile).getChannel();
newChannel = new FileOutputStream(newFile).getChannel();
oldChannel.transferTo(0, oldChannel.size(), newChannel);
oldFile.delete();
}
finally
{
try
{
if (in != null)
{
in.close();
}
if (out != null)
{
out.close();
}
if (oldChannel != null) oldChannel.close();
if (newChannel != null) newChannel.close();
}
catch (IOException e)
{
@ -337,16 +382,6 @@ public class MediaUtils
}
}
public static void removeOriginIfNeeded(@NonNull final ImageConfig imageConfig,
final int requestCode)
{
if (requestCode != ImagePickerModule.REQUEST_LAUNCH_IMAGE_CAPTURE)
{
return;
}
imageConfig.original.delete();
}
public static class RolloutPhotoResult
{

View File

@ -1,6 +1,6 @@
package com.imagepicker.utils;
import android.support.annotation.NonNull;
import androidx.annotation.NonNull;
import android.text.TextUtils;
import com.facebook.react.bridge.ReadableMap;

View File

@ -1,7 +1,6 @@
package com.imagepicker.utils;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
@ -10,9 +9,9 @@ import android.provider.DocumentsContract;
import android.provider.MediaStore;
import android.content.ContentUris;
import android.os.Environment;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.content.FileProvider;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.content.FileProvider;
import java.io.File;

View File

@ -2,10 +2,10 @@ package com.imagepicker.utils;
import android.content.Context;
import android.content.DialogInterface;
import android.graphics.drawable.ColorDrawable;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.app.AlertDialog;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import android.widget.ArrayAdapter;
import com.facebook.react.bridge.ReadableMap;

View File

@ -1,4 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="app_images" path="."/>
</paths>
<files-path name="shared" path="."/>
<external-path name="shared" path="."/>
<external-files-path name="shared" path="."/>
<root-path name="root" path="."/>
</paths>

View File

@ -45,8 +45,7 @@ import static org.powermock.api.mockito.PowerMockito.when;
@RunWith(RobolectricTestRunner.class)
@SuppressStaticInitializationFor("com.facebook.react.common.build.ReactBuildConfig")
@PrepareForTest({Arguments.class})
@PowerMockIgnore({"org.mockito.*", "org.robolectric.*", "android.*"})
@Config(manifest = Config.NONE)
@PowerMockIgnore({"org.mockito.*", "org.robolectric.*", "android.*", "jdk.internal.reflect.*"})
public class ImagePickerModuleTest
{
private static final int DEFAULT_THEME = R.style.DefaultExplainingPermissionsTheme;
@ -111,4 +110,4 @@ public class ImagePickerModuleTest
}
});
}
}
}

View File

@ -15,6 +15,9 @@ public class SampleCallback implements Callback
@Override
public void invoke(Object... args)
{
System.out.println(args.length);
System.out.println(String.valueOf(args[0]));
System.out.println(args[0].getClass());
for (int i = 0; i < args.length; i++) {
if (lookingForError(args[i])) {
break;

View File

@ -1,16 +1,13 @@
package com.imagepicker.testing;
import android.net.Uri;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.StyleRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StyleRes;
import com.facebook.react.bridge.Callback;
import com.facebook.react.bridge.ReactApplicationContext;
import com.imagepicker.ImagePickerModule;
import com.imagepicker.ResponseHelper;
import java.lang.reflect.Field;
/**
* Created by rusfearuth on 10.04.17.

View File

@ -1,78 +0,0 @@
package com.imagepicker.testing.mock;
import android.annotation.TargetApi;
import android.media.ExifInterface;
import android.os.Build;
import org.robolectric.annotation.Implements;
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
/**
* Created by rusfearuth on 13.04.17.
*/
@Implements(ExifInterface.class)
public class MockExifInterface
extends ExifInterface
{
private HashMap<String, String> metadata = new HashMap<>();
public MockExifInterface(String filename) throws
IOException
{
super(filename);
}
@TargetApi(Build.VERSION_CODES.N)
public MockExifInterface(FileDescriptor fileDescriptor) throws
IOException
{
super(fileDescriptor);
}
@TargetApi(Build.VERSION_CODES.N)
public MockExifInterface(InputStream inputStream) throws
IOException
{
super(inputStream);
}
@Override
public void setAttribute(String attr, String value)
{
metadata.put(attr, value);
}
@Override
public String getAttribute(String attr)
{
return metadata.get(attr);
}
@Override
public int getAttributeInt(String attr, int defaultValue)
{
if (!metadata.containsKey(attr))
{
return defaultValue;
}
return Integer.valueOf(metadata.get(attr));
}
@Override
public boolean getLatLong(float[] latLong)
{
if (!metadata.containsKey(ExifInterface.TAG_GPS_LATITUDE) ||
!metadata.containsKey(ExifInterface.TAG_GPS_LONGITUDE))
{
return false;
}
latLong[0] = Float.valueOf(metadata.get(ExifInterface.TAG_GPS_LATITUDE));
latLong[1] = Float.valueOf(metadata.get(ExifInterface.TAG_GPS_LONGITUDE));
return true;
}
}

View File

@ -1,296 +0,0 @@
package com.imagepicker.testing.utils;
import android.app.Application;
import android.graphics.Bitmap;
import android.media.ExifInterface;
import android.os.Environment;
import android.support.annotation.NonNull;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.JavaOnlyArray;
import com.facebook.react.bridge.JavaOnlyMap;
import com.imagepicker.ImagePickerModule;
import com.imagepicker.ResponseHelper;
import com.imagepicker.media.ImageConfig;
import com.imagepicker.testing.mock.MockExifInterface;
import com.imagepicker.utils.MediaUtils;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PowerMockIgnore;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.core.classloader.annotations.SuppressStaticInitializationFor;
import org.powermock.modules.junit4.rule.PowerMockRule;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowBitmap;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertNotSame;
import static junit.framework.Assert.assertNull;
import static junit.framework.Assert.assertTrue;
import static org.powermock.api.mockito.PowerMockito.when;
import static org.powermock.api.mockito.PowerMockito.whenNew;
/**
* Created by rusfearuth on 12.04.17.
*/
@RunWith(RobolectricTestRunner.class)
@SuppressStaticInitializationFor("com.facebook.react.common.build.ReactBuildConfig")
@PrepareForTest({ Arguments.class, MediaUtils.class })
@PowerMockIgnore({"org.mockito.*", "org.robolectric.*", "android.*"})
@Config(manifest = Config.NONE)
public class MediaUtilsTest
{
@Rule
public PowerMockRule rule = new PowerMockRule();
private ExifInterface exifMock;
@Before
public void setUp() throws Exception
{
PowerMockito.mockStatic(Arguments.class);
when(Arguments.createArray()).thenAnswer(new Answer<Object>() {
@Override
public Object answer(InvocationOnMock invocation) throws Throwable {
return new JavaOnlyArray();
}
});
when(Arguments.createMap()).thenAnswer(new Answer<Object>() {
@Override
public Object answer(InvocationOnMock invocation) throws Throwable {
return new JavaOnlyMap();
}
});
exifMock = new MockExifInterface("");
whenNew(ExifInterface.class).withAnyArguments().thenReturn(exifMock);
}
@Test
public void testCreatingFile() throws IOException
{
Application application = RuntimeEnvironment.application;
File newFile = MediaUtils.createNewFile(application);
assertNotNull("File was created", newFile);
newFile.createNewFile();
assertTrue("File exists", newFile.exists());
}
@Test
public void testGetResizedFile()
{
Application application = RuntimeEnvironment.application;
Bitmap original = ShadowBitmap.createBitmap(100, 100, Bitmap.Config.ALPHA_8);
File file = MediaUtils.createNewFile(application);
assertTrue("Image was created", saveToFile(file, original));
ImageConfig config = new ImageConfig(file, null, 50, 50, 100, 0, false);
ImageConfig resizedConfig = MediaUtils.getResizedImage(application, config, 10, 10);
assertNotNull("Image was resized", resizedConfig.resized);
assertNotSame(
"Original and resized files aren't the same",
config.original.getAbsolutePath(),
resizedConfig.resized.getAbsolutePath()
);
assertNotSame(
"Original and resized files have different size",
config.original.length(),
resizedConfig.resized.length()
);
}
@Test
public void testRemoveUselessFiles()
{
Application application = RuntimeEnvironment.application;
Bitmap original = ShadowBitmap.createBitmap(100, 100, Bitmap.Config.ALPHA_8);
File originalFile = MediaUtils.createNewFile(application);
assertTrue("Original file was created", saveToFile(originalFile, original));
Bitmap resized = ShadowBitmap.createBitmap(10, 10, Bitmap.Config.ALPHA_8);
File resizedFile = MediaUtils.createNewFile(application);
assertTrue("Resized file was created", saveToFile(resizedFile, resized));
assertTrue("Original file exists", originalFile.exists());
assertTrue("Resized file exists", resizedFile.exists());
ImageConfig config = new ImageConfig(originalFile, resizedFile, 100, 100, 100, 0, false);
MediaUtils.removeUselessFiles(ImagePickerModule.REQUEST_LAUNCH_IMAGE_LIBRARY, config);
assertTrue("Original file exists, because requestCode is invalid", originalFile.exists());
assertTrue("Resized file exists, because requestCode is invalid", resizedFile.exists());
MediaUtils.removeUselessFiles(ImagePickerModule.REQUEST_LAUNCH_IMAGE_CAPTURE, config);
assertFalse("Original file was removed", config.original.exists());
assertFalse("Resized file was removed", config.resized.exists());
}
@Test
public void testReadExifInterface() throws Exception
{
final SimpleDateFormat exifDatetimeFormat = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss");
final DateFormat isoFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
isoFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
final long currentTimestamp = System.currentTimeMillis();
final String fileDateTime = exifDatetimeFormat.format(new Date(currentTimestamp));
final String dateTimeForCheckout = isoFormat.format(new Date(currentTimestamp));
final Application application = RuntimeEnvironment.application;
final Bitmap original = ShadowBitmap.createBitmap(100, 100, Bitmap.Config.ALPHA_8);
final File originalFile = MediaUtils.createNewFile(application);
final ImageConfig imageConfig = new ImageConfig(originalFile, null, 100, 100, 100, 0, false);
assertTrue("Original file was created", saveToFile(originalFile, original));
ExifInterface exif = new ExifInterface(originalFile.getAbsolutePath());
exif.setAttribute(ExifInterface.TAG_ORIENTATION, String.valueOf(ExifInterface.ORIENTATION_ROTATE_270));
exif.setAttribute(ExifInterface.TAG_GPS_LATITUDE, String.valueOf(1.0f));
exif.setAttribute(ExifInterface.TAG_GPS_LONGITUDE, String.valueOf(-1.0f));
exif.setAttribute(ExifInterface.TAG_DATETIME, fileDateTime);
exif.saveAttributes();
ResponseHelper helper = new ResponseHelper();
helper.cleanResponse();
MediaUtils.ReadExifResult result = MediaUtils.readExifInterface(helper, imageConfig);
assertNull("Exif interface was read", result.error);
assertNotNull("Orientation was read", helper.getResponse().getInt("originalRotation"));
assertEquals("Orientation value is", helper.getResponse().getInt("originalRotation"), 270);
assertNotNull("Latitude was read", helper.getResponse().getDouble("latitude"));
assertEquals("Latitude value is", Math.floor(helper.getResponse().getDouble("latitude")), Math.floor(1.0f));
assertNotNull("Longitude was read", helper.getResponse().getDouble("longitude"));
assertEquals("Longitude value is", Math.floor(helper.getResponse().getDouble("longitude")), Math.floor(-1.0f));
assertNotNull("DateTime was read", helper.getResponse().getString("timestamp"));
assertEquals("DateTIme value is", helper.getResponse().getString("timestamp"), dateTimeForCheckout);
assertNotNull("Vertical flag was generated", helper.getResponse().getBoolean("isVertical"));
assertFalse("Is image vertical", helper.getResponse().getBoolean("isVertical"));
}
@Test
public void testRolloutPhotoFromCamera()
{
Application application = RuntimeEnvironment.application;
Bitmap original = ShadowBitmap.createBitmap(100, 100, Bitmap.Config.ALPHA_8);
File originalFile = MediaUtils.createNewFile(application);
assertTrue("Original file was created", saveToFile(originalFile, original));
ImageConfig imageConfig = new ImageConfig(originalFile, null, 100, 100, 100, 0, true);
MediaUtils.RolloutPhotoResult result = MediaUtils.rolloutPhotoFromCamera(imageConfig);
assertNull("Rollout of original file has done", result.error);
assertNotSame("Original files are different", imageConfig.original.getAbsolutePath(), result.imageConfig.original.getAbsolutePath());
assertEquals("Original file names are the same", imageConfig.original.getName(), result.imageConfig.original.getName());
assertTrue("Original file was moved", result.imageConfig.original.getAbsolutePath().toLowerCase().contains("/dcim/"));
assertFalse("Original file was moved", imageConfig.original.exists());
Bitmap resized = ShadowBitmap.createBitmap(10, 10, Bitmap.Config.ALPHA_8);
File resizedFile = MediaUtils.createNewFile(application);
assertTrue("Resized file was created", saveToFile(resizedFile, resized));
imageConfig = imageConfig.withResizedFile(resizedFile);
result = MediaUtils.rolloutPhotoFromCamera(imageConfig);
assertNull("Rollout of resized file has done", result.error);
assertNotSame("Different resized files", imageConfig.resized.getAbsolutePath(), result.imageConfig.resized.getAbsolutePath());
assertEquals("Resized file names are the same", imageConfig.resized.getName(), result.imageConfig.resized.getName());
assertTrue("Resized file was moved", result.imageConfig.resized.getAbsolutePath().toLowerCase().contains("/dcim/"));
assertFalse("Resized file was moved", imageConfig.original.exists());
}
@Test
public void testMoveFile() throws IOException
{
Application application = RuntimeEnvironment.application;
File oldFile = new File(application.getCacheDir(), "original.txt");
oldFile.createNewFile();
File targetDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM);
File newFile = new File(targetDir, oldFile.getName());
MediaUtils.moveFile(oldFile, newFile);
assertFalse("Old file has left", oldFile.exists());
assertTrue("New file was copied", newFile.exists());
}
@Test
public void testRemoveOriginIfNeeded() throws IOException
{
Application application = RuntimeEnvironment.application;
Bitmap original = ShadowBitmap.createBitmap(100, 100, Bitmap.Config.ALPHA_8);
File originalFile = MediaUtils.createNewFile(application);
assertTrue("Original file was created", saveToFile(originalFile, original));
assertTrue("Original file exists", originalFile.exists());
ImageConfig imageConfig = new ImageConfig(originalFile, null, 100, 100, 100, 0, false);
MediaUtils.removeOriginIfNeeded(imageConfig, ImagePickerModule.REQUEST_LAUNCH_IMAGE_LIBRARY);
assertTrue("Original file wasn't removed on REQUEST_LAUNCH_IMAGE_LIBRARY", originalFile.exists());
MediaUtils.removeOriginIfNeeded(imageConfig, ImagePickerModule.REQUEST_LAUNCH_IMAGE_CAPTURE);
assertFalse("Original file was removed on REQUEST_LAUNCH_IMAGE_CAPTURE", originalFile.exists());
}
private boolean saveToFile(@NonNull final File imageFile,
@NonNull final Bitmap bitmap)
{
boolean result = false;
FileOutputStream fos = null;
try
{
fos = new FileOutputStream(imageFile);
bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos);
result = true;
}
catch (Exception e)
{
}
finally
{
if (fos != null)
{
try
{
fos.close();
}
catch (IOException e)
{
result = false;
e.printStackTrace();
}
}
}
return result;
}
}

View File

@ -1,33 +0,0 @@
package com.imagepicker.testing.utils;
import android.net.Uri;
import com.imagepicker.utils.MediaUtils;
import com.imagepicker.utils.RealPathUtil;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import org.robolectric.internal.SdkEnvironment;
import java.io.File;
import static junit.framework.Assert.assertNotNull;
/**
* Created by rusfearuth on 13.04.17.
*/
@RunWith(RobolectricTestRunner.class)
public class RealPathUtilTest
{
@Test
public void testCompatUriFromFile()
{
File newFile = MediaUtils.createNewFile(RuntimeEnvironment.application);
Uri uri = RealPathUtil.compatUriFromFile(RuntimeEnvironment.application, newFile);
assertNotNull("Uri was created", uri);
}
}

3
babel.config.js Normal file
View File

@ -0,0 +1,3 @@
module.exports = {
presets: ['module:metro-react-native-babel-preset'],
};

195
docs/Install.md Normal file
View File

@ -0,0 +1,195 @@
# Install
```
yarn add react-native-image-picker
# RN >= 0.60
cd ios && pod install
# RN < 0.60
react-native link react-native-image-picker
```
⚠️ If you need to support React Native < 0.40, you must install this package: `react-native-image-picker@0.24`.
## Post-install Steps
### iOS
For iOS 10+:
Add the `NSPhotoLibraryUsageDescription`, `NSCameraUsageDescription`, `NSPhotoLibraryAddUsageDescription` and `NSMicrophoneUsageDescription` (if allowing video) keys to your `Info.plist` with strings describing why your app needs these permissions.
**Note: You will get a SIGABRT crash if you don't complete this step**
```xml
<plist version="1.0">
<dict>
...
<key>NSPhotoLibraryUsageDescription</key>
<string>$(PRODUCT_NAME) would like access to your photo gallery</string>
<key>NSCameraUsageDescription</key>
<string>$(PRODUCT_NAME) would like to use your camera</string>
<key>NSPhotoLibraryAddUsageDescription</key>
<string>$(PRODUCT_NAME) would like to save photos to your photo gallery</string>
<key>NSMicrophoneUsageDescription</key>
<string>$(PRODUCT_NAME) would like to use your microphone (for videos)</string>
</dict>
</plist>
```
⚠️ If you are planning on submitting your application to app store:
To be compliant with Guideline 5.1.1 - Legal - Privacy - Data Collection and Storage, the permission request alert should specify how your app will use this feature to help users understand why your app is requesting access to their personal data.
```
$(PRODUCT_NAME) would like access to your photo gallery to change your profile picture
```
### Android
Add the required permissions in `AndroidManifest.xml`:
```xml
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
```
### Android (Optional)
If you've defined _[project-wide properties](https://developer.android.com/studio/build/gradle-tips.html)_ (**recommended**) in your root `build.gradle`, this library will detect the presence of the following properties:
```groovy
buildscript {...}
allprojects {...}
/**
+ Project-wide Gradle configuration properties
*/
ext {
compileSdkVersion = 27
targetSdkVersion = 27
buildToolsVersion = "27.0.3"
}
```
Customization settings of dialog `android/app/res/values/themes.xml` (`android/app/res/values/style.xml` is a valid path as well):
```xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="DefaultExplainingPermissionsTheme" parent="Theme.AppCompat.Light.Dialog.Alert">
<!-- Used for the buttons -->
<item name="colorAccent">@color/your_color</item>
<!-- Used for the title and text -->
<item name="android:textColorPrimary">@color/your_color</item>
<!-- Used for the background -->
<item name="android:background">@color/your_color</item>
</style>
</resources>
```
## Manual Installation
### iOS
1. In the XCode's "Project navigator", right click on your project's Libraries folder ➜ `Add Files to <...>`.
1. Go to `node_modules``react-native-image-picker``ios` ➜ select `RNImagePicker.xcodeproj`.
1. Add `libRNImagePicker.a` to `Build Phases -> Link Binary With Libraries`.
1. Refer to [Post-install Steps](Install.md#post-install-steps).
1. Compile and have fun.
### Android
1. Add the following lines to `android/settings.gradle`:
```gradle
include ':react-native-image-picker'
project(':react-native-image-picker').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-image-picker/android')
```
2. Update the android build tools version to `2.2.+` in `android/build.gradle`:
```gradle
buildscript {
...
dependencies {
classpath 'com.android.tools.build:gradle:2.2.+' // <- USE 2.2.+ version
}
...
}
...
```
3. Update the gradle version to `2.14.1` in `android/gradle/wrapper/gradle-wrapper.properties`:
```
...
distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip
```
4. Add the implementation line to the dependencies in `android/app/build.gradle`:
```gradle
dependencies {
implementation project(':react-native-image-picker')
}
```
5. Add the import and link the package in `MainApplication.java`:
```java
import com.imagepicker.ImagePickerPackage; // <-- add this import
public class MainApplication extends Application implements ReactApplication {
@Override
protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList(
new MainReactPackage(),
new ImagePickerPackage(), // <-- add this line
// OR if you want to customize dialog style
new ImagePickerPackage(R.style.my_dialog_style)
);
}
}
```
6. If `MainActivity` is not instance of `ReactActivity`, you will need to implement `OnImagePickerPermissionsCallback` to `MainActivity`:
```java
import com.imagepicker.permissions.OnImagePickerPermissionsCallback; // <- add this import
import com.facebook.react.modules.core.PermissionListener; // <- add this import
public class MainActivity extends YourActivity implements OnImagePickerPermissionsCallback {
private PermissionListener listener; // <- add this attribute
// Your methods here
// Copy from here
@Override
public void setPermissionListener(PermissionListener listener)
{
this.listener = listener;
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults)
{
if (listener != null)
{
listener.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
// To here
}
```
This code allows to pass result of request permissions to native part.
7. Refer to [Post-install Steps](Install.md#post-install-steps).

95
docs/Reference.md Normal file
View File

@ -0,0 +1,95 @@
# API Reference
This document lays out the current public properties and methods for the React Native Image Picker.
## Methods
### `showImagePicker()`
```js
static showImagePicker(options?, callback)
```
Display the image picker.
See [Options](#options) for further information on `options`.
The `callback` will be called with a response object, refer to [The Response Object](#the-response-object).
### `launchCamera()`
```js
static launchCamera(options?, callback)
```
Skip the alert dialog and launch the camera directly.
See [Options](#options) for further information on `options`.
The `callback` will be called with a response object, refer to [The Response Object](#the-response-object).
### `launchImageLibrary()`
```js
static launchImageLibrary(options?, callback)
```
Skip the alert dialog and launch the image library directly.
See [Options](#options) for further information on `options`.
The `callback` will be called with a response object, refer to [The Response Object](#the-response-object).
## Options
| option | iOS | Android | Info |
| ----------------------------- | --- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| title | OK | OK | Specify `null` or empty string to remove the title |
| cancelButtonTitle | OK | OK | Specify `null` or empty string to remove this button |
| takePhotoButtonTitle | OK | OK | Specify `null` or empty string to remove this button |
| chooseFromLibraryButtonTitle | OK | OK | Specify `null` or empty string to remove this button |
| chooseWhichLibraryTitle | - | OK | Specify `null` or empty string to use default Android title. Is shown when user has multiple apps that can open library. |
| customButtons | OK | OK | An array containing objects with the name and title of buttons |
| tintColor | OK | - | Text color to use on buttons |
| cameraType | OK | - | 'front' or 'back' |
| mediaType | OK | OK | 'photo', 'video', or 'mixed' |
| maxWidth | OK | OK | Photos only |
| maxHeight | OK | OK | Photos only |
| quality | OK | OK | 0 to 1, photos only |
| videoQuality | OK | OK | 'low', 'medium', or 'high' on iOS, 'low' or 'high' on Android |
| durationLimit | OK | OK | Max video recording time, in seconds |
| rotation | - | OK | Photos only, 0 to 360 degrees of rotation |
| allowsEditing | OK | - | bool - enables built-in iOS functionality to resize the image after selection |
| noData | OK | OK | If true, disables the base64 `data` field from being generated (greatly improves performance on large photos) |
| storageOptions | OK | OK | If this key is provided, the image will be saved in your app's `Documents` directory on iOS (rather than a temporary directory). On Android this key does not affect the image location (Android always defaults to the public `Pictures` directory) |
| storageOptions.skipBackup | OK | - | If true, the photo will NOT be backed up to iCloud |
| storageOptions.path | OK | OK | If set, will save the image at `Documents/[path]/` rather than the root `Documents` for iOS, and `Pictures/[path]/` on Android. |
| storageOptions.cameraRoll | OK | OK | If true, the cropped photo will be saved to the iOS Camera Roll or Android DCIM folder. |
| storageOptions.waitUntilSaved | OK | - | If true, will delay the response callback until after the photo/video was saved to the Camera Roll. If the photo or video was just taken, then the file name and timestamp fields are only provided in the response object when this AND `cameraRoll` are both true. |
| storageOptions.privateDirectory | - | OK | If true, the photo will be saved to the apps private files directory (Android/data/your_package/files/Pictures) |
| permissionDenied.title | - | OK | Title of explaining permissions dialog. By default `Permission denied`. |
| permissionDenied.text | - | OK | Message of explaining permissions dialog. By default `To be able to take pictures with your camera and choose images from your library.`. |
| permissionDenied.reTryTitle | - | OK | Title of re-try button. By default `re-try` |
| permissionDenied.okTitle | - | OK | Title of ok button. By default `I'm sure` |
## The Response Object
| key | iOS | Android | Description |
| ---------------- | ---------------------- | ----------- | ---------------------------------------------------------------------- |
| didCancel | OK | OK | Informs you if the user cancelled the process |
| error | OK | OK | Contains an error message, if there is one |
| customButton | OK | OK | If the user tapped one of your custom buttons, contains the name of it |
| data | OK | OK | The base64 encoded image data (photos only) |
| uri | OK | OK | The uri to the local file asset on the device (photo or video) |
| origURL | OK | - | The URL of the original asset in photo library, if it exists |
| isVertical | OK | OK | Will be true if the image is vertically oriented |
| width | OK | OK | Image dimensions (photos only) |
| height | OK | OK | Image dimensions (photos only) |
| fileSize | OK | OK | The file size (photos only) |
| type | OK | OK | The file type (photos only) |
| fileName | OK (photos and videos) | OK (photos) | The file name, if available
| path | - | OK | The file path |
| latitude | OK | OK | Latitude metadata, if available |
| longitude | OK | OK | Longitude metadata, if available |
| timestamp | OK | OK | Timestamp metadata, if available, in ISO8601 UTC format |
| originalRotation | - | OK | Rotation degrees (photos only) _See [#109](/../../issues/199)_ |

3
example/.babelrc Normal file
View File

@ -0,0 +1,3 @@
{
"presets": ["module:metro-react-native-babel-preset"]
}

6
example/.buckconfig Normal file
View File

@ -0,0 +1,6 @@
[android]
target = Google Inc.:Google APIs:23
[maven_repositories]
central = https://repo1.maven.org/maven2

70
example/.flowconfig Normal file
View File

@ -0,0 +1,70 @@
[ignore]
; We fork some components by platform
.*/*[.]android.js
; Ignore "BUCK" generated dirs
<PROJECT_ROOT>/\.buckd/
; Ignore unexpected extra "@providesModule"
.*/node_modules/.*/node_modules/fbjs/.*
; Ignore duplicate module providers
; For RN Apps installed via npm, "Libraries" folder is inside
; "node_modules/react-native" but in the source repo it is in the root
.*/Libraries/react-native/React.js
; Ignore polyfills
.*/Libraries/polyfills/.*
; Ignore metro
.*/node_modules/metro/.*
[include]
[libs]
node_modules/react-native/Libraries/react-native/react-native-interface.js
node_modules/react-native/flow/
node_modules/react-native/flow-github/
[options]
emoji=true
esproposal.optional_chaining=enable
esproposal.nullish_coalescing=enable
module.system=haste
module.system.haste.use_name_reducers=true
# get basename
module.system.haste.name_reducers='^.*/\([a-zA-Z0-9$_.-]+\.js\(\.flow\)?\)$' -> '\1'
# strip .js or .js.flow suffix
module.system.haste.name_reducers='^\(.*\)\.js\(\.flow\)?$' -> '\1'
# strip .ios suffix
module.system.haste.name_reducers='^\(.*\)\.ios$' -> '\1'
module.system.haste.name_reducers='^\(.*\)\.android$' -> '\1'
module.system.haste.name_reducers='^\(.*\)\.native$' -> '\1'
module.system.haste.paths.blacklist=.*/__tests__/.*
module.system.haste.paths.blacklist=.*/__mocks__/.*
module.system.haste.paths.blacklist=<PROJECT_ROOT>/node_modules/react-native/Libraries/Animated/src/polyfills/.*
module.system.haste.paths.whitelist=<PROJECT_ROOT>/node_modules/react-native/Libraries/.*
munge_underscores=true
module.name_mapper='^[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> 'RelativeImageStub'
module.file_ext=.js
module.file_ext=.jsx
module.file_ext=.json
module.file_ext=.native.js
suppress_type=$FlowIssue
suppress_type=$FlowFixMe
suppress_type=$FlowFixMeProps
suppress_type=$FlowFixMeState
suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(<VERSION>\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)
suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(<VERSION>\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)?:? #[0-9]+
suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy
suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError
[version]
^0.78.0

1
example/.gitattributes vendored Normal file
View File

@ -0,0 +1 @@
*.pbxproj -text

56
example/.gitignore vendored Normal file
View File

@ -0,0 +1,56 @@
# OSX
#
.DS_Store
# Xcode
#
build/
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
xcuserdata
*.xccheckout
*.moved-aside
DerivedData
*.hmap
*.ipa
*.xcuserstate
project.xcworkspace
# Android/IntelliJ
#
build/
.idea
.gradle
local.properties
*.iml
# node.js
#
node_modules/
npm-debug.log
yarn-error.log
# BUCK
buck-out/
\.buckd/
*.keystore
# fastlane
#
# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
# screenshots whenever they are needed.
# For more information about the recommended setup visit:
# https://docs.fastlane.tools/best-practices/source-control/
*/fastlane/report.xml
*/fastlane/Preview.html
*/fastlane/screenshots
# Bundle artifact
*.jsbundle

View File

@ -1,53 +1,56 @@
/** @format */
import React from 'react';
import {
AppRegistry,
Image,
PixelRatio,
StyleSheet,
Text,
View,
PixelRatio,
TouchableOpacity,
Image,
View,
} from 'react-native';
import ImagePicker from 'react-native-image-picker';
export default class App extends React.Component {
state = {
avatarSource: null,
videoSource: null
videoSource: null,
};
constructor(props) {
super(props);
this.selectPhotoTapped = this.selectPhotoTapped.bind(this);
this.selectVideoTapped = this.selectVideoTapped.bind(this);
}
selectPhotoTapped() {
const options = {
quality: 1.0,
maxWidth: 500,
maxHeight: 500,
storageOptions: {
skipBackup: true
}
skipBackup: true,
},
};
ImagePicker.showImagePicker(options, (response) => {
ImagePicker.showImagePicker(options, response => {
console.log('Response = ', response);
if (response.didCancel) {
console.log('User cancelled photo picker');
}
else if (response.error) {
} else if (response.error) {
console.log('ImagePicker Error: ', response.error);
}
else if (response.customButton) {
} else if (response.customButton) {
console.log('User tapped custom button: ', response.customButton);
}
else {
let source = { uri: response.uri };
} else {
let source = {uri: response.uri};
// You can also display the image using data:
// let source = { uri: 'data:image/jpeg;base64,' + response.data };
this.setState({
avatarSource: source
avatarSource: source,
});
}
});
@ -58,24 +61,21 @@ export default class App extends React.Component {
title: 'Video Picker',
takePhotoButtonTitle: 'Take Video...',
mediaType: 'video',
videoQuality: 'medium'
videoQuality: 'medium',
};
ImagePicker.showImagePicker(options, (response) => {
ImagePicker.showImagePicker(options, response => {
console.log('Response = ', response);
if (response.didCancel) {
console.log('User cancelled video picker');
}
else if (response.error) {
} else if (response.error) {
console.log('ImagePicker Error: ', response.error);
}
else if (response.customButton) {
} else if (response.customButton) {
console.log('User tapped custom button: ', response.customButton);
}
else {
} else {
this.setState({
videoSource: response.uri
videoSource: response.uri,
});
}
});
@ -85,10 +85,13 @@ export default class App extends React.Component {
return (
<View style={styles.container}>
<TouchableOpacity onPress={this.selectPhotoTapped.bind(this)}>
<View style={[styles.avatar, styles.avatarContainer, {marginBottom: 20}]}>
{ this.state.avatarSource === null ? <Text>Select a Photo</Text> :
<Image style={styles.avatar} source={this.state.avatarSource} />
}
<View
style={[styles.avatar, styles.avatarContainer, {marginBottom: 20}]}>
{this.state.avatarSource === null ? (
<Text>Select a Photo</Text>
) : (
<Image style={styles.avatar} source={this.state.avatarSource} />
)}
</View>
</TouchableOpacity>
@ -98,13 +101,14 @@ export default class App extends React.Component {
</View>
</TouchableOpacity>
{ this.state.videoSource &&
<Text style={{margin: 8, textAlign: 'center'}}>{this.state.videoSource}</Text>
}
{this.state.videoSource && (
<Text style={{margin: 8, textAlign: 'center'}}>
{this.state.videoSource}
</Text>
)}
</View>
);
}
}
const styles = StyleSheet.create({
@ -112,17 +116,17 @@ const styles = StyleSheet.create({
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF'
backgroundColor: '#F5FCFF',
},
avatarContainer: {
borderColor: '#9B9B9B',
borderWidth: 1 / PixelRatio.get(),
justifyContent: 'center',
alignItems: 'center'
alignItems: 'center',
},
avatar: {
borderRadius: 75,
width: 150,
height: 150
}
height: 150,
},
});

19
example/README.md Normal file
View File

@ -0,0 +1,19 @@
# Example
An example for experimenting with react-native-image-picker.
## Usage
Install dependencies using:
```bash
yarn install
```
Start the packager with:
```bash
yarn start
```
You will have to reinstall `react-native-image-picker` every time you do changes in the library: `rm -rf node_modules && yarn install` (from `./example` folder).

View File

@ -1,18 +1,17 @@
import re
# To learn about Buck see [Docs](https://buckbuild.com/).
# To run your application with Buck:
# - install Buck
# - `npm start` - to start the packager
# - `cd android`
# - `keytool -genkey -v -keystore keystores/debug.keystore -storepass android -alias androiddebugkey -keypass android -dname "CN=Android Debug,O=Android,C=US`
# - `keytool -genkey -v -keystore keystores/debug.keystore -storepass android -alias androiddebugkey -keypass android -dname "CN=Android Debug,O=Android,C=US"`
# - `./gradlew :app:copyDownloadableDepsToLibs` - make all Gradle compile dependencies available to Buck
# - `buck install -r android/app` - compile, install and run application
#
lib_deps = []
for jarfile in glob(['libs/*.jar']):
name = 'jars__' + re.sub(r'^.*/([^/]+)\.jar$', r'\1', jarfile)
name = 'jars__' + jarfile[jarfile.rindex('/') + 1: jarfile.rindex('.jar')]
lib_deps.append(':' + name)
prebuilt_jar(
name = name,
@ -20,7 +19,7 @@ for jarfile in glob(['libs/*.jar']):
)
for aarfile in glob(['libs/*.aar']):
name = 'aars__' + re.sub(r'^.*/([^/]+)\.aar$', r'\1', aarfile)
name = 'aars__' + aarfile[aarfile.rindex('/') + 1: aarfile.rindex('.aar')]
lib_deps.append(':' + name)
android_prebuilt_aar(
name = name,
@ -28,39 +27,39 @@ for aarfile in glob(['libs/*.aar']):
)
android_library(
name = 'all-libs',
exported_deps = lib_deps
name = "all-libs",
exported_deps = lib_deps,
)
android_library(
name = 'app-code',
srcs = glob([
'src/main/java/**/*.java',
]),
deps = [
':all-libs',
':build_config',
':res',
],
name = "app-code",
srcs = glob([
"src/main/java/**/*.java",
]),
deps = [
":all-libs",
":build_config",
":res",
],
)
android_build_config(
name = 'build_config',
package = 'com.autoblogvr',
name = "build_config",
package = "com.example",
)
android_resource(
name = 'res',
res = 'src/main/res',
package = 'com.autoblogvr',
name = "res",
package = "com.example",
res = "src/main/res",
)
android_binary(
name = 'app',
package_type = 'debug',
manifest = 'src/main/AndroidManifest.xml',
keystore = '//android/keystores:debug',
deps = [
':app-code',
],
name = "app",
keystore = "//android/keystores:debug",
manifest = "src/main/AndroidManifest.xml",
package_type = "debug",
deps = [
":app-code",
],
)

View File

@ -9,7 +9,7 @@ import com.android.build.OutputFile
* cycle. By default, bundleDebugJsAndAssets is skipped, as in debug/dev mode we prefer to load the
* bundle directly from the development server. Below you can see all the possible configurations
* and their defaults. If you decide to add a configuration block, make sure to add it before the
* `apply from: "react.gradle"` line.
* `apply from: "../../node_modules/react-native/react.gradle"` line.
*
* project.ext.react = [
* // the name of the generated asset file containing your JS bundle
@ -33,6 +33,13 @@ import com.android.build.OutputFile
* // bundleInPaidRelease: true,
* // bundleInBeta: true,
*
* // whether to disable dev mode in custom build variants (by default only disabled in release)
* // for example: to disable dev mode in the staging build type (if configured)
* devDisabledInStaging: true,
* // The configuration property can be in the following formats
* // 'devDisabledIn${productFlavor}${buildType}'
* // 'devDisabledIn${buildType}'
*
* // the root of your project, i.e. where "package.json" lives
* root: "../../",
*
@ -55,10 +62,20 @@ import com.android.build.OutputFile
* // date; if you have any other folders that you want to ignore for performance reasons (gradle
* // indexes the entire tree), add them here. Alternatively, if you have JS files in android/
* // for example, you might want to remove it from here.
* inputExcludes: ["android/**", "ios/**"]
* inputExcludes: ["android/**", "ios/**"],
*
* // override which node gets called and with what additional arguments
* nodeExecutableAndArgs: ["node"],
*
* // supply additional arguments to the packager
* extraPackagerArgs: []
* ]
*/
project.ext.react = [
entryFile: "index.js"
]
apply from: "../../node_modules/react-native/react.gradle"
/**
@ -77,13 +94,13 @@ def enableSeparateBuildPerCPUArchitecture = false
def enableProguardInReleaseBuilds = false
android {
compileSdkVersion 23
buildToolsVersion "23.0.1"
compileSdkVersion rootProject.ext.compileSdkVersion
buildToolsVersion rootProject.ext.buildToolsVersion
defaultConfig {
applicationId "com.example"
minSdkVersion 16
targetSdkVersion 22
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 1
versionName "1.0"
ndk {
@ -120,9 +137,15 @@ android {
}
dependencies {
compile fileTree(dir: "libs", include: ["*.jar"])
compile "com.android.support:appcompat-v7:23.0.1"
compile "com.facebook.react:react-native:+" // From node_modules
compile project(':react-native-image-picker')
api project(':react-native-image-picker')
implementation fileTree(dir: "libs", include: ["*.jar"])
implementation "com.android.support:appcompat-v7:${rootProject.ext.supportLibVersion}"
implementation "com.facebook.react:react-native:+" // From node_modules
}
// Run this once to be able to run the application with BUCK
// puts all compile dependencies into folder libs for BUCK to use
task copyDownloadableDepsToLibs(type: Copy) {
from configurations.compile
into 'libs'
}

17
example/android/app/proguard-rules.pro vendored Normal file
View File

@ -0,0 +1,17 @@
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}

View File

@ -2,21 +2,21 @@
package="com.example">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-feature android:name="android.hardware.camera" android:required="false"/>
<uses-feature android:name="android.hardware.camera.autofocus" />
<application
android:name=".MainApplication"
android:allowBackup="true"
android:label="@string/app_name"
android:icon="@mipmap/ic_launcher"
android:allowBackup="false"
android:theme="@style/AppTheme">
<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:configChanges="keyboard|keyboardHidden|orientation|screenSize">
android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
android:windowSoftInputMode="adjustResize">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />

View File

@ -1,19 +1,17 @@
package com.example;
import android.app.Application;
import android.util.Log;
import com.facebook.react.ReactApplication;
import com.facebook.react.ReactInstanceManager;
import com.imagepicker.ImagePickerPackage;
import com.facebook.react.ReactNativeHost;
import com.facebook.react.ReactPackage;
import com.facebook.react.shell.MainReactPackage;
import com.facebook.soloader.SoLoader;
import java.util.Arrays;
import java.util.List;
import com.imagepicker.ImagePickerPackage;
public class MainApplication extends Application implements ReactApplication {
private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
@ -29,10 +27,21 @@ public class MainApplication extends Application implements ReactApplication {
new ImagePickerPackage()
);
}
@Override
protected String getJSMainModuleName() {
return "index";
}
};
@Override
public ReactNativeHost getReactNativeHost() {
return mReactNativeHost;
return mReactNativeHost;
}
@Override
public void onCreate() {
super.onCreate();
SoLoader.init(this, /* native exopackage */ false);
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@ -1,11 +1,19 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
ext {
buildToolsVersion = "27.0.3"
minSdkVersion = 16
compileSdkVersion = 27
targetSdkVersion = 26
supportLibVersion = "27.1.1"
}
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.2.+'
classpath 'com.android.tools.build:gradle:3.1.4'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
@ -15,6 +23,7 @@ buildscript {
allprojects {
repositories {
mavenLocal()
google()
jcenter()
maven {
// All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
@ -22,3 +31,9 @@ allprojects {
}
}
}
task wrapper(type: Wrapper) {
gradleVersion = '4.4'
distributionUrl = distributionUrl.replace("bin", "all")
}

View File

@ -16,5 +16,3 @@
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
android.useDeprecatedNdk=true

Binary file not shown.

View File

@ -1,6 +1,5 @@
#Thu Jun 09 11:55:07 EDT 2016
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip

View File

@ -1,4 +1,4 @@
#!/usr/bin/env bash
#!/usr/bin/env sh
##############################################################################
##
@ -6,47 +6,6 @@
##
##############################################################################
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn ( ) {
echo "$*"
}
die ( ) {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
esac
# For Cygwin, ensure paths are in UNIX format before anything is touched.
if $cygwin ; then
[ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
fi
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
@ -61,9 +20,49 @@ while [ -h "$PRG" ] ; do
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >&-
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >&-
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
@ -90,7 +89,7 @@ location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
@ -114,6 +113,7 @@ fi
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
@ -154,11 +154,19 @@ if $cygwin ; then
esac
fi
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
function splitJvmOpts() {
JVM_OPTS=("$@")
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
APP_ARGS=$(save "$@")
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "$@"

View File

@ -8,14 +8,14 @@
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
@ -46,10 +46,9 @@ echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windowz variants
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
if "%@eval[2+2]" == "4" goto 4NT_args
:win9xME_args
@rem Slurp the command line arguments.
@ -60,11 +59,6 @@ set _SKIP=2
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
goto execute
:4NT_args
@rem Get arguments from the 4NT Shell from JP Software
set CMD_LINE_ARGS=%$
:execute
@rem Setup the command line

View File

@ -0,0 +1,8 @@
keystore(
name = "debug",
properties = "debug.keystore.properties",
store = "debug.keystore",
visibility = [
"PUBLIC",
],
)

View File

@ -0,0 +1,4 @@
key.store=debug.keystore
key.alias=androiddebugkey
key.store.password=android
key.alias.password=android

View File

@ -0,0 +1,5 @@
rootProject.name = 'Example'
include ':react-native-image-picker'
project(':react-native-image-picker').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-image-picker/android')
include ':app'

4
example/app.json Normal file
View File

@ -0,0 +1,4 @@
{
"name": "Example",
"displayName": "Example"
}

7
example/index.js Normal file
View File

@ -0,0 +1,7 @@
/** @format */
import {AppRegistry} from 'react-native';
import App from './App';
import {name as appName} from './app.json';
AppRegistry.registerComponent(appName, () => App);

View File

@ -7,7 +7,7 @@
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<string>org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
@ -22,17 +22,6 @@
<string>1</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
<key>NSPhotoLibraryUsageDescription</key>
<string></string>
<key>NSCameraUsageDescription</key>
<string></string>
<key>NSMicrophoneUsageDescription</key>
<string></string>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIRequiredDeviceCapabilities</key>
@ -47,5 +36,19 @@
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
<key>NSLocationWhenInUseUsageDescription</key>
<string></string>
<key>NSAppTransportSecurity</key>
<!--See http://ste.vn/2015/06/10/configuring-app-transport-security-ios-9-osx-10-11/ -->
<dict>
<key>NSExceptionDomains</key>
<dict>
<key>localhost</key>
<dict>
<key>NSExceptionAllowsInsecureHTTPLoads</key>
<true/>
</dict>
</dict>
</dict>
</dict>
</plist>

View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
</dict>
</plist>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,129 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0940"
version = "1.3">
<BuildAction
parallelizeBuildables = "NO"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "2D2A28121D9B038B00D4039D"
BuildableName = "libReact.a"
BlueprintName = "React-tvOS"
ReferencedContainer = "container:../node_modules/react-native/React/React.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "2D02E47A1E0B4A5D006451C7"
BuildableName = "Example-tvOS.app"
BlueprintName = "Example-tvOS"
ReferencedContainer = "container:Example.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "NO"
buildForArchiving = "NO"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "2D02E48F1E0B4A5D006451C7"
BuildableName = "Example-tvOSTests.xctest"
BlueprintName = "Example-tvOSTests"
ReferencedContainer = "container:Example.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "2D02E48F1E0B4A5D006451C7"
BuildableName = "Example-tvOSTests.xctest"
BlueprintName = "Example-tvOSTests"
ReferencedContainer = "container:Example.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "2D02E47A1E0B4A5D006451C7"
BuildableName = "Example-tvOS.app"
BlueprintName = "Example-tvOS"
ReferencedContainer = "container:Example.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "2D02E47A1E0B4A5D006451C7"
BuildableName = "Example-tvOS.app"
BlueprintName = "Example-tvOS"
ReferencedContainer = "container:Example.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "2D02E47A1E0B4A5D006451C7"
BuildableName = "Example-tvOS.app"
BlueprintName = "Example-tvOS"
ReferencedContainer = "container:Example.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0820"
LastUpgradeVersion = "0940"
version = "1.3">
<BuildAction
parallelizeBuildables = "NO"
@ -34,6 +34,20 @@
ReferencedContainer = "container:Example.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "NO"
buildForArchiving = "NO"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "00E356ED1AD99517003FC87E"
BuildableName = "ExampleTests.xctest"
BlueprintName = "ExampleTests"
ReferencedContainer = "container:Example.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
@ -42,6 +56,16 @@
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "00E356ED1AD99517003FC87E"
BuildableName = "ExampleTests.xctest"
BlueprintName = "ExampleTests"
ReferencedContainer = "container:Example.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
<MacroExpansion>
<BuildableReference

View File

@ -0,0 +1,14 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <UIKit/UIKit.h>
@interface AppDelegate : UIResponder <UIApplicationDelegate>
@property (nonatomic, strong) UIWindow *window;
@end

View File

@ -0,0 +1,35 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "AppDelegate.h"
#import <React/RCTBundleURLProvider.h>
#import <React/RCTRootView.h>
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
NSURL *jsCodeLocation;
jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil];
RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation
moduleName:@"Example"
initialProperties:nil
launchOptions:launchOptions];
rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1];
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
UIViewController *rootViewController = [UIViewController new];
rootViewController.view = rootView;
self.window.rootViewController = rootViewController;
[self.window makeKeyAndVisible];
return YES;
}
@end

Some files were not shown because too many files have changed in this diff Show More