作者 廖明明

修改android跳转到图片选择库奔溃的问题

正在显示 84 个修改的文件 包含 4762 行增加0 行删除

要显示太多修改。

为保证性能只显示 84 of 84+ 个文件。

  1 +# Contributor Covenant Code of Conduct
  2 +
  3 +## Our Pledge
  4 +
  5 +In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
  6 +
  7 +## Our Standards
  8 +
  9 +Examples of behavior that contributes to creating a positive environment include:
  10 +
  11 +* Using welcoming and inclusive language
  12 +* Being respectful of differing viewpoints and experiences
  13 +* Gracefully accepting constructive criticism
  14 +* Focusing on what is best for the community
  15 +* Showing empathy towards other community members
  16 +
  17 +Examples of unacceptable behavior by participants include:
  18 +
  19 +* The use of sexualized language or imagery and unwelcome sexual attention or advances
  20 +* Trolling, insulting/derogatory comments, and personal or political attacks
  21 +* Public or private harassment
  22 +* Publishing others' private information, such as a physical or electronic address, without explicit permission
  23 +* Other conduct which could reasonably be considered inappropriate in a professional setting
  24 +
  25 +## Our Responsibilities
  26 +
  27 +Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
  28 +
  29 +Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
  30 +
  31 +## Scope
  32 +
  33 +This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
  34 +
  35 +## Enforcement
  36 +
  37 +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at pusic007@gmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
  38 +
  39 +Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
  40 +
  41 +## Attribution
  42 +
  43 +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
  44 +
  45 +[homepage]: http://contributor-covenant.org
  46 +[version]: http://contributor-covenant.org/version/1/4/
  1 +# Contribute
  2 +
  3 +## Introduction
  4 +
  5 +First, thank you for considering contributing to react-native-image-crop-picker! It's people like you that make the open source community such a great community! 😊
  6 +
  7 +We welcome any type of contribution, not only code. You can help with
  8 +- **QA**: file bug reports, the more details you can give the better (e.g. screenshots with the console open)
  9 +- **Marketing**: writing blog posts, howto's, printing stickers, ...
  10 +- **Community**: presenting the project at meetups, organizing a dedicated meetup for the local community, ...
  11 +- **Code**: take a look at the [open issues](https://github.com/ivpusic/react-native-image-crop-picker/issues). Even if you can't write code, commenting on them, showing that you care about a given
  12 +matters. It helps us triage them.
  13 +- **Money**: we welcome financial contributions in full transparency on our [open collective](https://opencollective.com/react-native-image-crop-picker).
  14 +
  15 +## Your First Contribution
  16 +
  17 +Working on your first Pull Request? You can learn how from this *free* series, [How to Contribute to an Open Source Project on GitHub](https://egghead.io/series/how-to-contribute-to-an-open-source-project-on-github).
  18 +
  19 +## Submitting code
  20 +
  21 +Any code change should be submitted as a pull request. The description should explain what the code does and give steps to execute it. The pull request should also contain tests.
  22 +
  23 +## Code review process
  24 +
  25 +The bigger the pull request, the longer it will take to review and merge. Try to break down large pull requests in smaller chunks that are easier to review and merge.
  26 +It is also always helpful to have some context for your pull request. What was the purpose? Why does it matter to you?
  27 +
  28 +## Financial contributions
  29 +
  30 +We also welcome financial contributions in full transparency on our [open collective](https://opencollective.com/react-native-image-crop-picker).
  31 +Anyone can file an expense. If the expense makes sense for the development of the community, it will be "merged" in the ledger of our open collective by the core contributors and the person who filed the expense will be reimbursed.
  32 +
  33 +## Questions
  34 +
  35 +If you have any questions, create an [issue](https://github.com/ivpusic/react-native-image-crop-picker/issues) (protip: do a quick search first to see if someone else didn't ask the same question before!).
  36 +You can also reach us at hello@react-native-image-crop-picker.opencollective.com.
  37 +
  38 +## Credits
  39 +
  40 +### Contributors
  41 +
  42 +Thank you to all the people who have already contributed to react-native-image-crop-picker!
  43 +<a href="graphs/contributors"><img src="https://opencollective.com/react-native-image-crop-picker/contributors.svg?width=890" /></a>
  44 +
  45 +
  46 +### Backers
  47 +
  48 +Thank you to all our backers! [[Become a backer](https://opencollective.com/react-native-image-crop-picker#backer)]
  49 +
  50 +<a href="https://opencollective.com/react-native-image-crop-picker#backers" target="_blank"><img src="https://opencollective.com/react-native-image-crop-picker/backers.svg?width=890"></a>
  51 +
  52 +
  53 +### Sponsors
  54 +
  55 +Thank you to all our sponsors! (please ask your company to also support this open source project by [becoming a sponsor](https://opencollective.com/react-native-image-crop-picker#sponsor))
  56 +
  57 +<a href="https://opencollective.com/react-native-image-crop-picker/sponsor/0/website" target="_blank"><img src="https://opencollective.com/react-native-image-crop-picker/sponsor/0/avatar.svg"></a>
  58 +<a href="https://opencollective.com/react-native-image-crop-picker/sponsor/1/website" target="_blank"><img src="https://opencollective.com/react-native-image-crop-picker/sponsor/1/avatar.svg"></a>
  59 +<a href="https://opencollective.com/react-native-image-crop-picker/sponsor/2/website" target="_blank"><img src="https://opencollective.com/react-native-image-crop-picker/sponsor/2/avatar.svg"></a>
  60 +<a href="https://opencollective.com/react-native-image-crop-picker/sponsor/3/website" target="_blank"><img src="https://opencollective.com/react-native-image-crop-picker/sponsor/3/avatar.svg"></a>
  61 +<a href="https://opencollective.com/react-native-image-crop-picker/sponsor/4/website" target="_blank"><img src="https://opencollective.com/react-native-image-crop-picker/sponsor/4/avatar.svg"></a>
  62 +<a href="https://opencollective.com/react-native-image-crop-picker/sponsor/5/website" target="_blank"><img src="https://opencollective.com/react-native-image-crop-picker/sponsor/5/avatar.svg"></a>
  63 +<a href="https://opencollective.com/react-native-image-crop-picker/sponsor/6/website" target="_blank"><img src="https://opencollective.com/react-native-image-crop-picker/sponsor/6/avatar.svg"></a>
  64 +<a href="https://opencollective.com/react-native-image-crop-picker/sponsor/7/website" target="_blank"><img src="https://opencollective.com/react-native-image-crop-picker/sponsor/7/avatar.svg"></a>
  65 +<a href="https://opencollective.com/react-native-image-crop-picker/sponsor/8/website" target="_blank"><img src="https://opencollective.com/react-native-image-crop-picker/sponsor/8/avatar.svg"></a>
  66 +<a href="https://opencollective.com/react-native-image-crop-picker/sponsor/9/website" target="_blank"><img src="https://opencollective.com/react-native-image-crop-picker/sponsor/9/avatar.svg"></a>
  67 +
  68 +<!-- This `CONTRIBUTING.md` is based on @nayafia's template https://github.com/nayafia/contributing-template -->
  1 +### Version
  2 +Tell us which versions you are using:
  3 +
  4 +- react-native-image-crop-picker v0.?.?
  5 +- react-native v0.?.?
  6 +
  7 +### Platform
  8 +Tell us to which platform this issue is related
  9 +
  10 +- iOS
  11 +- Android
  12 +
  13 +### Expected behaviour
  14 +
  15 +
  16 +
  17 +### Actual behaviour
  18 +
  19 +
  20 +
  21 +### Steps to reproduce
  22 +
  23 +1.
  24 +
  25 +2.
  26 +
  27 +3.
  28 +
  29 +### Attachments
  30 +
  31 +// stacktrace or any other useful debug info
  32 +
  33 +Love react-native-image-crop-picker? Please consider supporting our collective:
  34 +👉 https://opencollective.com/react-native-image-crop-picker/donate
  1 +MIT License
  2 +
  3 +Copyright (c) 2017 Ivan Pusic
  4 +
  5 +Permission is hereby granted, free of charge, to any person obtaining a copy
  6 +of this software and associated documentation files (the "Software"), to deal
  7 +in the Software without restriction, including without limitation the rights
  8 +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9 +copies of the Software, and to permit persons to whom the Software is
  10 +furnished to do so, subject to the following conditions:
  11 +
  12 +The above copyright notice and this permission notice shall be included in all
  13 +copies or substantial portions of the Software.
  14 +
  15 +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16 +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17 +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18 +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19 +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  20 +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  21 +SOFTWARE.
  1 +# react-native-image-crop-picker
  2 +
  3 +[![Backers on Open Collective](https://opencollective.com/react-native-image-crop-picker/backers/badge.svg)](#backers) [![Sponsors on Open Collective](https://opencollective.com/react-native-image-crop-picker/sponsors/badge.svg)](#sponsors)
  4 +
  5 +<img src="svg.svg" width="50%" height="50%" />
  6 +
  7 +iOS/Android image picker with support for camera, video, configurable compression, multiple images and cropping
  8 +
  9 +## Result
  10 +
  11 +<p align="left">
  12 + <img width=200 title="iOS Single Pick" src="https://github.com/ivpusic/react-native-image-crop-picker/blob/master/images/ios_single_pick.png">
  13 +<img width=200 title="iOS Crop" src="https://github.com/ivpusic/react-native-image-crop-picker/blob/master/images/ios_crop.png">
  14 +<img width=200 title="iOS Multiple Pick" src="https://github.com/ivpusic/react-native-image-crop-picker/blob/master/images/ios_multiple_pick.png">
  15 +</p>
  16 +
  17 +## Usage
  18 +
  19 +Import library
  20 +
  21 +```javascript
  22 +import ImagePicker from 'react-native-image-crop-picker';
  23 +```
  24 +
  25 +### Select from gallery
  26 +
  27 +Call single image picker with cropping
  28 +```javascript
  29 +ImagePicker.openPicker({
  30 + width: 300,
  31 + height: 400,
  32 + cropping: true
  33 +}).then(image => {
  34 + console.log(image);
  35 +});
  36 +```
  37 +
  38 +Call multiple image picker
  39 +
  40 +```javascript
  41 +ImagePicker.openPicker({
  42 + multiple: true
  43 +}).then(images => {
  44 + console.log(images);
  45 +});
  46 +```
  47 +
  48 +Select video only from gallery
  49 +
  50 +```javascript
  51 +ImagePicker.openPicker({
  52 + mediaType: "video",
  53 +}).then((video) => {
  54 + console.log(video);
  55 +});
  56 +```
  57 +
  58 +**Android: The prop 'cropping' has been known to cause videos not to be display in the gallery on Android. Please do not set cropping to true when selecting videos.**
  59 +
  60 +
  61 +### Select from camera
  62 +
  63 +#### Image
  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 +#### Video
  76 +
  77 +```javascript
  78 +ImagePicker.openCamera({
  79 + mediaType: 'video',
  80 +}).then(image => {
  81 + console.log(image);
  82 +});
  83 +```
  84 +
  85 +### Crop picture
  86 +
  87 +```javascript
  88 +ImagePicker.openCropper({
  89 + path: 'my-file-path.jpg',
  90 + width: 300,
  91 + height: 400
  92 +}).then(image => {
  93 + console.log(image);
  94 +});
  95 +```
  96 +
  97 +### Optional cleanup
  98 +
  99 +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, or `cleanSingle(path)` to clean single tmp file.
  100 +
  101 +```javascript
  102 +ImagePicker.clean().then(() => {
  103 + console.log('removed all tmp images from tmp directory');
  104 +}).catch(e => {
  105 + alert(e);
  106 +});
  107 +```
  108 +
  109 +### Request Object
  110 +
  111 +| Property | Type | Description |
  112 +| --------------------------------------- | :--------------------------------------: | :--------------------------------------- |
  113 +| cropping | bool (default false) | Enable or disable cropping |
  114 +| width | number | Width of result image when used with `cropping` option |
  115 +| height | number | Height of result image when used with `cropping` option |
  116 +| multiple | bool (default false) | Enable or disable multiple image selection |
  117 +| writeTempFile (ios only) | bool (default true) | When set to false, does not write temporary files for the selected images. This is useful to improve performance when you are retrieving file contents with the `includeBase64` option and don't need to read files from disk. |
  118 +| includeBase64 | bool (default false) | When set to true, the image file content will be available as a base64-encoded string in the `data` property. Hint: To use this string as an image source, use it like: ``<Image source={{uri: `data:${image.mime};base64,${image.data}`}} />`` |
  119 +| includeExif | bool (default false) | Include image exif data in the response |
  120 +| avoidEmptySpaceAroundImage | bool (default true) | When set to true, the image will always fill the mask space. |
  121 +| cropperActiveWidgetColor (android only) | string (default `"#424242"`) | When cropping image, determines ActiveWidget color. |
  122 +| cropperStatusBarColor (android only) | string (default `#424242`) | When cropping image, determines the color of StatusBar. |
  123 +| cropperToolbarColor (android only) | string (default `#424242`) | When cropping image, determines the color of Toolbar. |
  124 +| freeStyleCropEnabled (android only) | bool (default false) | Enables user to apply custom rectangle area for cropping |
  125 +| cropperToolbarTitle | string (default `Edit Photo`) | When cropping image, determines the title of Toolbar. |
  126 +| cropperCircleOverlay | bool (default false) | Enable or disable circular cropping mask. |
  127 +| disableCropperColorSetters (android only)| bool (default false) | When cropping image, disables the color setters for cropping library. |
  128 +| minFiles (ios only) | number (default 1) | Min number of files to select when using `multiple` option |
  129 +| maxFiles (ios only) | number (default 5) | Max number of files to select when using `multiple` option |
  130 +| waitAnimationEnd (ios only) | bool (default true) | Promise will resolve/reject once ViewController `completion` block is called |
  131 +| smartAlbums (ios only) | array ([supported values](https://github.com/ivpusic/react-native-image-crop-picker/blob/master/README.md#smart-album-types-ios)) (default ['UserLibrary', 'PhotoStream', 'Panoramas', 'Videos', 'Bursts']) | List of smart albums to choose from |
  132 +| useFrontCamera | bool (default false) | Whether to default to the front/'selfie' camera when opened |
  133 +| compressVideoPreset (ios only) | string (default MediumQuality) | Choose which preset will be used for video compression |
  134 +| compressImageMaxWidth | number (default none) | Compress image with maximum width |
  135 +| compressImageMaxHeight | number (default none) | Compress image with maximum height |
  136 +| compressImageQuality | number (default 1 (Android)/0.8 (iOS)) | Compress image with quality (from 0 to 1, where 1 is best quality). On iOS, values larger than 0.8 don't produce a noticable quality increase in most images, while a value of 0.8 will reduce the file size by about half or less compared to a value of 1. |
  137 +| loadingLabelText (ios only) | string (default "Processing assets...") | Text displayed while photo is loading in picker |
  138 +| mediaType | string (default any) | Accepted mediaType for image selection, can be one of: 'photo', 'video', or 'any' |
  139 +| showsSelectedCount (ios only) | bool (default true) | Whether to show the number of selected assets |
  140 +| forceJpg (ios only) | bool (default false) | Whether to convert photos to JPG. This will also convert any Live Photo into its JPG representation |
  141 +| showCropGuidelines (android only) | bool (default true) | Whether to show the 3x3 grid on top of the image during cropping |
  142 +| showCropFrame (android only) | bool (default true) | Whether to show crop frame during cropping |
  143 +| hideBottomControls (android only) | bool (default false) | Whether to display bottom controls |
  144 +| enableRotationGesture (android only) | bool (default false) | Whether to enable rotating the image by hand gesture |
  145 +| cropperChooseText (ios only)  |           string (default choose)        | Choose button text |
  146 +| cropperCancelText (ios only) | string (default Cancel) | Cancel button text |
  147 +
  148 +#### Smart Album Types (ios)
  149 +
  150 +```
  151 +['PhotoStream', 'Generic', 'Panoramas', 'Videos', 'Favorites', 'Timelapses', 'AllHidden', 'RecentlyAdded', 'Bursts', 'SlomoVideos', 'UserLibrary', 'SelfPortraits', 'Screenshots', 'DepthEffect', 'LivePhotos', 'Animated', 'LongExposure']
  152 +```
  153 +
  154 +### Response Object
  155 +
  156 +| Property | Type | Description |
  157 +| ------------------------- | :----: | :--------------------------------------- |
  158 +| path | string | Selected image location. This is null when the `writeTempFile` option is set to false. |
  159 +| localIdentifier(ios only) | string | Selected images' localidentifier, used for PHAsset searching |
  160 +| sourceURL(ios only) | string | Selected images' source path, do not have write access |
  161 +| filename(ios only) | string | Selected images' filename |
  162 +| width | number | Selected image width |
  163 +| height | number | Selected image height |
  164 +| mime | string | Selected image MIME type (image/jpeg, image/png) |
  165 +| size | number | Selected image size in bytes |
  166 +| data | base64 | Optional base64 selected file representation |
  167 +| exif | object | Extracted exif data from image. Response format is platform specific |
  168 +| cropRect | object | Cropped image rectangle (width, height, x, y) |
  169 +| creationDate (ios only) | string | UNIX timestamp when image was created |
  170 +| modificationDate | string | UNIX timestamp when image was last modified |
  171 +
  172 +# Install
  173 +
  174 +## Step 1
  175 +
  176 +```bash
  177 +npm i react-native-image-crop-picker --save
  178 +```
  179 +
  180 +## Step 2
  181 +
  182 +### iOS
  183 +
  184 +#### - If you use Cocoapods which is highly recommended:
  185 +
  186 +```bash
  187 +cd ios
  188 +pod init
  189 +```
  190 +
  191 +After this edit Podfile. Example content is following:
  192 +
  193 +```bash
  194 +platform :ios, '8.0'
  195 +
  196 +target '<project_name>' do
  197 + # this is very important to have!
  198 + rn_path = '../node_modules/react-native'
  199 + pod 'yoga', path: "#{rn_path}/ReactCommon/yoga/yoga.podspec"
  200 + pod 'React', path: rn_path, subspecs: [
  201 + 'Core',
  202 + 'RCTActionSheet',
  203 + 'RCTAnimation',
  204 + 'RCTGeolocation',
  205 + 'RCTImage',
  206 + 'RCTLinkingIOS',
  207 + 'RCTNetwork',
  208 + 'RCTSettings',
  209 + 'RCTText',
  210 + 'RCTVibration',
  211 + 'RCTWebSocket'
  212 + ]
  213 +
  214 + pod 'RNImageCropPicker', :path => '../node_modules/react-native-image-crop-picker'
  215 +end
  216 +
  217 +# very important to have, unless you removed React dependencies for Libraries
  218 +# and you rely on Cocoapods to manage it
  219 +post_install do |installer|
  220 + installer.pods_project.targets.each do |target|
  221 + if target.name == "React"
  222 + target.remove_from_project
  223 + end
  224 + end
  225 +end
  226 +```
  227 +
  228 +After this run:
  229 +
  230 +```bash
  231 +pod install
  232 +```
  233 +
  234 +After this use `ios/<project_name>.xcworkspace`. **Do not use** `ios/<project_name>.xcodeproj`.
  235 +
  236 +#### - If you are not using Cocoapods which is not recommended:
  237 +
  238 +```bash
  239 +react-native link react-native-image-crop-picker
  240 +```
  241 +
  242 +### Android
  243 +
  244 +```bash
  245 +react-native link react-native-image-crop-picker
  246 +```
  247 +
  248 +## Post-install steps
  249 +
  250 +### iOS
  251 +
  252 +#### Step 1
  253 +
  254 +In Xcode open Info.plist and add string key `NSPhotoLibraryUsageDescription` with value that describes why you need access to user photos. More info here https://forums.developer.apple.com/thread/62229. Depending on what features you use, you also may need `NSCameraUsageDescription` and `NSMicrophoneUsageDescription` keys.
  255 +
  256 +#### Step 2
  257 +
  258 +##### Only if you are not using Cocoapods
  259 +
  260 +- Click on project General tab
  261 + - Under `Deployment Info` set `Deployment Target` to `8.0`
  262 + - Under `Embedded Binaries` click `+` and add `RSKImageCropper.framework` and `QBImagePicker.framework`
  263 +
  264 +#### Step Optional - To localizate the camera / gallery text buttons
  265 +
  266 +- Open your Xcode project
  267 +- Go to your project settings by opening the project name on the Navigation (left side)
  268 +- Select your project in the project list
  269 +- Should be into the Info tab and add in Localizations the language your app was missing throughout the +
  270 +- Rebuild and you should now have your app camera and gallery with the classic ios text in the language you added.
  271 +
  272 +### Android
  273 +
  274 +- Make sure you are using Gradle >= `2.2.x` (android/build.gradle)
  275 +
  276 +```gradle
  277 +buildscript {
  278 + ...
  279 + dependencies {
  280 + classpath 'com.android.tools.build:gradle:2.2.3'
  281 + ...
  282 + }
  283 + ...
  284 +}
  285 +```
  286 +
  287 +- **VERY IMPORTANT** Add the following to your `build.gradle`'s repositories section. (android/build.gradle)
  288 +
  289 +```gradle
  290 +allprojects {
  291 + repositories {
  292 + mavenLocal()
  293 + jcenter()
  294 + maven { url "$rootDir/../node_modules/react-native/android" }
  295 +
  296 + // ADD THIS
  297 + maven { url 'https://maven.google.com' }
  298 +
  299 + // ADD THIS
  300 + maven { url "https://jitpack.io" }
  301 + }
  302 +}
  303 +```
  304 +
  305 +- Add `useSupportLibrary` (android/app/build.gradle)
  306 +
  307 +```gradle
  308 +android {
  309 + ...
  310 +
  311 + defaultConfig {
  312 + ...
  313 + vectorDrawables.useSupportLibrary = true
  314 + ...
  315 + }
  316 + ...
  317 +}
  318 +```
  319 +
  320 +- Use Android SDK >= 26 (android/app/build.gradle)
  321 +
  322 +```gradle
  323 +android {
  324 + compileSdkVersion 27
  325 + buildToolsVersion "27.0.3"
  326 + ...
  327 +
  328 + defaultConfig {
  329 + ...
  330 + targetSdkVersion 27
  331 + ...
  332 + }
  333 + ...
  334 +}
  335 +```
  336 +
  337 +- [Optional] If you want to use camera picker in your project, add following to `app\src\main\AndroidManifest.xml`
  338 + - `<uses-permission android:name="android.permission.CAMERA"/>`
  339 +
  340 +- [Optional] If you want to use front camera, also add following to `app\src\main\AndroidManifest.xml`
  341 + - `<uses-feature android:name="android.hardware.camera" android:required="false" />`
  342 + - `<uses-feature android:name="android.hardware.camera.front" android:required="false" />`
  343 +
  344 +## Production build
  345 +
  346 +### iOS
  347 +
  348 +#### Cocoapods (Highly recommended)
  349 +
  350 +- You are already done
  351 +
  352 +#### Manual
  353 +
  354 +If you are using pre-built frameworks from `ios/ImageCropPickerSDK`, then before deploying app to production you should strip off simulator ARCHs from these, or you can add frameworks from `Libraries/imageCropPicker/Libraries/_framework_name_.xcodeproj/Products/_framework_name_.framework` to Embedded Binaries instead of pre-built ones.
  355 +Related issue: https://github.com/ivpusic/react-native-image-crop-picker/issues/61.
  356 +
  357 +Details for second approach:
  358 +
  359 +1. Remove the pre-built frameworks from `Embedded Binaries`
  360 +2. Build for Device
  361 +3. Add the newly built binaries for both frameworks to `Embedded Binaries` (located at `Libraries/imageCropPicker/Libraries/_framework_name_.xcodeproj/Products/_framework_name_.framework`)
  362 +
  363 +## TO DO
  364 +
  365 +- [ ] [Android] Standardize multiple select
  366 +- [ ] [Android] Video compression
  367 +
  368 +
  369 +## Contributors
  370 +
  371 +This project exists thanks to all the people who contribute. [[Contribute]](CONTRIBUTING.md).
  372 +<a href="graphs/contributors"><img src="https://opencollective.com/react-native-image-crop-picker/contributors.svg?width=890" /></a>
  373 +
  374 +
  375 +## Backers
  376 +
  377 +Thank you to all our backers! 🙏 [[Become a backer](https://opencollective.com/react-native-image-crop-picker#backer)]
  378 +
  379 +<a href="https://opencollective.com/react-native-image-crop-picker#backers" target="_blank"><img src="https://opencollective.com/react-native-image-crop-picker/backers.svg?width=890"></a>
  380 +
  381 +
  382 +## Sponsors
  383 +
  384 +Support this project by becoming a sponsor. Your logo will show up here with a link to your website. [[Become a sponsor](https://opencollective.com/react-native-image-crop-picker#sponsor)]
  385 +
  386 +<a href="https://opencollective.com/react-native-image-crop-picker/sponsor/0/website" target="_blank"><img src="https://opencollective.com/react-native-image-crop-picker/sponsor/0/avatar.svg"></a>
  387 +<a href="https://opencollective.com/react-native-image-crop-picker/sponsor/1/website" target="_blank"><img src="https://opencollective.com/react-native-image-crop-picker/sponsor/1/avatar.svg"></a>
  388 +<a href="https://opencollective.com/react-native-image-crop-picker/sponsor/2/website" target="_blank"><img src="https://opencollective.com/react-native-image-crop-picker/sponsor/2/avatar.svg"></a>
  389 +<a href="https://opencollective.com/react-native-image-crop-picker/sponsor/3/website" target="_blank"><img src="https://opencollective.com/react-native-image-crop-picker/sponsor/3/avatar.svg"></a>
  390 +<a href="https://opencollective.com/react-native-image-crop-picker/sponsor/4/website" target="_blank"><img src="https://opencollective.com/react-native-image-crop-picker/sponsor/4/avatar.svg"></a>
  391 +<a href="https://opencollective.com/react-native-image-crop-picker/sponsor/5/website" target="_blank"><img src="https://opencollective.com/react-native-image-crop-picker/sponsor/5/avatar.svg"></a>
  392 +<a href="https://opencollective.com/react-native-image-crop-picker/sponsor/6/website" target="_blank"><img src="https://opencollective.com/react-native-image-crop-picker/sponsor/6/avatar.svg"></a>
  393 +<a href="https://opencollective.com/react-native-image-crop-picker/sponsor/7/website" target="_blank"><img src="https://opencollective.com/react-native-image-crop-picker/sponsor/7/avatar.svg"></a>
  394 +<a href="https://opencollective.com/react-native-image-crop-picker/sponsor/8/website" target="_blank"><img src="https://opencollective.com/react-native-image-crop-picker/sponsor/8/avatar.svg"></a>
  395 +<a href="https://opencollective.com/react-native-image-crop-picker/sponsor/9/website" target="_blank"><img src="https://opencollective.com/react-native-image-crop-picker/sponsor/9/avatar.svg"></a>
  396 +
  397 +
  398 +
  399 +## License
  400 +*MIT*
  1 +package = JSON.parse(File.read(File.join(__dir__, "package.json")))
  2 +version = package['version']
  3 +
  4 +Pod::Spec.new do |s|
  5 + s.name = "RNImageCropPicker"
  6 + s.version = version
  7 + s.summary = package["description"]
  8 + s.requires_arc = true
  9 + s.license = 'MIT'
  10 + s.homepage = 'n/a'
  11 + s.authors = { "ivpusic" => "" }
  12 + s.source = { :git => "https://github.com/ivpusic/react-native-image-crop-picker", :tag => 'v#{version}'}
  13 + s.source_files = 'ios/src/*.{h,m}'
  14 + s.platform = :ios, "8.0"
  15 + s.dependency 'RSKImageCropper'
  16 + s.dependency 'QBImagePickerController'
  17 + s.dependency 'React/Core'
  18 +end
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<module external.linked.project.id="android" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="unspecified" type="JAVA_MODULE" version="4">
  3 + <component name="FacetManager">
  4 + <facet type="android-gradle" name="Android-Gradle">
  5 + <configuration>
  6 + <option name="GRADLE_PROJECT_PATH" value=":" />
  7 + </configuration>
  8 + </facet>
  9 + <facet type="android" name="Android">
  10 + <configuration>
  11 + <option name="SELECTED_BUILD_VARIANT" value="debug" />
  12 + <option name="SELECTED_TEST_ARTIFACT" value="_android_test_" />
  13 + <option name="ASSEMBLE_TASK_NAME" value="assembleDebug" />
  14 + <option name="COMPILE_JAVA_TASK_NAME" value="compileDebugSources" />
  15 + <afterSyncTasks>
  16 + <task>generateDebugSources</task>
  17 + </afterSyncTasks>
  18 + <option name="ALLOW_USER_CONFIGURATION" value="false" />
  19 + <option name="MANIFEST_FILE_RELATIVE_PATH" value="/src/main/AndroidManifest.xml" />
  20 + <option name="RES_FOLDER_RELATIVE_PATH" value="/src/main/res" />
  21 + <option name="RES_FOLDERS_RELATIVE_PATH" value="" />
  22 + <option name="ASSETS_FOLDER_RELATIVE_PATH" value="/src/main/assets" />
  23 + <option name="LIBRARY_PROJECT" value="true" />
  24 + </configuration>
  25 + </facet>
  26 + </component>
  27 + <component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_7" inherit-compiler-output="false">
  28 + <output url="file://$MODULE_DIR$/build/intermediates/classes/debug" />
  29 + <output-test url="file://$MODULE_DIR$/build/intermediates/classes/test/debug" />
  30 + <exclude-output />
  31 + <content url="file://$MODULE_DIR$">
  32 + <sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/debug" isTestSource="false" generated="true" />
  33 + <sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/debug" isTestSource="false" generated="true" />
  34 + <sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/debug" isTestSource="false" generated="true" />
  35 + <sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/debug" isTestSource="false" generated="true" />
  36 + <sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/debug" type="java-resource" />
  37 + <sourceFolder url="file://$MODULE_DIR$/build/generated/res/resValues/debug" type="java-resource" />
  38 + <sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/androidTest/debug" isTestSource="true" generated="true" />
  39 + <sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/androidTest/debug" isTestSource="true" generated="true" />
  40 + <sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/androidTest/debug" isTestSource="true" generated="true" />
  41 + <sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/androidTest/debug" isTestSource="true" generated="true" />
  42 + <sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/androidTest/debug" type="java-test-resource" />
  43 + <sourceFolder url="file://$MODULE_DIR$/build/generated/res/resValues/androidTest/debug" type="java-test-resource" />
  44 + <sourceFolder url="file://$MODULE_DIR$/src/debug/res" type="java-resource" />
  45 + <sourceFolder url="file://$MODULE_DIR$/src/debug/resources" type="java-resource" />
  46 + <sourceFolder url="file://$MODULE_DIR$/src/debug/assets" type="java-resource" />
  47 + <sourceFolder url="file://$MODULE_DIR$/src/debug/aidl" isTestSource="false" />
  48 + <sourceFolder url="file://$MODULE_DIR$/src/debug/java" isTestSource="false" />
  49 + <sourceFolder url="file://$MODULE_DIR$/src/debug/jni" isTestSource="false" />
  50 + <sourceFolder url="file://$MODULE_DIR$/src/debug/rs" isTestSource="false" />
  51 + <sourceFolder url="file://$MODULE_DIR$/src/debug/shaders" isTestSource="false" />
  52 + <sourceFolder url="file://$MODULE_DIR$/src/testDebug/res" type="java-test-resource" />
  53 + <sourceFolder url="file://$MODULE_DIR$/src/testDebug/resources" type="java-test-resource" />
  54 + <sourceFolder url="file://$MODULE_DIR$/src/testDebug/assets" type="java-test-resource" />
  55 + <sourceFolder url="file://$MODULE_DIR$/src/testDebug/aidl" isTestSource="true" />
  56 + <sourceFolder url="file://$MODULE_DIR$/src/testDebug/java" isTestSource="true" />
  57 + <sourceFolder url="file://$MODULE_DIR$/src/testDebug/jni" isTestSource="true" />
  58 + <sourceFolder url="file://$MODULE_DIR$/src/testDebug/rs" isTestSource="true" />
  59 + <sourceFolder url="file://$MODULE_DIR$/src/testDebug/shaders" isTestSource="true" />
  60 + <sourceFolder url="file://$MODULE_DIR$/src/main/res" type="java-resource" />
  61 + <sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
  62 + <sourceFolder url="file://$MODULE_DIR$/src/main/assets" type="java-resource" />
  63 + <sourceFolder url="file://$MODULE_DIR$/src/main/aidl" isTestSource="false" />
  64 + <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
  65 + <sourceFolder url="file://$MODULE_DIR$/src/main/jni" isTestSource="false" />
  66 + <sourceFolder url="file://$MODULE_DIR$/src/main/rs" isTestSource="false" />
  67 + <sourceFolder url="file://$MODULE_DIR$/src/main/shaders" isTestSource="false" />
  68 + <sourceFolder url="file://$MODULE_DIR$/src/androidTest/res" type="java-test-resource" />
  69 + <sourceFolder url="file://$MODULE_DIR$/src/androidTest/resources" type="java-test-resource" />
  70 + <sourceFolder url="file://$MODULE_DIR$/src/androidTest/assets" type="java-test-resource" />
  71 + <sourceFolder url="file://$MODULE_DIR$/src/androidTest/aidl" isTestSource="true" />
  72 + <sourceFolder url="file://$MODULE_DIR$/src/androidTest/java" isTestSource="true" />
  73 + <sourceFolder url="file://$MODULE_DIR$/src/androidTest/jni" isTestSource="true" />
  74 + <sourceFolder url="file://$MODULE_DIR$/src/androidTest/rs" isTestSource="true" />
  75 + <sourceFolder url="file://$MODULE_DIR$/src/androidTest/shaders" isTestSource="true" />
  76 + <sourceFolder url="file://$MODULE_DIR$/src/test/res" type="java-test-resource" />
  77 + <sourceFolder url="file://$MODULE_DIR$/src/test/resources" type="java-test-resource" />
  78 + <sourceFolder url="file://$MODULE_DIR$/src/test/assets" type="java-test-resource" />
  79 + <sourceFolder url="file://$MODULE_DIR$/src/test/aidl" isTestSource="true" />
  80 + <sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
  81 + <sourceFolder url="file://$MODULE_DIR$/src/test/jni" isTestSource="true" />
  82 + <sourceFolder url="file://$MODULE_DIR$/src/test/rs" isTestSource="true" />
  83 + <sourceFolder url="file://$MODULE_DIR$/src/test/shaders" isTestSource="true" />
  84 + <excludeFolder url="file://$MODULE_DIR$/build/intermediates/annotations" />
  85 + <excludeFolder url="file://$MODULE_DIR$/build/intermediates/assets" />
  86 + <excludeFolder url="file://$MODULE_DIR$/build/intermediates/blame" />
  87 + <excludeFolder url="file://$MODULE_DIR$/build/intermediates/bundles" />
  88 + <excludeFolder url="file://$MODULE_DIR$/build/intermediates/classes" />
  89 + <excludeFolder url="file://$MODULE_DIR$/build/intermediates/dependency-cache" />
  90 + <excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/animated-vector-drawable/23.3.0/jars" />
  91 + <excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/appcompat-v7/23.3.0/jars" />
  92 + <excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/recyclerview-v7/23.0.1/jars" />
  93 + <excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/support-v4/23.3.0/jars" />
  94 + <excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/support-vector-drawable/23.3.0/jars" />
  95 + <excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.facebook.fresco/drawee/0.8.1/jars" />
  96 + <excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.facebook.fresco/fbcore/0.8.1/jars" />
  97 + <excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.facebook.fresco/fresco/0.8.1/jars" />
  98 + <excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.facebook.fresco/imagepipeline-okhttp/0.8.1/jars" />
  99 + <excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.facebook.fresco/imagepipeline/0.8.1/jars" />
  100 + <excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.facebook.react/react-native/0.20.1/jars" />
  101 + <excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.yalantis/ucrop/1.5.0/jars" />
  102 + <excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/org.webkit/android-jsc/r174650/jars" />
  103 + <excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental" />
  104 + <excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental-safeguard" />
  105 + <excludeFolder url="file://$MODULE_DIR$/build/intermediates/jniLibs" />
  106 + <excludeFolder url="file://$MODULE_DIR$/build/intermediates/lint" />
  107 + <excludeFolder url="file://$MODULE_DIR$/build/intermediates/res" />
  108 + <excludeFolder url="file://$MODULE_DIR$/build/intermediates/rs" />
  109 + <excludeFolder url="file://$MODULE_DIR$/build/intermediates/shaders" />
  110 + <excludeFolder url="file://$MODULE_DIR$/build/intermediates/symbols" />
  111 + <excludeFolder url="file://$MODULE_DIR$/build/intermediates/transforms" />
  112 + <excludeFolder url="file://$MODULE_DIR$/build/outputs" />
  113 + <excludeFolder url="file://$MODULE_DIR$/build/tmp" />
  114 + </content>
  115 + <orderEntry type="jdk" jdkName="1.8" jdkType="JavaSDK" />
  116 + <orderEntry type="sourceFolder" forTests="false" />
  117 + <orderEntry type="library" exported="" name="okhttp-ws-2.5.0" level="project" />
  118 + <orderEntry type="library" exported="" name="okio-1.6.0" level="project" />
  119 + <orderEntry type="library" exported="" name="okhttp-2.5.0" level="project" />
  120 + <orderEntry type="library" exported="" name="stetho-1.2.0" level="project" />
  121 + <orderEntry type="library" exported="" name="jsr305-3.0.0" level="project" />
  122 + <orderEntry type="library" exported="" name="fbcore-0.8.1" level="project" />
  123 + <orderEntry type="library" exported="" name="commons-cli-1.2" level="project" />
  124 + <orderEntry type="library" exported="" name="recyclerview-v7-23.0.1" level="project" />
  125 + <orderEntry type="library" exported="" name="imagepipeline-0.8.1" level="project" />
  126 + <orderEntry type="library" exported="" name="android-jsc-r174650" level="project" />
  127 + <orderEntry type="library" exported="" name="fresco-0.8.1" level="project" />
  128 + <orderEntry type="library" exported="" name="support-v4-23.3.0" level="project" />
  129 + <orderEntry type="library" exported="" name="imagepipeline-okhttp-0.8.1" level="project" />
  130 + <orderEntry type="library" exported="" name="bolts-android-1.1.4" level="project" />
  131 + <orderEntry type="library" exported="" name="drawee-0.8.1" level="project" />
  132 + <orderEntry type="library" exported="" name="support-annotations-23.3.0" level="project" />
  133 + <orderEntry type="library" exported="" name="support-vector-drawable-23.3.0" level="project" />
  134 + <orderEntry type="library" exported="" name="animated-vector-drawable-23.3.0" level="project" />
  135 + <orderEntry type="library" exported="" name="appcompat-v7-23.3.0" level="project" />
  136 + <orderEntry type="library" exported="" name="library-2.4.0" level="project" />
  137 + <orderEntry type="library" exported="" name="stetho-okhttp-1.2.0" level="project" />
  138 + <orderEntry type="library" exported="" name="ucrop-1.5.0" level="project" />
  139 + <orderEntry type="library" exported="" name="jackson-core-2.2.3" level="project" />
  140 + <orderEntry type="library" exported="" name="okhttp-3.2.0" level="project" />
  141 + <orderEntry type="library" exported="" name="react-native-0.20.1" level="project" />
  142 + </component>
  143 +</module>
  1 +apply plugin: 'com.android.library'
  2 +
  3 +def DEFAULT_COMPILE_SDK_VERSION = 28
  4 +def DEFAULT_BUILD_TOOLS_VERSION = "28.0.3"
  5 +def DEFAULT_TARGET_SDK_VERSION = 28
  6 +def DEFAULT_MIN_SDK_VERSION = 16
  7 +
  8 +android {
  9 + compileSdkVersion rootProject.hasProperty('compileSdkVersion') ? rootProject.compileSdkVersion : DEFAULT_COMPILE_SDK_VERSION
  10 + buildToolsVersion rootProject.hasProperty('buildToolsVersion') ? rootProject.buildToolsVersion : DEFAULT_BUILD_TOOLS_VERSION
  11 +
  12 + defaultConfig {
  13 + minSdkVersion rootProject.hasProperty('minSdkVersion') ? rootProject.minSdkVersion : DEFAULT_MIN_SDK_VERSION
  14 + targetSdkVersion rootProject.hasProperty('targetSdkVersion') ? rootProject.targetSdkVersion : DEFAULT_TARGET_SDK_VERSION
  15 + versionCode 1
  16 + }
  17 + lintOptions {
  18 + abortOnError false
  19 + }
  20 +}
  21 +
  22 +dependencies {
  23 + implementation 'com.facebook.react:react-native:+'
  24 + implementation 'com.github.yalantis:ucrop:2.2.2-native'
  25 +}
  1 +#Tue May 17 14:28:43 CEST 2016
  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-2.10-all.zip
  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 "$@"
  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
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<module external.linked.project.id=":react-native-image-crop-picker" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$/../../../android" external.system.id="GRADLE" type="JAVA_MODULE" version="4">
  3 + <component name="FacetManager">
  4 + <facet type="android-gradle" name="Android-Gradle">
  5 + <configuration>
  6 + <option name="GRADLE_PROJECT_PATH" value=":react-native-image-crop-picker" />
  7 + <option name="LAST_SUCCESSFUL_SYNC_AGP_VERSION" value="3.5.2" />
  8 + <option name="LAST_KNOWN_AGP_VERSION" value="3.5.2" />
  9 + </configuration>
  10 + </facet>
  11 + <facet type="android" name="Android">
  12 + <configuration>
  13 + <option name="SELECTED_BUILD_VARIANT" value="debug" />
  14 + <option name="ASSEMBLE_TASK_NAME" value="assembleDebug" />
  15 + <option name="COMPILE_JAVA_TASK_NAME" value="compileDebugSources" />
  16 + <afterSyncTasks>
  17 + <task>generateDebugSources</task>
  18 + </afterSyncTasks>
  19 + <option name="ALLOW_USER_CONFIGURATION" value="false" />
  20 + <option name="MANIFEST_FILE_RELATIVE_PATH" value="/src/main/AndroidManifest.xml" />
  21 + <option name="RES_FOLDER_RELATIVE_PATH" value="/src/main/res" />
  22 + <option name="RES_FOLDERS_RELATIVE_PATH" value="file://$MODULE_DIR$/src/main/res" />
  23 + <option name="TEST_RES_FOLDERS_RELATIVE_PATH" value="" />
  24 + <option name="ASSETS_FOLDER_RELATIVE_PATH" value="/src/main/assets" />
  25 + <option name="PROJECT_TYPE" value="1" />
  26 + </configuration>
  27 + </facet>
  28 + </component>
  29 + <component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_7">
  30 + <output url="file://$MODULE_DIR$/build/intermediates/javac/debug/classes" />
  31 + <output-test url="file://$MODULE_DIR$/build/intermediates/javac/debugUnitTest/classes" />
  32 + <exclude-output />
  33 + <content url="file://$MODULE_DIR$">
  34 + <sourceFolder url="file://$MODULE_DIR$/build/generated/ap_generated_sources/debug/out" isTestSource="false" generated="true" />
  35 + <sourceFolder url="file://$MODULE_DIR$/build/generated/aidl_source_output_dir/debug/compileDebugAidl/out" isTestSource="false" generated="true" />
  36 + <sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/debug" isTestSource="false" generated="true" />
  37 + <sourceFolder url="file://$MODULE_DIR$/build/generated/renderscript_source_output_dir/debug/compileDebugRenderscript/out" isTestSource="false" generated="true" />
  38 + <sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/debug" type="java-resource" generated="true" />
  39 + <sourceFolder url="file://$MODULE_DIR$/build/generated/res/resValues/debug" type="java-resource" generated="true" />
  40 + <sourceFolder url="file://$MODULE_DIR$/build/generated/ap_generated_sources/debugAndroidTest/out" isTestSource="true" generated="true" />
  41 + <sourceFolder url="file://$MODULE_DIR$/build/generated/aidl_source_output_dir/debugAndroidTest/compileDebugAndroidTestAidl/out" isTestSource="true" generated="true" />
  42 + <sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/androidTest/debug" isTestSource="true" generated="true" />
  43 + <sourceFolder url="file://$MODULE_DIR$/build/generated/renderscript_source_output_dir/debugAndroidTest/compileDebugAndroidTestRenderscript/out" isTestSource="true" generated="true" />
  44 + <sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/androidTest/debug" type="java-test-resource" generated="true" />
  45 + <sourceFolder url="file://$MODULE_DIR$/build/generated/res/resValues/androidTest/debug" type="java-test-resource" generated="true" />
  46 + <sourceFolder url="file://$MODULE_DIR$/build/generated/ap_generated_sources/debugUnitTest/out" isTestSource="true" generated="true" />
  47 + <sourceFolder url="file://$MODULE_DIR$/src/debug/res" type="java-resource" />
  48 + <sourceFolder url="file://$MODULE_DIR$/src/debug/resources" type="java-resource" />
  49 + <sourceFolder url="file://$MODULE_DIR$/src/debug/assets" type="java-resource" />
  50 + <sourceFolder url="file://$MODULE_DIR$/src/debug/aidl" isTestSource="false" />
  51 + <sourceFolder url="file://$MODULE_DIR$/src/debug/java" isTestSource="false" />
  52 + <sourceFolder url="file://$MODULE_DIR$/src/debug/rs" isTestSource="false" />
  53 + <sourceFolder url="file://$MODULE_DIR$/src/debug/shaders" isTestSource="false" />
  54 + <sourceFolder url="file://$MODULE_DIR$/src/androidTestDebug/res" type="java-test-resource" />
  55 + <sourceFolder url="file://$MODULE_DIR$/src/androidTestDebug/resources" type="java-test-resource" />
  56 + <sourceFolder url="file://$MODULE_DIR$/src/androidTestDebug/assets" type="java-test-resource" />
  57 + <sourceFolder url="file://$MODULE_DIR$/src/androidTestDebug/aidl" isTestSource="true" />
  58 + <sourceFolder url="file://$MODULE_DIR$/src/androidTestDebug/java" isTestSource="true" />
  59 + <sourceFolder url="file://$MODULE_DIR$/src/androidTestDebug/rs" isTestSource="true" />
  60 + <sourceFolder url="file://$MODULE_DIR$/src/androidTestDebug/shaders" isTestSource="true" />
  61 + <sourceFolder url="file://$MODULE_DIR$/src/testDebug/res" type="java-test-resource" />
  62 + <sourceFolder url="file://$MODULE_DIR$/src/testDebug/resources" type="java-test-resource" />
  63 + <sourceFolder url="file://$MODULE_DIR$/src/testDebug/assets" type="java-test-resource" />
  64 + <sourceFolder url="file://$MODULE_DIR$/src/testDebug/aidl" isTestSource="true" />
  65 + <sourceFolder url="file://$MODULE_DIR$/src/testDebug/java" isTestSource="true" />
  66 + <sourceFolder url="file://$MODULE_DIR$/src/testDebug/rs" isTestSource="true" />
  67 + <sourceFolder url="file://$MODULE_DIR$/src/testDebug/shaders" isTestSource="true" />
  68 + <sourceFolder url="file://$MODULE_DIR$/src/main/res" type="java-resource" />
  69 + <sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
  70 + <sourceFolder url="file://$MODULE_DIR$/src/main/assets" type="java-resource" />
  71 + <sourceFolder url="file://$MODULE_DIR$/src/main/aidl" isTestSource="false" />
  72 + <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
  73 + <sourceFolder url="file://$MODULE_DIR$/src/main/rs" isTestSource="false" />
  74 + <sourceFolder url="file://$MODULE_DIR$/src/main/shaders" isTestSource="false" />
  75 + <sourceFolder url="file://$MODULE_DIR$/src/androidTest/res" type="java-test-resource" />
  76 + <sourceFolder url="file://$MODULE_DIR$/src/androidTest/resources" type="java-test-resource" />
  77 + <sourceFolder url="file://$MODULE_DIR$/src/androidTest/assets" type="java-test-resource" />
  78 + <sourceFolder url="file://$MODULE_DIR$/src/androidTest/aidl" isTestSource="true" />
  79 + <sourceFolder url="file://$MODULE_DIR$/src/androidTest/java" isTestSource="true" />
  80 + <sourceFolder url="file://$MODULE_DIR$/src/androidTest/rs" isTestSource="true" />
  81 + <sourceFolder url="file://$MODULE_DIR$/src/androidTest/shaders" isTestSource="true" />
  82 + <sourceFolder url="file://$MODULE_DIR$/src/test/res" type="java-test-resource" />
  83 + <sourceFolder url="file://$MODULE_DIR$/src/test/resources" type="java-test-resource" />
  84 + <sourceFolder url="file://$MODULE_DIR$/src/test/assets" type="java-test-resource" />
  85 + <sourceFolder url="file://$MODULE_DIR$/src/test/aidl" isTestSource="true" />
  86 + <sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
  87 + <sourceFolder url="file://$MODULE_DIR$/src/test/rs" isTestSource="true" />
  88 + <sourceFolder url="file://$MODULE_DIR$/src/test/shaders" isTestSource="true" />
  89 + <excludeFolder url="file://$MODULE_DIR$/build" />
  90 + </content>
  91 + <orderEntry type="jdk" jdkName="Android API 28 Platform" jdkType="Android SDK" />
  92 + <orderEntry type="sourceFolder" forTests="false" />
  93 + <orderEntry type="library" name="Gradle: com.facebook.infer.annotation:infer-annotation:0.11.2@jar" level="project" />
  94 + <orderEntry type="library" name="Gradle: com.google.code.findbugs:jsr305:3.0.2@jar" level="project" />
  95 + <orderEntry type="library" name="Gradle: javax.inject:javax.inject:1@jar" level="project" />
  96 + <orderEntry type="library" name="Gradle: com.android.support:collections:28.0.0@jar" level="project" />
  97 + <orderEntry type="library" name="Gradle: android.arch.lifecycle:common:1.1.1@jar" level="project" />
  98 + <orderEntry type="library" name="Gradle: android.arch.core:common:1.1.1@jar" level="project" />
  99 + <orderEntry type="library" name="Gradle: com.android.support:support-annotations:28.0.0@jar" level="project" />
  100 + <orderEntry type="library" name="Gradle: com.squareup.okhttp3:okhttp-urlconnection:3.12.1@jar" level="project" />
  101 + <orderEntry type="library" name="Gradle: com.squareup.okhttp3:okhttp:3.12.1@jar" level="project" />
  102 + <orderEntry type="library" name="Gradle: com.squareup.okio:okio:1.15.0@jar" level="project" />
  103 + <orderEntry type="library" name="Gradle: com.facebook.react:react-native:0.59.9@aar" level="project" />
  104 + <orderEntry type="library" name="Gradle: com.github.yalantis:ucrop:2.2.2-native@aar" level="project" />
  105 + <orderEntry type="library" name="Gradle: com.android.support:appcompat-v7:28.0.0@aar" level="project" />
  106 + <orderEntry type="library" name="Gradle: com.android.support:support-fragment:28.0.0@aar" level="project" />
  107 + <orderEntry type="library" name="Gradle: com.android.support:animated-vector-drawable:28.0.0@aar" level="project" />
  108 + <orderEntry type="library" name="Gradle: com.android.support:support-core-ui:28.0.0@aar" level="project" />
  109 + <orderEntry type="library" name="Gradle: com.android.support:support-core-utils:28.0.0@aar" level="project" />
  110 + <orderEntry type="library" name="Gradle: com.android.support:support-vector-drawable:28.0.0@aar" level="project" />
  111 + <orderEntry type="library" name="Gradle: com.android.support:loader:28.0.0@aar" level="project" />
  112 + <orderEntry type="library" name="Gradle: com.android.support:viewpager:28.0.0@aar" level="project" />
  113 + <orderEntry type="library" name="Gradle: com.android.support:coordinatorlayout:28.0.0@aar" level="project" />
  114 + <orderEntry type="library" name="Gradle: com.android.support:drawerlayout:28.0.0@aar" level="project" />
  115 + <orderEntry type="library" name="Gradle: com.android.support:slidingpanelayout:28.0.0@aar" level="project" />
  116 + <orderEntry type="library" name="Gradle: com.android.support:customview:28.0.0@aar" level="project" />
  117 + <orderEntry type="library" name="Gradle: com.android.support:swiperefreshlayout:28.0.0@aar" level="project" />
  118 + <orderEntry type="library" name="Gradle: com.android.support:asynclayoutinflater:28.0.0@aar" level="project" />
  119 + <orderEntry type="library" name="Gradle: com.android.support:support-compat:28.0.0@aar" level="project" />
  120 + <orderEntry type="library" name="Gradle: com.android.support:versionedparcelable:28.0.0@aar" level="project" />
  121 + <orderEntry type="library" name="Gradle: com.android.support:cursoradapter:28.0.0@aar" level="project" />
  122 + <orderEntry type="library" name="Gradle: android.arch.lifecycle:runtime:1.1.1@aar" level="project" />
  123 + <orderEntry type="library" name="Gradle: com.android.support:documentfile:28.0.0@aar" level="project" />
  124 + <orderEntry type="library" name="Gradle: com.android.support:localbroadcastmanager:28.0.0@aar" level="project" />
  125 + <orderEntry type="library" name="Gradle: com.android.support:print:28.0.0@aar" level="project" />
  126 + <orderEntry type="library" name="Gradle: android.arch.lifecycle:viewmodel:1.1.1@aar" level="project" />
  127 + <orderEntry type="library" name="Gradle: android.arch.lifecycle:livedata:1.1.1@aar" level="project" />
  128 + <orderEntry type="library" name="Gradle: android.arch.lifecycle:livedata-core:1.1.1@aar" level="project" />
  129 + <orderEntry type="library" name="Gradle: android.arch.core:runtime:1.1.1@aar" level="project" />
  130 + <orderEntry type="library" name="Gradle: com.android.support:interpolator:28.0.0@aar" level="project" />
  131 + <orderEntry type="library" name="Gradle: com.facebook.fresco:fresco:1.10.0@aar" level="project" />
  132 + <orderEntry type="library" name="Gradle: com.facebook.fresco:fbcore:1.10.0@aar" level="project" />
  133 + <orderEntry type="library" name="Gradle: com.facebook.fresco:drawee:1.10.0@aar" level="project" />
  134 + <orderEntry type="library" name="Gradle: com.facebook.fresco:imagepipeline:1.10.0@aar" level="project" />
  135 + <orderEntry type="library" name="Gradle: com.facebook.fresco:imagepipeline-base:1.10.0@aar" level="project" />
  136 + <orderEntry type="library" name="Gradle: com.facebook.soloader:soloader:0.6.0@aar" level="project" />
  137 + <orderEntry type="library" name="Gradle: com.facebook.fresco:imagepipeline-okhttp3:1.10.0@aar" level="project" />
  138 + </component>
  139 +</module>
  1 +<manifest xmlns:android="http://schemas.android.com/apk/res/android"
  2 + package="com.reactnative.ivpusic.imagepicker">
  3 +
  4 + <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
  5 +
  6 + <application>
  7 +
  8 + <provider
  9 + android:name="android.support.v4.content.FileProvider"
  10 + android:authorities="${applicationId}.provider"
  11 + android:exported="false"
  12 + android:grantUriPermissions="true">
  13 + <meta-data
  14 + android:name="android.support.FILE_PROVIDER_PATHS"
  15 + android:resource="@xml/provider_paths" />
  16 + </provider>
  17 +
  18 + <activity
  19 + android:name="com.yalantis.ucrop.UCropActivity"
  20 + android:theme="@style/Theme.AppCompat.Light.NoActionBar" />
  21 + </application>
  22 +
  23 +</manifest>
  1 +package com.reactnative.ivpusic.imagepicker;
  2 +
  3 +import android.app.Activity;
  4 +import android.graphics.Bitmap;
  5 +import android.graphics.BitmapFactory;
  6 +import android.graphics.Matrix;
  7 +import android.media.ExifInterface;
  8 +import android.os.Environment;
  9 +import android.util.Log;
  10 +
  11 +import com.facebook.react.bridge.Promise;
  12 +import com.facebook.react.bridge.ReadableMap;
  13 +
  14 +import java.io.BufferedOutputStream;
  15 +import java.io.File;
  16 +import java.io.FileOutputStream;
  17 +import java.io.IOException;
  18 +import java.io.OutputStream;
  19 +import java.util.Arrays;
  20 +import java.util.List;
  21 +import java.util.UUID;
  22 +
  23 +/**
  24 + * Created by ipusic on 12/27/16.
  25 + */
  26 +
  27 +class Compression {
  28 +
  29 + File resize(String originalImagePath, int maxWidth, int maxHeight, int quality) throws IOException {
  30 + Bitmap original = BitmapFactory.decodeFile(originalImagePath);
  31 +
  32 + int width = original.getWidth();
  33 + int height = original.getHeight();
  34 +
  35 + // Use original image exif orientation data to preserve image orientation for the resized bitmap
  36 + ExifInterface originalExif = new ExifInterface(originalImagePath);
  37 + int originalOrientation = originalExif.getAttributeInt(ExifInterface.TAG_ORIENTATION, 1);
  38 +
  39 + Matrix rotationMatrix = new Matrix();
  40 + int rotationAngleInDegrees = getRotationInDegreesForOrientationTag(originalOrientation);
  41 + rotationMatrix.postRotate(rotationAngleInDegrees);
  42 +
  43 + float ratioBitmap = (float) width / (float) height;
  44 + float ratioMax = (float) maxWidth / (float) maxHeight;
  45 +
  46 + int finalWidth = maxWidth;
  47 + int finalHeight = maxHeight;
  48 +
  49 + if (ratioMax > 1) {
  50 + finalWidth = (int) ((float) maxHeight * ratioBitmap);
  51 + } else {
  52 + finalHeight = (int) ((float) maxWidth / ratioBitmap);
  53 + }
  54 +
  55 + Bitmap resized = Bitmap.createScaledBitmap(original, finalWidth, finalHeight, true);
  56 + resized = Bitmap.createBitmap(resized, 0, 0, finalWidth, finalHeight, rotationMatrix, true);
  57 + File resizeImageFile = new File(Environment.getExternalStoragePublicDirectory(
  58 + Environment.DIRECTORY_PICTURES), UUID.randomUUID() + ".jpg");
  59 +
  60 + OutputStream os = new BufferedOutputStream(new FileOutputStream(resizeImageFile));
  61 + resized.compress(Bitmap.CompressFormat.JPEG, quality, os);
  62 +
  63 + os.close();
  64 + original.recycle();
  65 + resized.recycle();
  66 +
  67 + return resizeImageFile;
  68 + }
  69 +
  70 + int getRotationInDegreesForOrientationTag(int orientationTag) {
  71 + switch(orientationTag){
  72 + case ExifInterface.ORIENTATION_ROTATE_90:
  73 + return 90;
  74 + case ExifInterface.ORIENTATION_ROTATE_270:
  75 + return -90;
  76 + case ExifInterface.ORIENTATION_ROTATE_180:
  77 + return 180;
  78 + default:
  79 + return 0;
  80 + }
  81 + }
  82 +
  83 + File compressImage(final ReadableMap options, final String originalImagePath, final BitmapFactory.Options bitmapOptions) throws IOException {
  84 + Integer maxWidth = options.hasKey("compressImageMaxWidth") ? options.getInt("compressImageMaxWidth") : null;
  85 + Integer maxHeight = options.hasKey("compressImageMaxHeight") ? options.getInt("compressImageMaxHeight") : null;
  86 + Double quality = options.hasKey("compressImageQuality") ? options.getDouble("compressImageQuality") : null;
  87 +
  88 + boolean isLossLess = (quality == null || quality == 1.0);
  89 + boolean useOriginalWidth = (maxWidth == null || maxWidth >= bitmapOptions.outWidth);
  90 + boolean useOriginalHeight = (maxHeight == null || maxHeight >= bitmapOptions.outHeight);
  91 +
  92 + List knownMimes = Arrays.asList("image/jpeg", "image/jpg", "image/png", "image/gif", "image/tiff");
  93 + boolean isKnownMimeType = (bitmapOptions.outMimeType != null && knownMimes.contains(bitmapOptions.outMimeType.toLowerCase()));
  94 +
  95 + if (isLossLess && useOriginalWidth && useOriginalHeight && isKnownMimeType) {
  96 + Log.d("image-crop-picker", "Skipping image compression");
  97 + return new File(originalImagePath);
  98 + }
  99 +
  100 + Log.d("image-crop-picker", "Image compression activated");
  101 +
  102 + // compression quality
  103 + int targetQuality = quality != null ? (int) (quality * 100) : 100;
  104 + Log.d("image-crop-picker", "Compressing image with quality " + targetQuality);
  105 +
  106 + if (maxWidth == null) {
  107 + maxWidth = bitmapOptions.outWidth;
  108 + } else {
  109 + maxWidth = Math.min(maxWidth, bitmapOptions.outWidth);
  110 + }
  111 +
  112 + if (maxHeight == null) {
  113 + maxHeight = bitmapOptions.outHeight;
  114 + } else {
  115 + maxHeight = Math.min(maxHeight, bitmapOptions.outHeight);
  116 + }
  117 +
  118 + return resize(originalImagePath, maxWidth, maxHeight, targetQuality);
  119 + }
  120 +
  121 + synchronized void compressVideo(final Activity activity, final ReadableMap options, final String originalVideo, final String compressedVideo, final Promise promise) {
  122 + // todo: video compression
  123 + // failed attempt 1: ffmpeg => slow and licensing issues
  124 + promise.resolve(originalVideo);
  125 + }
  126 +}
  1 +package com.reactnative.ivpusic.imagepicker;
  2 +
  3 +import android.media.ExifInterface;
  4 +import android.os.Build;
  5 +
  6 +import com.facebook.react.bridge.WritableMap;
  7 +import com.facebook.react.bridge.WritableNativeMap;
  8 +
  9 +import java.io.IOException;
  10 +import java.util.ArrayList;
  11 +import java.util.Arrays;
  12 +import java.util.List;
  13 +
  14 +import static android.media.ExifInterface.*;
  15 +
  16 +class ExifExtractor {
  17 +
  18 + static WritableMap extract(String path) throws IOException {
  19 + WritableMap exifData = new WritableNativeMap();
  20 +
  21 + List<String> attributes = getBasicAttributes();
  22 +
  23 + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
  24 + attributes.addAll(getLevel23Attributes());
  25 + }
  26 +
  27 + ExifInterface exif = new ExifInterface(path);
  28 +
  29 + for (String attribute : attributes) {
  30 + String value = exif.getAttribute(attribute);
  31 + exifData.putString(attribute, value);
  32 + }
  33 +
  34 + return exifData;
  35 + }
  36 +
  37 + private static List<String> getBasicAttributes() {
  38 + return new ArrayList<>(Arrays.asList(
  39 + TAG_APERTURE,
  40 + TAG_DATETIME,
  41 + TAG_EXPOSURE_TIME,
  42 + TAG_FLASH,
  43 + TAG_FOCAL_LENGTH,
  44 + TAG_GPS_ALTITUDE,
  45 + TAG_GPS_ALTITUDE_REF,
  46 + TAG_GPS_DATESTAMP,
  47 + TAG_GPS_LATITUDE,
  48 + TAG_GPS_LATITUDE_REF,
  49 + TAG_GPS_LONGITUDE,
  50 + TAG_GPS_LONGITUDE_REF,
  51 + TAG_GPS_PROCESSING_METHOD,
  52 + TAG_GPS_TIMESTAMP,
  53 + TAG_IMAGE_LENGTH,
  54 + TAG_IMAGE_WIDTH,
  55 + TAG_ISO,
  56 + TAG_MAKE,
  57 + TAG_MODEL,
  58 + TAG_ORIENTATION,
  59 + TAG_WHITE_BALANCE
  60 + ));
  61 + }
  62 +
  63 + private static List<String> getLevel23Attributes() {
  64 + return new ArrayList<>(Arrays.asList(
  65 + TAG_DATETIME_DIGITIZED,
  66 + TAG_SUBSEC_TIME,
  67 + TAG_SUBSEC_TIME_DIG,
  68 + TAG_SUBSEC_TIME_ORIG
  69 + ));
  70 + }
  71 +}
  1 +package com.reactnative.ivpusic.imagepicker;
  2 +
  3 +import android.Manifest;
  4 +import android.app.Activity;
  5 +import android.content.ClipData;
  6 +import android.content.Intent;
  7 +import android.content.pm.PackageManager;
  8 +import android.graphics.Bitmap;
  9 +import android.graphics.BitmapFactory;
  10 +import android.graphics.Color;
  11 +import android.media.MediaMetadataRetriever;
  12 +import android.net.Uri;
  13 +import android.os.Build;
  14 +import android.os.Environment;
  15 +import android.provider.MediaStore;
  16 +import android.support.annotation.RequiresApi;
  17 +import android.support.v4.app.ActivityCompat;
  18 +import android.support.v4.content.FileProvider;
  19 +import android.util.Base64;
  20 +import android.webkit.MimeTypeMap;
  21 +import android.content.ContentResolver;
  22 +
  23 +import com.facebook.react.bridge.ActivityEventListener;
  24 +import com.facebook.react.bridge.Callback;
  25 +import com.facebook.react.bridge.Promise;
  26 +import com.facebook.react.bridge.PromiseImpl;
  27 +import com.facebook.react.bridge.ReactApplicationContext;
  28 +import com.facebook.react.bridge.ReactContextBaseJavaModule;
  29 +import com.facebook.react.bridge.ReactMethod;
  30 +import com.facebook.react.bridge.ReadableMap;
  31 +import com.facebook.react.bridge.WritableMap;
  32 +import com.facebook.react.bridge.WritableNativeMap;
  33 +import com.facebook.react.modules.core.PermissionAwareActivity;
  34 +import com.facebook.react.modules.core.PermissionListener;
  35 +import com.yalantis.ucrop.UCrop;
  36 +import com.yalantis.ucrop.UCropActivity;
  37 +
  38 +import java.io.ByteArrayOutputStream;
  39 +import java.io.File;
  40 +import java.io.FileInputStream;
  41 +import java.io.FileNotFoundException;
  42 +import java.io.IOException;
  43 +import java.io.InputStream;
  44 +import java.util.ArrayList;
  45 +import java.util.Arrays;
  46 +import java.util.Collections;
  47 +import java.util.List;
  48 +import java.util.UUID;
  49 +import java.util.concurrent.Callable;
  50 +
  51 +class PickerModule extends ReactContextBaseJavaModule implements ActivityEventListener {
  52 +
  53 + private static final int IMAGE_PICKER_REQUEST = 61110;
  54 + private static final int CAMERA_PICKER_REQUEST = 61111;
  55 + private static final String E_ACTIVITY_DOES_NOT_EXIST = "E_ACTIVITY_DOES_NOT_EXIST";
  56 +
  57 + private static final String E_PICKER_CANCELLED_KEY = "E_PICKER_CANCELLED";
  58 + private static final String E_PICKER_CANCELLED_MSG = "User cancelled image selection";
  59 +
  60 + private static final String E_CALLBACK_ERROR = "E_CALLBACK_ERROR";
  61 + private static final String E_FAILED_TO_SHOW_PICKER = "E_FAILED_TO_SHOW_PICKER";
  62 + private static final String E_FAILED_TO_OPEN_CAMERA = "E_FAILED_TO_OPEN_CAMERA";
  63 + private static final String E_NO_IMAGE_DATA_FOUND = "E_NO_IMAGE_DATA_FOUND";
  64 + private static final String E_CAMERA_IS_NOT_AVAILABLE = "E_CAMERA_IS_NOT_AVAILABLE";
  65 + private static final String E_CANNOT_LAUNCH_CAMERA = "E_CANNOT_LAUNCH_CAMERA";
  66 + private static final String E_PERMISSIONS_MISSING = "E_PERMISSION_MISSING";
  67 + private static final String E_ERROR_WHILE_CLEANING_FILES = "E_ERROR_WHILE_CLEANING_FILES";
  68 +
  69 + private String mediaType = "any";
  70 + private boolean multiple = false;
  71 + private boolean includeBase64 = false;
  72 + private boolean includeExif = false;
  73 + private boolean cropping = false;
  74 + private boolean cropperCircleOverlay = false;
  75 + private boolean freeStyleCropEnabled = false;
  76 + private boolean showCropGuidelines = true;
  77 + private boolean showCropFrame = true;
  78 + private boolean hideBottomControls = false;
  79 + private boolean enableRotationGesture = false;
  80 + private boolean disableCropperColorSetters = false;
  81 + private boolean useFrontCamera = false;
  82 + private ReadableMap options;
  83 +
  84 + //Grey 800
  85 + private final String DEFAULT_TINT = "#424242";
  86 + private String cropperActiveWidgetColor = DEFAULT_TINT;
  87 + private String cropperStatusBarColor = DEFAULT_TINT;
  88 + private String cropperToolbarColor = DEFAULT_TINT;
  89 + private String cropperToolbarTitle = null;
  90 +
  91 + //Light Blue 500
  92 + private final String DEFAULT_WIDGET_COLOR = "#03A9F4";
  93 + private int width = 0;
  94 + private int height = 0;
  95 +
  96 + private Uri mCameraCaptureURI;
  97 + private String mCurrentMediaPath;
  98 + private ResultCollector resultCollector = new ResultCollector();
  99 + private Compression compression = new Compression();
  100 + private ReactApplicationContext reactContext;
  101 +
  102 + PickerModule(ReactApplicationContext reactContext) {
  103 + super(reactContext);
  104 + reactContext.addActivityEventListener(this);
  105 + this.reactContext = reactContext;
  106 + }
  107 +
  108 + private String getTmpDir(Activity activity) {
  109 + String tmpDir = activity.getCacheDir() + "/react-native-image-crop-picker";
  110 + new File(tmpDir).mkdir();
  111 +
  112 + return tmpDir;
  113 + }
  114 +
  115 + @Override
  116 + public String getName() {
  117 + return "ImageCropPicker";
  118 + }
  119 +
  120 + private void setConfiguration(final ReadableMap options) {
  121 + mediaType = options.hasKey("mediaType") ? options.getString("mediaType") : "any";
  122 + multiple = options.hasKey("multiple") && options.getBoolean("multiple");
  123 + includeBase64 = options.hasKey("includeBase64") && options.getBoolean("includeBase64");
  124 + includeExif = options.hasKey("includeExif") && options.getBoolean("includeExif");
  125 + width = options.hasKey("width") ? options.getInt("width") : 0;
  126 + height = options.hasKey("height") ? options.getInt("height") : 0;
  127 + cropping = options.hasKey("cropping") && options.getBoolean("cropping");
  128 + cropperActiveWidgetColor = options.hasKey("cropperActiveWidgetColor") ? options.getString("cropperActiveWidgetColor") : DEFAULT_TINT;
  129 + cropperStatusBarColor = options.hasKey("cropperStatusBarColor") ? options.getString("cropperStatusBarColor") : DEFAULT_TINT;
  130 + cropperToolbarColor = options.hasKey("cropperToolbarColor") ? options.getString("cropperToolbarColor") : DEFAULT_TINT;
  131 + cropperToolbarTitle = options.hasKey("cropperToolbarTitle") ? options.getString("cropperToolbarTitle") : null;
  132 + cropperCircleOverlay = options.hasKey("cropperCircleOverlay") && options.getBoolean("cropperCircleOverlay");
  133 + freeStyleCropEnabled = options.hasKey("freeStyleCropEnabled") && options.getBoolean("freeStyleCropEnabled");
  134 + showCropGuidelines = !options.hasKey("showCropGuidelines") || options.getBoolean("showCropGuidelines");
  135 + showCropFrame = !options.hasKey("showCropFrame") || options.getBoolean("showCropFrame");
  136 + hideBottomControls = options.hasKey("hideBottomControls") && options.getBoolean("hideBottomControls");
  137 + enableRotationGesture = options.hasKey("enableRotationGesture") && options.getBoolean("enableRotationGesture");
  138 + disableCropperColorSetters = options.hasKey("disableCropperColorSetters") && options.getBoolean("disableCropperColorSetters");
  139 + useFrontCamera = options.hasKey("useFrontCamera") && options.getBoolean("useFrontCamera");
  140 + this.options = options;
  141 + }
  142 +
  143 + private void deleteRecursive(File fileOrDirectory) {
  144 + if (fileOrDirectory.isDirectory()) {
  145 + for (File child : fileOrDirectory.listFiles()) {
  146 + deleteRecursive(child);
  147 + }
  148 + }
  149 +
  150 + fileOrDirectory.delete();
  151 + }
  152 +
  153 + @ReactMethod
  154 + public void clean(final Promise promise) {
  155 +
  156 + final Activity activity = getCurrentActivity();
  157 + final PickerModule module = this;
  158 +
  159 + if (activity == null) {
  160 + promise.reject(E_ACTIVITY_DOES_NOT_EXIST, "Activity doesn't exist");
  161 + return;
  162 + }
  163 +
  164 + permissionsCheck(activity, promise, Collections.singletonList(Manifest.permission.WRITE_EXTERNAL_STORAGE), new Callable<Void>() {
  165 + @Override
  166 + public Void call() {
  167 + try {
  168 + File file = new File(module.getTmpDir(activity));
  169 + if (!file.exists()) throw new Exception("File does not exist");
  170 +
  171 + module.deleteRecursive(file);
  172 + promise.resolve(null);
  173 + } catch (Exception ex) {
  174 + ex.printStackTrace();
  175 + promise.reject(E_ERROR_WHILE_CLEANING_FILES, ex.getMessage());
  176 + }
  177 +
  178 + return null;
  179 + }
  180 + });
  181 + }
  182 +
  183 + @ReactMethod
  184 + public void cleanSingle(final String pathToDelete, final Promise promise) {
  185 + if (pathToDelete == null) {
  186 + promise.reject(E_ERROR_WHILE_CLEANING_FILES, "Cannot cleanup empty path");
  187 + return;
  188 + }
  189 +
  190 + final Activity activity = getCurrentActivity();
  191 + final PickerModule module = this;
  192 +
  193 + if (activity == null) {
  194 + promise.reject(E_ACTIVITY_DOES_NOT_EXIST, "Activity doesn't exist");
  195 + return;
  196 + }
  197 +
  198 + permissionsCheck(activity, promise, Collections.singletonList(Manifest.permission.WRITE_EXTERNAL_STORAGE), new Callable<Void>() {
  199 + @Override
  200 + public Void call() throws Exception {
  201 + try {
  202 + String path = pathToDelete;
  203 + final String filePrefix = "file://";
  204 + if (path.startsWith(filePrefix)) {
  205 + path = path.substring(filePrefix.length());
  206 + }
  207 +
  208 + File file = new File(path);
  209 + if (!file.exists()) throw new Exception("File does not exist. Path: " + path);
  210 +
  211 + module.deleteRecursive(file);
  212 + promise.resolve(null);
  213 + } catch (Exception ex) {
  214 + ex.printStackTrace();
  215 + promise.reject(E_ERROR_WHILE_CLEANING_FILES, ex.getMessage());
  216 + }
  217 +
  218 + return null;
  219 + }
  220 + });
  221 + }
  222 +
  223 + private void permissionsCheck(final Activity activity, final Promise promise, final List<String> requiredPermissions, final Callable<Void> callback) {
  224 +
  225 + List<String> missingPermissions = new ArrayList<>();
  226 +
  227 + for (String permission : requiredPermissions) {
  228 + int status = ActivityCompat.checkSelfPermission(activity, permission);
  229 + if (status != PackageManager.PERMISSION_GRANTED) {
  230 + missingPermissions.add(permission);
  231 + }
  232 + }
  233 +
  234 + if (!missingPermissions.isEmpty()) {
  235 +
  236 + ((PermissionAwareActivity) activity).requestPermissions(missingPermissions.toArray(new String[missingPermissions.size()]), 1, new PermissionListener() {
  237 +
  238 + @Override
  239 + public boolean onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
  240 + if (requestCode == 1) {
  241 +
  242 + for (int grantResult : grantResults) {
  243 + if (grantResult == PackageManager.PERMISSION_DENIED) {
  244 + promise.reject(E_PERMISSIONS_MISSING, "Required permission missing");
  245 + return true;
  246 + }
  247 + }
  248 +
  249 + try {
  250 + callback.call();
  251 + } catch (Exception e) {
  252 + promise.reject(E_CALLBACK_ERROR, "Unknown error", e);
  253 + }
  254 + }
  255 +
  256 + return true;
  257 + }
  258 + });
  259 +
  260 + return;
  261 + }
  262 +
  263 + // all permissions granted
  264 + try {
  265 + callback.call();
  266 + } catch (Exception e) {
  267 + promise.reject(E_CALLBACK_ERROR, "Unknown error", e);
  268 + }
  269 + }
  270 +
  271 + @ReactMethod
  272 + public void openCamera(final ReadableMap options, final Promise promise) {
  273 + final Activity activity = getCurrentActivity();
  274 +
  275 + if (activity == null) {
  276 + promise.reject(E_ACTIVITY_DOES_NOT_EXIST, "Activity doesn't exist");
  277 + return;
  278 + }
  279 +
  280 + if (!isCameraAvailable(activity)) {
  281 + promise.reject(E_CAMERA_IS_NOT_AVAILABLE, "Camera not available");
  282 + return;
  283 + }
  284 +
  285 + setConfiguration(options);
  286 + resultCollector.setup(promise, false);
  287 +
  288 + permissionsCheck(activity, promise, Arrays.asList(Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE), new Callable<Void>() {
  289 + @Override
  290 + public Void call() {
  291 + initiateCamera(activity);
  292 + return null;
  293 + }
  294 + });
  295 + }
  296 +
  297 + private void initiateCamera(Activity activity) {
  298 +
  299 + try {
  300 + String intent;
  301 + File dataFile;
  302 +
  303 + if (mediaType.equals("video")) {
  304 + intent = MediaStore.ACTION_VIDEO_CAPTURE;
  305 + dataFile = createVideoFile();
  306 + } else {
  307 + intent = MediaStore.ACTION_IMAGE_CAPTURE;
  308 + dataFile = createImageFile();
  309 + }
  310 +
  311 + Intent cameraIntent = new Intent(intent);
  312 +
  313 + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
  314 + mCameraCaptureURI = Uri.fromFile(dataFile);
  315 + } else {
  316 + mCameraCaptureURI = FileProvider.getUriForFile(activity,
  317 + activity.getApplicationContext().getPackageName() + ".provider",
  318 + dataFile);
  319 + }
  320 +
  321 + cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, mCameraCaptureURI);
  322 +
  323 + if (this.useFrontCamera) {
  324 + cameraIntent.putExtra("android.intent.extras.CAMERA_FACING", 1);
  325 + cameraIntent.putExtra("android.intent.extras.LENS_FACING_FRONT", 1);
  326 + cameraIntent.putExtra("android.intent.extra.USE_FRONT_CAMERA", true);
  327 + }
  328 +
  329 + if (cameraIntent.resolveActivity(activity.getPackageManager()) == null) {
  330 + resultCollector.notifyProblem(E_CANNOT_LAUNCH_CAMERA, "Cannot launch camera");
  331 + return;
  332 + }
  333 +
  334 + activity.startActivityForResult(cameraIntent, CAMERA_PICKER_REQUEST);
  335 + } catch (Exception e) {
  336 + resultCollector.notifyProblem(E_FAILED_TO_OPEN_CAMERA, e);
  337 + }
  338 +
  339 + }
  340 +
  341 + private void initiatePicker(final Activity activity) {
  342 + try {
  343 + final Intent galleryIntent = new Intent(Intent.ACTION_GET_CONTENT);
  344 +
  345 + if (cropping || mediaType.equals("photo")) {
  346 + final Intent libraryIntent = new Intent(Intent.ACTION_PICK,MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
  347 + activity.startActivityForResult(libraryIntent, IMAGE_PICKER_REQUEST);
  348 + return;
  349 + }
  350 +
  351 + // if (cropping || mediaType.equals("photo")) {
  352 + // galleryIntent.setType("image/*");
  353 + // } else
  354 + if (mediaType.equals("video")) {
  355 + galleryIntent.setType("video/*");
  356 + } else {
  357 + galleryIntent.setType("*/*");
  358 + String[] mimetypes = {"image/*", "video/*"};
  359 + galleryIntent.putExtra(Intent.EXTRA_MIME_TYPES, mimetypes);
  360 + }
  361 +
  362 + galleryIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
  363 + galleryIntent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, multiple);
  364 + galleryIntent.addCategory(Intent.CATEGORY_OPENABLE);
  365 +
  366 + final Intent chooserIntent = Intent.createChooser(galleryIntent, "Pick an image");
  367 + activity.startActivityForResult(chooserIntent, IMAGE_PICKER_REQUEST);
  368 + } catch (Exception e) {
  369 + resultCollector.notifyProblem(E_FAILED_TO_SHOW_PICKER, e);
  370 + }
  371 + }
  372 +
  373 + @ReactMethod
  374 + public void openPicker(final ReadableMap options, final Promise promise) {
  375 + final Activity activity = getCurrentActivity();
  376 +
  377 + if (activity == null) {
  378 + promise.reject(E_ACTIVITY_DOES_NOT_EXIST, "Activity doesn't exist");
  379 + return;
  380 + }
  381 +
  382 + setConfiguration(options);
  383 + resultCollector.setup(promise, multiple);
  384 +
  385 + permissionsCheck(activity, promise, Collections.singletonList(Manifest.permission.WRITE_EXTERNAL_STORAGE), new Callable<Void>() {
  386 + @Override
  387 + public Void call() {
  388 + initiatePicker(activity);
  389 + return null;
  390 + }
  391 + });
  392 + }
  393 +
  394 + @ReactMethod
  395 + public void openCropper(final ReadableMap options, final Promise promise) {
  396 + final Activity activity = getCurrentActivity();
  397 +
  398 + if (activity == null) {
  399 + promise.reject(E_ACTIVITY_DOES_NOT_EXIST, "Activity doesn't exist");
  400 + return;
  401 + }
  402 +
  403 + setConfiguration(options);
  404 + resultCollector.setup(promise, false);
  405 +
  406 + Uri uri = Uri.parse(options.getString("path"));
  407 + startCropping(activity, uri);
  408 + }
  409 +
  410 + private String getBase64StringFromFile(String absoluteFilePath) {
  411 + InputStream inputStream;
  412 +
  413 + try {
  414 + inputStream = new FileInputStream(new File(absoluteFilePath));
  415 + } catch (FileNotFoundException e) {
  416 + e.printStackTrace();
  417 + return null;
  418 + }
  419 +
  420 + byte[] bytes;
  421 + byte[] buffer = new byte[8192];
  422 + int bytesRead;
  423 + ByteArrayOutputStream output = new ByteArrayOutputStream();
  424 +
  425 + try {
  426 + while ((bytesRead = inputStream.read(buffer)) != -1) {
  427 + output.write(buffer, 0, bytesRead);
  428 + }
  429 + } catch (IOException e) {
  430 + e.printStackTrace();
  431 + }
  432 +
  433 + bytes = output.toByteArray();
  434 + return Base64.encodeToString(bytes, Base64.NO_WRAP);
  435 + }
  436 +
  437 + private String getMimeType(String url) {
  438 + String mimeType = null;
  439 + Uri uri = Uri.fromFile(new File(url));
  440 + if (uri.getScheme().equals(ContentResolver.SCHEME_CONTENT)) {
  441 + ContentResolver cr = this.reactContext.getContentResolver();
  442 + mimeType = cr.getType(uri);
  443 + } else {
  444 + String fileExtension = MimeTypeMap.getFileExtensionFromUrl(uri
  445 + .toString());
  446 + if (fileExtension != null) {
  447 + mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(fileExtension.toLowerCase());
  448 + }
  449 + }
  450 + return mimeType;
  451 + }
  452 +
  453 + private WritableMap getSelection(Activity activity, Uri uri, boolean isCamera) throws Exception {
  454 + String path = resolveRealPath(activity, uri, isCamera);
  455 + if (path == null || path.isEmpty()) {
  456 + throw new Exception("Cannot resolve asset path.");
  457 + }
  458 +
  459 + String mime = getMimeType(path);
  460 + if (mime != null && mime.startsWith("video/")) {
  461 + getVideo(activity, path, mime);
  462 + return null;
  463 + }
  464 +
  465 + return getImage(activity, path);
  466 + }
  467 +
  468 + private void getAsyncSelection(final Activity activity, Uri uri, boolean isCamera) throws Exception {
  469 + String path = resolveRealPath(activity, uri, isCamera);
  470 + if (path == null || path.isEmpty()) {
  471 + resultCollector.notifyProblem(E_NO_IMAGE_DATA_FOUND, "Cannot resolve asset path.");
  472 + return;
  473 + }
  474 +
  475 + String mime = getMimeType(path);
  476 + if (mime != null && mime.startsWith("video/")) {
  477 + getVideo(activity, path, mime);
  478 + return;
  479 + }
  480 +
  481 + resultCollector.notifySuccess(getImage(activity, path));
  482 + }
  483 +
  484 + private Bitmap validateVideo(String path) throws Exception {
  485 + MediaMetadataRetriever retriever = new MediaMetadataRetriever();
  486 + retriever.setDataSource(path);
  487 + Bitmap bmp = retriever.getFrameAtTime();
  488 +
  489 + if (bmp == null) {
  490 + throw new Exception("Cannot retrieve video data");
  491 + }
  492 +
  493 + return bmp;
  494 + }
  495 +
  496 + private void getVideo(final Activity activity, final String path, final String mime) throws Exception {
  497 + validateVideo(path);
  498 + final String compressedVideoPath = getTmpDir(activity) + "/" + UUID.randomUUID().toString() + ".mp4";
  499 +
  500 + new Thread(new Runnable() {
  501 + @Override
  502 + public void run() {
  503 + compression.compressVideo(activity, options, path, compressedVideoPath, new PromiseImpl(new Callback() {
  504 + @Override
  505 + public void invoke(Object... args) {
  506 + String videoPath = (String) args[0];
  507 +
  508 + try {
  509 + Bitmap bmp = validateVideo(videoPath);
  510 + long modificationDate = new File(videoPath).lastModified();
  511 +
  512 + WritableMap video = new WritableNativeMap();
  513 + video.putInt("width", bmp.getWidth());
  514 + video.putInt("height", bmp.getHeight());
  515 + video.putString("mime", mime);
  516 + video.putInt("size", (int) new File(videoPath).length());
  517 + video.putString("path", "file://" + videoPath);
  518 + video.putString("modificationDate", String.valueOf(modificationDate));
  519 +
  520 + resultCollector.notifySuccess(video);
  521 + } catch (Exception e) {
  522 + resultCollector.notifyProblem(E_NO_IMAGE_DATA_FOUND, e);
  523 + }
  524 + }
  525 + }, new Callback() {
  526 + @Override
  527 + public void invoke(Object... args) {
  528 + WritableNativeMap ex = (WritableNativeMap) args[0];
  529 + resultCollector.notifyProblem(ex.getString("code"), ex.getString("message"));
  530 + }
  531 + }));
  532 + }
  533 + }).run();
  534 + }
  535 +
  536 + private String resolveRealPath(Activity activity, Uri uri, boolean isCamera) throws IOException {
  537 + String path;
  538 +
  539 + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
  540 + path = RealPathUtil.getRealPathFromURI(activity, uri);
  541 + } else {
  542 + if (isCamera) {
  543 + Uri mediaUri = Uri.parse(mCurrentMediaPath);
  544 + path = mediaUri.getPath();
  545 + } else {
  546 + path = RealPathUtil.getRealPathFromURI(activity, uri);
  547 + }
  548 + }
  549 +
  550 + return path;
  551 + }
  552 +
  553 + private BitmapFactory.Options validateImage(String path) throws Exception {
  554 + BitmapFactory.Options options = new BitmapFactory.Options();
  555 + options.inJustDecodeBounds = true;
  556 + options.inPreferredConfig = Bitmap.Config.RGB_565;
  557 + options.inDither = true;
  558 +
  559 + BitmapFactory.decodeFile(path, options);
  560 +
  561 + if (options.outMimeType == null || options.outWidth == 0 || options.outHeight == 0) {
  562 + throw new Exception("Invalid image selected");
  563 + }
  564 +
  565 + return options;
  566 + }
  567 +
  568 + private WritableMap getImage(final Activity activity, String path) throws Exception {
  569 + WritableMap image = new WritableNativeMap();
  570 +
  571 + if (path.startsWith("http://") || path.startsWith("https://")) {
  572 + throw new Exception("Cannot select remote files");
  573 + }
  574 + BitmapFactory.Options original = validateImage(path);
  575 +
  576 + // if compression options are provided image will be compressed. If none options is provided,
  577 + // then original image will be returned
  578 + File compressedImage = compression.compressImage(options, path, original);
  579 + String compressedImagePath = compressedImage.getPath();
  580 + BitmapFactory.Options options = validateImage(compressedImagePath);
  581 + long modificationDate = new File(path).lastModified();
  582 +
  583 + image.putString("path", "file://" + compressedImagePath);
  584 + image.putInt("width", options.outWidth);
  585 + image.putInt("height", options.outHeight);
  586 + image.putString("mime", options.outMimeType);
  587 + image.putInt("size", (int) new File(compressedImagePath).length());
  588 + image.putString("modificationDate", String.valueOf(modificationDate));
  589 +
  590 + if (includeBase64) {
  591 + image.putString("data", getBase64StringFromFile(compressedImagePath));
  592 + }
  593 +
  594 + if (includeExif) {
  595 + try {
  596 + WritableMap exif = ExifExtractor.extract(path);
  597 + image.putMap("exif", exif);
  598 + } catch (Exception ex) {
  599 + ex.printStackTrace();
  600 + }
  601 + }
  602 +
  603 + return image;
  604 + }
  605 +
  606 + private void configureCropperColors(UCrop.Options options) {
  607 + int activeWidgetColor = Color.parseColor(cropperActiveWidgetColor);
  608 + int toolbarColor = Color.parseColor(cropperToolbarColor);
  609 + int statusBarColor = Color.parseColor(cropperStatusBarColor);
  610 + options.setToolbarColor(toolbarColor);
  611 + options.setStatusBarColor(statusBarColor);
  612 + if (activeWidgetColor == Color.parseColor(DEFAULT_TINT)) {
  613 + /*
  614 + Default tint is grey => use a more flashy color that stands out more as the call to action
  615 + Here we use 'Light Blue 500' from https://material.google.com/style/color.html#color-color-palette
  616 + */
  617 + options.setActiveWidgetColor(Color.parseColor(DEFAULT_WIDGET_COLOR));
  618 + } else {
  619 + //If they pass a custom tint color in, we use this for everything
  620 + options.setActiveWidgetColor(activeWidgetColor);
  621 + }
  622 + }
  623 +
  624 + private void startCropping(Activity activity, Uri uri) {
  625 + UCrop.Options options = new UCrop.Options();
  626 + options.setCompressionFormat(Bitmap.CompressFormat.JPEG);
  627 + options.setCompressionQuality(100);
  628 + options.setCircleDimmedLayer(cropperCircleOverlay);
  629 + options.setFreeStyleCropEnabled(freeStyleCropEnabled);
  630 + options.setShowCropGrid(showCropGuidelines);
  631 + options.setShowCropFrame(showCropFrame);
  632 + options.setHideBottomControls(hideBottomControls);
  633 + if (cropperToolbarTitle != null) {
  634 + options.setToolbarTitle(cropperToolbarTitle);
  635 + }
  636 + if (enableRotationGesture) {
  637 + // UCropActivity.ALL = enable both rotation & scaling
  638 + options.setAllowedGestures(
  639 + UCropActivity.ALL, // When 'scale'-tab active
  640 + UCropActivity.ALL, // When 'rotate'-tab active
  641 + UCropActivity.ALL // When 'aspect ratio'-tab active
  642 + );
  643 + }
  644 + if (!disableCropperColorSetters) {
  645 + configureCropperColors(options);
  646 + }
  647 +
  648 + UCrop uCrop = UCrop
  649 + .of(uri, Uri.fromFile(new File(this.getTmpDir(activity), UUID.randomUUID().toString() + ".jpg")))
  650 + .withOptions(options);
  651 +
  652 + if (width > 0 && height > 0) {
  653 + uCrop.withAspectRatio(width, height);
  654 + }
  655 +
  656 + uCrop.start(activity);
  657 + }
  658 +
  659 + private void imagePickerResult(Activity activity, final int requestCode, final int resultCode, final Intent data) {
  660 + if (resultCode == Activity.RESULT_CANCELED) {
  661 + resultCollector.notifyProblem(E_PICKER_CANCELLED_KEY, E_PICKER_CANCELLED_MSG);
  662 + } else if (resultCode == Activity.RESULT_OK) {
  663 + if (multiple) {
  664 + ClipData clipData = data.getClipData();
  665 +
  666 + try {
  667 + // only one image selected
  668 + if (clipData == null) {
  669 + resultCollector.setWaitCount(1);
  670 + getAsyncSelection(activity, data.getData(), false);
  671 + } else {
  672 + resultCollector.setWaitCount(clipData.getItemCount());
  673 + for (int i = 0; i < clipData.getItemCount(); i++) {
  674 + getAsyncSelection(activity, clipData.getItemAt(i).getUri(), false);
  675 + }
  676 + }
  677 + } catch (Exception ex) {
  678 + resultCollector.notifyProblem(E_NO_IMAGE_DATA_FOUND, ex.getMessage());
  679 + }
  680 +
  681 + } else {
  682 + Uri uri = data.getData();
  683 +
  684 + if (uri == null) {
  685 + resultCollector.notifyProblem(E_NO_IMAGE_DATA_FOUND, "Cannot resolve image url");
  686 + return;
  687 + }
  688 +
  689 + if (cropping) {
  690 + startCropping(activity, uri);
  691 + } else {
  692 + try {
  693 + getAsyncSelection(activity, uri, false);
  694 + } catch (Exception ex) {
  695 + resultCollector.notifyProblem(E_NO_IMAGE_DATA_FOUND, ex.getMessage());
  696 + }
  697 + }
  698 + }
  699 + }
  700 + }
  701 +
  702 + private void cameraPickerResult(Activity activity, final int requestCode, final int resultCode, final Intent data) {
  703 + if (resultCode == Activity.RESULT_CANCELED) {
  704 + resultCollector.notifyProblem(E_PICKER_CANCELLED_KEY, E_PICKER_CANCELLED_MSG);
  705 + } else if (resultCode == Activity.RESULT_OK) {
  706 + Uri uri = mCameraCaptureURI;
  707 +
  708 + if (uri == null) {
  709 + resultCollector.notifyProblem(E_NO_IMAGE_DATA_FOUND, "Cannot resolve image url");
  710 + return;
  711 + }
  712 +
  713 + if (cropping) {
  714 + UCrop.Options options = new UCrop.Options();
  715 + options.setCompressionFormat(Bitmap.CompressFormat.JPEG);
  716 + startCropping(activity, uri);
  717 + } else {
  718 + try {
  719 + resultCollector.setWaitCount(1);
  720 + WritableMap result = getSelection(activity, uri, true);
  721 +
  722 + // If recording a video getSelection handles resultCollector part itself and returns null
  723 + if (result != null) {
  724 + resultCollector.notifySuccess(result);
  725 + }
  726 + } catch (Exception ex) {
  727 + resultCollector.notifyProblem(E_NO_IMAGE_DATA_FOUND, ex.getMessage());
  728 + }
  729 + }
  730 + }
  731 + }
  732 +
  733 + private void croppingResult(Activity activity, final int requestCode, final int resultCode, final Intent data) {
  734 + if (data != null) {
  735 + Uri resultUri = UCrop.getOutput(data);
  736 +
  737 + if (resultUri != null) {
  738 + try {
  739 + if (width > 0 && height > 0) {
  740 + resultUri = Uri.fromFile(compression.resize(resultUri.getPath(), width, height, 100));
  741 + }
  742 +
  743 + WritableMap result = getSelection(activity, resultUri, false);
  744 +
  745 + if (result != null) {
  746 + result.putMap("cropRect", PickerModule.getCroppedRectMap(data));
  747 +
  748 + resultCollector.setWaitCount(1);
  749 + resultCollector.notifySuccess(result);
  750 + } else {
  751 + throw new Exception("Cannot crop video files");
  752 + }
  753 + } catch (Exception ex) {
  754 + resultCollector.notifyProblem(E_NO_IMAGE_DATA_FOUND, ex.getMessage());
  755 + }
  756 + } else {
  757 + resultCollector.notifyProblem(E_NO_IMAGE_DATA_FOUND, "Cannot find image data");
  758 + }
  759 + } else {
  760 + resultCollector.notifyProblem(E_PICKER_CANCELLED_KEY, E_PICKER_CANCELLED_MSG);
  761 + }
  762 + }
  763 +
  764 + @Override
  765 + public void onActivityResult(Activity activity, final int requestCode, final int resultCode, final Intent data) {
  766 + if (requestCode == IMAGE_PICKER_REQUEST) {
  767 + imagePickerResult(activity, requestCode, resultCode, data);
  768 + } else if (requestCode == CAMERA_PICKER_REQUEST) {
  769 + cameraPickerResult(activity, requestCode, resultCode, data);
  770 + } else if (requestCode == UCrop.REQUEST_CROP) {
  771 + croppingResult(activity, requestCode, resultCode, data);
  772 + }
  773 + }
  774 +
  775 + @Override
  776 + public void onNewIntent(Intent intent) {
  777 + }
  778 +
  779 + private boolean isCameraAvailable(Activity activity) {
  780 + return activity.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)
  781 + || activity.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY);
  782 + }
  783 +
  784 + private File createImageFile() throws IOException {
  785 +
  786 + String imageFileName = "image-" + UUID.randomUUID().toString();
  787 + File path = Environment.getExternalStoragePublicDirectory(
  788 + Environment.DIRECTORY_PICTURES);
  789 +
  790 + if (!path.exists() && !path.isDirectory()) {
  791 + path.mkdirs();
  792 + }
  793 +
  794 + File image = File.createTempFile(imageFileName, ".jpg", path);
  795 +
  796 + // Save a file: path for use with ACTION_VIEW intents
  797 + mCurrentMediaPath = "file:" + image.getAbsolutePath();
  798 +
  799 + return image;
  800 +
  801 + }
  802 +
  803 + private File createVideoFile() throws IOException {
  804 +
  805 + String videoFileName = "video-" + UUID.randomUUID().toString();
  806 + File path = Environment.getExternalStoragePublicDirectory(
  807 + Environment.DIRECTORY_PICTURES);
  808 +
  809 + if (!path.exists() && !path.isDirectory()) {
  810 + path.mkdirs();
  811 + }
  812 +
  813 + File video = File.createTempFile(videoFileName, ".mp4", path);
  814 +
  815 + // Save a file: path for use with ACTION_VIEW intents
  816 + mCurrentMediaPath = "file:" + video.getAbsolutePath();
  817 +
  818 + return video;
  819 +
  820 + }
  821 +
  822 + private static WritableMap getCroppedRectMap(Intent data) {
  823 + final int DEFAULT_VALUE = -1;
  824 + final WritableMap map = new WritableNativeMap();
  825 +
  826 + map.putInt("x", data.getIntExtra(UCrop.EXTRA_OUTPUT_OFFSET_X, DEFAULT_VALUE));
  827 + map.putInt("y", data.getIntExtra(UCrop.EXTRA_OUTPUT_OFFSET_Y, DEFAULT_VALUE));
  828 + map.putInt("width", data.getIntExtra(UCrop.EXTRA_OUTPUT_IMAGE_WIDTH, DEFAULT_VALUE));
  829 + map.putInt("height", data.getIntExtra(UCrop.EXTRA_OUTPUT_IMAGE_HEIGHT, DEFAULT_VALUE));
  830 +
  831 + return map;
  832 + }
  833 +}
  1 +package com.reactnative.ivpusic.imagepicker;
  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 +/**
  14 + * Created by ipusic on 5/16/16.
  15 + */
  16 +public class PickerPackage implements ReactPackage {
  17 +
  18 + // Deprecated RN 0.47
  19 + public List<Class<? extends JavaScriptModule>> createJSModules() {
  20 + return Collections.emptyList();
  21 + }
  22 +
  23 + @Override
  24 + public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
  25 + return Collections.emptyList();
  26 + }
  27 +
  28 + @Override
  29 + public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
  30 + List<NativeModule> modules = new ArrayList<>();
  31 + modules.add(new PickerModule(reactContext));
  32 +
  33 + return modules;
  34 + }
  35 +}
  1 +package com.reactnative.ivpusic.imagepicker;
  2 +
  3 +import android.content.ContentUris;
  4 +import android.content.Context;
  5 +import android.database.Cursor;
  6 +import android.net.Uri;
  7 +import android.os.Build;
  8 +import android.os.Environment;
  9 +import android.provider.DocumentsContract;
  10 +import android.provider.MediaStore;
  11 +
  12 +import java.io.File;
  13 +import java.io.FileOutputStream;
  14 +import java.io.IOException;
  15 +import java.io.InputStream;
  16 +
  17 +class RealPathUtil {
  18 + static String getRealPathFromURI(final Context context, final Uri uri) throws IOException {
  19 +
  20 + final boolean isKitKat = Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT;
  21 +
  22 + // DocumentProvider
  23 + if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
  24 + // ExternalStorageProvider
  25 + if (isExternalStorageDocument(uri)) {
  26 + final String docId = DocumentsContract.getDocumentId(uri);
  27 + final String[] split = docId.split(":");
  28 + final String type = split[0];
  29 +
  30 + if ("primary".equalsIgnoreCase(type)) {
  31 + return Environment.getExternalStorageDirectory() + "/" + split[1];
  32 + } else {
  33 + final int splitIndex = docId.indexOf(':', 1);
  34 + final String tag = docId.substring(0, splitIndex);
  35 + final String path = docId.substring(splitIndex + 1);
  36 +
  37 + String nonPrimaryVolume = getPathToNonPrimaryVolume(context, tag);
  38 + if (nonPrimaryVolume != null) {
  39 + String result = nonPrimaryVolume + "/" + path;
  40 + File file = new File(result);
  41 + if (file.exists() && file.canRead()) {
  42 + return result;
  43 + }
  44 + return null;
  45 + }
  46 + }
  47 + }
  48 + // DownloadsProvider
  49 + else if (isDownloadsDocument(uri)) {
  50 + final String id = DocumentsContract.getDocumentId(uri);
  51 + final Uri contentUri = ContentUris.withAppendedId(
  52 + Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
  53 +
  54 + return getDataColumn(context, contentUri, null, null);
  55 + }
  56 + // MediaProvider
  57 + else if (isMediaDocument(uri)) {
  58 + final String docId = DocumentsContract.getDocumentId(uri);
  59 + final String[] split = docId.split(":");
  60 + final String type = split[0];
  61 +
  62 + Uri contentUri = null;
  63 + if ("image".equals(type)) {
  64 + contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
  65 + } else if ("video".equals(type)) {
  66 + contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
  67 + } else if ("audio".equals(type)) {
  68 + contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
  69 + }
  70 +
  71 + final String selection = "_id=?";
  72 + final String[] selectionArgs = new String[] {
  73 + split[1]
  74 + };
  75 +
  76 + return getDataColumn(context, contentUri, selection, selectionArgs);
  77 + }
  78 + }
  79 + // MediaStore (and general)
  80 + else if ("content".equalsIgnoreCase(uri.getScheme())) {
  81 + // Return the remote address
  82 + if (isGooglePhotosUri(uri))
  83 + return uri.getLastPathSegment();
  84 + return getDataColumn(context, uri, null, null);
  85 + }
  86 + // File
  87 + else if ("file".equalsIgnoreCase(uri.getScheme())) {
  88 + return uri.getPath();
  89 + }
  90 +
  91 + return null;
  92 + }
  93 +
  94 + /**
  95 + * If an image/video has been selected from a cloud storage, this method
  96 + * should be call to download the file in the cache folder.
  97 + *
  98 + * @param context The context
  99 + * @param fileName donwloaded file's name
  100 + * @param uri file's URI
  101 + * @return file that has been written
  102 + */
  103 + private static File writeToFile(Context context, String fileName, Uri uri) {
  104 + String tmpDir = context.getCacheDir() + "/react-native-image-crop-picker";
  105 + Boolean created = new File(tmpDir).mkdir();
  106 + fileName = fileName.substring(fileName.lastIndexOf('/') + 1);
  107 + File path = new File(tmpDir);
  108 + File file = new File(path, fileName);
  109 + try {
  110 + FileOutputStream oos = new FileOutputStream(file);
  111 + byte[] buf = new byte[8192];
  112 + InputStream is = context.getContentResolver().openInputStream(uri);
  113 + int c = 0;
  114 + while ((c = is.read(buf, 0, buf.length)) > 0) {
  115 + oos.write(buf, 0, c);
  116 + oos.flush();
  117 + }
  118 + oos.close();
  119 + is.close();
  120 + } catch (Exception e) {
  121 + e.printStackTrace();
  122 + }
  123 + return file;
  124 + }
  125 +
  126 + /**
  127 + * Get the value of the data column for this Uri. This is useful for
  128 + * MediaStore Uris, and other file-based ContentProviders.
  129 + *
  130 + * @param context The context.
  131 + * @param uri The Uri to query.
  132 + * @param selection (Optional) Filter used in the query.
  133 + * @param selectionArgs (Optional) Selection arguments used in the query.
  134 + * @return The value of the _data column, which is typically a file path.
  135 + */
  136 + private static String getDataColumn(Context context, Uri uri, String selection,
  137 + String[] selectionArgs) {
  138 +
  139 + Cursor cursor = null;
  140 + final String[] projection = {
  141 + MediaStore.MediaColumns.DATA,
  142 + MediaStore.MediaColumns.DISPLAY_NAME,
  143 + };
  144 +
  145 + try {
  146 + cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs,
  147 + null);
  148 + if (cursor != null && cursor.moveToFirst()) {
  149 + // Fall back to writing to file if _data column does not exist
  150 + final int index = cursor.getColumnIndex(MediaStore.MediaColumns.DATA);
  151 + String path = index > -1 ? cursor.getString(index) : null;
  152 + if (path != null) {
  153 + return cursor.getString(index);
  154 + } else {
  155 + final int indexDisplayName = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DISPLAY_NAME);
  156 + String fileName = cursor.getString(indexDisplayName);
  157 + File fileWritten = writeToFile(context, fileName, uri);
  158 + return fileWritten.getAbsolutePath();
  159 + }
  160 + }
  161 + } finally {
  162 + if (cursor != null)
  163 + cursor.close();
  164 + }
  165 + return null;
  166 + }
  167 +
  168 +
  169 + /**
  170 + * @param uri The Uri to check.
  171 + * @return Whether the Uri authority is ExternalStorageProvider.
  172 + */
  173 + private static boolean isExternalStorageDocument(Uri uri) {
  174 + return "com.android.externalstorage.documents".equals(uri.getAuthority());
  175 + }
  176 +
  177 + /**
  178 + * @param uri The Uri to check.
  179 + * @return Whether the Uri authority is DownloadsProvider.
  180 + */
  181 + private static boolean isDownloadsDocument(Uri uri) {
  182 + return "com.android.providers.downloads.documents".equals(uri.getAuthority());
  183 + }
  184 +
  185 + /**
  186 + * @param uri The Uri to check.
  187 + * @return Whether the Uri authority is MediaProvider.
  188 + */
  189 + private static boolean isMediaDocument(Uri uri) {
  190 + return "com.android.providers.media.documents".equals(uri.getAuthority());
  191 + }
  192 +
  193 + /**
  194 + * @param uri The Uri to check.
  195 + * @return Whether the Uri authority is Google Photos.
  196 + */
  197 + private static boolean isGooglePhotosUri(Uri uri) {
  198 + return "com.google.android.apps.photos.content".equals(uri.getAuthority());
  199 + }
  200 +
  201 + private static String getPathToNonPrimaryVolume(Context context, String tag) {
  202 + File[] volumes = context.getExternalCacheDirs();
  203 + if (volumes != null) {
  204 + for (File volume : volumes) {
  205 + if (volume != null) {
  206 + String path = volume.getAbsolutePath();
  207 + if (path != null) {
  208 + int index = path.indexOf(tag);
  209 + if (index != -1) {
  210 + return path.substring(0, index) + tag;
  211 + }
  212 + }
  213 + }
  214 + }
  215 + }
  216 + return null;
  217 + }
  218 +
  219 +}
  1 +package com.reactnative.ivpusic.imagepicker;
  2 +
  3 +import android.util.Log;
  4 +
  5 +import com.facebook.react.bridge.Promise;
  6 +import com.facebook.react.bridge.WritableArray;
  7 +import com.facebook.react.bridge.WritableMap;
  8 +import com.facebook.react.bridge.WritableNativeArray;
  9 +
  10 +import java.util.concurrent.atomic.AtomicInteger;
  11 +
  12 +/**
  13 + * Created by ipusic on 12/28/16.
  14 + */
  15 +
  16 +class ResultCollector {
  17 + private Promise promise;
  18 + private int waitCount;
  19 + private boolean multiple;
  20 + private AtomicInteger waitCounter;
  21 + private WritableArray arrayResult;
  22 + private boolean resultSent;
  23 +
  24 + synchronized void setup(Promise promise, boolean multiple) {
  25 + this.promise = promise;
  26 + this.multiple = multiple;
  27 +
  28 + this.resultSent = false;
  29 + this.waitCount = 0;
  30 + this.waitCounter = new AtomicInteger(0);
  31 +
  32 + if (multiple) {
  33 + this.arrayResult = new WritableNativeArray();
  34 + }
  35 + }
  36 +
  37 + // if user has provided "multiple" option, we will wait for X number of result to come,
  38 + // and also return result as an array
  39 + synchronized void setWaitCount(int waitCount) {
  40 + this.waitCount = waitCount;
  41 + this.waitCounter = new AtomicInteger(0);
  42 + }
  43 +
  44 + synchronized private boolean isRequestValid() {
  45 + if (resultSent) {
  46 + Log.w("image-crop-picker", "Skipping result, already sent...");
  47 + return false;
  48 + }
  49 +
  50 + if (promise == null) {
  51 + Log.w("image-crop-picker", "Trying to notify success but promise is not set");
  52 + return false;
  53 + }
  54 +
  55 + return true;
  56 + }
  57 +
  58 + synchronized void notifySuccess(WritableMap result) {
  59 + if (!isRequestValid()) {
  60 + return;
  61 + }
  62 +
  63 + if (multiple) {
  64 + arrayResult.pushMap(result);
  65 + int currentCount = waitCounter.addAndGet(1);
  66 +
  67 + if (currentCount == waitCount) {
  68 + promise.resolve(arrayResult);
  69 + resultSent = true;
  70 + }
  71 + } else {
  72 + promise.resolve(result);
  73 + resultSent = true;
  74 + }
  75 + }
  76 +
  77 + synchronized void notifyProblem(String code, String message) {
  78 + if (!isRequestValid()) {
  79 + return;
  80 + }
  81 +
  82 + Log.e("image-crop-picker", "Promise rejected. " + message);
  83 + promise.reject(code, message);
  84 + resultSent = true;
  85 + }
  86 +
  87 + synchronized void notifyProblem(String code, Throwable throwable) {
  88 + if (!isRequestValid()) {
  89 + return;
  90 + }
  91 +
  92 + Log.e("image-crop-picker", "Promise rejected. " + throwable.getMessage());
  93 + promise.reject(code, throwable);
  94 + resultSent = true;
  95 + }
  96 +}
  1 +<?xml version="1.0" encoding="utf-8"?>
  2 +<paths xmlns:android="http://schemas.android.com/apk/res/android">
  3 + <external-path name="external_files" path="."/>
  4 +</paths>
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<module type="JAVA_MODULE" version="4">
  3 + <component name="NewModuleRootManager" inherit-compiler-output="true">
  4 + <exclude-output />
  5 + <content url="file://$MODULE_DIR$">
  6 + <sourceFolder url="file://$MODULE_DIR$/java" isTestSource="true" />
  7 + </content>
  8 + <orderEntry type="inheritedJdk" />
  9 + <orderEntry type="sourceFolder" forTests="false" />
  10 + </component>
  11 +</module>
  1 +declare module "react-native-image-crop-picker" {
  2 + export interface Options {
  3 + cropping?: boolean;
  4 + width?: number;
  5 + height?: number;
  6 + multiple?: boolean;
  7 + path?: string;
  8 + includeBase64?: boolean;
  9 + includeExif?: boolean;
  10 + avoidEmptySpaceAroundImage?: boolean;
  11 + cropperActiveWidgetColor?: string;
  12 + cropperStatusBarColor?: string;
  13 + cropperToolbarColor?: string;
  14 + cropperToolbarTitle?: string;
  15 + freeStyleCropEnabled?: boolean;
  16 + cropperTintColor?: string;
  17 + cropperCircleOverlay?: boolean;
  18 + disableCropperColorSetters?: boolean;
  19 + maxFiles?: number;
  20 + waitAnimationEnd?: boolean;
  21 + smartAlbums?: string[];
  22 + useFrontCamera?: boolean;
  23 + compressVideoPreset?: string;
  24 + compressImageMaxWidth?: number;
  25 + compressImageMaxHeight?: number;
  26 + compressImageQuality?: number;
  27 + loadingLabelText?: string;
  28 + mediaType?: string;
  29 + showsSelectedCount?: boolean;
  30 + forceJpg?: boolean;
  31 + showCropGuidelines?: boolean;
  32 + hideBottomControls?: boolean;
  33 + enableRotationGesture?: boolean;
  34 + cropperCancelText?: string;
  35 + cropperChooseText?: string;
  36 + }
  37 +
  38 + export interface Image {
  39 + path: string;
  40 + size: number;
  41 + data: null | string;
  42 + width: number;
  43 + height: number;
  44 + mime: string;
  45 + exif: null | object;
  46 + cropRect: null | CropRect;
  47 + filename: string;
  48 + creationDate: string;
  49 + modificationDate?: string;
  50 +
  51 + }
  52 +
  53 + export interface CropRect {
  54 + x: number;
  55 + y: number;
  56 + width: number;
  57 + height: number;
  58 + }
  59 +
  60 + export function openPicker(options: Options): Promise<Image | Image[]>;
  61 + export function openCamera(options: Options): Promise<Image | Image[]>;
  62 + export function openCropper(options: Options): Promise<Image>;
  63 + export function clean(): Promise<void>;
  64 + export function cleanSingle(path: string): Promise<void>;
  65 +
  66 + export interface ImageCropPicker {
  67 + openPicker(options: Options): Promise<Image | Image[]>;
  68 + openCamera(options: Options): Promise<Image | Image[]>;
  69 + openCropper(options: Options): Promise<Image>;
  70 + clean(): Promise<void>;
  71 + cleanSingle(path: string): Promise<void>;
  72 + }
  73 +
  74 + const ImageCropPicker: ImageCropPicker;
  75 +
  76 + export default ImageCropPicker;
  77 +}
  1 +import React from 'react';
  2 +
  3 +import {NativeModules} from 'react-native';
  4 +export default NativeModules.ImageCropPicker;
  1 +//
  2 +// QBImagePicker.h
  3 +// QBImagePicker
  4 +//
  5 +// Created by Katsuma Tanaka on 2015/04/03.
  6 +// Copyright (c) 2015 Katsuma Tanaka. All rights reserved.
  7 +//
  8 +
  9 +#import <Foundation/Foundation.h>
  10 +
  11 +//! Project version number for QBImagePicker.
  12 +FOUNDATION_EXPORT double QBImagePickerVersionNumber;
  13 +
  14 +//! Project version string for QBImagePicker.
  15 +FOUNDATION_EXPORT const unsigned char QBImagePickerVersionString[];
  16 +
  17 +// In this header, you should import all the public headers of your framework using statements like #import <QBImagePicker/PublicHeader.h>
  18 +#import <QBImagePicker/QBImagePickerController.h>
  1 +//
  2 +// QBImagePickerController.h
  3 +// QBImagePicker
  4 +//
  5 +// Created by Katsuma Tanaka on 2015/04/03.
  6 +// Copyright (c) 2015 Katsuma Tanaka. All rights reserved.
  7 +//
  8 +
  9 +#import <UIKit/UIKit.h>
  10 +#import <Photos/Photos.h>
  11 +
  12 +@class QBImagePickerController;
  13 +
  14 +@protocol QBImagePickerControllerDelegate <NSObject>
  15 +
  16 +@optional
  17 +- (void)qb_imagePickerController:(QBImagePickerController *)imagePickerController didFinishPickingAssets:(NSArray *)assets;
  18 +- (void)qb_imagePickerControllerDidCancel:(QBImagePickerController *)imagePickerController;
  19 +
  20 +- (BOOL)qb_imagePickerController:(QBImagePickerController *)imagePickerController shouldSelectAsset:(PHAsset *)asset;
  21 +- (void)qb_imagePickerController:(QBImagePickerController *)imagePickerController didSelectAsset:(PHAsset *)asset;
  22 +- (void)qb_imagePickerController:(QBImagePickerController *)imagePickerController didDeselectAsset:(PHAsset *)asset;
  23 +
  24 +@end
  25 +
  26 +typedef NS_ENUM(NSUInteger, QBImagePickerMediaType) {
  27 + QBImagePickerMediaTypeAny = 0,
  28 + QBImagePickerMediaTypeImage,
  29 + QBImagePickerMediaTypeVideo
  30 +};
  31 +
  32 +@interface QBImagePickerController : UIViewController
  33 +
  34 +@property (nonatomic, weak) id<QBImagePickerControllerDelegate> delegate;
  35 +
  36 +@property (nonatomic, strong, readonly) NSMutableOrderedSet *selectedAssets;
  37 +
  38 +@property (nonatomic, copy) NSArray *assetCollectionSubtypes;
  39 +@property (nonatomic, assign) QBImagePickerMediaType mediaType;
  40 +
  41 +@property (nonatomic, assign) BOOL allowsMultipleSelection;
  42 +@property (nonatomic, assign) NSUInteger minimumNumberOfSelection;
  43 +@property (nonatomic, assign) NSUInteger maximumNumberOfSelection;
  44 +
  45 +@property (nonatomic, copy) NSString *prompt;
  46 +@property (nonatomic, assign) BOOL showsNumberOfSelectedAssets;
  47 +
  48 +@property (nonatomic, assign) NSUInteger numberOfColumnsInPortrait;
  49 +@property (nonatomic, assign) NSUInteger numberOfColumnsInLandscape;
  50 +
  51 +@end
  1 +framework module QBImagePicker {
  2 + umbrella header "QBImagePicker.h"
  3 +
  4 + export *
  5 + module * { export * }
  6 +}
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
  3 +<plist version="1.0">
  4 +<dict>
  5 + <key>files</key>
  6 + <dict>
  7 + <key>Headers/QBImagePicker.h</key>
  8 + <data>
  9 + wIb/otiZ7bd2vyPJQF6JEeoqhqo=
  10 + </data>
  11 + <key>Headers/QBImagePickerController.h</key>
  12 + <data>
  13 + Pj/03SZs3R39/KDJC/Sw28hOcV4=
  14 + </data>
  15 + <key>Info.plist</key>
  16 + <data>
  17 + IcCz/8UZ07kkFONGwju3BBUrdSA=
  18 + </data>
  19 + <key>Modules/module.modulemap</key>
  20 + <data>
  21 + b8XazA/6z0jSXdJ08gOuLFgiplM=
  22 + </data>
  23 + <key>QBImagePicker.storyboardc/Info.plist</key>
  24 + <data>
  25 + iwZHbHPu3m3FYRI+FCDLbHeFs44=
  26 + </data>
  27 + <key>QBImagePicker.storyboardc/QBAlbumsNavigationController.nib</key>
  28 + <data>
  29 + 5MCnX6u0y0SAVdmtiddUomh9GyY=
  30 + </data>
  31 + <key>QBImagePicker.storyboardc/QBAlbumsViewController.nib</key>
  32 + <data>
  33 + pY80pANgQzeIQuYM9GWFeoXUSxQ=
  34 + </data>
  35 + <key>QBImagePicker.storyboardc/QBAssetsViewController.nib</key>
  36 + <data>
  37 + 7+OaszKW0bhc6RotWxC0o0SijKw=
  38 + </data>
  39 + <key>QBImagePicker.storyboardc/QL5-wR-LYt-view-66K-TS-Yoc.nib</key>
  40 + <data>
  41 + 0xNFW/713RpJkWqwnLEP8bUWcuI=
  42 + </data>
  43 + <key>QBImagePicker.storyboardc/QiH-NZ-ZGN-view-sD2-zK-ryo.nib</key>
  44 + <data>
  45 + Fj2aIfr+Es6yrxOSQzP/3Uhvvvs=
  46 + </data>
  47 + <key>de.lproj/QBImagePicker.strings</key>
  48 + <dict>
  49 + <key>hash</key>
  50 + <data>
  51 + aEwah75TRZyK1k7zsKbitGOHsEE=
  52 + </data>
  53 + <key>optional</key>
  54 + <true/>
  55 + </dict>
  56 + <key>en.lproj/QBImagePicker.strings</key>
  57 + <dict>
  58 + <key>hash</key>
  59 + <data>
  60 + /bN8mAXwM04zgDQj19s7yTHxKQs=
  61 + </data>
  62 + <key>optional</key>
  63 + <true/>
  64 + </dict>
  65 + <key>es.lproj/QBImagePicker.strings</key>
  66 + <dict>
  67 + <key>hash</key>
  68 + <data>
  69 + 3E8xykBQDKx9XRx4cigrfFeoUwo=
  70 + </data>
  71 + <key>optional</key>
  72 + <true/>
  73 + </dict>
  74 + <key>ja.lproj/QBImagePicker.strings</key>
  75 + <dict>
  76 + <key>hash</key>
  77 + <data>
  78 + 7j4cVfYmTo9Yfo4fA9g4AvzTLhw=
  79 + </data>
  80 + <key>optional</key>
  81 + <true/>
  82 + </dict>
  83 + <key>pl.lproj/QBImagePicker.strings</key>
  84 + <dict>
  85 + <key>hash</key>
  86 + <data>
  87 + LfadkhKMCL8TGzZnnEP7Pbsb0I4=
  88 + </data>
  89 + <key>optional</key>
  90 + <true/>
  91 + </dict>
  92 + <key>zh-Hans.lproj/QBImagePicker.strings</key>
  93 + <dict>
  94 + <key>hash</key>
  95 + <data>
  96 + 0F5a3/dLVWvW3skcaXrrOpnhPv0=
  97 + </data>
  98 + <key>optional</key>
  99 + <true/>
  100 + </dict>
  101 + </dict>
  102 + <key>files2</key>
  103 + <dict>
  104 + <key>Headers/QBImagePicker.h</key>
  105 + <dict>
  106 + <key>hash</key>
  107 + <data>
  108 + wIb/otiZ7bd2vyPJQF6JEeoqhqo=
  109 + </data>
  110 + <key>hash2</key>
  111 + <data>
  112 + Fh9fdGZCY2P54hZbKvAZwwcML6IHufU9QAmHXrcmAMI=
  113 + </data>
  114 + </dict>
  115 + <key>Headers/QBImagePickerController.h</key>
  116 + <dict>
  117 + <key>hash</key>
  118 + <data>
  119 + Pj/03SZs3R39/KDJC/Sw28hOcV4=
  120 + </data>
  121 + <key>hash2</key>
  122 + <data>
  123 + WcCD5/uhcGaZMLBPYHrRY/JkiB7BPQZqn5rkufhmY8c=
  124 + </data>
  125 + </dict>
  126 + <key>Modules/module.modulemap</key>
  127 + <dict>
  128 + <key>hash</key>
  129 + <data>
  130 + b8XazA/6z0jSXdJ08gOuLFgiplM=
  131 + </data>
  132 + <key>hash2</key>
  133 + <data>
  134 + t0ZEP5nTTzs177dyaKMwGF/sZUJu1h9MPbMCcGQ7acw=
  135 + </data>
  136 + </dict>
  137 + <key>QBImagePicker.storyboardc/Info.plist</key>
  138 + <dict>
  139 + <key>hash</key>
  140 + <data>
  141 + iwZHbHPu3m3FYRI+FCDLbHeFs44=
  142 + </data>
  143 + <key>hash2</key>
  144 + <data>
  145 + gyFhkTx7M92K1jKJpSG0Yl9R+mQfrd/YtnmTaP54ukY=
  146 + </data>
  147 + </dict>
  148 + <key>QBImagePicker.storyboardc/QBAlbumsNavigationController.nib</key>
  149 + <dict>
  150 + <key>hash</key>
  151 + <data>
  152 + 5MCnX6u0y0SAVdmtiddUomh9GyY=
  153 + </data>
  154 + <key>hash2</key>
  155 + <data>
  156 + SLNRGZnuwERXFNcJLu1791ZVDMDsNnnk49Xb01ZjoZo=
  157 + </data>
  158 + </dict>
  159 + <key>QBImagePicker.storyboardc/QBAlbumsViewController.nib</key>
  160 + <dict>
  161 + <key>hash</key>
  162 + <data>
  163 + pY80pANgQzeIQuYM9GWFeoXUSxQ=
  164 + </data>
  165 + <key>hash2</key>
  166 + <data>
  167 + KiZiF6NMbTf2YFOEQs4U1Yef9CSUk8GUiApZAjwtnOM=
  168 + </data>
  169 + </dict>
  170 + <key>QBImagePicker.storyboardc/QBAssetsViewController.nib</key>
  171 + <dict>
  172 + <key>hash</key>
  173 + <data>
  174 + 7+OaszKW0bhc6RotWxC0o0SijKw=
  175 + </data>
  176 + <key>hash2</key>
  177 + <data>
  178 + PLJdoPEFJT4ItZgiMWCnK0GDoCTxbI/2wAwBBb8l3wM=
  179 + </data>
  180 + </dict>
  181 + <key>QBImagePicker.storyboardc/QL5-wR-LYt-view-66K-TS-Yoc.nib</key>
  182 + <dict>
  183 + <key>hash</key>
  184 + <data>
  185 + 0xNFW/713RpJkWqwnLEP8bUWcuI=
  186 + </data>
  187 + <key>hash2</key>
  188 + <data>
  189 + ey42W/ZWm6W0Eea8Q4i9o79eTvpujgSK8HU9PTkbS/4=
  190 + </data>
  191 + </dict>
  192 + <key>QBImagePicker.storyboardc/QiH-NZ-ZGN-view-sD2-zK-ryo.nib</key>
  193 + <dict>
  194 + <key>hash</key>
  195 + <data>
  196 + Fj2aIfr+Es6yrxOSQzP/3Uhvvvs=
  197 + </data>
  198 + <key>hash2</key>
  199 + <data>
  200 + 0iH5b4oLNN+sXyFq/BdGICtJzSnj4ll873t0puG4aqM=
  201 + </data>
  202 + </dict>
  203 + <key>de.lproj/QBImagePicker.strings</key>
  204 + <dict>
  205 + <key>hash</key>
  206 + <data>
  207 + aEwah75TRZyK1k7zsKbitGOHsEE=
  208 + </data>
  209 + <key>hash2</key>
  210 + <data>
  211 + 5NH6glXlP7txoHgxKL83/BqWApGAb0poqKByJ1yo+9c=
  212 + </data>
  213 + <key>optional</key>
  214 + <true/>
  215 + </dict>
  216 + <key>en.lproj/QBImagePicker.strings</key>
  217 + <dict>
  218 + <key>hash</key>
  219 + <data>
  220 + /bN8mAXwM04zgDQj19s7yTHxKQs=
  221 + </data>
  222 + <key>hash2</key>
  223 + <data>
  224 + xZGH0lOWeKlYEMMZNToG0j+vQVTgUTNoydCOJBG+Kjs=
  225 + </data>
  226 + <key>optional</key>
  227 + <true/>
  228 + </dict>
  229 + <key>es.lproj/QBImagePicker.strings</key>
  230 + <dict>
  231 + <key>hash</key>
  232 + <data>
  233 + 3E8xykBQDKx9XRx4cigrfFeoUwo=
  234 + </data>
  235 + <key>hash2</key>
  236 + <data>
  237 + T0LWvjKc9Wmuxznj7gCFXRNeGYuFiF92kDcjOVI/WPo=
  238 + </data>
  239 + <key>optional</key>
  240 + <true/>
  241 + </dict>
  242 + <key>ja.lproj/QBImagePicker.strings</key>
  243 + <dict>
  244 + <key>hash</key>
  245 + <data>
  246 + 7j4cVfYmTo9Yfo4fA9g4AvzTLhw=
  247 + </data>
  248 + <key>hash2</key>
  249 + <data>
  250 + jxfNgX8MVlO7C53RGOjJwPc1dcSj9DxTyWW8N37BkaE=
  251 + </data>
  252 + <key>optional</key>
  253 + <true/>
  254 + </dict>
  255 + <key>pl.lproj/QBImagePicker.strings</key>
  256 + <dict>
  257 + <key>hash</key>
  258 + <data>
  259 + LfadkhKMCL8TGzZnnEP7Pbsb0I4=
  260 + </data>
  261 + <key>hash2</key>
  262 + <data>
  263 + BQCDVBKWaLio8bCJuurR2oc3ENu7K8c6N9ECEh98owU=
  264 + </data>
  265 + <key>optional</key>
  266 + <true/>
  267 + </dict>
  268 + <key>zh-Hans.lproj/QBImagePicker.strings</key>
  269 + <dict>
  270 + <key>hash</key>
  271 + <data>
  272 + 0F5a3/dLVWvW3skcaXrrOpnhPv0=
  273 + </data>
  274 + <key>hash2</key>
  275 + <data>
  276 + smMz2/UyQzPo54Zq6lLJj+H42kPbfO4Bfy0iP1jCyf8=
  277 + </data>
  278 + <key>optional</key>
  279 + <true/>
  280 + </dict>
  281 + </dict>
  282 + <key>rules</key>
  283 + <dict>
  284 + <key>^</key>
  285 + <true/>
  286 + <key>^.*\.lproj/</key>
  287 + <dict>
  288 + <key>optional</key>
  289 + <true/>
  290 + <key>weight</key>
  291 + <real>1000</real>
  292 + </dict>
  293 + <key>^.*\.lproj/locversion.plist$</key>
  294 + <dict>
  295 + <key>omit</key>
  296 + <true/>
  297 + <key>weight</key>
  298 + <real>1100</real>
  299 + </dict>
  300 + <key>^version.plist$</key>
  301 + <true/>
  302 + </dict>
  303 + <key>rules2</key>
  304 + <dict>
  305 + <key>.*\.dSYM($|/)</key>
  306 + <dict>
  307 + <key>weight</key>
  308 + <real>11</real>
  309 + </dict>
  310 + <key>^</key>
  311 + <dict>
  312 + <key>weight</key>
  313 + <real>20</real>
  314 + </dict>
  315 + <key>^(.*/)?\.DS_Store$</key>
  316 + <dict>
  317 + <key>omit</key>
  318 + <true/>
  319 + <key>weight</key>
  320 + <real>2000</real>
  321 + </dict>
  322 + <key>^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/</key>
  323 + <dict>
  324 + <key>nested</key>
  325 + <true/>
  326 + <key>weight</key>
  327 + <real>10</real>
  328 + </dict>
  329 + <key>^.*</key>
  330 + <true/>
  331 + <key>^.*\.lproj/</key>
  332 + <dict>
  333 + <key>optional</key>
  334 + <true/>
  335 + <key>weight</key>
  336 + <real>1000</real>
  337 + </dict>
  338 + <key>^.*\.lproj/locversion.plist$</key>
  339 + <dict>
  340 + <key>omit</key>
  341 + <true/>
  342 + <key>weight</key>
  343 + <real>1100</real>
  344 + </dict>
  345 + <key>^Info\.plist$</key>
  346 + <dict>
  347 + <key>omit</key>
  348 + <true/>
  349 + <key>weight</key>
  350 + <real>20</real>
  351 + </dict>
  352 + <key>^PkgInfo$</key>
  353 + <dict>
  354 + <key>omit</key>
  355 + <true/>
  356 + <key>weight</key>
  357 + <real>20</real>
  358 + </dict>
  359 + <key>^[^/]+$</key>
  360 + <dict>
  361 + <key>nested</key>
  362 + <true/>
  363 + <key>weight</key>
  364 + <real>10</real>
  365 + </dict>
  366 + <key>^embedded\.provisionprofile$</key>
  367 + <dict>
  368 + <key>weight</key>
  369 + <real>20</real>
  370 + </dict>
  371 + <key>^version\.plist$</key>
  372 + <dict>
  373 + <key>weight</key>
  374 + <real>20</real>
  375 + </dict>
  376 + </dict>
  377 +</dict>
  378 +</plist>
  1 +//
  2 +// CGGeometry+RSKImageCropper.h
  3 +//
  4 +// Copyright (c) 2015 Ruslan Skorb, http://ruslanskorb.com/
  5 +//
  6 +// Permission is hereby granted, free of charge, to any person obtaining a copy
  7 +// of this software and associated documentation files (the "Software"), to deal
  8 +// in the Software without restriction, including without limitation the rights
  9 +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10 +// copies of the Software, and to permit persons to whom the Software is
  11 +// furnished to do so, subject to the following conditions:
  12 +//
  13 +// The above copyright notice and this permission notice shall be included in
  14 +// all copies or substantial portions of the Software.
  15 +//
  16 +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17 +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18 +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  19 +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20 +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21 +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22 +// THE SOFTWARE.
  23 +//
  24 +
  25 +#import <CoreGraphics/CoreGraphics.h>
  26 +#import <tgmath.h>
  27 +
  28 +// tgmath functions aren't used on iOS when modules are enabled.
  29 +// Open Radar - http://www.openradar.me/16744288
  30 +// Work around this by redeclaring things here.
  31 +
  32 +#undef cos
  33 +#define cos(__x) __tg_cos(__tg_promote1((__x))(__x))
  34 +
  35 +#undef sin
  36 +#define sin(__x) __tg_sin(__tg_promote1((__x))(__x))
  37 +
  38 +#undef atan2
  39 +#define atan2(__x, __y) __tg_atan2(__tg_promote2((__x), (__y))(__x), \
  40 +__tg_promote2((__x), (__y))(__y))
  41 +
  42 +#undef pow
  43 +#define pow(__x, __y) __tg_pow(__tg_promote2((__x), (__y))(__x), \
  44 +__tg_promote2((__x), (__y))(__y))
  45 +
  46 +#undef sqrt
  47 +#define sqrt(__x) __tg_sqrt(__tg_promote1((__x))(__x))
  48 +
  49 +#undef fabs
  50 +#define fabs(__x) __tg_fabs(__tg_promote1((__x))(__x))
  51 +
  52 +#undef ceil
  53 +#define ceil(__x) __tg_ceil(__tg_promote1((__x))(__x))
  54 +
  55 +#undef floor
  56 +#define floor(__x) __tg_floor(__tg_promote1((__x))(__x))
  57 +
  58 +#undef round
  59 +#define round(__x) __tg_round(__tg_promote1((__x))(__x))
  60 +
  61 +#ifdef CGFLOAT_IS_DOUBLE
  62 + #define RSK_EPSILON DBL_EPSILON
  63 + #define RSK_MIN DBL_MIN
  64 +#else
  65 + #define RSK_EPSILON FLT_EPSILON
  66 + #define RSK_MIN FLT_MIN
  67 +#endif
  68 +
  69 +// Line segments.
  70 +struct RSKLineSegment {
  71 + CGPoint start;
  72 + CGPoint end;
  73 +};
  74 +typedef struct RSKLineSegment RSKLineSegment;
  75 +
  76 +// The "empty" point. This is the point returned when, for example, we
  77 +// intersect two disjoint line segments. Note that the null point is not the
  78 +// same as the zero point.
  79 +CG_EXTERN const CGPoint RSKPointNull;
  80 +
  81 +// Returns the exact center point of the given rectangle.
  82 +CGPoint RSKRectCenterPoint(CGRect rect);
  83 +
  84 +// Returns the `rect` scaled around the `point` by `sx` and `sy`.
  85 +CGRect RSKRectScaleAroundPoint(CGRect rect, CGPoint point, CGFloat sx, CGFloat sy);
  86 +
  87 +// Returns true if `point' is the null point, false otherwise.
  88 +bool RSKPointIsNull(CGPoint point);
  89 +
  90 +// Returns the `point` rotated around the `pivot` by `angle`.
  91 +CGPoint RSKPointRotateAroundPoint(CGPoint point, CGPoint pivot, CGFloat angle);
  92 +
  93 +// Returns the distance between two points.
  94 +CGFloat RSKPointDistance(CGPoint p1, CGPoint p2);
  95 +
  96 +// Make a line segment from two points `start` and `end`.
  97 +RSKLineSegment RSKLineSegmentMake(CGPoint start, CGPoint end);
  98 +
  99 +// Returns the line segment rotated around the `pivot` by `angle`.
  100 +RSKLineSegment RSKLineSegmentRotateAroundPoint(RSKLineSegment lineSegment, CGPoint pivot, CGFloat angle);
  101 +
  102 +// Returns the intersection of `ls1' and `ls2'. This may return a null point.
  103 +CGPoint RSKLineSegmentIntersection(RSKLineSegment ls1, RSKLineSegment ls2);
  1 +//
  2 +// RSKImageCropViewController+Protected.h
  3 +//
  4 +// Copyright (c) 2014-present Ruslan Skorb, http://ruslanskorb.com/
  5 +//
  6 +// Permission is hereby granted, free of charge, to any person obtaining a copy
  7 +// of this software and associated documentation files (the "Software"), to deal
  8 +// in the Software without restriction, including without limitation the rights
  9 +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10 +// copies of the Software, and to permit persons to whom the Software is
  11 +// furnished to do so, subject to the following conditions:
  12 +//
  13 +// The above copyright notice and this permission notice shall be included in
  14 +// all copies or substantial portions of the Software.
  15 +//
  16 +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17 +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18 +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  19 +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20 +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21 +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22 +// THE SOFTWARE.
  23 +//
  24 +
  25 +#import <UIKit/UIKit.h>
  26 +
  27 +NS_ASSUME_NONNULL_BEGIN
  28 +
  29 +/**
  30 + The methods in the RSKImageCropViewControllerProtectedMethods category
  31 + typically should only be called by subclasses which are implementing new
  32 + image crop view controllers. They may be overridden but must call super.
  33 + */
  34 +@interface RSKImageCropViewController (RSKImageCropViewControllerProtectedMethods)
  35 +
  36 +/**
  37 + Asynchronously crops the original image in accordance with the current settings and tells the delegate that the original image will be / has been cropped.
  38 + */
  39 +- (void)cropImage;
  40 +
  41 +/**
  42 + Tells the delegate that the crop has been canceled.
  43 + */
  44 +- (void)cancelCrop;
  45 +
  46 +/**
  47 + Resets the rotation angle, the position and the zoom scale of the original image to the default values.
  48 +
  49 + @param animated Set this value to YES to animate the reset.
  50 + */
  51 +- (void)reset:(BOOL)animated;
  52 +
  53 +/**
  54 + Sets the current rotation angle of the image in radians.
  55 +
  56 + @param rotationAngle The rotation angle of the image in radians.
  57 + */
  58 +- (void)setRotationAngle:(CGFloat)rotationAngle;
  59 +
  60 +/**
  61 + Sets the current scale factor for the image.
  62 +
  63 + @param zoomScale The scale factor for the image.
  64 + */
  65 +- (void)setZoomScale:(CGFloat)zoomScale;
  66 +
  67 +@end
  68 +
  69 +NS_ASSUME_NONNULL_END
  1 +//
  2 +// RSKImageCropViewController.h
  3 +//
  4 +// Copyright (c) 2014-present Ruslan Skorb, http://ruslanskorb.com/
  5 +//
  6 +// Permission is hereby granted, free of charge, to any person obtaining a copy
  7 +// of this software and associated documentation files (the "Software"), to deal
  8 +// in the Software without restriction, including without limitation the rights
  9 +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10 +// copies of the Software, and to permit persons to whom the Software is
  11 +// furnished to do so, subject to the following conditions:
  12 +//
  13 +// The above copyright notice and this permission notice shall be included in
  14 +// all copies or substantial portions of the Software.
  15 +//
  16 +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17 +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18 +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  19 +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20 +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21 +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22 +// THE SOFTWARE.
  23 +//
  24 +
  25 +#import <UIKit/UIKit.h>
  26 +
  27 +NS_ASSUME_NONNULL_BEGIN
  28 +
  29 +@protocol RSKImageCropViewControllerDataSource;
  30 +@protocol RSKImageCropViewControllerDelegate;
  31 +
  32 +/**
  33 + Types of supported crop modes.
  34 + */
  35 +typedef NS_ENUM(NSUInteger, RSKImageCropMode) {
  36 + RSKImageCropModeCircle,
  37 + RSKImageCropModeSquare,
  38 + RSKImageCropModeCustom
  39 +};
  40 +
  41 +@interface RSKImageCropViewController : UIViewController
  42 +
  43 +/**
  44 + Designated initializer. Initializes and returns a newly allocated view controller object with the specified image.
  45 +
  46 + @param originalImage The image for cropping.
  47 + */
  48 +- (instancetype)initWithImage:(UIImage *)originalImage;
  49 +
  50 +/**
  51 + Initializes and returns a newly allocated view controller object with the specified image and the specified crop mode.
  52 +
  53 + @param originalImage The image for cropping.
  54 + @param cropMode The mode for cropping.
  55 + */
  56 +- (instancetype)initWithImage:(UIImage *)originalImage cropMode:(RSKImageCropMode)cropMode;
  57 +
  58 +///-----------------------------
  59 +/// @name Accessing the Delegate
  60 +///-----------------------------
  61 +
  62 +/**
  63 + The receiver's delegate.
  64 +
  65 + @discussion A `RSKImageCropViewControllerDelegate` delegate responds to messages sent by completing / canceling crop the image in the image crop view controller.
  66 + */
  67 +@property (weak, nonatomic, nullable) id<RSKImageCropViewControllerDelegate> delegate;
  68 +
  69 +/**
  70 + The receiver's data source.
  71 +
  72 + @discussion A `RSKImageCropViewControllerDataSource` data source provides a custom rect and a custom path for the mask.
  73 + */
  74 +@property (weak, nonatomic, nullable) id<RSKImageCropViewControllerDataSource> dataSource;
  75 +
  76 +///--------------------------
  77 +/// @name Accessing the Image
  78 +///--------------------------
  79 +
  80 +/**
  81 + The image for cropping.
  82 + */
  83 +@property (strong, nonatomic) UIImage *originalImage;
  84 +
  85 +/// -----------------------------------
  86 +/// @name Accessing the Mask Attributes
  87 +/// -----------------------------------
  88 +
  89 +/**
  90 + The color of the layer with the mask. Default value is [UIColor colorWithRed:0.0f green:0.0f blue:0.0f alpha:0.7f].
  91 + */
  92 +@property (copy, nonatomic) UIColor *maskLayerColor;
  93 +
  94 +/**
  95 + The line width used when stroking the path of the mask layer. Default value is 1.0.
  96 + */
  97 +@property (assign, nonatomic) CGFloat maskLayerLineWidth;
  98 +
  99 +/**
  100 + The color to fill the stroked outline of the path of the mask layer, or nil for no stroking. Default valus is nil.
  101 + */
  102 +@property (copy, nonatomic, nullable) UIColor *maskLayerStrokeColor;
  103 +
  104 +/**
  105 + The rect of the mask.
  106 +
  107 + @discussion Updating each time before the crop view lays out its subviews.
  108 + */
  109 +@property (assign, readonly, nonatomic) CGRect maskRect;
  110 +
  111 +/**
  112 + The path of the mask.
  113 +
  114 + @discussion Updating each time before the crop view lays out its subviews.
  115 + */
  116 +@property (copy, readonly, nonatomic) UIBezierPath *maskPath;
  117 +
  118 +/// -----------------------------------
  119 +/// @name Accessing the Crop Attributes
  120 +/// -----------------------------------
  121 +
  122 +/**
  123 + The mode for cropping. Default value is `RSKImageCropModeCircle`.
  124 + */
  125 +@property (assign, nonatomic) RSKImageCropMode cropMode;
  126 +
  127 +/**
  128 + The crop rectangle.
  129 +
  130 + @discussion The value is calculated at run time.
  131 + */
  132 +@property (readonly, nonatomic) CGRect cropRect;
  133 +
  134 +/**
  135 + A value that specifies the current rotation angle of the image in radians.
  136 +
  137 +@discussion The value is calculated at run time.
  138 + */
  139 +@property (readonly, nonatomic) CGFloat rotationAngle;
  140 +
  141 +/**
  142 + A floating-point value that specifies the current scale factor applied to the image.
  143 +
  144 + @discussion The value is calculated at run time.
  145 + */
  146 +@property (readonly, nonatomic) CGFloat zoomScale;
  147 +
  148 +/**
  149 + A Boolean value that determines whether the image will always fill the mask space. Default value is `NO`.
  150 + */
  151 +@property (assign, nonatomic) BOOL avoidEmptySpaceAroundImage;
  152 +
  153 +/**
  154 + A Boolean value that determines whether the mask applies to the image after cropping. Default value is `NO`.
  155 + */
  156 +@property (assign, nonatomic) BOOL applyMaskToCroppedImage;
  157 +
  158 +/**
  159 + A Boolean value that controls whether the rotaion gesture is enabled. Default value is `NO`.
  160 +
  161 + @discussion To support the rotation when `cropMode` is `RSKImageCropModeCustom` you must implement the data source method `imageCropViewControllerCustomMovementRect:`.
  162 + */
  163 +@property (assign, getter=isRotationEnabled, nonatomic) BOOL rotationEnabled;
  164 +
  165 +/// -------------------------------
  166 +/// @name Accessing the UI Elements
  167 +/// -------------------------------
  168 +
  169 +/**
  170 + The Title Label.
  171 + */
  172 +@property (strong, nonatomic, readonly) UILabel *moveAndScaleLabel;
  173 +
  174 +/**
  175 + The Cancel Button.
  176 + */
  177 +@property (strong, nonatomic, readonly) UIButton *cancelButton;
  178 +
  179 +/**
  180 + The Choose Button.
  181 + */
  182 +@property (strong, nonatomic, readonly) UIButton *chooseButton;
  183 +
  184 +/// -------------------------------------------
  185 +/// @name Checking of the Interface Orientation
  186 +/// -------------------------------------------
  187 +
  188 +/**
  189 + Returns a Boolean value indicating whether the user interface is currently presented in a portrait orientation.
  190 +
  191 + @return YES if the interface orientation is portrait, otherwise returns NO.
  192 + */
  193 +- (BOOL)isPortraitInterfaceOrientation;
  194 +
  195 +/// -------------------------------------
  196 +/// @name Accessing the Layout Attributes
  197 +/// -------------------------------------
  198 +
  199 +/**
  200 + The inset of the circle mask rect's area within the crop view's area in portrait orientation. Default value is `15.0f`.
  201 + */
  202 +@property (assign, nonatomic) CGFloat portraitCircleMaskRectInnerEdgeInset;
  203 +
  204 +/**
  205 + The inset of the square mask rect's area within the crop view's area in portrait orientation. Default value is `20.0f`.
  206 + */
  207 +@property (assign, nonatomic) CGFloat portraitSquareMaskRectInnerEdgeInset;
  208 +
  209 +/**
  210 + The vertical space between the top of the 'Move and Scale' label and the top of the crop view in portrait orientation. Default value is `64.0f`.
  211 + */
  212 +@property (assign, nonatomic) CGFloat portraitMoveAndScaleLabelTopAndCropViewTopVerticalSpace;
  213 +
  214 +/**
  215 + The vertical space between the bottom of the crop view and the bottom of the 'Cancel' button in portrait orientation. Default value is `21.0f`.
  216 + */
  217 +@property (assign, nonatomic) CGFloat portraitCropViewBottomAndCancelButtonBottomVerticalSpace;
  218 +
  219 +/**
  220 + The vertical space between the bottom of the crop view and the bottom of the 'Choose' button in portrait orientation. Default value is `21.0f`.
  221 + */
  222 +@property (assign, nonatomic) CGFloat portraitCropViewBottomAndChooseButtonBottomVerticalSpace;
  223 +
  224 +/**
  225 + The horizontal space between the leading of the 'Cancel' button and the leading of the crop view in portrait orientation. Default value is `13.0f`.
  226 + */
  227 +@property (assign, nonatomic) CGFloat portraitCancelButtonLeadingAndCropViewLeadingHorizontalSpace;
  228 +
  229 +/**
  230 + The horizontal space between the trailing of the crop view and the trailing of the 'Choose' button in portrait orientation. Default value is `13.0f`.
  231 + */
  232 +@property (assign, nonatomic) CGFloat portraitCropViewTrailingAndChooseButtonTrailingHorizontalSpace;
  233 +
  234 +/**
  235 + The inset of the circle mask rect's area within the crop view's area in landscape orientation. Default value is `45.0f`.
  236 + */
  237 +@property (assign, nonatomic) CGFloat landscapeCircleMaskRectInnerEdgeInset;
  238 +
  239 +/**
  240 + The inset of the square mask rect's area within the crop view's area in landscape orientation. Default value is `45.0f`.
  241 + */
  242 +@property (assign, nonatomic) CGFloat landscapeSquareMaskRectInnerEdgeInset;
  243 +
  244 +/**
  245 + The vertical space between the top of the 'Move and Scale' label and the top of the crop view in landscape orientation. Default value is `12.0f`.
  246 + */
  247 +@property (assign, nonatomic) CGFloat landscapeMoveAndScaleLabelTopAndCropViewTopVerticalSpace;
  248 +
  249 +/**
  250 + The vertical space between the bottom of the crop view and the bottom of the 'Cancel' button in landscape orientation. Default value is `12.0f`.
  251 + */
  252 +@property (assign, nonatomic) CGFloat landscapeCropViewBottomAndCancelButtonBottomVerticalSpace;
  253 +
  254 +/**
  255 + The vertical space between the bottom of the crop view and the bottom of the 'Choose' button in landscape orientation. Default value is `12.0f`.
  256 + */
  257 +@property (assign, nonatomic) CGFloat landscapeCropViewBottomAndChooseButtonBottomVerticalSpace;
  258 +
  259 +/**
  260 + The horizontal space between the leading of the 'Cancel' button and the leading of the crop view in landscape orientation. Default value is `13.0f`.
  261 + */
  262 +@property (assign, nonatomic) CGFloat landscapeCancelButtonLeadingAndCropViewLeadingHorizontalSpace;
  263 +
  264 +/**
  265 + The horizontal space between the trailing of the crop view and the trailing of the 'Choose' button in landscape orientation. Default value is `13.0f`.
  266 + */
  267 +@property (assign, nonatomic) CGFloat landscapeCropViewTrailingAndChooseButtonTrailingHorizontalSpace;
  268 +
  269 +@end
  270 +
  271 +/**
  272 + The `RSKImageCropViewControllerDataSource` protocol is adopted by an object that provides a custom rect and a custom path for the mask.
  273 + */
  274 +@protocol RSKImageCropViewControllerDataSource <NSObject>
  275 +
  276 +/**
  277 + Asks the data source a custom rect for the mask.
  278 +
  279 + @param controller The crop view controller object to whom a rect is provided.
  280 +
  281 + @return A custom rect for the mask.
  282 +
  283 + @discussion Only valid if `cropMode` is `RSKImageCropModeCustom`.
  284 + */
  285 +- (CGRect)imageCropViewControllerCustomMaskRect:(RSKImageCropViewController *)controller;
  286 +
  287 +/**
  288 + Asks the data source a custom path for the mask.
  289 +
  290 + @param controller The crop view controller object to whom a path is provided.
  291 +
  292 + @return A custom path for the mask.
  293 +
  294 + @discussion Only valid if `cropMode` is `RSKImageCropModeCustom`.
  295 + */
  296 +- (UIBezierPath *)imageCropViewControllerCustomMaskPath:(RSKImageCropViewController *)controller;
  297 +
  298 +@optional
  299 +
  300 +/**
  301 + Asks the data source a custom rect in which the image can be moved.
  302 +
  303 + @param controller The crop view controller object to whom a rect is provided.
  304 +
  305 + @return A custom rect in which the image can be moved.
  306 +
  307 + @discussion Only valid if `cropMode` is `RSKImageCropModeCustom`. If you want to support the rotation when `cropMode` is `RSKImageCropModeCustom` you must implement it. Will be marked as `required` in version `2.0.0`.
  308 + */
  309 +- (CGRect)imageCropViewControllerCustomMovementRect:(RSKImageCropViewController *)controller;
  310 +
  311 +@end
  312 +
  313 +/**
  314 + The `RSKImageCropViewControllerDelegate` protocol defines messages sent to a image crop view controller delegate when crop image was canceled or the original image was cropped.
  315 + */
  316 +@protocol RSKImageCropViewControllerDelegate <NSObject>
  317 +
  318 +@optional
  319 +
  320 +/**
  321 + Tells the delegate that crop image has been canceled.
  322 + */
  323 +- (void)imageCropViewControllerDidCancelCrop:(RSKImageCropViewController *)controller;
  324 +
  325 +/**
  326 + Tells the delegate that the original image will be cropped.
  327 + */
  328 +- (void)imageCropViewController:(RSKImageCropViewController *)controller willCropImage:(UIImage *)originalImage;
  329 +
  330 +/**
  331 + Tells the delegate that the original image has been cropped. Additionally provides a crop rect used to produce image.
  332 + */
  333 +- (void)imageCropViewController:(RSKImageCropViewController *)controller didCropImage:(UIImage *)croppedImage usingCropRect:(CGRect)cropRect;
  334 +
  335 +/**
  336 + Tells the delegate that the original image has been cropped. Additionally provides a crop rect and a rotation angle used to produce image.
  337 + */
  338 +- (void)imageCropViewController:(RSKImageCropViewController *)controller didCropImage:(UIImage *)croppedImage usingCropRect:(CGRect)cropRect rotationAngle:(CGFloat)rotationAngle;
  339 +
  340 +@end
  341 +
  342 +NS_ASSUME_NONNULL_END
  1 +//
  2 +// RSKImageCropper.h
  3 +//
  4 +// Copyright (c) 2014-present Ruslan Skorb, http://ruslanskorb.com/
  5 +//
  6 +// Permission is hereby granted, free of charge, to any person obtaining a copy
  7 +// of this software and associated documentation files (the "Software"), to deal
  8 +// in the Software without restriction, including without limitation the rights
  9 +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10 +// copies of the Software, and to permit persons to whom the Software is
  11 +// furnished to do so, subject to the following conditions:
  12 +//
  13 +// The above copyright notice and this permission notice shall be included in
  14 +// all copies or substantial portions of the Software.
  15 +//
  16 +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17 +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18 +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  19 +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20 +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21 +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22 +// THE SOFTWARE.
  23 +//
  24 +
  25 +/**
  26 + `RSKImageCropper` is an image cropper for iOS like in the Contacts app with support for landscape orientation.
  27 + */
  28 +
  29 +#import <Foundation/Foundation.h>
  30 +
  31 +//! Project version number for RSKImageCropper.
  32 +FOUNDATION_EXPORT double RSKImageCropperVersionNumber;
  33 +
  34 +//! Project version string for RSKImageCropper.
  35 +FOUNDATION_EXPORT const unsigned char RSKImageCropperVersionString[];
  36 +
  37 +#import <RSKImageCropper/CGGeometry+RSKImageCropper.h>
  38 +#import <RSKImageCropper/RSKImageCropViewController.h>
  39 +#import <RSKImageCropper/RSKImageCropViewController+Protected.h>
  40 +#import <RSKImageCropper/RSKImageScrollView.h>
  41 +#import <RSKImageCropper/RSKInternalUtility.h>
  42 +#import <RSKImageCropper/RSKTouchView.h>
  43 +#import <RSKImageCropper/UIApplication+RSKImageCropper.h>
  44 +#import <RSKImageCropper/UIImage+RSKImageCropper.h>
  1 +/*
  2 + File: RSKImageScrollView.h
  3 + Abstract: Centers image within the scroll view and configures image sizing and display.
  4 + Version: 1.3 modified by Ruslan Skorb on 8/24/14.
  5 +
  6 + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple
  7 + Inc. ("Apple") in consideration of your agreement to the following
  8 + terms, and your use, installation, modification or redistribution of
  9 + this Apple software constitutes acceptance of these terms. If you do
  10 + not agree with these terms, please do not use, install, modify or
  11 + redistribute this Apple software.
  12 +
  13 + In consideration of your agreement to abide by the following terms, and
  14 + subject to these terms, Apple grants you a personal, non-exclusive
  15 + license, under Apple's copyrights in this original Apple software (the
  16 + "Apple Software"), to use, reproduce, modify and redistribute the Apple
  17 + Software, with or without modifications, in source and/or binary forms;
  18 + provided that if you redistribute the Apple Software in its entirety and
  19 + without modifications, you must retain this notice and the following
  20 + text and disclaimers in all such redistributions of the Apple Software.
  21 + Neither the name, trademarks, service marks or logos of Apple Inc. may
  22 + be used to endorse or promote products derived from the Apple Software
  23 + without specific prior written permission from Apple. Except as
  24 + expressly stated in this notice, no other rights or licenses, express or
  25 + implied, are granted by Apple herein, including but not limited to any
  26 + patent rights that may be infringed by your derivative works or by other
  27 + works in which the Apple Software may be incorporated.
  28 +
  29 + The Apple Software is provided by Apple on an "AS IS" basis. APPLE
  30 + MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
  31 + THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
  32 + FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
  33 + OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
  34 +
  35 + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
  36 + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  37 + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  38 + INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
  39 + MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
  40 + AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
  41 + STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
  42 + POSSIBILITY OF SUCH DAMAGE.
  43 +
  44 + Copyright (C) 2012 Apple Inc. All Rights Reserved.
  45 +
  46 + */
  47 +
  48 +#import <UIKit/UIKit.h>
  49 +
  50 +@interface RSKImageScrollView : UIScrollView
  51 +
  52 +@property (nonatomic, strong) UIImageView *zoomView;
  53 +@property (nonatomic, assign) BOOL aspectFill;
  54 +
  55 +- (void)displayImage:(UIImage *)image;
  56 +
  57 +@end
  1 +//
  2 +// RSKInternalUtility.h
  3 +// RSKImageCropperExample
  4 +//
  5 +// Created by Ruslan Skorb on 9/5/15.
  6 +// Copyright (c) 2015 Ruslan Skorb. All rights reserved.
  7 +//
  8 +
  9 +#import <Foundation/Foundation.h>
  10 +
  11 +/**
  12 + Returns a localized version of the string designated by the specified key and residing in the RSKImageCropper table.
  13 +
  14 + @param key The key for a string in the RSKImageCropper table.
  15 + @param comment The comment to place above the key-value pair in the strings file.
  16 +
  17 + @return A localized version of the string designated by key in the RSKImageCropper table.
  18 + */
  19 +FOUNDATION_EXPORT NSString * RSKLocalizedString(NSString *key, NSString *comment);
  20 +
  21 +@interface RSKInternalUtility : NSObject
  22 +
  23 +/**
  24 + Returns the NSBundle object for returning localized strings.
  25 +
  26 + @return The NSBundle object for returning localized strings.
  27 +
  28 + @discussion We assume a convention of a bundle named RSKImageCropperStrings.bundle, otherwise we
  29 + return the bundle associated with the RSKInternalUtility class.
  30 + */
  31 ++ (NSBundle *)bundleForStrings;
  32 +
  33 +@end
  1 +//
  2 +// RSKTouchView.h
  3 +//
  4 +// Copyright (c) 2014-present Ruslan Skorb, http://ruslanskorb.com/
  5 +//
  6 +// Permission is hereby granted, free of charge, to any person obtaining a copy
  7 +// of this software and associated documentation files (the "Software"), to deal
  8 +// in the Software without restriction, including without limitation the rights
  9 +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10 +// copies of the Software, and to permit persons to whom the Software is
  11 +// furnished to do so, subject to the following conditions:
  12 +//
  13 +// The above copyright notice and this permission notice shall be included in
  14 +// all copies or substantial portions of the Software.
  15 +//
  16 +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17 +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18 +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  19 +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20 +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21 +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22 +// THE SOFTWARE.
  23 +//
  24 +
  25 +#import <UIKit/UIKit.h>
  26 +
  27 +@interface RSKTouchView : UIView
  28 +
  29 +@property (weak, nonatomic) UIView *receiver;
  30 +
  31 +@end
  1 +//
  2 +// UIApplication+RSKImageCropper.h
  3 +//
  4 +// Copyright (c) 2015 Ruslan Skorb, http://ruslanskorb.com/
  5 +//
  6 +// Permission is hereby granted, free of charge, to any person obtaining a copy
  7 +// of this software and associated documentation files (the "Software"), to deal
  8 +// in the Software without restriction, including without limitation the rights
  9 +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10 +// copies of the Software, and to permit persons to whom the Software is
  11 +// furnished to do so, subject to the following conditions:
  12 +//
  13 +// The above copyright notice and this permission notice shall be included in
  14 +// all copies or substantial portions of the Software.
  15 +//
  16 +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17 +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18 +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  19 +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20 +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21 +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22 +// THE SOFTWARE.
  23 +//
  24 +
  25 +#import <UIKit/UIKit.h>
  26 +
  27 +/**
  28 + The category `RSKImageCropper` of the class `UIApplication` provides the method `rsk_sharedApplication` which returns `nil` in an application extension, otherwise it returns the singleton app instance.
  29 + */
  30 +@interface UIApplication (RSKImageCropper)
  31 +
  32 +/**
  33 + Returns `nil` in an application extension, otherwise returns the singleton app instance.
  34 +
  35 + @return `nil` in an application extension, otherwise the app instance is created in the `UIApplicationMain` function.
  36 + */
  37 ++ (UIApplication *)rsk_sharedApplication;
  38 +
  39 +@end
  1 +//
  2 +// UIImage+RSKImageCropper.h
  3 +//
  4 +// Copyright (c) 2014-present Ruslan Skorb, http://ruslanskorb.com/
  5 +//
  6 +// Permission is hereby granted, free of charge, to any person obtaining a copy
  7 +// of this software and associated documentation files (the "Software"), to deal
  8 +// in the Software without restriction, including without limitation the rights
  9 +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10 +// copies of the Software, and to permit persons to whom the Software is
  11 +// furnished to do so, subject to the following conditions:
  12 +//
  13 +// The above copyright notice and this permission notice shall be included in
  14 +// all copies or substantial portions of the Software.
  15 +//
  16 +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17 +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18 +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  19 +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20 +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21 +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22 +// THE SOFTWARE.
  23 +//
  24 +
  25 +#import <UIKit/UIKit.h>
  26 +
  27 +@interface UIImage (RSKImageCropper)
  28 +
  29 +// Fix the orientation of the image.
  30 +- (UIImage *)fixOrientation;
  31 +
  32 +// Rotate the image clockwise around the center by the angle, in radians.
  33 +- (UIImage *)rotateByAngle:(CGFloat)angleInRadians;
  34 +
  35 +@end
  1 +framework module RSKImageCropper {
  2 + umbrella header "RSKImageCropper.h"
  3 +
  4 + export *
  5 + module * { export * }
  6 +}
  1 +//
  2 +// RSKImageCropper.strings
  3 +//
  4 +// Copyright (c) 2014-present Ruslan Skorb, http://ruslanskorb.com/
  5 +//
  6 +// Permission is hereby granted, free of charge, to any person obtaining a copy
  7 +// of this software and associated documentation files (the "Software"), to deal
  8 +// in the Software without restriction, including without limitation the rights
  9 +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10 +// copies of the Software, and to permit persons to whom the Software is
  11 +// furnished to do so, subject to the following conditions:
  12 +//
  13 +// The above copyright notice and this permission notice shall be included in
  14 +// all copies or substantial portions of the Software.
  15 +//
  16 +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17 +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18 +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  19 +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20 +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21 +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22 +// THE SOFTWARE.
  23 +//
  24 +
  25 +/* Move and Scale label */
  26 +"Move and Scale" = "نقل وتحجيم";
  27 +/* Cancel button */
  28 +"Cancel" = "إلغاء";
  29 +/* Choose button */
  30 +"Choose" = "تحديد";
  1 +//
  2 +// RSKImageCropper.strings
  3 +//
  4 +// Copyright (c) 2014-present Ruslan Skorb, http://ruslanskorb.com/
  5 +//
  6 +// Permission is hereby granted, free of charge, to any person obtaining a copy
  7 +// of this software and associated documentation files (the "Software"), to deal
  8 +// in the Software without restriction, including without limitation the rights
  9 +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10 +// copies of the Software, and to permit persons to whom the Software is
  11 +// furnished to do so, subject to the following conditions:
  12 +//
  13 +// The above copyright notice and this permission notice shall be included in
  14 +// all copies or substantial portions of the Software.
  15 +//
  16 +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17 +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18 +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  19 +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20 +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21 +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22 +// THE SOFTWARE.
  23 +//
  24 +
  25 +/* Move and Scale label */
  26 +"Move and Scale" = "Moure i escalar";
  27 +/* Cancel button */
  28 +"Cancel" = "Cancel·lar";
  29 +/* Choose button */
  30 +"Choose" = "Seleccionar";
  1 +//
  2 +// RSKImageCropper.strings
  3 +//
  4 +// Copyright (c) 2014-present Ruslan Skorb, http://ruslanskorb.com/
  5 +//
  6 +// Permission is hereby granted, free of charge, to any person obtaining a copy
  7 +// of this software and associated documentation files (the "Software"), to deal
  8 +// in the Software without restriction, including without limitation the rights
  9 +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10 +// copies of the Software, and to permit persons to whom the Software is
  11 +// furnished to do so, subject to the following conditions:
  12 +//
  13 +// The above copyright notice and this permission notice shall be included in
  14 +// all copies or substantial portions of the Software.
  15 +//
  16 +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17 +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18 +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  19 +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20 +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21 +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22 +// THE SOFTWARE.
  23 +//
  24 +
  25 +/* Move and Scale label */
  26 +"Move and Scale" = "Vyberte výřez";
  27 +/* Cancel button */
  28 +"Cancel" = "Zrušit";
  29 +/* Choose button */
  30 +"Choose" = "Vybrat";
  1 +//
  2 +// RSKImageCropper.strings
  3 +//
  4 +// Copyright (c) 2014-present Ruslan Skorb, http://ruslanskorb.com/
  5 +//
  6 +// Permission is hereby granted, free of charge, to any person obtaining a copy
  7 +// of this software and associated documentation files (the "Software"), to deal
  8 +// in the Software without restriction, including without limitation the rights
  9 +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10 +// copies of the Software, and to permit persons to whom the Software is
  11 +// furnished to do so, subject to the following conditions:
  12 +//
  13 +// The above copyright notice and this permission notice shall be included in
  14 +// all copies or substantial portions of the Software.
  15 +//
  16 +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17 +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18 +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  19 +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20 +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21 +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22 +// THE SOFTWARE.
  23 +//
  24 +
  25 +/* Move and Scale label */
  26 +"Move and Scale" = "Flyt og skaler";
  27 +/* Cancel button */
  28 +"Cancel" = "Annuller";
  29 +/* Choose button */
  30 +"Choose" = "Vælg";
  1 +//
  2 +// RSKImageCropper.strings
  3 +//
  4 +// Copyright (c) 2014-present Ruslan Skorb, http://ruslanskorb.com/
  5 +//
  6 +// Permission is hereby granted, free of charge, to any person obtaining a copy
  7 +// of this software and associated documentation files (the "Software"), to deal
  8 +// in the Software without restriction, including without limitation the rights
  9 +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10 +// copies of the Software, and to permit persons to whom the Software is
  11 +// furnished to do so, subject to the following conditions:
  12 +//
  13 +// The above copyright notice and this permission notice shall be included in
  14 +// all copies or substantial portions of the Software.
  15 +//
  16 +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17 +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18 +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  19 +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20 +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21 +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22 +// THE SOFTWARE.
  23 +//
  24 +
  25 +/* Move and Scale label */
  26 +"Move and Scale" = "Bewegen und Skalieren";
  27 +/* Cancel button */
  28 +"Cancel" = "Abbrechen";
  29 +/* Choose button */
  30 +"Choose" = "Auswählen";
  1 +//
  2 +// RSKImageCropper.strings
  3 +//
  4 +// Copyright (c) 2014-present Ruslan Skorb, http://ruslanskorb.com/
  5 +//
  6 +// Permission is hereby granted, free of charge, to any person obtaining a copy
  7 +// of this software and associated documentation files (the "Software"), to deal
  8 +// in the Software without restriction, including without limitation the rights
  9 +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10 +// copies of the Software, and to permit persons to whom the Software is
  11 +// furnished to do so, subject to the following conditions:
  12 +//
  13 +// The above copyright notice and this permission notice shall be included in
  14 +// all copies or substantial portions of the Software.
  15 +//
  16 +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17 +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18 +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  19 +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20 +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21 +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22 +// THE SOFTWARE.
  23 +//
  24 +
  25 +/* Move and Scale label */
  26 +"Move and Scale" = "Aλλaγή θεσης και μεγεθoυς";
  27 +/* Cancel button */
  28 +"Cancel" = "Ακύρωση";
  29 +/* Choose button */
  30 +"Choose" = "Eπιλoγή";
  1 +//
  2 +// RSKImageCropper.strings
  3 +//
  4 +// Copyright (c) 2014-present Ruslan Skorb, http://ruslanskorb.com/
  5 +//
  6 +// Permission is hereby granted, free of charge, to any person obtaining a copy
  7 +// of this software and associated documentation files (the "Software"), to deal
  8 +// in the Software without restriction, including without limitation the rights
  9 +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10 +// copies of the Software, and to permit persons to whom the Software is
  11 +// furnished to do so, subject to the following conditions:
  12 +//
  13 +// The above copyright notice and this permission notice shall be included in
  14 +// all copies or substantial portions of the Software.
  15 +//
  16 +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17 +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18 +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  19 +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20 +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21 +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22 +// THE SOFTWARE.
  23 +//
  24 +
  25 +/* Move and Scale label */
  26 +"Move and Scale" = "Move and Scale";
  27 +/* Cancel button */
  28 +"Cancel" = "Cancel";
  29 +/* Choose button */
  30 +"Choose" = "Choose";
  1 +//
  2 +// RSKImageCropper.strings
  3 +//
  4 +// Copyright (c) 2014-present Ruslan Skorb, http://ruslanskorb.com/
  5 +//
  6 +// Permission is hereby granted, free of charge, to any person obtaining a copy
  7 +// of this software and associated documentation files (the "Software"), to deal
  8 +// in the Software without restriction, including without limitation the rights
  9 +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10 +// copies of the Software, and to permit persons to whom the Software is
  11 +// furnished to do so, subject to the following conditions:
  12 +//
  13 +// The above copyright notice and this permission notice shall be included in
  14 +// all copies or substantial portions of the Software.
  15 +//
  16 +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17 +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18 +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  19 +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20 +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21 +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22 +// THE SOFTWARE.
  23 +//
  24 +
  25 +/* Move and Scale label */
  26 +"Move and Scale" = "Mover y escalar";
  27 +/* Cancel button */
  28 +"Cancel" = "Cancelar";
  29 +/* Choose button */
  30 +"Choose" = "Usar";
  1 +//
  2 +// RSKImageCropper.strings
  3 +//
  4 +// Copyright (c) 2014-present Ruslan Skorb, http://ruslanskorb.com/
  5 +//
  6 +// Permission is hereby granted, free of charge, to any person obtaining a copy
  7 +// of this software and associated documentation files (the "Software"), to deal
  8 +// in the Software without restriction, including without limitation the rights
  9 +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10 +// copies of the Software, and to permit persons to whom the Software is
  11 +// furnished to do so, subject to the following conditions:
  12 +//
  13 +// The above copyright notice and this permission notice shall be included in
  14 +// all copies or substantial portions of the Software.
  15 +//
  16 +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17 +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18 +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  19 +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20 +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21 +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22 +// THE SOFTWARE.
  23 +//
  24 +
  25 +/* Move and Scale label */
  26 +"Move and Scale" = "Siirrä ja skaalaa";
  27 +/* Cancel button */
  28 +"Cancel" = "Kumoa";
  29 +/* Choose button */
  30 +"Choose" = "Valitse";
  1 +//
  2 +// RSKImageCropper.strings
  3 +//
  4 +// Copyright (c) 2014-present Ruslan Skorb, http://ruslanskorb.com/
  5 +//
  6 +// Permission is hereby granted, free of charge, to any person obtaining a copy
  7 +// of this software and associated documentation files (the "Software"), to deal
  8 +// in the Software without restriction, including without limitation the rights
  9 +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10 +// copies of the Software, and to permit persons to whom the Software is
  11 +// furnished to do so, subject to the following conditions:
  12 +//
  13 +// The above copyright notice and this permission notice shall be included in
  14 +// all copies or substantial portions of the Software.
  15 +//
  16 +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17 +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18 +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  19 +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20 +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21 +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22 +// THE SOFTWARE.
  23 +//
  24 +
  25 +/* Move and Scale label */
  26 +"Move and Scale" = "Recadrer";
  27 +/* Cancel button */
  28 +"Cancel" = "Annuler";
  29 +/* Choose button */
  30 +"Choose" = "Choisir";
  1 +//
  2 +// RSKImageCropper.strings
  3 +//
  4 +// Copyright (c) 2014-present Ruslan Skorb, http://ruslanskorb.com/
  5 +//
  6 +// Permission is hereby granted, free of charge, to any person obtaining a copy
  7 +// of this software and associated documentation files (the "Software"), to deal
  8 +// in the Software without restriction, including without limitation the rights
  9 +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10 +// copies of the Software, and to permit persons to whom the Software is
  11 +// furnished to do so, subject to the following conditions:
  12 +//
  13 +// The above copyright notice and this permission notice shall be included in
  14 +// all copies or substantial portions of the Software.
  15 +//
  16 +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17 +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18 +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  19 +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20 +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21 +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22 +// THE SOFTWARE.
  23 +//
  24 +
  25 +/* Move and Scale label */
  26 +"Move and Scale" = "הזז/י והתאם/י גודל";
  27 +/* Cancel button */
  28 +"Cancel" = "ביטול";
  29 +/* Choose button */
  30 +"Choose" = "בחר/י";
  1 +//
  2 +// RSKImageCropper.strings
  3 +//
  4 +// Copyright (c) 2014-present Ruslan Skorb, http://ruslanskorb.com/
  5 +//
  6 +// Permission is hereby granted, free of charge, to any person obtaining a copy
  7 +// of this software and associated documentation files (the "Software"), to deal
  8 +// in the Software without restriction, including without limitation the rights
  9 +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10 +// copies of the Software, and to permit persons to whom the Software is
  11 +// furnished to do so, subject to the following conditions:
  12 +//
  13 +// The above copyright notice and this permission notice shall be included in
  14 +// all copies or substantial portions of the Software.
  15 +//
  16 +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17 +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18 +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  19 +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20 +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21 +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22 +// THE SOFTWARE.
  23 +//
  24 +
  25 +/* Move and Scale label */
  26 +"Move and Scale" = "हिलाकर आकार बदलें";
  27 +/* Cancel button */
  28 +"Cancel" = "रद्द करें";
  29 +/* Choose button */
  30 +"Choose" = "चुनें";
  1 +//
  2 +// RSKImageCropper.strings
  3 +//
  4 +// Copyright (c) 2014-present Ruslan Skorb, http://ruslanskorb.com/
  5 +//
  6 +// Permission is hereby granted, free of charge, to any person obtaining a copy
  7 +// of this software and associated documentation files (the "Software"), to deal
  8 +// in the Software without restriction, including without limitation the rights
  9 +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10 +// copies of the Software, and to permit persons to whom the Software is
  11 +// furnished to do so, subject to the following conditions:
  12 +//
  13 +// The above copyright notice and this permission notice shall be included in
  14 +// all copies or substantial portions of the Software.
  15 +//
  16 +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17 +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18 +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  19 +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20 +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21 +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22 +// THE SOFTWARE.
  23 +//
  24 +
  25 +/* Move and Scale label */
  26 +"Move and Scale" = "Pomaknite i podesite";
  27 +/* Cancel button */
  28 +"Cancel" = "Poništi";
  29 +/* Choose button */
  30 +"Choose" = "Odaberi";
  1 +//
  2 +// RSKImageCropper.strings
  3 +//
  4 +// Copyright (c) 2014-present Ruslan Skorb, http://ruslanskorb.com/
  5 +//
  6 +// Permission is hereby granted, free of charge, to any person obtaining a copy
  7 +// of this software and associated documentation files (the "Software"), to deal
  8 +// in the Software without restriction, including without limitation the rights
  9 +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10 +// copies of the Software, and to permit persons to whom the Software is
  11 +// furnished to do so, subject to the following conditions:
  12 +//
  13 +// The above copyright notice and this permission notice shall be included in
  14 +// all copies or substantial portions of the Software.
  15 +//
  16 +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17 +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18 +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  19 +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20 +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21 +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22 +// THE SOFTWARE.
  23 +//
  24 +
  25 +/* Move and Scale label */
  26 +"Move and Scale" = "Mozgatás és méretezés";
  27 +/* Cancel button */
  28 +"Cancel" = "Mégsem";
  29 +/* Choose button */
  30 +"Choose" = "Kiválasztás";
  1 +//
  2 +// RSKImageCropper.strings
  3 +//
  4 +// Copyright (c) 2014-present Ruslan Skorb, http://ruslanskorb.com/
  5 +//
  6 +// Permission is hereby granted, free of charge, to any person obtaining a copy
  7 +// of this software and associated documentation files (the "Software"), to deal
  8 +// in the Software without restriction, including without limitation the rights
  9 +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10 +// copies of the Software, and to permit persons to whom the Software is
  11 +// furnished to do so, subject to the following conditions:
  12 +//
  13 +// The above copyright notice and this permission notice shall be included in
  14 +// all copies or substantial portions of the Software.
  15 +//
  16 +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17 +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18 +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  19 +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20 +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21 +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22 +// THE SOFTWARE.
  23 +//
  24 +
  25 +/* Move and Scale label */
  26 +"Move and Scale" = "Geser dan Ubah Skala";
  27 +/* Cancel button */
  28 +"Cancel" = "Batalkan";
  29 +/* Choose button */
  30 +"Choose" = "Pilih";
  1 +//
  2 +// RSKImageCropper.strings
  3 +//
  4 +// Copyright (c) 2014-present Ruslan Skorb, http://ruslanskorb.com/
  5 +//
  6 +// Permission is hereby granted, free of charge, to any person obtaining a copy
  7 +// of this software and associated documentation files (the "Software"), to deal
  8 +// in the Software without restriction, including without limitation the rights
  9 +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10 +// copies of the Software, and to permit persons to whom the Software is
  11 +// furnished to do so, subject to the following conditions:
  12 +//
  13 +// The above copyright notice and this permission notice shall be included in
  14 +// all copies or substantial portions of the Software.
  15 +//
  16 +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17 +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18 +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  19 +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20 +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21 +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22 +// THE SOFTWARE.
  23 +//
  24 +
  25 +/* Move and Scale label */
  26 +"Move and Scale" = "Sposta e ridimensiona";
  27 +/* Cancel button */
  28 +"Cancel" = "Annulla";
  29 +/* Choose button */
  30 +"Choose" = "Scegli";
  1 +//
  2 +// RSKImageCropper.strings
  3 +//
  4 +// Copyright (c) 2014-present Ruslan Skorb, http://ruslanskorb.com/
  5 +//
  6 +// Permission is hereby granted, free of charge, to any person obtaining a copy
  7 +// of this software and associated documentation files (the "Software"), to deal
  8 +// in the Software without restriction, including without limitation the rights
  9 +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10 +// copies of the Software, and to permit persons to whom the Software is
  11 +// furnished to do so, subject to the following conditions:
  12 +//
  13 +// The above copyright notice and this permission notice shall be included in
  14 +// all copies or substantial portions of the Software.
  15 +//
  16 +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17 +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18 +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  19 +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20 +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21 +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22 +// THE SOFTWARE.
  23 +//
  24 +
  25 +/* Move and Scale label */
  26 +"Move and Scale" = "移動と拡大縮小";
  27 +/* Cancel button */
  28 +"Cancel" = "キャンセル";
  29 +/* Choose button */
  30 +"Choose" = "選択";
  1 +//
  2 +// RSKImageCropper.strings
  3 +//
  4 +// Copyright (c) 2014-present Ruslan Skorb, http://ruslanskorb.com/
  5 +//
  6 +// Permission is hereby granted, free of charge, to any person obtaining a copy
  7 +// of this software and associated documentation files (the "Software"), to deal
  8 +// in the Software without restriction, including without limitation the rights
  9 +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10 +// copies of the Software, and to permit persons to whom the Software is
  11 +// furnished to do so, subject to the following conditions:
  12 +//
  13 +// The above copyright notice and this permission notice shall be included in
  14 +// all copies or substantial portions of the Software.
  15 +//
  16 +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17 +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18 +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  19 +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20 +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21 +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22 +// THE SOFTWARE.
  23 +//
  24 +
  25 +/* Move and Scale label */
  26 +"Move and Scale" = "Alih dan Skala";
  27 +/* Cancel button */
  28 +"Cancel" = "Batal";
  29 +/* Choose button */
  30 +"Choose" = "Pilih";
  1 +//
  2 +// RSKImageCropper.strings
  3 +//
  4 +// Copyright (c) 2014-present Ruslan Skorb, http://ruslanskorb.com/
  5 +//
  6 +// Permission is hereby granted, free of charge, to any person obtaining a copy
  7 +// of this software and associated documentation files (the "Software"), to deal
  8 +// in the Software without restriction, including without limitation the rights
  9 +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10 +// copies of the Software, and to permit persons to whom the Software is
  11 +// furnished to do so, subject to the following conditions:
  12 +//
  13 +// The above copyright notice and this permission notice shall be included in
  14 +// all copies or substantial portions of the Software.
  15 +//
  16 +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17 +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18 +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  19 +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20 +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21 +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22 +// THE SOFTWARE.
  23 +//
  24 +
  25 +/* Move and Scale label */
  26 +"Move and Scale" = "Flytt og skaler";
  27 +/* Cancel button */
  28 +"Cancel" = "Avbryt";
  29 +/* Choose button */
  30 +"Choose" = "Velg";
  1 +//
  2 +// RSKImageCropper.strings
  3 +//
  4 +// Copyright (c) 2014-present Ruslan Skorb, http://ruslanskorb.com/
  5 +//
  6 +// Permission is hereby granted, free of charge, to any person obtaining a copy
  7 +// of this software and associated documentation files (the "Software"), to deal
  8 +// in the Software without restriction, including without limitation the rights
  9 +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10 +// copies of the Software, and to permit persons to whom the Software is
  11 +// furnished to do so, subject to the following conditions:
  12 +//
  13 +// The above copyright notice and this permission notice shall be included in
  14 +// all copies or substantial portions of the Software.
  15 +//
  16 +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17 +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18 +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  19 +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20 +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21 +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22 +// THE SOFTWARE.
  23 +//
  24 +
  25 +/* Move and Scale label */
  26 +"Move and Scale" = "Verplaats en wijzig grootte";
  27 +/* Cancel button */
  28 +"Cancel" = "Annuleer";
  29 +/* Choose button */
  30 +"Choose" = "Kies";
  1 +//
  2 +// RSKImageCropper.strings
  3 +//
  4 +// Copyright (c) 2014-present Ruslan Skorb, http://ruslanskorb.com/
  5 +//
  6 +// Permission is hereby granted, free of charge, to any person obtaining a copy
  7 +// of this software and associated documentation files (the "Software"), to deal
  8 +// in the Software without restriction, including without limitation the rights
  9 +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10 +// copies of the Software, and to permit persons to whom the Software is
  11 +// furnished to do so, subject to the following conditions:
  12 +//
  13 +// The above copyright notice and this permission notice shall be included in
  14 +// all copies or substantial portions of the Software.
  15 +//
  16 +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17 +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18 +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  19 +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20 +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21 +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22 +// THE SOFTWARE.
  23 +//
  24 +
  25 +/* Move and Scale label */
  26 +"Move and Scale" = "Wybierz kadr";
  27 +/* Cancel button */
  28 +"Cancel" = "Anuluj";
  29 +/* Choose button */
  30 +"Choose" = "Wybierz";
  1 +//
  2 +// RSKImageCropper.strings
  3 +//
  4 +// Copyright (c) 2014-present Ruslan Skorb, http://ruslanskorb.com/
  5 +//
  6 +// Permission is hereby granted, free of charge, to any person obtaining a copy
  7 +// of this software and associated documentation files (the "Software"), to deal
  8 +// in the Software without restriction, including without limitation the rights
  9 +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10 +// copies of the Software, and to permit persons to whom the Software is
  11 +// furnished to do so, subject to the following conditions:
  12 +//
  13 +// The above copyright notice and this permission notice shall be included in
  14 +// all copies or substantial portions of the Software.
  15 +//
  16 +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17 +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18 +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  19 +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20 +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21 +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22 +// THE SOFTWARE.
  23 +//
  24 +
  25 +/* Move and Scale label */
  26 +"Move and Scale" = "Mover e Redimensionar";
  27 +/* Cancel button */
  28 +"Cancel" = "Cancelar";
  29 +/* Choose button */
  30 +"Choose" = "Escolher";
  1 +//
  2 +// RSKImageCropper.strings
  3 +//
  4 +// Copyright (c) 2014-present Ruslan Skorb, http://ruslanskorb.com/
  5 +//
  6 +// Permission is hereby granted, free of charge, to any person obtaining a copy
  7 +// of this software and associated documentation files (the "Software"), to deal
  8 +// in the Software without restriction, including without limitation the rights
  9 +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10 +// copies of the Software, and to permit persons to whom the Software is
  11 +// furnished to do so, subject to the following conditions:
  12 +//
  13 +// The above copyright notice and this permission notice shall be included in
  14 +// all copies or substantial portions of the Software.
  15 +//
  16 +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17 +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18 +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  19 +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20 +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21 +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22 +// THE SOFTWARE.
  23 +//
  24 +
  25 +/* Move and Scale label */
  26 +"Move and Scale" = "Mover e dimensionar";
  27 +/* Cancel button */
  28 +"Cancel" = "Cancelar";
  29 +/* Choose button */
  30 +"Choose" = "Escolher";
  1 +//
  2 +// RSKImageCropper.strings
  3 +//
  4 +// Copyright (c) 2014-present Ruslan Skorb, http://ruslanskorb.com/
  5 +//
  6 +// Permission is hereby granted, free of charge, to any person obtaining a copy
  7 +// of this software and associated documentation files (the "Software"), to deal
  8 +// in the Software without restriction, including without limitation the rights
  9 +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10 +// copies of the Software, and to permit persons to whom the Software is
  11 +// furnished to do so, subject to the following conditions:
  12 +//
  13 +// The above copyright notice and this permission notice shall be included in
  14 +// all copies or substantial portions of the Software.
  15 +//
  16 +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17 +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18 +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  19 +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20 +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21 +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22 +// THE SOFTWARE.
  23 +//
  24 +
  25 +/* Move and Scale label */
  26 +"Move and Scale" = "Mutaţi și redimensionaţi";
  27 +/* Cancel button */
  28 +"Cancel" = "Anulaţi";
  29 +/* Choose button */
  30 +"Choose" = "Selectaţi";
  1 +//
  2 +// RSKImageCropper.strings
  3 +//
  4 +// Copyright (c) 2014-present Ruslan Skorb, http://ruslanskorb.com/
  5 +//
  6 +// Permission is hereby granted, free of charge, to any person obtaining a copy
  7 +// of this software and associated documentation files (the "Software"), to deal
  8 +// in the Software without restriction, including without limitation the rights
  9 +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10 +// copies of the Software, and to permit persons to whom the Software is
  11 +// furnished to do so, subject to the following conditions:
  12 +//
  13 +// The above copyright notice and this permission notice shall be included in
  14 +// all copies or substantial portions of the Software.
  15 +//
  16 +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17 +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18 +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  19 +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20 +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21 +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22 +// THE SOFTWARE.
  23 +//
  24 +
  25 +/* Move and Scale label */
  26 +"Move and Scale" = "Cдвиг и масштаб";
  27 +/* Cancel button */
  28 +"Cancel" = "Отменить";
  29 +/* Choose button */
  30 +"Choose" = "Выбрать";
  31 +
  1 +//
  2 +// RSKImageCropper.strings
  3 +//
  4 +// Copyright (c) 2014-present Ruslan Skorb, http://ruslanskorb.com/
  5 +//
  6 +// Permission is hereby granted, free of charge, to any person obtaining a copy
  7 +// of this software and associated documentation files (the "Software"), to deal
  8 +// in the Software without restriction, including without limitation the rights
  9 +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10 +// copies of the Software, and to permit persons to whom the Software is
  11 +// furnished to do so, subject to the following conditions:
  12 +//
  13 +// The above copyright notice and this permission notice shall be included in
  14 +// all copies or substantial portions of the Software.
  15 +//
  16 +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17 +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18 +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  19 +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20 +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21 +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22 +// THE SOFTWARE.
  23 +//
  24 +
  25 +/* Move and Scale label */
  26 +"Move and Scale" = "Posunutie a veľkosť";
  27 +/* Cancel button */
  28 +"Cancel" = "Zrušiť";
  29 +/* Choose button */
  30 +"Choose" = "Vybrať";
  1 +//
  2 +// RSKImageCropper.strings
  3 +//
  4 +// Copyright (c) 2014-present Ruslan Skorb, http://ruslanskorb.com/
  5 +//
  6 +// Permission is hereby granted, free of charge, to any person obtaining a copy
  7 +// of this software and associated documentation files (the "Software"), to deal
  8 +// in the Software without restriction, including without limitation the rights
  9 +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10 +// copies of the Software, and to permit persons to whom the Software is
  11 +// furnished to do so, subject to the following conditions:
  12 +//
  13 +// The above copyright notice and this permission notice shall be included in
  14 +// all copies or substantial portions of the Software.
  15 +//
  16 +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17 +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18 +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  19 +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20 +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21 +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22 +// THE SOFTWARE.
  23 +//
  24 +
  25 +/* Move and Scale label */
  26 +"Move and Scale" = "Flytta och skalanpassa";
  27 +/* Cancel button */
  28 +"Cancel" = "Avbryt";
  29 +/* Choose button */
  30 +"Choose" = "Välj";
  1 +//
  2 +// RSKImageCropper.strings
  3 +//
  4 +// Copyright (c) 2014-present Ruslan Skorb, http://ruslanskorb.com/
  5 +//
  6 +// Permission is hereby granted, free of charge, to any person obtaining a copy
  7 +// of this software and associated documentation files (the "Software"), to deal
  8 +// in the Software without restriction, including without limitation the rights
  9 +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10 +// copies of the Software, and to permit persons to whom the Software is
  11 +// furnished to do so, subject to the following conditions:
  12 +//
  13 +// The above copyright notice and this permission notice shall be included in
  14 +// all copies or substantial portions of the Software.
  15 +//
  16 +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17 +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18 +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  19 +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20 +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21 +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22 +// THE SOFTWARE.
  23 +//
  24 +
  25 +/* Move and Scale label */
  26 +"Move and Scale" = "Taşı ve Ölçekle";
  27 +/* Cancel button */
  28 +"Cancel" = "Vazgeç";
  29 +/* Choose button */
  30 +"Choose" = "Seç";
  1 +//
  2 +// RSKImageCropper.strings
  3 +//
  4 +// Copyright (c) 2014-present Ruslan Skorb, http://ruslanskorb.com/
  5 +//
  6 +// Permission is hereby granted, free of charge, to any person obtaining a copy
  7 +// of this software and associated documentation files (the "Software"), to deal
  8 +// in the Software without restriction, including without limitation the rights
  9 +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10 +// copies of the Software, and to permit persons to whom the Software is
  11 +// furnished to do so, subject to the following conditions:
  12 +//
  13 +// The above copyright notice and this permission notice shall be included in
  14 +// all copies or substantial portions of the Software.
  15 +//
  16 +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17 +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18 +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  19 +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20 +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21 +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22 +// THE SOFTWARE.
  23 +//
  24 +
  25 +/* Move and Scale label */
  26 +"Move and Scale" = "Pyxaйтe i вiдмipяйтe";
  27 +/* Cancel button */
  28 +"Cancel" = "Скасувати";
  29 +/* Choose button */
  30 +"Choose" = "Вибрати";
  1 +//
  2 +// RSKImageCropper.strings
  3 +//
  4 +// Copyright (c) 2014-present Ruslan Skorb, http://ruslanskorb.com/
  5 +//
  6 +// Permission is hereby granted, free of charge, to any person obtaining a copy
  7 +// of this software and associated documentation files (the "Software"), to deal
  8 +// in the Software without restriction, including without limitation the rights
  9 +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10 +// copies of the Software, and to permit persons to whom the Software is
  11 +// furnished to do so, subject to the following conditions:
  12 +//
  13 +// The above copyright notice and this permission notice shall be included in
  14 +// all copies or substantial portions of the Software.
  15 +//
  16 +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17 +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18 +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  19 +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20 +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21 +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22 +// THE SOFTWARE.
  23 +//
  24 +
  25 +/* Move and Scale label */
  26 +"Move and Scale" = "Di chuyển và Chia tý lệ";
  27 +/* Cancel button */
  28 +"Cancel" = "Hủy";
  29 +/* Choose button */
  30 +"Choose" = "Chọn";