├── .gitignore
├── .npmignore
├── README.md
├── RNCustomizedImagePicker.podspec
├── android
├── build.gradle
├── gradle
│ └── wrapper
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── com
│ │ └── mg
│ │ └── app
│ │ ├── Compression.java
│ │ ├── PickerModule.java
│ │ └── PickerPackage.java
│ └── res
│ ├── mipmap-hdpi
│ └── ic_launcher.png
│ └── values
│ ├── colors.xml
│ ├── strings.xml
│ └── themes.xml
├── images
├── pic.png
├── pic2.png
└── pic3.png
├── index.android.js
├── index.js
├── ios
├── ImageCropPicker.h
├── ImageCropPicker.m
├── ImageCropPicker.xcodeproj
│ └── project.pbxproj
├── NSDictionary+SYSafeConvert.h
└── NSDictionary+SYSafeConvert.m
└── package.json
/.gitignore:
--------------------------------------------------------------------------------
1 | # OSX
2 | #
3 | .DS_Store
4 |
5 | # Xcode
6 | #
7 | build/
8 | *.pbxuser
9 | !default.pbxuser
10 | *.mode1v3
11 | !default.mode1v3
12 | *.mode2v3
13 | !default.mode2v3
14 | *.perspectivev3
15 | !default.perspectivev3
16 | xcuserdata
17 | *.xccheckout
18 | *.moved-aside
19 | DerivedData
20 | *.hmap
21 | *.ipa
22 | *.xcuserstate
23 |
24 | # Android/IntelliJ
25 | #
26 | build/
27 | .idea
28 | .gradle
29 | local.properties
30 | *.iml
31 | /android/local.properties
32 | # node.js
33 | #
34 | node_modules/
35 |
36 | # BUCK
37 | buck-out/
38 | \.buckd/
39 | *.keystore
40 | !debug.keystore
41 | !xiushang.keystore
42 | # fastlane
43 | #
44 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
45 | # screenshots whenever they are needed.
46 | # For more information about the recommended setup visit:
47 | # https://docs.fastlane.tools/best-practices/source-control/
48 |
49 | */fastlane/report.xml
50 | */fastlane/Preview.html
51 | */fastlane/screenshots
52 |
53 | # Bundle artifact
54 | #*.jsbundle
55 |
56 | # CocoaPods
57 | /ios/Pods/
58 | /*.lock
59 | /*.log
60 | /ios/xiushangApp.xcworkspace/contents.xcworkspacedata
61 | /ios/xiushangApp.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
62 | /ios/Podfile.lock
63 |
64 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | .git
2 | example
3 | node_modules
4 | build
5 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # react-native-customized-image-picker
2 |
3 | iOS/Android image picker with support for camera, video compression, multiple images and cropping
4 |
5 | ## Result
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | ## Usage
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 | #### example
34 |
35 | https://github.com/shijingsh/pickExample
36 |
37 | #### Import library
38 |
39 | ```javascript
40 | import ImagePicker from "react-native-customized-image-picker";
41 | ```
42 |
43 | #### Select from gallery
44 |
45 | Call single image picker
46 |
47 | ```javascript
48 | ImagePicker.openPicker({}).then(image => {
49 | console.log(image);
50 | });
51 | ```
52 |
53 | Call multiple image picker
54 |
55 | ```javascript
56 | ImagePicker.openPicker({
57 | multiple: true
58 | }).then(images => {
59 | console.log(images);
60 | });
61 | ```
62 |
63 | ### Select from camera
64 |
65 | ```javascript
66 | ImagePicker.openCamera({
67 | width: 300,
68 | height: 400,
69 | cropping: true
70 | }).then(image => {
71 | console.log(image);
72 | });
73 | ```
74 |
75 | Select video
76 |
77 | ```javascript
78 | ImagePicker.openCamera({
79 | width: 300,
80 | height: 400,
81 | isVideo: true
82 | }).then(image => {
83 | console.log(image);
84 | });
85 | ```
86 |
87 | ### Optional cleanup
88 |
89 | Module is creating tmp images which are going to be cleaned up automatically somewhere in the future. If you want to force cleanup, you can use `clean` to clean all tmp files.
90 | Delete the cut, compression, and photographed pictures.
91 |
92 | ```javascript
93 | ImagePicker.clean()
94 | .then(() => {
95 | console.log("removed all tmp images from tmp directory");
96 | })
97 | .catch(e => {
98 | console.log(e);
99 | });
100 | ```
101 |
102 | #### Request Object
103 |
104 | | Property | Type | Description |
105 | | ---------------------- | :----------------------: | :-------------------------------------------------------------------- |
106 | | cropping | bool (default false) | Enable or disable cropping |
107 | | width | number(default 200) | Width of result image when used with `cropping` option |
108 | | height | number(default 200) | Height of result image when used with `cropping` option |
109 | | multiple | bool (default false) | Enable or disable multiple image selection |
110 | | isCamera | bool (default false) | Enable or disable camera selection |
111 | | openCameraOnStart | bool (default false) | Enable or disable turn on the camera when it starts |
112 | | returnAfterShot | bool (default false) | Enable or disable pictures taken directly |
113 | | multipleShot | bool (default false) | Enable or disable Capture image multiple time |
114 | | maxSize | number (default 9) | set image count |
115 | | spanCount | number (default 3) | Set the number of pictures displayed in a row |
116 | | includeBase64 | bool (default false) | Enable or disable includeBase64 |
117 | | compressQuality | number([0-100]) | Picture compression ratio |
118 | | minCompressSize | number | Setting the minimum size of the compressed file(kb) |
119 | | title | string | Sets the title of the page |
120 | | isVideo | bool (default false) | Enable or disable video only |
121 | | isSelectBoth | bool (default false) | Enable or disable select both images and videos |
122 | | isHidePreview | bool (default false) | Enable or disable hidden preview button |
123 | | isHideVideoPreview | bool (default false) | Enable or disable hidden video preview button |
124 | | isPlayGif | bool (default false) | Enable or disable play gif |
125 | | hideCropBottomControls | bool (default true) | Enable or disable crop controls |
126 | | aspectRatioX | number (default 1) | Set an aspect ratio X for crop bounds. |
127 | | aspectRatioY | number (default 1) | Set an aspect ratio Y for crop bounds. |
128 | | videoQuality | number (default 1) | 1: high 0: low. |
129 | | imageLoader | string (default "GLIDE") | Sets the imageLoader of the page,enum(PICASSO,GLIDE,FRESCO,UNIVERSAL) |
130 |
131 | #### Response Object
132 |
133 | | Property | Type | Description |
134 | | -------- | :----: | :----------------------------------------------- |
135 | | path | string | Selected image location |
136 | | width | number | Selected image width |
137 | | height | number | Selected image height |
138 | | mime | string | Selected image MIME type (image/jpeg, image/png) |
139 | | size | number | Selected image size in bytes |
140 | | data | base64 | Optional base64 selected file representation |
141 |
142 | ## Install
143 |
144 | ```
145 | npm i react-native-customized-image-picker --save
146 | yarn add react-native-customized-image-picker
147 | ```
148 |
149 | #### android
150 |
151 | #### add permission /android/app/src/main/AndroidManifest.xml
152 |
153 | ```xml
154 |
155 |
156 |
157 |
158 |
159 |
160 | ```
161 |
162 | ### iOS
163 |
164 | - [TZImagePickerController](https://github.com/banchichen/TZImagePickerController)
165 |
166 | #### info.plist add the following to the file
167 |
168 | ```
169 | NSCameraUsageDescription
170 | 1
171 | NSLocationWhenInUseUsageDescription
172 |
173 | NSPhotoLibraryAddUsageDescription
174 | 1
175 | NSPhotoLibraryUsageDescription
176 | 1
177 | ```
178 | auto linked
179 |
180 | #### pod install
181 | cd ios and run
182 | ```bash
183 | pod install
184 | ```
185 |
186 | ## Setting themes
187 |
188 | #### Setting language
189 |
190 | - Add file gallery_strings.xml under the directory "yourProject\android\app\src\main\res\values".
191 |
192 | ```xml
193 |
194 | Load more
195 | No more
196 | Loading…
197 |
198 | Complete
199 | Complete(%1$d/%2$d)
200 | You can only choose %1$d photos
201 | %1$d/%2$d
202 | photos
203 | video
204 | App request to read your album
205 | >App request to Camera
206 | Absolutely empty
207 | The device has no camera
208 | Camera not available
209 | preview
210 | All pictures
211 | All video
212 | Photograph
213 | Selected
214 | pictures
215 | cut
216 | record video
217 |
218 | ```
219 |
220 | #### Setting style
221 |
222 | - modify file styles.xml under the directory "yourProject\android\app\src\main\res\values".
223 |
224 | ```xml
225 |
226 |
229 |
261 |
262 |
266 |
267 | ```
268 |
269 | - modify file AndroidManifest.xml .
270 |
271 | ```xml
272 |
277 |
278 |
286 |
287 |
291 |
292 |
293 | ```
294 |
295 | ##### Important content
296 |
297 | - xmlns:tools="http://schemas.android.com/tools"
298 | - tools:replace="android:theme"
299 | - android:theme="@style/AppTheme"
300 | - cn.finalteam.rxgalleryfinal.ui.activity.MediaActivity Theme_Light.Default
301 |
302 | ## License
303 |
304 | _MIT_
305 |
--------------------------------------------------------------------------------
/RNCustomizedImagePicker.podspec:
--------------------------------------------------------------------------------
1 |
2 | require 'json'
3 | package = JSON.parse(File.read(File.join(__dir__, 'package.json')))
4 |
5 | Pod::Spec.new do |s|
6 | s.name = "RNCustomizedImagePicker"
7 | s.version = package["version"]
8 | s.summary = package['description']
9 | s.homepage = "https://github.com/liukefu2050/react-native-customized-image-picker"
10 | s.license = "MIT"
11 | s.author = { "author" => "liukefu2050@sina.com" }
12 | s.platform = :ios, "10.0"
13 | s.source = { :git => "https://github.com/liukefu2050/react-native-customized-image-picker.git", :tag => "master" }
14 | s.source_files = "**/*.{h,m}"
15 | s.requires_arc = true
16 | s.resource = "TZImagePickerController/TZImagePickerController.bundle"
17 |
18 | s.dependency "React"
19 | s.dependency "TZImagePickerController"
20 |
21 | end
22 |
--------------------------------------------------------------------------------
/android/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | def safeExtGet(prop, fallback) {
4 | rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
5 | }
6 | def _buildToolsVersion = safeExtGet("buildToolsVersion", "30.0.2")
7 | def _compileSdkVersion = safeExtGet("compileSdkVersion", 30)
8 | def _glideVersion = safeExtGet("glideVersion", "3.7.0")
9 | def _minSdkVersion = safeExtGet("minSdkVersion", 16)
10 | def _reactNativeVersion = safeExtGet("reactNativeVersion", "+")
11 | def _targetSdkVersion = safeExtGet("targetSdkVersion", 30)
12 |
13 | android {
14 | compileSdkVersion _compileSdkVersion
15 | buildToolsVersion _buildToolsVersion
16 |
17 | defaultConfig {
18 | minSdkVersion _minSdkVersion
19 | targetSdkVersion _targetSdkVersion
20 | versionCode 1
21 | }
22 | compileOptions {
23 | sourceCompatibility JavaVersion.VERSION_1_8
24 | targetCompatibility JavaVersion.VERSION_1_8
25 | }
26 | lintOptions {
27 | abortOnError false
28 | }
29 | }
30 |
31 |
32 | dependencies {
33 | implementation fileTree(dir: 'libs', include: ['*.jar'])
34 | testImplementation 'junit:junit:4.12'
35 | implementation 'com.android.support:appcompat-v7:28.0.0'
36 | implementation "com.facebook.react:react-native:${_reactNativeVersion}"
37 |
38 | //implementation ('com.github.shijingsh:RxCustomizedImagePicker:1.3.0'){
39 | // exclude module:'fileprovider'
40 | //}
41 | implementation 'com.github.shijingsh:RxCustomizedImagePicker:1.3.1'
42 | //rxgalleryfinal依赖appcompat-v7和recyclerview-v7扩展卡库
43 | implementation 'com.android.support:recyclerview-v7:28.0.0'
44 |
45 | implementation 'com.squareup.picasso:picasso:2.5.2'
46 | implementation 'com.facebook.fresco:fresco:1.3.0'
47 | implementation 'com.facebook.fresco:animated-gif:1.3.0'
48 | implementation "com.github.bumptech.glide:glide:${_glideVersion}"
49 | implementation 'com.nostra13.universalimageloader:universal-image-loader:1.9.5'
50 | implementation 'id.zelory:compressor:2.1.1'
51 | }
52 |
--------------------------------------------------------------------------------
/android/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shijingsh/react-native-customized-image-picker/7335f7acde315eec10cf5fa3b5620d048e8b6b91/android/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Fri Nov 09 16:48:16 GMT+08:00 2018
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip
7 |
--------------------------------------------------------------------------------
/android/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/android/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/android/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 |
10 |
11 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/android/src/main/java/com/mg/app/Compression.java:
--------------------------------------------------------------------------------
1 | package com.mg.app;
2 |
3 | import android.app.Activity;
4 | import android.graphics.Bitmap;
5 | import android.os.Environment;
6 |
7 | import com.facebook.react.bridge.Promise;
8 | import com.facebook.react.bridge.ReadableMap;
9 |
10 | import java.io.File;
11 | import java.io.FileInputStream;
12 | import java.io.FileNotFoundException;
13 | import java.io.IOException;
14 | import java.util.Calendar;
15 | import java.util.Random;
16 |
17 | import cn.finalteam.rxgalleryfinal.ui.fragment.MediaGridFragment;
18 | import id.zelory.compressor.Compressor;
19 |
20 |
21 | class Compression {
22 |
23 | public File compressImage(final Activity activity, final ReadableMap options, final String originalImagePath) throws IOException {
24 | Integer maxWidth = options.hasKey("width") ? options.getInt("width") : null;
25 | Integer maxHeight = options.hasKey("height") ? options.getInt("height") : null;
26 | Integer quality = options.hasKey("compressQuality") ? options.getInt("compressQuality") : null;
27 | Integer minCompressSize = options.hasKey("minCompressSize") ? options.getInt("minCompressSize") : null;
28 |
29 | if (maxWidth == null || maxWidth<=0) {
30 | // maxWidth = 300;
31 | }
32 | if (maxHeight == null || maxHeight<=0 ) {
33 | // maxHeight = 300;
34 | }
35 | if (quality == null || quality >= 100 || quality <= 0) {
36 | return new File(originalImagePath);
37 | }
38 | //小于最小压缩大小,就不压缩
39 | if(minCompressSize != null && minCompressSize >0 && getFileSize(new File(originalImagePath)) < minCompressSize * 1024){
40 | return new File(originalImagePath);
41 | }
42 | String path = MediaGridFragment.getImageStoreDirByStr();
43 | Compressor compressor = new Compressor(activity)
44 | .setCompressFormat(Bitmap.CompressFormat.JPEG)
45 | .setDestinationDirectoryPath(path+File.separator+"compressed");
46 |
47 | compressor.setQuality(quality);
48 |
49 | if (maxWidth != null) {
50 | compressor.setMaxWidth(maxWidth);
51 | }
52 |
53 | if (maxHeight != null) {
54 | compressor.setMaxHeight(maxHeight);
55 | }
56 |
57 | File image = new File(originalImagePath);
58 |
59 | String[] paths = image.getName().split("\\.(?=[^\\.]+$)");
60 | String compressedFileName ="IMG_" +generateRandomFilename() + "_compressed";
61 |
62 | if(paths.length > 1)
63 | compressedFileName += "." + paths[1];
64 |
65 | return compressor
66 | .compressToFile(image, compressedFileName);
67 | }
68 |
69 | public String generateRandomFilename(){
70 |
71 | Random random = new Random();
72 | int ends = random.nextInt(99);
73 | String randomStr = String.format("%02d",ends);
74 |
75 | Calendar calCurrent = Calendar.getInstance();
76 |
77 | String now = String.valueOf(calCurrent.getTimeInMillis());
78 |
79 | return now + randomStr;
80 | }
81 |
82 | synchronized void compressVideo(final Activity activity, final ReadableMap options,
83 | final String originalVideo, final String compressedVideo, final Promise promise) {
84 | // todo: video compression
85 | // failed attempt 1: ffmpeg => slow and licensing issues
86 | promise.resolve(originalVideo);
87 | }
88 |
89 | /**
90 | * 获取指定文件大小
91 | * @param file
92 | * @return
93 | * @throws Exception
94 | */
95 | public static long getFileSize(File file) {
96 | long size = 0;
97 | if (file.exists()) {
98 | FileInputStream fis = null;
99 | try {
100 | fis = new FileInputStream(file);
101 | size = fis.available();
102 | } catch (FileNotFoundException e) {
103 | e.printStackTrace();
104 | } catch (IOException e) {
105 | e.printStackTrace();
106 | } finally {
107 | try {
108 | fis.close();
109 | } catch (IOException e) {
110 | e.printStackTrace();
111 | }
112 | }
113 | }
114 | return size;
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/android/src/main/java/com/mg/app/PickerModule.java:
--------------------------------------------------------------------------------
1 | package com.mg.app;
2 |
3 | import android.app.Activity;
4 | import android.graphics.Bitmap;
5 | import android.graphics.BitmapFactory;
6 | import android.media.MediaMetadataRetriever;
7 | import android.util.Base64;
8 | import android.webkit.MimeTypeMap;
9 |
10 | import com.facebook.react.bridge.Promise;
11 | import com.facebook.react.bridge.ReactApplicationContext;
12 | import com.facebook.react.bridge.ReactContextBaseJavaModule;
13 | import com.facebook.react.bridge.ReactMethod;
14 | import com.facebook.react.bridge.ReadableMap;
15 | import com.facebook.react.bridge.WritableArray;
16 | import com.facebook.react.bridge.WritableMap;
17 | import com.facebook.react.bridge.WritableNativeArray;
18 | import com.facebook.react.bridge.WritableNativeMap;
19 | import com.nostra13.universalimageloader.cache.disc.naming.Md5FileNameGenerator;
20 | import com.nostra13.universalimageloader.core.ImageLoader;
21 | import com.nostra13.universalimageloader.core.ImageLoaderConfiguration;
22 | import com.nostra13.universalimageloader.core.assist.QueueProcessingType;
23 |
24 | import java.io.ByteArrayOutputStream;
25 | import java.io.File;
26 | import java.io.FileInputStream;
27 | import java.io.FileNotFoundException;
28 | import java.io.IOException;
29 | import java.io.InputStream;
30 | import java.math.BigDecimal;
31 | import java.util.List;
32 |
33 | import cn.finalteam.rxgalleryfinal.RxGalleryFinal;
34 | import cn.finalteam.rxgalleryfinal.ui.fragment.MediaGridFragment;
35 | import cn.finalteam.rxgalleryfinal.bean.ImageCropBean;
36 | import cn.finalteam.rxgalleryfinal.bean.MediaBean;
37 | import cn.finalteam.rxgalleryfinal.imageloader.ImageLoaderType;
38 | import cn.finalteam.rxgalleryfinal.rxbus.RxBusResultDisposable;
39 | import cn.finalteam.rxgalleryfinal.rxbus.event.ImageMultipleResultEvent;
40 | import cn.finalteam.rxgalleryfinal.rxbus.event.ImageRadioResultEvent;
41 | import cn.finalteam.rxgalleryfinal.ui.RxGalleryListener;
42 | import cn.finalteam.rxgalleryfinal.ui.base.IRadioImageCheckedListener;
43 |
44 | class PickerModule extends ReactContextBaseJavaModule {
45 | private static final String E_ACTIVITY_DOES_NOT_EXIST = "E_ACTIVITY_DOES_NOT_EXIST";
46 |
47 | private Promise mPickerPromise;
48 |
49 | private boolean cropping = false;
50 | private boolean multiple = false;
51 | private boolean isCamera = false;
52 | private boolean includeBase64 = false;
53 | private boolean openCameraOnStart = false;
54 | private boolean isVideo = false;
55 | private boolean isHidePreview = false;
56 | private boolean isPlayGif = false;
57 | private boolean isHideVideoPreview = false;
58 | private boolean isSelectBoth = false;
59 | private int videoQuality = 1;
60 | private String title = null;
61 | private String imageLoader = null;
62 | //Light Blue 500
63 | private int width = 0;
64 | private int height = 0;
65 | private int maxSize = 9;
66 | private int compressQuality = -1;
67 | private boolean returnAfterShot = false;
68 | private boolean multipleShot = false;
69 | private int spanCount = 3;
70 | private boolean hideCropBottomControls = true;
71 | //宽高比
72 | private float aspectRatioX = 1;
73 | private float aspectRatioY = 1;
74 | private final ReactApplicationContext mReactContext;
75 |
76 | private Compression compression = new Compression();
77 | private ReadableMap options;
78 | PickerModule(ReactApplicationContext reactContext) {
79 | super(reactContext);
80 | mReactContext = reactContext;
81 | }
82 |
83 | @Override
84 | public String getName() {
85 | return "ImageCropPicker";
86 | }
87 |
88 | private void setConfiguration(final ReadableMap options) {
89 | multiple = options.hasKey("multiple") && options.getBoolean("multiple");
90 | isCamera = options.hasKey("isCamera") && options.getBoolean("isCamera");
91 | openCameraOnStart = options.hasKey("openCameraOnStart") && options.getBoolean("openCameraOnStart");
92 | width = options.hasKey("width") ? options.getInt("width") : width;
93 | height = options.hasKey("height") ? options.getInt("height") : height;
94 | maxSize = options.hasKey("maxSize") ? options.getInt("maxSize") : (multiple?9:1);
95 | cropping = options.hasKey("cropping") ? options.getBoolean("cropping") : cropping;
96 | includeBase64 = options.hasKey("includeBase64") && options.getBoolean("includeBase64");
97 | compressQuality = options.hasKey("compressQuality") ? options.getInt("compressQuality") : compressQuality;
98 | title = options.hasKey("title") ? options.getString("title") : title;
99 | returnAfterShot = options.hasKey("returnAfterShot") && options.getBoolean("returnAfterShot");
100 | multipleShot = options.hasKey("multipleShot") && options.getBoolean("multipleShot");
101 | isVideo = options.hasKey("isVideo") && options.getBoolean("isVideo");
102 | isSelectBoth = options.hasKey("isSelectBoth") && options.getBoolean("isSelectBoth");
103 | videoQuality = options.hasKey("videoQuality") ? options.getInt("videoQuality"):1;
104 | isHidePreview = options.hasKey("isHidePreview") && options.getBoolean("isHidePreview");
105 | isHideVideoPreview = options.hasKey("isHideVideoPreview") && options.getBoolean("isHideVideoPreview");
106 | isPlayGif = options.hasKey("isPlayGif") && options.getBoolean("isPlayGif");
107 | spanCount = options.hasKey("spanCount") ? options.getInt("spanCount") : spanCount;
108 | hideCropBottomControls = options.hasKey("hideCropBottomControls") ? options.getBoolean("hideCropBottomControls") : hideCropBottomControls;
109 |
110 | aspectRatioX = options.hasKey("aspectRatioX") ? options.getInt("aspectRatioX") : aspectRatioX;
111 | aspectRatioY = options.hasKey("aspectRatioY") ? options.getInt("aspectRatioY") : aspectRatioY;
112 |
113 | imageLoader = options.hasKey("imageLoader") ? options.getString("imageLoader") : imageLoader;
114 | this.options = options;
115 | }
116 |
117 | private static String getMimeType(String url) {
118 | String type = null;
119 | String extension = MimeTypeMap.getFileExtensionFromUrl(url);
120 | if (extension != null) {
121 | type = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
122 | }
123 |
124 | return type;
125 | }
126 |
127 | private WritableMap getAsyncSelection(final Activity activity,String path) throws Exception {
128 | String mime = getMimeType(path);
129 | if (mime != null && mime.startsWith("video/")) {
130 | return getVideo(activity, path, mime);
131 | }
132 |
133 | return getImage(activity,path);
134 | }
135 |
136 | private WritableMap getAsyncSelection(final Activity activity, ImageCropBean result) throws Exception {
137 |
138 | String path = result.getOriginalPath();
139 | return getAsyncSelection(activity,path);
140 | }
141 | private WritableMap getAsyncSelection(final Activity activity,MediaBean result) throws Exception {
142 |
143 | String path = result.getOriginalPath();
144 | return getAsyncSelection(activity,path);
145 | }
146 |
147 | private WritableMap getVideo(Activity activity, String path, String mime) throws Exception {
148 | Bitmap bmp = validateVideo(path);
149 |
150 | WritableMap video = new WritableNativeMap();
151 | video.putInt("width", bmp.getWidth());
152 | video.putInt("height", bmp.getHeight());
153 | video.putString("mime", mime);
154 | video.putInt("size", (int) new File(path).length());
155 | video.putString("path", "file://" + path);
156 |
157 | return video;
158 | }
159 |
160 | private WritableMap getImage(final Activity activity,String path) throws Exception {
161 | WritableMap image = new WritableNativeMap();
162 | if (path.startsWith("http://") || path.startsWith("https://")) {
163 | throw new Exception("Cannot select remote files");
164 | }
165 | validateImage(path);
166 |
167 | // if compression options are provided image will be compressed. If none options is provided,
168 | // then original image will be returned
169 | File compressedImage = compression.compressImage(activity, options, path);
170 | String compressedImagePath = compressedImage.getPath();
171 | BitmapFactory.Options options = validateImage(compressedImagePath);
172 |
173 | image.putString("path", "file://" + compressedImagePath);
174 | image.putInt("width", options.outWidth);
175 | image.putInt("height", options.outHeight);
176 | image.putString("mime", options.outMimeType);
177 | image.putInt("size", (int) new File(compressedImagePath).length());
178 |
179 | if (includeBase64) {
180 | image.putString("data", getBase64StringFromFile(compressedImagePath));
181 | }
182 |
183 | return image;
184 | }
185 |
186 | private WritableMap getImage(final Activity activity, ImageCropBean result) throws Exception {
187 |
188 | String path = result.getOriginalPath();
189 | return getImage(activity,path);
190 | }
191 | private WritableMap getImage(final Activity activity,MediaBean result) throws Exception {
192 |
193 | String path = result.getOriginalPath();
194 | return getImage(activity,path);
195 | }
196 | private void initImageLoader(Activity activity) {
197 |
198 | ImageLoaderConfiguration.Builder config = new ImageLoaderConfiguration.Builder(activity);
199 | config.threadPriority(Thread.NORM_PRIORITY - 2);
200 | config.denyCacheImageMultipleSizesInMemory();
201 | config.diskCacheFileNameGenerator(new Md5FileNameGenerator());
202 | config.diskCacheSize(50 * 1024 * 1024); // 50 MiB
203 | config.tasksProcessingOrder(QueueProcessingType.LIFO);
204 | ImageLoader.getInstance().init(config.build());
205 | }
206 |
207 | private void initCrop(RxGalleryFinal rxGalleryFinal){
208 | //设置剪切最大宽度,高度
209 | if(this.width>0 && this.height>0){
210 | rxGalleryFinal.cropMaxResultSize(this.width,this.height);
211 | }
212 | //用户设置的宽高比优先
213 | if(aspectRatioX>1||aspectRatioY>1){
214 | rxGalleryFinal.cropWithAspectRatio(aspectRatioX,aspectRatioY);
215 | rxGalleryFinal.cropFreeStyleCropEnabled(true);
216 | }else {
217 | if(this.width>0 && this.height>0){
218 | //x=宽,y=高
219 | if(this.width>this.height){
220 | float f = new BigDecimal(this.width).divide(new BigDecimal(this.height),1,BigDecimal.ROUND_HALF_UP).floatValue();
221 | rxGalleryFinal.cropWithAspectRatio(f*10,10);
222 | }else {
223 | float f = new BigDecimal(this.height).divide(new BigDecimal(this.width),1,BigDecimal.ROUND_HALF_UP).floatValue();
224 | rxGalleryFinal.cropWithAspectRatio(10,f*10);
225 | }
226 |
227 | rxGalleryFinal.cropFreeStyleCropEnabled(false);
228 | }else{
229 | rxGalleryFinal.cropWithAspectRatio(aspectRatioX,aspectRatioY);
230 | rxGalleryFinal.cropFreeStyleCropEnabled(true);
231 | }
232 | }
233 | rxGalleryFinal.cropHideBottomControls(this.hideCropBottomControls);
234 | }
235 |
236 | @ReactMethod
237 | public void openPicker(final ReadableMap options, final Promise promise) {
238 | final Activity activity = getCurrentActivity();
239 |
240 | if (activity == null) {
241 | promise.reject(E_ACTIVITY_DOES_NOT_EXIST, "Activity doesn't exist");
242 | return;
243 | }
244 |
245 | setConfiguration(options);
246 | initImageLoader(activity);
247 | mPickerPromise = promise;
248 |
249 | RxGalleryFinal rxGalleryFinal = RxGalleryFinal.with(activity);
250 | rxGalleryFinal.spanCount(spanCount);
251 | rxGalleryFinal.setVideoQuality(videoQuality);
252 | if(openCameraOnStart){
253 | rxGalleryFinal.openCameraOnStart();
254 | }else if(!isCamera){
255 | rxGalleryFinal.hideCamera();
256 | }
257 | if(compressQuality>0){
258 | rxGalleryFinal.cropropCompressionQuality(compressQuality);
259 | }
260 | if(title != null){
261 | rxGalleryFinal.setTitle(title);
262 | }
263 | if(returnAfterShot){
264 | rxGalleryFinal.returnAfterShot();
265 | }
266 | if(multipleShot){
267 | rxGalleryFinal.multipleShot();
268 | }
269 | if(isVideo){
270 | rxGalleryFinal.video();
271 | }else {
272 | rxGalleryFinal.image();
273 | }
274 | if(isSelectBoth){
275 | rxGalleryFinal.selectBoth();
276 | }
277 | if(isHidePreview){
278 | rxGalleryFinal.hidePreview();
279 | }
280 | if (isHideVideoPreview){
281 | rxGalleryFinal.videoPreview();
282 | }
283 | if(isPlayGif){
284 | rxGalleryFinal.gif();
285 | }
286 | if (imageLoader != null){
287 | switch (imageLoader){
288 | case "PICASSO":
289 | rxGalleryFinal.imageLoader(ImageLoaderType.PICASSO);
290 | break;
291 | case "GLIDE":
292 | rxGalleryFinal.imageLoader(ImageLoaderType.GLIDE);
293 | break;
294 | case "FRESCO":
295 | rxGalleryFinal.imageLoader(ImageLoaderType.FRESCO);
296 | break;
297 | case "UNIVERSAL":
298 | rxGalleryFinal.imageLoader(ImageLoaderType.UNIVERSAL);
299 | break;
300 | default:
301 | break;
302 | }
303 | }else{
304 | rxGalleryFinal.imageLoader(ImageLoaderType.GLIDE);
305 | }
306 | if(!this.multiple) {
307 | if(cropping){
308 | rxGalleryFinal.crop();
309 | initCrop(rxGalleryFinal);
310 | //裁剪图片的回调
311 | RxGalleryListener
312 | .getInstance()
313 | .setRadioImageCheckedListener(
314 | new IRadioImageCheckedListener() {
315 | @Override
316 | public void cropAfter(Object t) {
317 | WritableArray resultArr = new WritableNativeArray();
318 | try {
319 | if(t!=null)
320 | resultArr.pushMap(getAsyncSelection(activity,t.toString()));
321 | } catch (Exception e) {
322 | e.printStackTrace();
323 | }
324 | if(resultArr.size()==0){
325 | mPickerPromise.reject("cancel","cancel");
326 | }else{
327 | mPickerPromise.resolve(resultArr);
328 | }
329 | }
330 |
331 | @Override
332 | public boolean isActivityFinish() {
333 | return true;
334 | }
335 | });
336 | }
337 | rxGalleryFinal
338 | .radio()
339 | .subscribe(new RxBusResultDisposable() {
340 | @Override
341 | protected void onEvent(ImageRadioResultEvent imageRadioResultEvent) throws Exception {
342 | if(!cropping){
343 | ImageCropBean result = imageRadioResultEvent.getResult();
344 | WritableArray resultArr = new WritableNativeArray();
345 | if(result!=null)
346 | resultArr.pushMap(getAsyncSelection(activity,result));
347 | if(resultArr.size()==0){
348 | mPickerPromise.reject("cancel","cancel");
349 | }else{
350 | mPickerPromise.resolve(resultArr);
351 | }
352 | }
353 | }
354 | })
355 | .openGallery();
356 | } else {
357 | rxGalleryFinal
358 | .multiple()
359 | .maxSize(maxSize)
360 | .subscribe(new RxBusResultDisposable() {
361 | @Override
362 | protected void onEvent(ImageMultipleResultEvent imageMultipleResultEvent) throws Exception {
363 | List list = imageMultipleResultEvent.getResult();
364 | WritableArray resultArr = new WritableNativeArray();
365 | for(MediaBean bean:list){
366 | if(bean!=null)
367 | resultArr.pushMap(getAsyncSelection(activity,bean));
368 | }
369 | if(resultArr.size()==0){
370 | mPickerPromise.reject("cancel","cancel");
371 | }else{
372 | mPickerPromise.resolve(resultArr);
373 | }
374 | }
375 |
376 | @Override
377 | public void onComplete() {
378 | super.onComplete();
379 | }
380 | })
381 | .openGallery();
382 | }
383 | }
384 |
385 | @ReactMethod
386 | public void clean(final Promise promise) {
387 | String path = MediaGridFragment.getImageStoreDirByStr();
388 | File file = new File(path);
389 |
390 | deleteRecursive(file);
391 | promise.resolve(null);
392 | }
393 |
394 | @ReactMethod
395 | public void openCamera(final ReadableMap options, final Promise promise) {
396 | {
397 | final Activity activity = getCurrentActivity();
398 |
399 | if (activity == null) {
400 | promise.reject(E_ACTIVITY_DOES_NOT_EXIST, "Activity doesn't exist");
401 | return;
402 | }
403 |
404 | setConfiguration(options);
405 | initImageLoader(activity);
406 | mPickerPromise = promise;
407 |
408 | RxGalleryFinal rxGalleryFinal = RxGalleryFinal.with(activity);
409 | rxGalleryFinal.setVideoQuality(videoQuality);
410 | if(compressQuality>0){
411 | rxGalleryFinal.cropropCompressionQuality(compressQuality);
412 | }
413 | rxGalleryFinal.openCameraOnStart();
414 | rxGalleryFinal.returnAfterShot();
415 | if(isVideo){
416 | rxGalleryFinal.video();
417 | }else {
418 | rxGalleryFinal.image();
419 | }
420 | if(isSelectBoth){
421 | rxGalleryFinal.selectBoth();
422 | }
423 | if(isPlayGif){
424 | rxGalleryFinal.gif();
425 | }
426 | if (imageLoader != null){
427 | switch (imageLoader){
428 | case "PICASSO":
429 | rxGalleryFinal.imageLoader(ImageLoaderType.PICASSO);
430 | break;
431 | case "GLIDE":
432 | rxGalleryFinal.imageLoader(ImageLoaderType.GLIDE);
433 | break;
434 | case "FRESCO":
435 | rxGalleryFinal.imageLoader(ImageLoaderType.FRESCO);
436 | break;
437 | case "UNIVERSAL":
438 | rxGalleryFinal.imageLoader(ImageLoaderType.UNIVERSAL);
439 | break;
440 | default:
441 | break;
442 | }
443 | }else{
444 | rxGalleryFinal.imageLoader(ImageLoaderType.GLIDE);
445 | }
446 | if(!this.multiple) {
447 | if(cropping){
448 | rxGalleryFinal.crop();
449 | initCrop(rxGalleryFinal);
450 | //裁剪图片的回调
451 | RxGalleryListener
452 | .getInstance()
453 | .setRadioImageCheckedListener(
454 | new IRadioImageCheckedListener() {
455 | @Override
456 | public void cropAfter(Object t) {
457 | WritableArray resultArr = new WritableNativeArray();
458 | try {
459 | if(t!=null)
460 | resultArr.pushMap(getAsyncSelection(activity,t.toString()));
461 | } catch (Exception e) {
462 | e.printStackTrace();
463 | }
464 | if(resultArr.size()==0){
465 | mPickerPromise.reject("cancel","cancel");
466 | }else{
467 | mPickerPromise.resolve(resultArr);
468 | }
469 | }
470 |
471 | @Override
472 | public boolean isActivityFinish() {
473 | return true;
474 | }
475 | });
476 | }
477 | rxGalleryFinal
478 | .radio()
479 | .subscribe(new RxBusResultDisposable() {
480 | @Override
481 | protected void onEvent(ImageRadioResultEvent imageRadioResultEvent) throws Exception {
482 | if(!cropping){
483 | ImageCropBean result = imageRadioResultEvent.getResult();
484 | WritableArray resultArr = new WritableNativeArray();
485 | if(result!=null)
486 | resultArr.pushMap(getAsyncSelection(activity,result));
487 | if(resultArr.size()==0){
488 | mPickerPromise.reject("cancel","cancel");
489 | }else{
490 | mPickerPromise.resolve(resultArr);
491 | }
492 | }
493 | }
494 | })
495 | .openGallery();
496 | }
497 | }
498 | }
499 | private String getBase64StringFromFile(String absoluteFilePath) {
500 | InputStream inputStream;
501 |
502 | try {
503 | inputStream = new FileInputStream(new File(absoluteFilePath));
504 | } catch (FileNotFoundException e) {
505 | e.printStackTrace();
506 | return null;
507 | }
508 |
509 | byte[] bytes;
510 | byte[] buffer = new byte[8192];
511 | int bytesRead;
512 | ByteArrayOutputStream output = new ByteArrayOutputStream();
513 |
514 | try {
515 | while ((bytesRead = inputStream.read(buffer)) != -1) {
516 | output.write(buffer, 0, bytesRead);
517 | }
518 | } catch (IOException e) {
519 | e.printStackTrace();
520 | }
521 |
522 | bytes = output.toByteArray();
523 | return Base64.encodeToString(bytes, Base64.NO_WRAP);
524 | }
525 |
526 |
527 | private BitmapFactory.Options validateImage(String path) throws Exception {
528 | BitmapFactory.Options options = new BitmapFactory.Options();
529 | options.inJustDecodeBounds = true;
530 | options.inPreferredConfig = Bitmap.Config.RGB_565;
531 | options.inDither = true;
532 |
533 | BitmapFactory.decodeFile(path, options);
534 |
535 | if (options.outMimeType == null || options.outWidth == 0 || options.outHeight == 0) {
536 | throw new Exception("Invalid image selected");
537 | }
538 |
539 | return options;
540 | }
541 |
542 | private Bitmap validateVideo(String path) throws Exception {
543 | MediaMetadataRetriever retriever = new MediaMetadataRetriever();
544 | retriever.setDataSource(path);
545 | Bitmap bmp = retriever.getFrameAtTime();
546 |
547 | if (bmp == null) {
548 | throw new Exception("Cannot retrieve video data");
549 | }
550 |
551 | return bmp;
552 | }
553 |
554 | private void deleteRecursive(File fileOrDirectory) {
555 | if (fileOrDirectory.isDirectory()) {
556 | for (File child : fileOrDirectory.listFiles()) {
557 | deleteRecursive(child);
558 | }
559 | }
560 |
561 | fileOrDirectory.delete();
562 | }
563 | }
564 |
--------------------------------------------------------------------------------
/android/src/main/java/com/mg/app/PickerPackage.java:
--------------------------------------------------------------------------------
1 | package com.mg.app;
2 |
3 | import com.facebook.react.ReactPackage;
4 | import com.facebook.react.bridge.JavaScriptModule;
5 | import com.facebook.react.bridge.NativeModule;
6 | import com.facebook.react.bridge.ReactApplicationContext;
7 | import com.facebook.react.uimanager.ViewManager;
8 |
9 | import java.util.ArrayList;
10 | import java.util.Collections;
11 | import java.util.List;
12 |
13 | public class PickerPackage implements ReactPackage {
14 |
15 | public List> createJSModules() {
16 | return Collections.emptyList();
17 | }
18 |
19 | public List createViewManagers(ReactApplicationContext reactContext) {
20 | return Collections.emptyList();
21 | }
22 |
23 | public List createNativeModules(ReactApplicationContext reactContext) {
24 | List modules = new ArrayList<>();
25 | modules.add(new PickerModule(reactContext));
26 |
27 | return modules;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/android/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shijingsh/react-native-customized-image-picker/7335f7acde315eec10cf5fa3b5620d048e8b6b91/android/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | #FFFFFF
6 | #FFFFFF
7 | #000000
8 |
9 | #2196F3
10 | #1976D2
11 | #FF4081
12 |
13 | #FF5722
14 | #E64A19
15 | #FF4081
16 |
17 | #00BCD4
18 | #0097A7
19 | #FF4081
20 |
21 | #4CAF50
22 | #388E3C
23 | #FF4081
24 |
25 | #009688
26 | #00796B
27 | #FF4081
28 |
29 | #da4336
30 | #b93221
31 | #3D5AFE
32 |
33 | #E91E63
34 | #C2185B
35 | #3D5AFE
36 |
37 | #673AB7
38 | #512DA8
39 | #FF4081
40 |
41 | #616161
42 | #424242
43 | #00E676
44 |
45 | #2A2A2F
46 | #151515
47 | #FF4081
48 |
49 | #785447
50 | #5C3F36
51 | #785447
52 |
53 |
--------------------------------------------------------------------------------
/android/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | app_name
3 |
4 |
--------------------------------------------------------------------------------
/android/src/main/res/values/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
11 |
12 |
20 |
21 |
27 |
28 |
33 |
34 |
39 |
40 |
45 |
46 |
51 |
52 |
57 |
58 |
63 |
64 |
69 |
70 |
75 |
76 |
81 |
82 |
87 |
88 |
--------------------------------------------------------------------------------
/images/pic.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shijingsh/react-native-customized-image-picker/7335f7acde315eec10cf5fa3b5620d048e8b6b91/images/pic.png
--------------------------------------------------------------------------------
/images/pic2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shijingsh/react-native-customized-image-picker/7335f7acde315eec10cf5fa3b5620d048e8b6b91/images/pic2.png
--------------------------------------------------------------------------------
/images/pic3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shijingsh/react-native-customized-image-picker/7335f7acde315eec10cf5fa3b5620d048e8b6b91/images/pic3.png
--------------------------------------------------------------------------------
/index.android.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shijingsh/react-native-customized-image-picker/7335f7acde315eec10cf5fa3b5620d048e8b6b91/index.android.js
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import {NativeModules} from 'react-native';
4 | export default NativeModules.ImageCropPicker;
5 |
--------------------------------------------------------------------------------
/ios/ImageCropPicker.h:
--------------------------------------------------------------------------------
1 |
2 | #if __has_include("RCTBridgeModule.h")
3 | #import "RCTBridgeModule.h"
4 | #else
5 | #import
6 | #endif
7 | #import
8 | #import "TZImagePickerController.h"
9 | @interface ImageCropPicker : NSObject
10 |
11 | @end
12 |
13 |
--------------------------------------------------------------------------------
/ios/ImageCropPicker.m:
--------------------------------------------------------------------------------
1 |
2 | #import "ImageCropPicker.h"
3 |
4 | #import "TZImageManager.h"
5 | #import "NSDictionary+SYSafeConvert.h"
6 | #import "TZImageCropManager.h"
7 | #import
8 | #import
9 |
10 | @interface ImageCropPicker ()
11 |
12 | @property (nonatomic, strong) UIImagePickerController *imagePickerVc;
13 | @property (nonatomic, strong) NSDictionary *cameraOptions;
14 | /**
15 | 保存Promise的resolve block
16 | */
17 | @property (nonatomic, copy) RCTPromiseResolveBlock resolveBlock;
18 | /**
19 | 保存Promise的reject block
20 | */
21 | @property (nonatomic, copy) RCTPromiseRejectBlock rejectBlock;
22 |
23 | /**
24 | 保存选中的图片数组
25 | */
26 | @property (nonatomic, strong) NSMutableArray *selectedAssets;
27 | @end
28 |
29 | @implementation ImageCropPicker
30 |
31 | - (instancetype)init {
32 | self = [super init];
33 | if (self) {
34 | _selectedAssets = [NSMutableArray array];
35 | }
36 | return self;
37 | }
38 |
39 | - (void)dealloc {
40 | _selectedAssets = nil;
41 | }
42 |
43 | RCT_EXPORT_MODULE()
44 |
45 |
46 | RCT_REMAP_METHOD(openPicker,
47 | options:(NSDictionary *)options
48 | showImagePickerResolver:(RCTPromiseResolveBlock)resolve
49 | rejecter:(RCTPromiseRejectBlock)reject) {
50 | self.cameraOptions = options;
51 | self.resolveBlock = resolve;
52 | self.rejectBlock = reject;
53 |
54 | BOOL isVideo = [options sy_boolForKey:@"isVideo"] || NO;
55 | if(!isVideo){
56 | [self openImagePicker];
57 | }else{
58 | [self openVideoPicker];
59 | }
60 |
61 |
62 | }
63 |
64 | RCT_REMAP_METHOD(openCamera,
65 | options:(NSDictionary *)options
66 | openCameraResolver:(RCTPromiseResolveBlock)resolve
67 | rejecter:(RCTPromiseRejectBlock)reject) {
68 | self.cameraOptions = options;
69 | self.resolveBlock = resolve;
70 | self.rejectBlock = reject;
71 | [self takePhoto];
72 | }
73 |
74 | RCT_REMAP_METHOD(clean,
75 | cleanResolver:(RCTPromiseResolveBlock)resolve
76 | rejecter:(RCTPromiseRejectBlock)reject) {
77 | NSFileManager *fileManager = [NSFileManager defaultManager];
78 | [fileManager removeItemAtPath: [NSString stringWithFormat:@"%@ImageCropPicker", NSTemporaryDirectory()] error:nil];
79 |
80 | resolve(nil);
81 | }
82 |
83 |
84 | - (void)openVideoPicker {
85 | NSInteger maxSize = [self.cameraOptions sy_integerForKey:@"maxSize"];
86 | BOOL isCamera = [self.cameraOptions sy_boolForKey:@"isCamera"];
87 | BOOL cropping = [self.cameraOptions sy_boolForKey:@"cropping"];
88 | BOOL isGif = [self.cameraOptions sy_boolForKey:@"isGif"];
89 | BOOL allowPickingVideo = [self.cameraOptions sy_boolForKey:@"allowPickingVideo"];
90 | BOOL allowPickingMultipleVideo = [self.cameraOptions sy_boolForKey:@"allowPickingMultipleVideo"];
91 | BOOL allowPickingImage = [self.cameraOptions sy_boolForKey:@"allowPickingImage"];
92 | BOOL allowTakeVideo = [self.cameraOptions sy_boolForKey:@"allowTakeVideo"];
93 | BOOL showCropCircle = [self.cameraOptions sy_boolForKey:@"showCropCircle"];
94 | BOOL isRecordSelected = [self.cameraOptions sy_boolForKey:@"isRecordSelected"];
95 | BOOL allowPickingOriginalPhoto = [self.cameraOptions sy_boolForKey:@"allowPickingOriginalPhoto"];
96 | BOOL sortAscendingByModificationDate = [self.cameraOptions sy_boolForKey:@"sortAscendingByModificationDate"];
97 | BOOL showSelectedIndex = [self.cameraOptions sy_boolForKey:@"showSelectedIndex"];
98 | BOOL multiple = [self.cameraOptions sy_boolForKey:@"multiple"];
99 | NSInteger CropW = [self.cameraOptions sy_integerForKey:@"CropW"];
100 | NSInteger CropH = [self.cameraOptions sy_integerForKey:@"CropH"];
101 | NSInteger circleCropRadius = [self.cameraOptions sy_integerForKey:@"circleCropRadius"];
102 | NSInteger videoMaximumDuration = [self.cameraOptions sy_integerForKey:@"videoMaximumDuration"];
103 | NSInteger compressQuality = [self.cameraOptions sy_integerForKey:@"compressQuality"];
104 | BOOL isVideo = YES;
105 | if(multiple){
106 | maxSize = maxSize?maxSize:9;
107 | }else{
108 | maxSize = maxSize?maxSize:1;
109 | }
110 | if(!CropW){
111 | CropW = [self.cameraOptions sy_integerForKey:@"width"];
112 | }
113 | if(!CropW){
114 | CropW = 300;
115 | }
116 | if(!CropH){
117 | CropH = [self.cameraOptions sy_integerForKey:@"height"];
118 | }
119 | if(!CropH){
120 | CropH = 300;
121 | }
122 | TZImagePickerController *imagePickerVc = [[TZImagePickerController alloc] initWithMaxImagesCount:maxSize delegate:self];
123 |
124 | imagePickerVc.maxImagesCount = maxSize;
125 | imagePickerVc.allowPickingGif = isGif; // 允许GIF
126 | imagePickerVc.allowTakePicture = isCamera; // 允许用户在内部拍照
127 | imagePickerVc.allowPickingVideo = isVideo; // 不允许视频
128 | imagePickerVc.allowPickingImage = allowPickingImage;
129 | imagePickerVc.allowTakeVideo = isCamera; // 允许拍摄视频
130 | imagePickerVc.videoMaximumDuration = videoMaximumDuration;
131 | imagePickerVc.allowPickingMultipleVideo = multiple;
132 | imagePickerVc.allowPickingOriginalPhoto = allowPickingOriginalPhoto; // 允许原图
133 | imagePickerVc.sortAscendingByModificationDate = sortAscendingByModificationDate;
134 | imagePickerVc.alwaysEnableDoneBtn = YES;
135 | imagePickerVc.allowCrop = cropping; // 裁剪
136 | imagePickerVc.autoDismiss = NO;
137 | imagePickerVc.showSelectedIndex = showSelectedIndex;
138 | imagePickerVc.modalPresentationStyle = UIModalPresentationFullScreen;
139 |
140 | if (isRecordSelected) {
141 | imagePickerVc.selectedAssets = self.selectedAssets; // 当前已选中的图片
142 | }
143 |
144 | if (maxSize == 1) {
145 | // 单选模式
146 | imagePickerVc.showSelectBtn = NO;
147 |
148 | if(cropping){
149 | if(showCropCircle) {
150 | imagePickerVc.needCircleCrop = showCropCircle; //圆形裁剪
151 | imagePickerVc.circleCropRadius = circleCropRadius; //圆形半径
152 | } else {
153 | CGFloat x = ([[UIScreen mainScreen] bounds].size.width - CropW) / 2;
154 | CGFloat y = ([[UIScreen mainScreen] bounds].size.height - CropH) / 2;
155 | imagePickerVc.cropRect = CGRectMake(x,y,CropW,CropH);
156 | }
157 | }
158 | }
159 |
160 |
161 | __weak TZImagePickerController *weakPicker = imagePickerVc;
162 | // Documentation source = through comments in TZImagePicker.
163 | // If multiple videos are selected, this callback is called.
164 | [imagePickerVc setDidFinishPickingPhotosWithInfosHandle:^(NSArray *photos,NSArray *assets,BOOL isSelectOriginalPhoto,NSArray *infos) {
165 | [weakPicker showProgressHUD];
166 | [self handleAssets:assets photos:photos compressQuality:compressQuality isSelectOriginalPhoto:isSelectOriginalPhoto completion:^(NSArray *selecteds) {
167 |
168 | [self invokeSuccessWithResult:selecteds];
169 | [weakPicker dismissViewControllerAnimated:YES completion:nil];
170 | [weakPicker hideProgressHUD];
171 | } fail:^(NSError *error) {
172 | [weakPicker dismissViewControllerAnimated:YES completion:nil];
173 | [weakPicker hideProgressHUD];
174 | }];
175 | }];
176 |
177 |
178 | // This callback is called only when picking SINGLE video.
179 | [imagePickerVc setDidFinishPickingVideoHandle:^(UIImage *coverImage, PHAsset *asset) {
180 | [weakPicker showProgressHUD];
181 | // TODO: someshow change presetName based on provided compressQuality?
182 | // Does it even matter here?
183 | [[TZImageManager manager] getVideoOutputPathWithAsset:asset presetName:AVAssetExportPresetHighestQuality success:^(NSString *outputPath) {
184 | NSLog(@"视频导出成功:%@", outputPath);
185 | [self invokeSuccessWithResult:@[[self handleVideoData:outputPath asset:asset coverImage:coverImage compressQuality:compressQuality]]];
186 | [weakPicker dismissViewControllerAnimated:YES completion:nil];
187 | [weakPicker hideProgressHUD];
188 | } failure:^(NSString *errorMessage, NSError *error) {
189 | NSLog(@"视频导出失败:%@,error:%@",errorMessage, error);
190 | [weakPicker dismissViewControllerAnimated:YES completion:nil];
191 | [weakPicker hideProgressHUD];
192 | }];
193 | }];
194 |
195 | [imagePickerVc setImagePickerControllerDidCancelHandle:^{
196 | [weakPicker dismissViewControllerAnimated:YES completion:nil];
197 | [self invokeError];
198 | [weakPicker hideProgressHUD];
199 | }];
200 |
201 | [[self topViewController] presentViewController:imagePickerVc animated:YES completion:nil];
202 | }
203 |
204 | - (void)openImagePicker {
205 | // 照片最大可选张数
206 | NSInteger maxSize = [self.cameraOptions sy_integerForKey:@"maxSize"];
207 | // 显示内部拍照按钮
208 | BOOL isCamera = [self.cameraOptions sy_boolForKey:@"isCamera"];
209 | BOOL cropping = [self.cameraOptions sy_boolForKey:@"cropping"];
210 | BOOL isGif = [self.cameraOptions sy_boolForKey:@"isGif"];
211 | BOOL showCropCircle = [self.cameraOptions sy_boolForKey:@"showCropCircle"];
212 | BOOL isRecordSelected = [self.cameraOptions sy_boolForKey:@"isRecordSelected"];
213 | BOOL allowPickingOriginalPhoto = [self.cameraOptions sy_boolForKey:@"allowPickingOriginalPhoto"];
214 | BOOL allowPickingMultipleVideo = [self.cameraOptions sy_boolForKey:@"allowPickingMultipleVideo"];
215 | BOOL sortAscendingByModificationDate = [self.cameraOptions sy_boolForKey:@"sortAscendingByModificationDate"];
216 | BOOL showSelectedIndex = [self.cameraOptions sy_boolForKey:@"showSelectedIndex"];
217 | BOOL multiple = [self.cameraOptions sy_boolForKey:@"multiple"];
218 | NSInteger CropW = [self.cameraOptions sy_integerForKey:@"CropW"];
219 | NSInteger CropH = [self.cameraOptions sy_integerForKey:@"CropH"];
220 | NSInteger circleCropRadius = [self.cameraOptions sy_integerForKey:@"circleCropRadius"];
221 | NSInteger compressQuality = [self.cameraOptions sy_integerForKey:@"compressQuality"];
222 | if(multiple){
223 | maxSize = maxSize?maxSize:9;
224 | }else{
225 | maxSize = maxSize?maxSize:1;
226 | }
227 | if(!CropW){
228 | CropW = [self.cameraOptions sy_integerForKey:@"width"];
229 | }
230 | if(!CropW){
231 | CropW = 300;
232 | }
233 | if(!CropH){
234 | CropH = [self.cameraOptions sy_integerForKey:@"height"];
235 | }
236 | if(!CropH){
237 | CropH = 300;
238 | }
239 | TZImagePickerController *imagePickerVc = [[TZImagePickerController alloc] initWithMaxImagesCount:maxSize delegate:self];
240 |
241 | imagePickerVc.maxImagesCount = maxSize;
242 | imagePickerVc.allowPickingGif = isGif; // 允许GIF
243 | imagePickerVc.allowTakePicture = isCamera; // 允许用户在内部拍照
244 | imagePickerVc.allowPickingVideo = NO; // 不允许视频
245 | imagePickerVc.showSelectedIndex = showSelectedIndex;
246 | imagePickerVc.allowPickingOriginalPhoto = allowPickingOriginalPhoto; // 允许原图
247 | imagePickerVc.sortAscendingByModificationDate = sortAscendingByModificationDate;
248 | imagePickerVc.alwaysEnableDoneBtn = YES;
249 | imagePickerVc.allowPickingMultipleVideo = isGif ? YES : allowPickingMultipleVideo;
250 | imagePickerVc.allowCrop = cropping; // 裁剪
251 | imagePickerVc.modalPresentationStyle = UIModalPresentationFullScreen;
252 |
253 | if (isRecordSelected) {
254 | imagePickerVc.selectedAssets = self.selectedAssets; // 当前已选中的图片
255 | }
256 |
257 | if (maxSize == 1) {
258 | // 单选模式
259 | imagePickerVc.showSelectBtn = NO;
260 |
261 | if(cropping){
262 | if(showCropCircle) {
263 | imagePickerVc.needCircleCrop = showCropCircle; //圆形裁剪
264 | imagePickerVc.circleCropRadius = circleCropRadius; //圆形半径
265 | } else {
266 | CGFloat x = ([[UIScreen mainScreen] bounds].size.width - CropW) / 2;
267 | CGFloat y = ([[UIScreen mainScreen] bounds].size.height - CropH) / 2;
268 | imagePickerVc.cropRect = CGRectMake(x,y,CropW,CropH);
269 | }
270 | }
271 | }
272 |
273 | __weak TZImagePickerController *weakPicker = imagePickerVc;
274 | [imagePickerVc setDidFinishPickingPhotosWithInfosHandle:^(NSArray *photos,NSArray *assets,BOOL isSelectOriginalPhoto,NSArray *infos) {
275 | if (isRecordSelected) {
276 | self.selectedAssets = [NSMutableArray arrayWithArray:assets];
277 | }
278 | [weakPicker showProgressHUD];
279 | if (maxSize == 1 && cropping) {
280 | [self invokeSuccessWithResult:@[[self handleCropImage:photos[0] phAsset:assets[0] compressQuality:compressQuality]]];
281 | } else {
282 | [infos enumerateObjectsUsingBlock:^(NSDictionary * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
283 | [self handleAssets:assets photos:photos compressQuality:compressQuality isSelectOriginalPhoto:isSelectOriginalPhoto completion:^(NSArray *selecteds) {
284 | [self invokeSuccessWithResult:selecteds];
285 | } fail:^(NSError *error) {
286 |
287 | }];
288 | }];
289 | }
290 | [weakPicker hideProgressHUD];
291 | }];
292 |
293 | __weak TZImagePickerController *weakPickerVc = imagePickerVc;
294 | [imagePickerVc setImagePickerControllerDidCancelHandle:^{
295 | [self invokeError];
296 | [weakPickerVc hideProgressHUD];
297 | }];
298 |
299 | [[self topViewController] presentViewController:imagePickerVc animated:YES completion:nil];
300 | }
301 |
302 | - (UIImagePickerController *)imagePickerVc {
303 | if (_imagePickerVc == nil) {
304 | _imagePickerVc = [[UIImagePickerController alloc] init];
305 | _imagePickerVc.delegate = self;
306 | }
307 | return _imagePickerVc;
308 | }
309 |
310 | #pragma mark - UIImagePickerController
311 | - (void)takePhoto {
312 | AVAuthorizationStatus authStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
313 | NSString * cancelStr = [NSBundle tz_localizedStringForKey:@"Cancel"];
314 | NSString * settingStr = [NSBundle tz_localizedStringForKey:@"Setting"];
315 |
316 | NSDictionary *infoDict = [TZCommonTools tz_getInfoDictionary];
317 | NSString *appName = [infoDict valueForKey:@"CFBundleDisplayName"];
318 | if (!appName) appName = [infoDict valueForKey:@"CFBundleName"];
319 | if (!appName) appName = [infoDict valueForKey:@"CFBundleExecutable"];
320 |
321 | NSString *title = [NSBundle tz_localizedStringForKey:@"Can not use camera"];
322 | NSString *message = [NSString stringWithFormat:[NSBundle tz_localizedStringForKey:@"Please allow %@ to access your camera in \"Settings -> Privacy -> Camera\""],appName];
323 |
324 | if (authStatus == AVAuthorizationStatusRestricted || authStatus == AVAuthorizationStatusDenied) {
325 | // 无相机权限 做一个友好的提示
326 |
327 | UIAlertView * alert = [[UIAlertView alloc]initWithTitle:title message:message delegate:self cancelButtonTitle:cancelStr otherButtonTitles:settingStr, nil];
328 | [alert show];
329 | } else if (authStatus == AVAuthorizationStatusNotDetermined) {
330 | // fix issue 466, 防止用户首次拍照拒绝授权时相机页黑屏
331 | [AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {
332 | if (granted) {
333 | dispatch_async(dispatch_get_main_queue(), ^{
334 | [self takePhoto];
335 | });
336 | }
337 | }];
338 | // 拍照之前还需要检查相册权限
339 | } else if ([PHPhotoLibrary authorizationStatus] == 2) { // 已被拒绝,没有相册权限,将无法保存拍的照片
340 | UIAlertView * alert = [[UIAlertView alloc]initWithTitle:title message:message delegate:self cancelButtonTitle:cancelStr otherButtonTitles:settingStr, nil];
341 | [alert show];
342 | } else if ([PHPhotoLibrary authorizationStatus] == 0) { // 未请求过相册权限
343 | [[TZImageManager manager] requestAuthorizationWithCompletion:^{
344 | [self takePhoto];
345 | }];
346 | } else {
347 | [self pushImagePickerController];
348 | }
349 | }
350 |
351 | // 调用相机
352 | - (void)pushImagePickerController {
353 | UIImagePickerControllerSourceType sourceType = UIImagePickerControllerSourceTypeCamera;
354 | if ([UIImagePickerController isSourceTypeAvailable: UIImagePickerControllerSourceTypeCamera]) {
355 | self.imagePickerVc.sourceType = sourceType;
356 | [[self topViewController] presentViewController:self.imagePickerVc animated:YES completion:nil];
357 | } else {
358 | NSLog(@"模拟器中无法打开照相机,请在真机中使用");
359 | }
360 | }
361 |
362 | - (void)imagePickerController:(UIImagePickerController*)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
363 | [picker dismissViewControllerAnimated:YES completion:^{
364 | NSString *type = [info objectForKey:UIImagePickerControllerMediaType];
365 | if ([type isEqualToString:@"public.image"]) {
366 |
367 | TZImagePickerController *tzImagePickerVc = [[TZImagePickerController alloc] initWithMaxImagesCount:1 delegate:nil];
368 | tzImagePickerVc.sortAscendingByModificationDate = NO;
369 | [tzImagePickerVc showProgressHUD];
370 | UIImage *image = [info objectForKey:UIImagePickerControllerOriginalImage];
371 |
372 | // save photo and get asset / 保存图片,获取到asset
373 | [[TZImageManager manager] savePhotoWithImage:image location:NULL completion:^(PHAsset *asset, NSError *error){
374 | if (error) {
375 | [tzImagePickerVc hideProgressHUD];
376 | NSLog(@"图片保存失败 %@",error);
377 | } else {
378 | [tzImagePickerVc hideProgressHUD];
379 |
380 | TZAssetModel *assetModel = [[TZImageManager manager] createModelWithAsset:asset];
381 | BOOL cropping = [self.cameraOptions sy_boolForKey:@"cropping"];
382 | BOOL showCropCircle = [self.cameraOptions sy_boolForKey:@"showCropCircle"];
383 | NSInteger CropW = [self.cameraOptions sy_integerForKey:@"CropW"];
384 | NSInteger CropH = [self.cameraOptions sy_integerForKey:@"CropH"];
385 | NSInteger circleCropRadius = [self.cameraOptions sy_integerForKey:@"circleCropRadius"];
386 | NSInteger compressQuality = [self.cameraOptions sy_integerForKey:@"compressQuality"];
387 |
388 | if (cropping) {
389 | TZImagePickerController *imagePicker = [[TZImagePickerController alloc] initCropTypeWithAsset:assetModel.asset photo:image completion:^(UIImage *cropImage, id asset) {
390 | [self invokeSuccessWithResult:@[[self handleCropImage:cropImage phAsset:asset compressQuality:compressQuality]]];
391 | }];
392 | imagePicker.allowPickingImage = YES;
393 | if(showCropCircle) {
394 | imagePicker.needCircleCrop = showCropCircle; //圆形裁剪
395 | imagePicker.circleCropRadius = circleCropRadius; //圆形半径
396 | } else {
397 | CGFloat x = ([[UIScreen mainScreen] bounds].size.width - CropW) / 2;
398 | CGFloat y = ([[UIScreen mainScreen] bounds].size.height - CropH) / 2;
399 | imagePicker.cropRect = CGRectMake(x,y,CropW,CropH);
400 | }
401 | [[self topViewController] presentViewController:imagePicker animated:YES completion:nil];
402 | } else {
403 | [self invokeSuccessWithResult:@[[self handleCropImage:image phAsset:asset compressQuality:compressQuality]]];
404 | }
405 | }
406 | }];
407 | }
408 | }];
409 | }
410 |
411 | - (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker {
412 | [self invokeError];
413 | if ([picker isKindOfClass:[UIImagePickerController class]]) {
414 | [picker dismissViewControllerAnimated:YES completion:nil];
415 | }
416 | }
417 |
418 | #pragma mark - UIAlertViewDelegate
419 | - (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
420 | if (buttonIndex == 1) { // 去设置界面,开启相机访问权限
421 | [[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]];
422 | }
423 | }
424 |
425 | - (BOOL)isAssetCanSelect:(PHAsset *)asset {
426 | BOOL allowPickingGif = [self.cameraOptions sy_boolForKey:@"isGif"];
427 | BOOL isGIF = [[TZImageManager manager] getAssetType:asset] == TZAssetModelMediaTypePhotoGif;
428 | if (!allowPickingGif && isGIF) {
429 | return NO;
430 | }
431 | return YES;
432 | }
433 |
434 | /// 异步处理获取图片
435 | - (void)handleAssets:(NSArray *)assets photos:(NSArray*)photos compressQuality:(CGFloat)compressQuality isSelectOriginalPhoto:(BOOL)isSelectOriginalPhoto completion:(void (^)(NSArray *selecteds))completion fail:(void(^)(NSError *error))fail {
436 | NSMutableArray *selectedPhotos = [NSMutableArray array];
437 |
438 | [assets enumerateObjectsUsingBlock:^(PHAsset * _Nonnull asset, NSUInteger idx, BOOL * _Nonnull stop) {
439 | if (asset.mediaType == PHAssetMediaTypeVideo) {
440 | [[TZImageManager manager] getVideoOutputPathWithAsset:asset presetName:AVAssetExportPresetHighestQuality success:^(NSString *outputPath) {
441 | [selectedPhotos addObject:[self handleVideoData:outputPath asset:asset coverImage:photos[idx] compressQuality:compressQuality]];
442 | if ([selectedPhotos count] == [assets count]) {
443 | completion(selectedPhotos);
444 | }
445 | if (idx + 1 == [assets count] && [selectedPhotos count] != [assets count]) {
446 | fail(nil);
447 | }
448 | } failure:^(NSString *errorMessage, NSError *error) {
449 |
450 | }];
451 | } else {
452 | BOOL isGIF = [[TZImageManager manager] getAssetType:asset] == TZAssetModelMediaTypePhotoGif;
453 | if (isGIF || isSelectOriginalPhoto) {
454 | [[TZImageManager manager] requestImageDataForAsset:asset completion:^(NSData *imageData, NSString *dataUTI, UIImageOrientation orientation, NSDictionary *info) {
455 | [selectedPhotos addObject:[self handleOriginalPhotoData:imageData phAsset:asset isGIF:isGIF compressQuality:compressQuality]];
456 | if ([selectedPhotos count] == [assets count]) {
457 | completion(selectedPhotos);
458 | }
459 | if (idx + 1 == [assets count] && [selectedPhotos count] != [assets count]) {
460 | fail(nil);
461 | }
462 | } progressHandler:^(double progress, NSError *error, BOOL *stop, NSDictionary *info) {
463 |
464 | }];
465 | } else {
466 | [selectedPhotos addObject:[self handleCropImage:photos[idx] phAsset:asset compressQuality:compressQuality]];
467 | if ([selectedPhotos count] == [assets count]) {
468 | completion(selectedPhotos);
469 | }
470 | }
471 | }
472 | }];
473 | }
474 |
475 | /// 处理裁剪图片数据
476 | - (NSDictionary *)handleCropImage:(UIImage *)image phAsset:(PHAsset *)phAsset compressQuality:(CGFloat)compressQuality {
477 | [self createDir];
478 |
479 | NSMutableDictionary *photo = [NSMutableDictionary dictionary];
480 | NSString *filename = [NSString stringWithFormat:@"%@%@", [[NSUUID UUID] UUIDString], [phAsset valueForKey:@"filename"]];
481 | NSString *fileExtension = [filename pathExtension];
482 | NSMutableString *filePath = [NSMutableString string];
483 | BOOL isPNG = [fileExtension hasSuffix:@"PNG"] || [fileExtension hasSuffix:@"png"];
484 |
485 | if (isPNG) {
486 | [filePath appendString:[NSString stringWithFormat:@"%@ImageCropPicker/%@", NSTemporaryDirectory(), filename]];
487 | } else {
488 | [filePath appendString:[NSString stringWithFormat:@"%@ImageCropPicker/%@.jpg", NSTemporaryDirectory(), [filename stringByDeletingPathExtension]]];
489 | }
490 |
491 | NSData *writeData = isPNG ? UIImagePNGRepresentation(image) : UIImageJPEGRepresentation(image, compressQuality/100);
492 | [writeData writeToFile:filePath atomically:YES];
493 |
494 | photo[@"path"] = filePath;
495 | photo[@"width"] = @(image.size.width);
496 | photo[@"height"] = @(image.size.height);
497 | NSInteger size = [[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:nil].fileSize;
498 | photo[@"size"] = @(size);
499 | photo[@"mediaType"] = @(phAsset.mediaType);
500 | if ([self.cameraOptions sy_boolForKey:@"includeBase64"]) {
501 | photo[@"data"] = [NSString stringWithFormat:@"%@", [writeData base64EncodedStringWithOptions:0]];
502 | }
503 |
504 | return photo;
505 | }
506 |
507 | /// 处理原图数据
508 | - (NSDictionary *)handleOriginalPhotoData:(NSData *)data phAsset:(PHAsset *)phAsset isGIF:(BOOL)isGIF compressQuality:(CGFloat)compressQuality {
509 | [self createDir];
510 |
511 | NSMutableDictionary *photo = [NSMutableDictionary dictionary];
512 | NSString *filename = [NSString stringWithFormat:@"%@%@", [[NSUUID UUID] UUIDString], [phAsset valueForKey:@"filename"]];
513 | NSString *fileExtension = [filename pathExtension];
514 | UIImage *image = nil;
515 | NSData *writeData = nil;
516 | NSMutableString *filePath = [NSMutableString string];
517 |
518 | BOOL isPNG = [fileExtension hasSuffix:@"PNG"] || [fileExtension hasSuffix:@"png"];
519 |
520 | if (isGIF) {
521 | image = [UIImage sd_tz_animatedGIFWithData:data];
522 | writeData = data;
523 | } else {
524 | image = [UIImage imageWithData: data];
525 | writeData = isPNG ? UIImagePNGRepresentation(image) : UIImageJPEGRepresentation(image, compressQuality/100);
526 | }
527 |
528 | if (isPNG || isGIF) {
529 | [filePath appendString:[NSString stringWithFormat:@"%@ImageCropPicker/%@", NSTemporaryDirectory(), filename]];
530 | } else {
531 | [filePath appendString:[NSString stringWithFormat:@"%@ImageCropPicker/%@.jpg", NSTemporaryDirectory(), [filename stringByDeletingPathExtension]]];
532 | }
533 |
534 | [writeData writeToFile:filePath atomically:YES];
535 |
536 | photo[@"path"] = filePath;
537 | photo[@"width"] = @(image.size.width);
538 | photo[@"height"] = @(image.size.height);
539 | NSInteger size = [[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:nil].fileSize;
540 | photo[@"size"] = @(size);
541 | photo[@"mediaType"] = @(phAsset.mediaType);
542 | if ([self.cameraOptions sy_boolForKey:@"includeBase64"] && !isGIF) {
543 | photo[@"data"] = [NSString stringWithFormat:@"data:image/jpeg;base64,%@", [writeData base64EncodedStringWithOptions:0]];
544 | }
545 |
546 | return photo;
547 | }
548 |
549 | /// 处理视频数据
550 | - (NSDictionary *)handleVideoData:(NSString *)outputPath asset:(PHAsset *)asset coverImage:(UIImage *)coverImage compressQuality:(CGFloat)compressQuality {
551 | NSMutableDictionary *video = [NSMutableDictionary dictionary];
552 | video[@"path"] = outputPath;
553 | video[@"fileName"] = [asset valueForKey:@"filename"];
554 | NSInteger size = [[NSFileManager defaultManager] attributesOfItemAtPath:outputPath error:nil].fileSize;
555 | video[@"size"] = @(size);
556 | video[@"duration"] = @(asset.duration);
557 | video[@"width"] = @(asset.pixelWidth);
558 | video[@"height"] = @(asset.pixelHeight);
559 | video[@"type"] = @"video";
560 | video[@"mime"] = @"video/mp4";
561 | // iOS only
562 | video[@"coverUri"] = [self handleCropImage:coverImage phAsset:asset compressQuality:compressQuality][@"path"];
563 | video[@"favorite"] = @(asset.favorite);
564 | video[@"mediaType"] = @(asset.mediaType);
565 |
566 | return video;
567 | }
568 |
569 | /// 创建SyanImageCaches缓存目录
570 | - (BOOL)createDir {
571 | NSString * path = [NSString stringWithFormat:@"%@ImageCropPicker", NSTemporaryDirectory()];;
572 | NSFileManager *fileManager = [NSFileManager defaultManager];
573 | BOOL isDir;
574 | if (![fileManager fileExistsAtPath:path isDirectory:&isDir]) {
575 | //先判断目录是否存在,不存在才创建
576 | BOOL res = [fileManager createDirectoryAtPath:path withIntermediateDirectories:YES attributes:nil error:nil];
577 | return res;
578 | } else return NO;
579 | }
580 |
581 |
582 | - (void)invokeSuccessWithResult:(NSArray *)photos {
583 | if (self.resolveBlock) {
584 | self.resolveBlock(photos);
585 | self.resolveBlock = nil;
586 | }
587 | }
588 |
589 | - (void)invokeError {
590 | if (self.rejectBlock) {
591 | self.rejectBlock(@"cancel", @"cancel", nil);
592 | self.rejectBlock = nil;
593 | }
594 | }
595 |
596 | + (BOOL)requiresMainQueueSetup
597 | {
598 | return YES;
599 | }
600 |
601 | - (UIViewController *)topViewController {
602 | UIViewController *rootViewController = RCTPresentedViewController();
603 | return rootViewController;
604 | }
605 |
606 | - (dispatch_queue_t)methodQueue {
607 | return dispatch_get_main_queue();
608 | }
609 |
610 | @end
611 |
--------------------------------------------------------------------------------
/ios/ImageCropPicker.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 23F50D671F3C041A004B3E61 /* NSBundle+TZImagePicker.m in Sources */ = {isa = PBXBuildFile; fileRef = 23F50D4B1F3C0419004B3E61 /* NSBundle+TZImagePicker.m */; };
11 | 23F50D681F3C041A004B3E61 /* TZAssetCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 23F50D4D1F3C0419004B3E61 /* TZAssetCell.m */; };
12 | 23F50D691F3C041A004B3E61 /* TZAssetModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 23F50D4F1F3C0419004B3E61 /* TZAssetModel.m */; };
13 | 23F50D6A1F3C041A004B3E61 /* TZGifPhotoPreviewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 23F50D511F3C0419004B3E61 /* TZGifPhotoPreviewController.m */; };
14 | 23F50D6B1F3C041A004B3E61 /* TZImageCropManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 23F50D531F3C0419004B3E61 /* TZImageCropManager.m */; };
15 | 23F50D6C1F3C041A004B3E61 /* TZImageManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 23F50D551F3C041A004B3E61 /* TZImageManager.m */; };
16 | 23F50D6D1F3C041A004B3E61 /* TZImagePickerController.m in Sources */ = {isa = PBXBuildFile; fileRef = 23F50D581F3C041A004B3E61 /* TZImagePickerController.m */; };
17 | 23F50D6E1F3C041A004B3E61 /* TZLocationManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 23F50D5A1F3C041A004B3E61 /* TZLocationManager.m */; };
18 | 23F50D6F1F3C041A004B3E61 /* TZPhotoPickerController.m in Sources */ = {isa = PBXBuildFile; fileRef = 23F50D5C1F3C041A004B3E61 /* TZPhotoPickerController.m */; };
19 | 23F50D701F3C041A004B3E61 /* TZPhotoPreviewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 23F50D5E1F3C041A004B3E61 /* TZPhotoPreviewCell.m */; };
20 | 23F50D711F3C041A004B3E61 /* TZPhotoPreviewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 23F50D601F3C041A004B3E61 /* TZPhotoPreviewController.m */; };
21 | 23F50D721F3C041A004B3E61 /* TZProgressView.m in Sources */ = {isa = PBXBuildFile; fileRef = 23F50D621F3C041A004B3E61 /* TZProgressView.m */; };
22 | 23F50D731F3C041A004B3E61 /* TZVideoPlayerController.m in Sources */ = {isa = PBXBuildFile; fileRef = 23F50D641F3C041A004B3E61 /* TZVideoPlayerController.m */; };
23 | 23F50D741F3C041A004B3E61 /* UIView+Layout.m in Sources */ = {isa = PBXBuildFile; fileRef = 23F50D661F3C041A004B3E61 /* UIView+Layout.m */; };
24 | B3E7B58A1CC2AC0600A0062D /* ImageCropPicker.m in Sources */ = {isa = PBXBuildFile; fileRef = B3E7B5891CC2AC0600A0062D /* ImageCropPicker.m */; };
25 | EC236E871F978C3B00D528A5 /* NSDictionary+SYSafeConvert.m in Sources */ = {isa = PBXBuildFile; fileRef = EC236E861F978C3B00D528A5 /* NSDictionary+SYSafeConvert.m */; };
26 | /* End PBXBuildFile section */
27 |
28 | /* Begin PBXCopyFilesBuildPhase section */
29 | 58B511D91A9E6C8500147676 /* CopyFiles */ = {
30 | isa = PBXCopyFilesBuildPhase;
31 | buildActionMask = 2147483647;
32 | dstPath = "include/$(PRODUCT_NAME)";
33 | dstSubfolderSpec = 16;
34 | files = (
35 | );
36 | runOnlyForDeploymentPostprocessing = 0;
37 | };
38 | /* End PBXCopyFilesBuildPhase section */
39 |
40 | /* Begin PBXFileReference section */
41 | 134814201AA4EA6300B7C361 /* libImageCropPicker.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libImageCropPicker.a; sourceTree = BUILT_PRODUCTS_DIR; };
42 | 23F50D4A1F3C0419004B3E61 /* NSBundle+TZImagePicker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSBundle+TZImagePicker.h"; sourceTree = ""; };
43 | 23F50D4B1F3C0419004B3E61 /* NSBundle+TZImagePicker.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSBundle+TZImagePicker.m"; sourceTree = ""; };
44 | 23F50D4C1F3C0419004B3E61 /* TZAssetCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TZAssetCell.h; sourceTree = ""; };
45 | 23F50D4D1F3C0419004B3E61 /* TZAssetCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TZAssetCell.m; sourceTree = ""; };
46 | 23F50D4E1F3C0419004B3E61 /* TZAssetModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TZAssetModel.h; sourceTree = ""; };
47 | 23F50D4F1F3C0419004B3E61 /* TZAssetModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TZAssetModel.m; sourceTree = ""; };
48 | 23F50D501F3C0419004B3E61 /* TZGifPhotoPreviewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TZGifPhotoPreviewController.h; sourceTree = ""; };
49 | 23F50D511F3C0419004B3E61 /* TZGifPhotoPreviewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TZGifPhotoPreviewController.m; sourceTree = ""; };
50 | 23F50D521F3C0419004B3E61 /* TZImageCropManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TZImageCropManager.h; sourceTree = ""; };
51 | 23F50D531F3C0419004B3E61 /* TZImageCropManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TZImageCropManager.m; sourceTree = ""; };
52 | 23F50D541F3C041A004B3E61 /* TZImageManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TZImageManager.h; sourceTree = ""; };
53 | 23F50D551F3C041A004B3E61 /* TZImageManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TZImageManager.m; sourceTree = ""; };
54 | 23F50D561F3C041A004B3E61 /* TZImagePickerController.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = TZImagePickerController.bundle; sourceTree = ""; };
55 | 23F50D571F3C041A004B3E61 /* TZImagePickerController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TZImagePickerController.h; sourceTree = ""; };
56 | 23F50D581F3C041A004B3E61 /* TZImagePickerController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TZImagePickerController.m; sourceTree = ""; };
57 | 23F50D591F3C041A004B3E61 /* TZLocationManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TZLocationManager.h; sourceTree = ""; };
58 | 23F50D5A1F3C041A004B3E61 /* TZLocationManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TZLocationManager.m; sourceTree = ""; };
59 | 23F50D5B1F3C041A004B3E61 /* TZPhotoPickerController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TZPhotoPickerController.h; sourceTree = ""; };
60 | 23F50D5C1F3C041A004B3E61 /* TZPhotoPickerController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TZPhotoPickerController.m; sourceTree = ""; };
61 | 23F50D5D1F3C041A004B3E61 /* TZPhotoPreviewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TZPhotoPreviewCell.h; sourceTree = ""; };
62 | 23F50D5E1F3C041A004B3E61 /* TZPhotoPreviewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TZPhotoPreviewCell.m; sourceTree = ""; };
63 | 23F50D5F1F3C041A004B3E61 /* TZPhotoPreviewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TZPhotoPreviewController.h; sourceTree = ""; };
64 | 23F50D601F3C041A004B3E61 /* TZPhotoPreviewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TZPhotoPreviewController.m; sourceTree = ""; };
65 | 23F50D611F3C041A004B3E61 /* TZProgressView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TZProgressView.h; sourceTree = ""; };
66 | 23F50D621F3C041A004B3E61 /* TZProgressView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TZProgressView.m; sourceTree = ""; };
67 | 23F50D631F3C041A004B3E61 /* TZVideoPlayerController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TZVideoPlayerController.h; sourceTree = ""; };
68 | 23F50D641F3C041A004B3E61 /* TZVideoPlayerController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TZVideoPlayerController.m; sourceTree = ""; };
69 | 23F50D651F3C041A004B3E61 /* UIView+Layout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIView+Layout.h"; sourceTree = ""; };
70 | 23F50D661F3C041A004B3E61 /* UIView+Layout.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIView+Layout.m"; sourceTree = ""; };
71 | B3E7B5881CC2AC0600A0062D /* ImageCropPicker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ImageCropPicker.h; sourceTree = ""; };
72 | B3E7B5891CC2AC0600A0062D /* ImageCropPicker.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ImageCropPicker.m; sourceTree = ""; };
73 | EC236E851F978C3B00D528A5 /* NSDictionary+SYSafeConvert.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSDictionary+SYSafeConvert.h"; sourceTree = ""; };
74 | EC236E861F978C3B00D528A5 /* NSDictionary+SYSafeConvert.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSDictionary+SYSafeConvert.m"; sourceTree = ""; };
75 | /* End PBXFileReference section */
76 |
77 | /* Begin PBXFrameworksBuildPhase section */
78 | 58B511D81A9E6C8500147676 /* Frameworks */ = {
79 | isa = PBXFrameworksBuildPhase;
80 | buildActionMask = 2147483647;
81 | files = (
82 | );
83 | runOnlyForDeploymentPostprocessing = 0;
84 | };
85 | /* End PBXFrameworksBuildPhase section */
86 |
87 | /* Begin PBXGroup section */
88 | 134814211AA4EA7D00B7C361 /* Products */ = {
89 | isa = PBXGroup;
90 | children = (
91 | 134814201AA4EA6300B7C361 /* libImageCropPicker.a */,
92 | );
93 | name = Products;
94 | sourceTree = "";
95 | };
96 | 23F50D491F3C0419004B3E61 /* TZImagePickerController */ = {
97 | isa = PBXGroup;
98 | children = (
99 | 23F50D4A1F3C0419004B3E61 /* NSBundle+TZImagePicker.h */,
100 | 23F50D4B1F3C0419004B3E61 /* NSBundle+TZImagePicker.m */,
101 | 23F50D4C1F3C0419004B3E61 /* TZAssetCell.h */,
102 | 23F50D4D1F3C0419004B3E61 /* TZAssetCell.m */,
103 | 23F50D4E1F3C0419004B3E61 /* TZAssetModel.h */,
104 | 23F50D4F1F3C0419004B3E61 /* TZAssetModel.m */,
105 | 23F50D501F3C0419004B3E61 /* TZGifPhotoPreviewController.h */,
106 | 23F50D511F3C0419004B3E61 /* TZGifPhotoPreviewController.m */,
107 | 23F50D521F3C0419004B3E61 /* TZImageCropManager.h */,
108 | 23F50D531F3C0419004B3E61 /* TZImageCropManager.m */,
109 | 23F50D541F3C041A004B3E61 /* TZImageManager.h */,
110 | 23F50D551F3C041A004B3E61 /* TZImageManager.m */,
111 | 23F50D561F3C041A004B3E61 /* TZImagePickerController.bundle */,
112 | 23F50D571F3C041A004B3E61 /* TZImagePickerController.h */,
113 | 23F50D581F3C041A004B3E61 /* TZImagePickerController.m */,
114 | 23F50D591F3C041A004B3E61 /* TZLocationManager.h */,
115 | 23F50D5A1F3C041A004B3E61 /* TZLocationManager.m */,
116 | 23F50D5B1F3C041A004B3E61 /* TZPhotoPickerController.h */,
117 | 23F50D5C1F3C041A004B3E61 /* TZPhotoPickerController.m */,
118 | 23F50D5D1F3C041A004B3E61 /* TZPhotoPreviewCell.h */,
119 | 23F50D5E1F3C041A004B3E61 /* TZPhotoPreviewCell.m */,
120 | 23F50D5F1F3C041A004B3E61 /* TZPhotoPreviewController.h */,
121 | 23F50D601F3C041A004B3E61 /* TZPhotoPreviewController.m */,
122 | 23F50D611F3C041A004B3E61 /* TZProgressView.h */,
123 | 23F50D621F3C041A004B3E61 /* TZProgressView.m */,
124 | 23F50D631F3C041A004B3E61 /* TZVideoPlayerController.h */,
125 | 23F50D641F3C041A004B3E61 /* TZVideoPlayerController.m */,
126 | 23F50D651F3C041A004B3E61 /* UIView+Layout.h */,
127 | 23F50D661F3C041A004B3E61 /* UIView+Layout.m */,
128 | );
129 | path = TZImagePickerController;
130 | sourceTree = "";
131 | };
132 | 58B511D21A9E6C8500147676 = {
133 | isa = PBXGroup;
134 | children = (
135 | EC236E851F978C3B00D528A5 /* NSDictionary+SYSafeConvert.h */,
136 | EC236E861F978C3B00D528A5 /* NSDictionary+SYSafeConvert.m */,
137 | 23F50D491F3C0419004B3E61 /* TZImagePickerController */,
138 | B3E7B5881CC2AC0600A0062D /* ImageCropPicker.h */,
139 | B3E7B5891CC2AC0600A0062D /* ImageCropPicker.m */,
140 | 134814211AA4EA7D00B7C361 /* Products */,
141 | );
142 | sourceTree = "";
143 | };
144 | /* End PBXGroup section */
145 |
146 | /* Begin PBXNativeTarget section */
147 | 58B511DA1A9E6C8500147676 /* ImageCropPicker */ = {
148 | isa = PBXNativeTarget;
149 | buildConfigurationList = 58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "ImageCropPicker" */;
150 | buildPhases = (
151 | 58B511D71A9E6C8500147676 /* Sources */,
152 | 58B511D81A9E6C8500147676 /* Frameworks */,
153 | 58B511D91A9E6C8500147676 /* CopyFiles */,
154 | );
155 | buildRules = (
156 | );
157 | dependencies = (
158 | );
159 | name = ImageCropPicker;
160 | productName = RCTDataManager;
161 | productReference = 134814201AA4EA6300B7C361 /* libImageCropPicker.a */;
162 | productType = "com.apple.product-type.library.static";
163 | };
164 | /* End PBXNativeTarget section */
165 |
166 | /* Begin PBXProject section */
167 | 58B511D31A9E6C8500147676 /* Project object */ = {
168 | isa = PBXProject;
169 | attributes = {
170 | LastUpgradeCheck = 0610;
171 | ORGANIZATIONNAME = Facebook;
172 | TargetAttributes = {
173 | 58B511DA1A9E6C8500147676 = {
174 | CreatedOnToolsVersion = 6.1.1;
175 | };
176 | };
177 | };
178 | buildConfigurationList = 58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "ImageCropPicker" */;
179 | compatibilityVersion = "Xcode 3.2";
180 | developmentRegion = English;
181 | hasScannedForEncodings = 0;
182 | knownRegions = (
183 | en,
184 | );
185 | mainGroup = 58B511D21A9E6C8500147676;
186 | productRefGroup = 58B511D21A9E6C8500147676;
187 | projectDirPath = "";
188 | projectRoot = "";
189 | targets = (
190 | 58B511DA1A9E6C8500147676 /* ImageCropPicker */,
191 | );
192 | };
193 | /* End PBXProject section */
194 |
195 | /* Begin PBXSourcesBuildPhase section */
196 | 58B511D71A9E6C8500147676 /* Sources */ = {
197 | isa = PBXSourcesBuildPhase;
198 | buildActionMask = 2147483647;
199 | files = (
200 | 23F50D6F1F3C041A004B3E61 /* TZPhotoPickerController.m in Sources */,
201 | 23F50D691F3C041A004B3E61 /* TZAssetModel.m in Sources */,
202 | 23F50D701F3C041A004B3E61 /* TZPhotoPreviewCell.m in Sources */,
203 | 23F50D6C1F3C041A004B3E61 /* TZImageManager.m in Sources */,
204 | 23F50D671F3C041A004B3E61 /* NSBundle+TZImagePicker.m in Sources */,
205 | 23F50D6A1F3C041A004B3E61 /* TZGifPhotoPreviewController.m in Sources */,
206 | EC236E871F978C3B00D528A5 /* NSDictionary+SYSafeConvert.m in Sources */,
207 | 23F50D6B1F3C041A004B3E61 /* TZImageCropManager.m in Sources */,
208 | 23F50D711F3C041A004B3E61 /* TZPhotoPreviewController.m in Sources */,
209 | 23F50D741F3C041A004B3E61 /* UIView+Layout.m in Sources */,
210 | 23F50D731F3C041A004B3E61 /* TZVideoPlayerController.m in Sources */,
211 | 23F50D721F3C041A004B3E61 /* TZProgressView.m in Sources */,
212 | 23F50D6E1F3C041A004B3E61 /* TZLocationManager.m in Sources */,
213 | B3E7B58A1CC2AC0600A0062D /* ImageCropPicker.m in Sources */,
214 | 23F50D681F3C041A004B3E61 /* TZAssetCell.m in Sources */,
215 | 23F50D6D1F3C041A004B3E61 /* TZImagePickerController.m in Sources */,
216 | );
217 | runOnlyForDeploymentPostprocessing = 0;
218 | };
219 | /* End PBXSourcesBuildPhase section */
220 |
221 | /* Begin XCBuildConfiguration section */
222 | 58B511ED1A9E6C8500147676 /* Debug */ = {
223 | isa = XCBuildConfiguration;
224 | buildSettings = {
225 | ALWAYS_SEARCH_USER_PATHS = NO;
226 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
227 | CLANG_CXX_LIBRARY = "libc++";
228 | CLANG_ENABLE_MODULES = YES;
229 | CLANG_ENABLE_OBJC_ARC = YES;
230 | CLANG_WARN_BOOL_CONVERSION = YES;
231 | CLANG_WARN_CONSTANT_CONVERSION = YES;
232 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
233 | CLANG_WARN_EMPTY_BODY = YES;
234 | CLANG_WARN_ENUM_CONVERSION = YES;
235 | CLANG_WARN_INT_CONVERSION = YES;
236 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
237 | CLANG_WARN_UNREACHABLE_CODE = YES;
238 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
239 | COPY_PHASE_STRIP = NO;
240 | ENABLE_STRICT_OBJC_MSGSEND = YES;
241 | GCC_C_LANGUAGE_STANDARD = gnu99;
242 | GCC_DYNAMIC_NO_PIC = NO;
243 | GCC_OPTIMIZATION_LEVEL = 0;
244 | GCC_PREPROCESSOR_DEFINITIONS = (
245 | "DEBUG=1",
246 | "$(inherited)",
247 | );
248 | GCC_SYMBOLS_PRIVATE_EXTERN = NO;
249 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
250 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
251 | GCC_WARN_UNDECLARED_SELECTOR = YES;
252 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
253 | GCC_WARN_UNUSED_FUNCTION = YES;
254 | GCC_WARN_UNUSED_VARIABLE = YES;
255 | IPHONEOS_DEPLOYMENT_TARGET = 7.0;
256 | MTL_ENABLE_DEBUG_INFO = YES;
257 | ONLY_ACTIVE_ARCH = YES;
258 | SDKROOT = iphoneos;
259 | };
260 | name = Debug;
261 | };
262 | 58B511EE1A9E6C8500147676 /* Release */ = {
263 | isa = XCBuildConfiguration;
264 | buildSettings = {
265 | ALWAYS_SEARCH_USER_PATHS = NO;
266 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
267 | CLANG_CXX_LIBRARY = "libc++";
268 | CLANG_ENABLE_MODULES = YES;
269 | CLANG_ENABLE_OBJC_ARC = YES;
270 | CLANG_WARN_BOOL_CONVERSION = YES;
271 | CLANG_WARN_CONSTANT_CONVERSION = YES;
272 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
273 | CLANG_WARN_EMPTY_BODY = YES;
274 | CLANG_WARN_ENUM_CONVERSION = YES;
275 | CLANG_WARN_INT_CONVERSION = YES;
276 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
277 | CLANG_WARN_UNREACHABLE_CODE = YES;
278 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
279 | COPY_PHASE_STRIP = YES;
280 | ENABLE_NS_ASSERTIONS = NO;
281 | ENABLE_STRICT_OBJC_MSGSEND = YES;
282 | GCC_C_LANGUAGE_STANDARD = gnu99;
283 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
284 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
285 | GCC_WARN_UNDECLARED_SELECTOR = YES;
286 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
287 | GCC_WARN_UNUSED_FUNCTION = YES;
288 | GCC_WARN_UNUSED_VARIABLE = YES;
289 | IPHONEOS_DEPLOYMENT_TARGET = 7.0;
290 | MTL_ENABLE_DEBUG_INFO = NO;
291 | SDKROOT = iphoneos;
292 | VALIDATE_PRODUCT = YES;
293 | };
294 | name = Release;
295 | };
296 | 58B511F01A9E6C8500147676 /* Debug */ = {
297 | isa = XCBuildConfiguration;
298 | buildSettings = {
299 | HEADER_SEARCH_PATHS = (
300 | "$(inherited)",
301 | "\"$(SRCROOT)/../../react-native/React\"",
302 | );
303 | LIBRARY_SEARCH_PATHS = "$(inherited)";
304 | OTHER_LDFLAGS = "-ObjC";
305 | PRODUCT_NAME = ImageCropPicker;
306 | SKIP_INSTALL = YES;
307 | };
308 | name = Debug;
309 | };
310 | 58B511F11A9E6C8500147676 /* Release */ = {
311 | isa = XCBuildConfiguration;
312 | buildSettings = {
313 | HEADER_SEARCH_PATHS = (
314 | "$(inherited)",
315 | "\"$(SRCROOT)/../../react-native/React\"",
316 | );
317 | LIBRARY_SEARCH_PATHS = "$(inherited)";
318 | OTHER_LDFLAGS = "-ObjC";
319 | PRODUCT_NAME = ImageCropPicker;
320 | SKIP_INSTALL = YES;
321 | };
322 | name = Release;
323 | };
324 | /* End XCBuildConfiguration section */
325 |
326 | /* Begin XCConfigurationList section */
327 | 58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "ImageCropPicker" */ = {
328 | isa = XCConfigurationList;
329 | buildConfigurations = (
330 | 58B511ED1A9E6C8500147676 /* Debug */,
331 | 58B511EE1A9E6C8500147676 /* Release */,
332 | );
333 | defaultConfigurationIsVisible = 0;
334 | defaultConfigurationName = Release;
335 | };
336 | 58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "ImageCropPicker" */ = {
337 | isa = XCConfigurationList;
338 | buildConfigurations = (
339 | 58B511F01A9E6C8500147676 /* Debug */,
340 | 58B511F11A9E6C8500147676 /* Release */,
341 | );
342 | defaultConfigurationIsVisible = 0;
343 | defaultConfigurationName = Release;
344 | };
345 | /* End XCConfigurationList section */
346 | };
347 | rootObject = 58B511D31A9E6C8500147676 /* Project object */;
348 | }
349 |
--------------------------------------------------------------------------------
/ios/NSDictionary+SYSafeConvert.h:
--------------------------------------------------------------------------------
1 | //
2 | // NSMutableDictionary+SYSafeConvert.h
3 | // ImageCropPicker
4 | //
5 | // Created by CookieJ on 2017/10/18.
6 | // Copyright © 2017年 Facebook. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface NSMutableDictionary (SYSafeConvert)
12 |
13 | - (void)sy_setObject:(id)value forKey:(NSString *)key;
14 |
15 | - (void)sy_setInteger:(NSInteger)value forKey:(NSString *)key;
16 |
17 | - (void)sy_setBool:(BOOL)value forKey:(NSString *)key;
18 |
19 | @end
20 |
21 | @interface NSDictionary (SYSafeConvert)
22 |
23 | - (NSString *)sy_stringForKey:(NSString *)key;
24 |
25 | - (BOOL)sy_boolForKey:(NSString *)key;
26 |
27 | - (NSInteger)sy_integerForKey:(NSString *)key;
28 |
29 | @end
30 |
--------------------------------------------------------------------------------
/ios/NSDictionary+SYSafeConvert.m:
--------------------------------------------------------------------------------
1 | //
2 | // NSMutableDictionary+SYSafeConvert.m
3 | // ImageCropPicker
4 | //
5 | // Created by CookieJ on 2017/10/18.
6 | // Copyright © 2017年 Facebook. All rights reserved.
7 | //
8 |
9 | #import "NSDictionary+SYSafeConvert.h"
10 |
11 | @implementation NSMutableDictionary (SYSafeConvert)
12 |
13 | - (void)sy_setObject:(id)value forKey:(NSString *)key {
14 | if (![self isKindOfClass:[NSMutableDictionary class]]) {
15 | NSLog(@"类型有误,非字典无法设置值!");
16 | return;
17 | }
18 |
19 | if (value && value != [NSNull null] && key) {
20 | [self setObject:value forKey:key];
21 | }
22 | }
23 |
24 | - (void)sy_setBool:(BOOL)value forKey:(NSString *)key {
25 | if (![self isKindOfClass:[NSMutableDictionary class]]) {
26 | NSLog(@"类型有误,非字典无法设置值!");
27 | return;
28 | }
29 |
30 | if (key) {
31 | [self setObject:@(value) forKey:key];
32 | }
33 | }
34 |
35 | - (void)sy_setInteger:(NSInteger)value forKey:(NSString *)key {
36 | if (![self isKindOfClass:[NSMutableDictionary class]]) {
37 | NSLog(@"类型有误,非字典无法设置值!");
38 | return;
39 | }
40 |
41 | if (key) {
42 | [self setObject:@(value) forKey:key];
43 | }
44 | }
45 |
46 | @end
47 |
48 | @implementation NSDictionary (SYSafeConvert)
49 |
50 | - (BOOL)sy_boolForKey:(NSString *)key {
51 | if (![self isKindOfClass:[NSDictionary class]]) {
52 | NSLog(@"类型有误,无法从非字典取值!");
53 | return nil;
54 | }
55 |
56 | id value = [self objectForKey:key];
57 | if ([value isKindOfClass:[NSNumber class]] || [value isKindOfClass:[NSString class]]) {
58 | return [value boolValue];
59 | }
60 | return NO;
61 | }
62 |
63 | - (NSInteger)sy_integerForKey:(NSString *)key {
64 | if (![self isKindOfClass:[NSDictionary class]]) {
65 | NSLog(@"类型有误,无法从非字典取值!");
66 | return nil;
67 | }
68 |
69 | id value = [self objectForKey:key];
70 | if ([value isKindOfClass:[NSNumber class]] || [value isKindOfClass:[NSString class]]) {
71 | return [value integerValue];
72 | }
73 | return 0;
74 | }
75 |
76 | - (NSString *)sy_stringForKey:(NSString *)key {
77 | if (![self isKindOfClass:[NSDictionary class]]) {
78 | NSLog(@"类型有误,无法从非字典取值!");
79 | return nil;
80 | }
81 |
82 | id value = [self objectForKey:key];
83 | if ([value isKindOfClass:[NSString class]]) {
84 | return (NSString *)value;
85 | }
86 | if ([value isKindOfClass:[NSNumber class]]) {
87 | return [value stringValue];
88 | }
89 | return nil;
90 | }
91 |
92 | @end
93 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-native-customized-image-picker",
3 | "version": "1.3.4",
4 | "description": "Select single or multiple images, with croping option",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "repository": {
10 | "type": "git",
11 | "url": "git+https://github.com/liukefu2050/react-native-customized-image-picker"
12 | },
13 | "keywords": [
14 | "react",
15 | "native",
16 | "react-native",
17 | "image",
18 | "picker",
19 | "crop",
20 | "cropping",
21 | "multiple",
22 | "camera"
23 | ],
24 | "author": "Ivan Pusic,liukefu",
25 | "license": "MIT",
26 | "bugs": {
27 | "url": "https://github.com/liukefu2050/react-native-customized-image-picker/issues"
28 | },
29 | "homepage": "https://github.com/liukefu2050/react-native-customized-image-picker#readme",
30 | "peerDependencies": {
31 | "react-native": ">=0.33.0"
32 | }
33 | }
34 |
--------------------------------------------------------------------------------