Compare commits

..

1 Commits

Author SHA1 Message Date
Alexander Ustinov
8270dbb227 Refactoring source code and adding old PRs 2017-04-13 15:08:31 +07:00
122 changed files with 2345 additions and 25804 deletions

View File

@ -1,27 +0,0 @@
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

View File

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

View File

@ -1,52 +0,0 @@
/**
* 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
View File

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

41
.github/stale.yml vendored
View File

@ -1,41 +0,0 @@
# 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,17 +1,15 @@
# OSX
#
.DS_Store
### Android Studio ###
.idea/
.gradle/
local.properties
# node.js
#
node_modules/
npm-debug.log
yarn-error.log
### Xcode ###
# Xcode
#
## Build generated
build/
DerivedData/
## Various settings
*.pbxuser
!default.pbxuser
*.mode1v3
@ -20,42 +18,22 @@ build/
!default.mode2v3
*.perspectivev3
!default.perspectivev3
xcuserdata
*.xccheckout
xcuserdata/
## Other
*.moved-aside
DerivedData
*.hmap
*.ipa
*.xcuserstate
project.xcworkspace
*.xccheckout
*.xcscmblueprint
# CocoaPods
/ios/Pods/
### /Xcode ###
# Android/IntelliJ
#
build/
.idea
.gradle
local.properties
### OS X
.DS_Store
### Node
node_modules
*.log
yarn.lock
## Android iml
*.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,4 +1,3 @@
example/
Example/
images/
node_modules/
yarn.lock

View File

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

View File

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

View File

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

View File

@ -1,49 +0,0 @@
# 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.

65
Example/.flowconfig Normal file
View File

@ -0,0 +1,65 @@
[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 Normal file
View File

@ -0,0 +1,34 @@
# 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,56 +1,53 @@
/** @format */
import React from 'react';
import {
Image,
PixelRatio,
AppRegistry,
StyleSheet,
Text,
TouchableOpacity,
View,
PixelRatio,
TouchableOpacity,
Image,
} 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
});
}
});
@ -61,21 +58,24 @@ 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,13 +85,10 @@ 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>
@ -101,14 +98,13 @@ 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({
@ -116,17 +112,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
}
});

View File

@ -0,0 +1,18 @@
<?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,17 +1,18 @@
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__' + jarfile[jarfile.rindex('/') + 1: jarfile.rindex('.jar')]
name = 'jars__' + re.sub(r'^.*/([^/]+)\.jar$', r'\1', jarfile)
lib_deps.append(':' + name)
prebuilt_jar(
name = name,
@ -19,7 +20,7 @@ for jarfile in glob(['libs/*.jar']):
)
for aarfile in glob(['libs/*.aar']):
name = 'aars__' + aarfile[aarfile.rindex('/') + 1: aarfile.rindex('.aar')]
name = 'aars__' + re.sub(r'^.*/([^/]+)\.aar$', r'\1', aarfile)
lib_deps.append(':' + name)
android_prebuilt_aar(
name = name,
@ -27,39 +28,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.example",
name = 'build_config',
package = 'com.autoblogvr',
)
android_resource(
name = "res",
package = "com.example",
res = "src/main/res",
name = 'res',
res = 'src/main/res',
package = 'com.autoblogvr',
)
android_binary(
name = "app",
keystore = "//android/keystores:debug",
manifest = "src/main/AndroidManifest.xml",
package_type = "debug",
deps = [
":app-code",
],
name = 'app',
package_type = 'debug',
manifest = 'src/main/AndroidManifest.xml',
keystore = '//android/keystores: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: "../../node_modules/react-native/react.gradle"` line.
* `apply from: "react.gradle"` line.
*
* project.ext.react = [
* // the name of the generated asset file containing your JS bundle
@ -33,13 +33,6 @@ 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: "../../",
*
@ -62,20 +55,10 @@ 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/**"],
*
* // override which node gets called and with what additional arguments
* nodeExecutableAndArgs: ["node"],
*
* // supply additional arguments to the packager
* extraPackagerArgs: []
* inputExcludes: ["android/**", "ios/**"]
* ]
*/
project.ext.react = [
entryFile: "index.js"
]
apply from: "../../node_modules/react-native/react.gradle"
/**
@ -94,13 +77,13 @@ def enableSeparateBuildPerCPUArchitecture = false
def enableProguardInReleaseBuilds = false
android {
compileSdkVersion rootProject.ext.compileSdkVersion
buildToolsVersion rootProject.ext.buildToolsVersion
compileSdkVersion 23
buildToolsVersion "23.0.1"
defaultConfig {
applicationId "com.example"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
minSdkVersion 16
targetSdkVersion 22
versionCode 1
versionName "1.0"
ndk {
@ -137,15 +120,9 @@ android {
}
dependencies {
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
}
compile fileTree(dir: "libs", include: ["*.jar"])
compile "com.android.support:appcompat-v7:23.0.1"
compile "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'
compile project(':react-native-image-picker')
}

67
Example/android/app/proguard-rules.pro vendored Normal file
View File

@ -0,0 +1,67 @@
# 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.**

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:windowSoftInputMode="adjustResize">
android:configChanges="keyboard|keyboardHidden|orientation|screenSize">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />

View File

@ -1,17 +1,19 @@
package com.example;
import android.app.Application;
import android.util.Log;
import com.facebook.react.ReactApplication;
import com.imagepicker.ImagePickerPackage;
import com.facebook.react.ReactInstanceManager;
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) {
@ -27,21 +29,10 @@ public class MainApplication extends Application implements ReactApplication {
new ImagePickerPackage()
);
}
@Override
protected String getJSMainModuleName() {
return "index";
}
};
@Override
public ReactNativeHost getReactNativeHost() {
return mReactNativeHost;
}
@Override
public void onCreate() {
super.onCreate();
SoLoader.init(this, /* native exopackage */ false);
return mReactNativeHost;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

View File

@ -1,19 +1,11 @@
// 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:3.1.4'
classpath 'com.android.tools.build:gradle:2.2.+'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
@ -23,7 +15,6 @@ buildscript {
allprojects {
repositories {
mavenLocal()
google()
jcenter()
maven {
// All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
@ -31,9 +22,3 @@ allprojects {
}
}
}
task wrapper(type: Wrapper) {
gradleVersion = '4.4'
distributionUrl = distributionUrl.replace("bin", "all")
}

View File

@ -16,3 +16,5 @@
# 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,5 +1,6 @@
#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-4.4-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip

View File

@ -1,4 +1,4 @@
#!/usr/bin/env sh
#!/usr/bin/env bash
##############################################################################
##
@ -6,6 +6,47 @@
##
##############################################################################
# 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"
@ -20,49 +61,9 @@ while [ -h "$PRG" ] ; do
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
cd "`dirname \"$PRG\"`/" >&-
APP_HOME="`pwd -P`"
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
cd "$SAVED" >&-
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
@ -89,7 +90,7 @@ location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
@ -113,7 +114,6 @@ 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,19 +154,11 @@ if $cygwin ; then
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
function splitJvmOpts() {
JVM_OPTS=("$@")
}
APP_ARGS=$(save "$@")
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
# 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" "$@"
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"

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,9 +46,10 @@ echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
@rem Get command-line arguments, handling Windowz 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.
@ -59,6 +60,11 @@ 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,6 @@
rootProject.name = 'Example'
include ':app'
include ':react-native-image-picker'
project(':react-native-image-picker').projectDir = new File(settingsDir, '../../android')

15
Example/index.android.js Normal file
View File

@ -0,0 +1,15 @@
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);

15
Example/index.ios.js Normal file
View File

@ -0,0 +1,15 @@
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

@ -0,0 +1,833 @@
// !$*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,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0940"
LastUpgradeVersion = "0820"
version = "1.3">
<BuildAction
parallelizeBuildables = "NO"
@ -34,20 +34,6 @@
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
@ -56,16 +42,6 @@
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,16 @@
/**
* 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

@ -0,0 +1,30 @@
/**
* 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

@ -7,7 +7,7 @@
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)</string>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
@ -22,6 +22,17 @@
<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>
@ -36,19 +47,5 @@
</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

@ -1,8 +1,10 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
* 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>

14
Example/package.json Normal file
View File

@ -0,0 +1,14 @@
{
"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 @@
MIT License
The MIT License (MIT)
Copyright (c) 2015-present, Facebook, Inc.
Copyright (c) 2015 Marc Shilling
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,94 +1,197 @@
# 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://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)
# 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)
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:
🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧
🚧🚧🚧🚧[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"> |
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">
#### _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.
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)
## React Native Compatibility
To use this library you need to ensure you match up with the correct version of React Native you are using.
## Install
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.
### 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`
| `@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` |
`npm install react-native-image-picker@latest --save`
### Automatic Installation
## Getting Started
`react-native link`
```
yarn add react-native-image-picker
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.
# RN >= 0.60
npx pod-install
### Manual Installation
# RN < 0.60
react-native link react-native-image-picker
#### 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>
```
You will also need to add `UsageDescription` on iOS and some permissions on Android, refer to the [Install doc](docs/Install.md).
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.
## Usage
```javascript
import ImagePicker from 'react-native-image-picker';
var ImagePicker = require('react-native-image-picker');
// More info on all the options is below in the API Reference... just some common use cases shown here
const options = {
// More info on all the options is below in the README...just some common use cases shown here
var 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 in the API Reference)
* The second arg is the callback which sends object: response (more info below in README)
*/
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 {
const source = { uri: response.uri };
}
else {
let source = { uri: response.uri };
// You can also display the image using data:
// const source = { uri: 'data:image/jpeg;base64,' + response.data };
// let 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} />
```
@ -97,25 +200,69 @@ 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)
For more, read the [API Reference](docs/Reference.md).
### Options
## License
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`
[MIT](LICENSE.md)
### 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)*

View File

@ -1,65 +1,62 @@
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:3.4.1'
classpath 'com.android.tools.build:gradle:2.2.+'
}
}
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 getExtOrIntegerDefault('compileSdkVersion')
buildToolsVersion getExtOrDefault('buildToolsVersion')
compileSdkVersion 25
buildToolsVersion "25.0.2"
defaultConfig {
minSdkVersion getExtOrIntegerDefault('minSdkVersion')
targetSdkVersion getExtOrIntegerDefault('targetSdkVersion')
minSdkVersion 16
targetSdkVersion 25
versionCode 1
versionName computeVersionName()
}
lintOptions {
abortOnError false
}
testOptions {
unitTests {
includeAndroidResources = true
}
unitTests.returnDefaultValues = true
}
}
repositories {
mavenLocal()
mavenCentral()
maven {
url "$projectDir/../node_modules/react-native/android"
url "$projectDir/../Example/node_modules/react-native/android"
}
maven {
url "$projectDir/../node_modules/jsc-android/dist"
url "$projectDir/../../react-native/android"
}
google()
jcenter()
}
dependencies {
api "com.facebook.react:react-native:+" // From node_modules
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"
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}"
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}"
}

View File

@ -1,9 +1,3 @@
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
MOCKITO_CORE_VERSION=1.+
POWERMOCK_VERSION=1.6.2
FEST_ASSERT_CORE_VERSION=2.0M10

View File

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

View File

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

View File

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

View File

@ -6,17 +6,15 @@ 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 androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StyleRes;
import androidx.core.app.ActivityCompat;
import androidx.appcompat.app.AlertDialog;
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 android.text.TextUtils;
import android.util.Base64;
import android.util.Patterns;
@ -30,12 +28,11 @@ 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;
@ -48,22 +45,16 @@ 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;
@ -76,13 +67,10 @@ 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
@ -112,18 +100,18 @@ public class ImagePickerModule extends ReactContextBaseJavaModule
if (!permissionsGranted)
{
responseHelper.invokeError(permissionRequestCallback, "Permissions weren't granted");
responseHelper.invokeError(callback, "Permissions weren't granted");
return false;
}
switch (requestCode)
{
case REQUEST_PERMISSIONS_FOR_CAMERA:
launchCamera(options, permissionRequestCallback);
launchCamera(options, callback);
break;
case REQUEST_PERMISSIONS_FOR_LIBRARY:
launchImageLibrary(options, permissionRequestCallback);
launchImageLibrary(options, callback);
break;
}
@ -131,11 +119,6 @@ public class ImagePickerModule extends ReactContextBaseJavaModule
}
};
public ImagePickerModule(ReactApplicationContext reactContext)
{
this(reactContext, DEFAULT_EXPLAINING_PERMISSION_DIALIOG_THEME);
}
public ImagePickerModule(ReactApplicationContext reactContext,
@StyleRes final int dialogThemeId)
{
@ -148,7 +131,7 @@ public class ImagePickerModule extends ReactContextBaseJavaModule
@Override
public String getName() {
return NAME;
return "ImagePickerManager";
}
@ReactMethod
@ -213,10 +196,7 @@ public class ImagePickerModule extends ReactContextBaseJavaModule
public void doOnCancel()
{
if (callback != null) {
responseHelper.invokeCancel(callback);
callback = null;
}
responseHelper.invokeCancel(callback);
}
public void launchCamera()
@ -228,8 +208,6 @@ 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");
@ -243,7 +221,6 @@ public class ImagePickerModule extends ReactContextBaseJavaModule
return;
}
this.callback = callback;
this.options = options;
if (!permissionsCheck(currentActivity, callback, REQUEST_PERMISSIONS_FOR_CAMERA))
@ -271,15 +248,10 @@ public class ImagePickerModule extends ReactContextBaseJavaModule
requestCode = REQUEST_LAUNCH_IMAGE_CAPTURE;
cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
final File original = createNewFile(reactContext, this.options, false);
final File original = createNewFile(reactContext);
imageConfig = imageConfig.withOriginalFile(original);
if (imageConfig.original != null) {
cameraCaptureURI = RealPathUtil.compatUriFromFile(reactContext, imageConfig.original);
}else {
responseHelper.invokeError(callback, "Couldn't get file path for photo");
return;
}
cameraCaptureURI = RealPathUtil.compatUriFromFile(reactContext, imageConfig.original);
if (cameraCaptureURI == null)
{
responseHelper.invokeError(callback, "Couldn't get file path for photo");
@ -294,16 +266,7 @@ public class ImagePickerModule extends ReactContextBaseJavaModule
return;
}
// 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);
}
}
this.callback = callback;
try
{
@ -324,15 +287,12 @@ 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))
@ -355,11 +315,6 @@ 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)
@ -368,15 +323,11 @@ public class ImagePickerModule extends ReactContextBaseJavaModule
return;
}
this.callback = callback;
try
{
String chooseWhichLibraryTitle = null;
if (ReadableMapUtils.hasAndNotEmptyString(options, "chooseWhichLibraryTitle"))
{
chooseWhichLibraryTitle = options.getString("chooseWhichLibraryTitle");
}
currentActivity.startActivityForResult(Intent.createChooser(libraryIntent, chooseWhichLibraryTitle), requestCode);
currentActivity.startActivityForResult(libraryIntent, requestCode);
}
catch (ActivityNotFoundException e)
{
@ -480,7 +431,7 @@ public class ImagePickerModule extends ReactContextBaseJavaModule
}
else
{
imageConfig = getResizedImage(reactContext, this.options, imageConfig, initialWidth, initialHeight, requestCode);
imageConfig = getResizedImage(reactContext, imageConfig, initialWidth, initialHeight);
if (imageConfig.resized == null)
{
removeUselessFiles(requestCode, imageConfig);
@ -495,6 +446,7 @@ public class ImagePickerModule extends ReactContextBaseJavaModule
updatedResultResponse(uri, imageConfig.resized.getAbsolutePath());
fileScan(reactContext, imageConfig.resized.getAbsolutePath());
MediaUtils.removeOriginIfNeeded(imageConfig, requestCode);
}
}
@ -576,18 +528,10 @@ public class ImagePickerModule extends ReactContextBaseJavaModule
final int cameraPermission = ActivityCompat
.checkSelfPermission(activity, Manifest.permission.CAMERA);
boolean permissionsGranted = false;
final boolean permissionsGrated = writePermission == PackageManager.PERMISSION_GRANTED &&
cameraPermission == PackageManager.PERMISSION_GRANTED;
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)
if (!permissionsGrated)
{
final Boolean dontAskAgain = ActivityCompat.shouldShowRequestPermissionRationale(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE) && ActivityCompat.shouldShowRequestPermissionRationale(activity, Manifest.permission.CAMERA);
@ -628,33 +572,16 @@ public class ImagePickerModule extends ReactContextBaseJavaModule
innerActivity.startActivityForResult(intent, 1);
}
});
if (dialog != null) {
dialog.show();
}
dialog.show();
return false;
}
else
{
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;
}
String[] PERMISSIONS = {Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.CAMERA};
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);
@ -665,8 +592,6 @@ 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);
}
@ -742,26 +667,20 @@ 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) {
@ -771,10 +690,6 @@ 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 androidx.annotation.StyleRes;
import android.support.annotation.StyleRes;
import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.JavaScriptModule;
@ -13,11 +13,12 @@ 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 = ImagePickerModule.DEFAULT_EXPLAINING_PERMISSION_DIALIOG_THEME;
this.dialogThemeId = DEFAULT_EXPLAINING_PERMISSION_DIALIOG_THEME;
}
public ImagePickerPackage(@StyleRes final int dialogThemeId)
@ -30,7 +31,7 @@ public class ImagePickerPackage implements ReactPackage {
return Arrays.<NativeModule>asList(new ImagePickerModule(reactContext, dialogThemeId));
}
// Deprecated RN 0.47
@Override
public List<Class<? extends JavaScriptModule>> createJSModules() {
return Collections.emptyList();
}
@ -39,4 +40,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 androidx.annotation.NonNull;
import android.support.annotation.NonNull;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.Callback;
@ -73,9 +73,6 @@ public class ResponseHelper
public void invokeResponse(@NonNull final Callback callback)
{
if (callback == null) {
return;
}
callback.invoke(response);
}
}

View File

@ -1,8 +1,7 @@
package com.imagepicker.media;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import android.webkit.MimeTypeMap;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import com.facebook.react.bridge.ReadableMap;
@ -78,18 +77,9 @@ 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, quality, this.rotation,
this.maxHeight, this.quality, this.rotation,
this.saveToCameraRoll
);
}
@ -117,12 +107,12 @@ public class ImageConfig
int maxWidth = 0;
if (options.hasKey("maxWidth"))
{
maxWidth = (int) options.getDouble("maxWidth");
maxWidth = options.getInt("maxWidth");
}
int maxHeight = 0;
if (options.hasKey("maxHeight"))
{
maxHeight = (int) options.getDouble("maxHeight");
maxHeight = options.getInt("maxHeight");
}
int quality = 100;
if (options.hasKey("quality"))
@ -132,7 +122,7 @@ public class ImageConfig
int rotation = 0;
if (options.hasKey("rotation"))
{
rotation = (int) options.getDouble("rotation");
rotation = options.getInt("rotation");
}
boolean saveToCameraRoll = false;
if (options.hasKey("storageOptions"))

View File

@ -1,6 +1,6 @@
package com.imagepicker.permissions;
import androidx.annotation.NonNull;
import android.support.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 androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.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,16 +26,7 @@ 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,5 +1,7 @@
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 androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import android.support.annotation.NonNull;
import android.support.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 androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Log;
import com.facebook.react.bridge.ReadableMap;
@ -20,6 +20,7 @@ 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;
@ -36,55 +37,15 @@ import static com.imagepicker.ImagePickerModule.REQUEST_LAUNCH_IMAGE_CAPTURE;
public class MediaUtils
{
public static @Nullable File createNewFile(@NonNull final Context reactContext,
@NonNull final ReadableMap options,
@NonNull final boolean forceLocal)
public static @NonNull File createNewFile(@NonNull final Context reactContext)
{
final String filename = new StringBuilder("image-")
.append(UUID.randomUUID().toString())
.append(".jpg")
.toString();
// 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);
}
final File path = reactContext.getExternalFilesDir(Environment.DIRECTORY_PICTURES);
File result = new File(path, filename);
try
{
path.mkdirs();
result.createNewFile();
}
catch (IOException e)
{
e.printStackTrace();
result = null;
}
path.mkdirs();
return result;
}
@ -92,37 +53,24 @@ 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,
int initialWidth,
int initialHeight,
final int requestCode)
final int initialWidth,
final int initialHeight)
{
BitmapFactory.Options imageOptions = new BitmapFactory.Options();
imageOptions.inScaled = false;
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;
}
}
// FIXME: OOM here
Bitmap photo = BitmapFactory.decodeFile(imageConfig.original.getAbsolutePath(), imageOptions);
if (photo == null)
{
return imageConfig;
return null;
}
ImageConfig result = imageConfig;
@ -177,8 +125,7 @@ public class MediaUtils
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
scaledPhoto.compress(Bitmap.CompressFormat.JPEG, result.quality, bytes);
final boolean forceLocal = requestCode == REQUEST_LAUNCH_IMAGE_CAPTURE;
final File resized = createNewFile(context, options, !forceLocal);
final File resized = createNewFile(context);
if (resized == null)
{
@ -197,9 +144,11 @@ public class MediaUtils
result = result.withResizedFile(resized);
try (FileOutputStream fos = new FileOutputStream(result.resized))
FileOutputStream fos;
try
{
bytes.writeTo(fos);
fos = new FileOutputStream(result.resized);
fos.write(bytes.toByteArray());
}
catch (IOException e)
{
@ -267,14 +216,11 @@ 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];
exif.getLatLong(latlng);
float latitude = latlng[0];
float longitude = latlng[1];
if(latitude != 0f || longitude != 0f)
float[] latLng = new float[2];
if(exif.getLatLong(latLng))
{
responseHelper.putDouble("latitude", latitude);
responseHelper.putDouble("longitude", longitude);
responseHelper.putDouble("latitude", latLng[0]);
responseHelper.putDouble("longitude", latLng[1]);
}
final String timestamp = exif.getAttribute(ExifInterface.TAG_DATETIME);
@ -354,26 +300,35 @@ 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.
*/
private static void moveFile(@NonNull final File oldFile,
public static void moveFile(@NonNull final File oldFile,
@NonNull final File newFile) throws IOException
{
FileChannel oldChannel = null;
FileChannel newChannel = null;
FileInputStream in = new FileInputStream(oldFile);
FileOutputStream out = new FileOutputStream(newFile);
try
{
oldChannel = new FileInputStream(oldFile).getChannel();
newChannel = new FileOutputStream(newFile).getChannel();
oldChannel.transferTo(0, oldChannel.size(), newChannel);
byte[] buf = new byte[1024];
int len;
while ((len = in.read(buf)) > 0) {
out.write(buf, 0, len);
}
in.close();
out.close();
oldFile.delete();
}
finally
{
try
{
if (oldChannel != null) oldChannel.close();
if (newChannel != null) newChannel.close();
if (in != null)
{
in.close();
}
if (out != null)
{
out.close();
}
}
catch (IOException e)
{
@ -382,6 +337,16 @@ 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 androidx.annotation.NonNull;
import android.support.annotation.NonNull;
import android.text.TextUtils;
import com.facebook.react.bridge.ReadableMap;

View File

@ -1,6 +1,7 @@
package com.imagepicker.utils;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
@ -9,9 +10,9 @@ import android.provider.DocumentsContract;
import android.provider.MediaStore;
import android.content.ContentUris;
import android.os.Environment;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.content.FileProvider;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.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 androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import android.graphics.drawable.ColorDrawable;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.app.AlertDialog;
import android.widget.ArrayAdapter;
import com.facebook.react.bridge.ReadableMap;

View File

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

View File

@ -45,7 +45,8 @@ 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.*", "jdk.internal.reflect.*"})
@PowerMockIgnore({"org.mockito.*", "org.robolectric.*", "android.*"})
@Config(manifest = Config.NONE)
public class ImagePickerModuleTest
{
private static final int DEFAULT_THEME = R.style.DefaultExplainingPermissionsTheme;
@ -110,4 +111,4 @@ public class ImagePickerModuleTest
}
});
}
}
}

View File

@ -15,9 +15,6 @@ 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,13 +1,16 @@
package com.imagepicker.testing;
import android.net.Uri;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StyleRes;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.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

@ -0,0 +1,78 @@
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

@ -0,0 +1,296 @@
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

@ -0,0 +1,33 @@
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);
}
}

View File

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

View File

@ -1,195 +0,0 @@
# 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).

View File

@ -1,95 +0,0 @@
# 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)_ |

View File

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

View File

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

View File

@ -1,70 +0,0 @@
[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

View File

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

56
example/.gitignore vendored
View File

@ -1,56 +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/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,19 +0,0 @@
# 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,17 +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 *;
#}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

View File

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

View File

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

View File

@ -1,5 +0,0 @@
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'

View File

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

View File

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

View File

@ -1,24 +0,0 @@
<?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

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