├── src ├── ios │ ├── ELCImagePicker │ │ ├── Resources │ │ │ ├── Overlay.png │ │ │ ├── Overlay@2x.png │ │ │ ├── ELCAlbumPickerController.xib │ │ │ ├── ELCAssetTablePicker.xib │ │ │ └── ELCAssetPicker.xib │ │ ├── ELCAssetCell.h │ │ ├── ELCAssetPickerFilterDelegate.h │ │ ├── ELCAssetSelectionDelegate.h │ │ ├── ELCAsset.h │ │ ├── ELCAlbumPickerController.h │ │ ├── ELCAsset.m │ │ ├── ELCAssetTablePicker.h │ │ ├── ELCImagePickerController.h │ │ ├── ELCImagePickerController.m │ │ ├── ELCAssetCell.m │ │ ├── ELCAlbumPickerController.m │ │ └── ELCAssetTablePicker.m │ ├── SOSPicker.h │ └── SOSPicker.m └── android │ ├── Library │ ├── res │ │ ├── drawable-hdpi │ │ │ ├── icon.png │ │ │ ├── image_bg.9.png │ │ │ └── loading_icon.png │ │ ├── drawable-ldpi │ │ │ └── icon.png │ │ ├── drawable-mdpi │ │ │ ├── icon.png │ │ │ ├── ic_launcher.png │ │ │ ├── ic_action_done_dark.png │ │ │ ├── ic_action_done_light.png │ │ │ ├── ic_action_discard_dark.png │ │ │ └── ic_action_discard_light.png │ │ ├── drawable-xhdpi │ │ │ ├── ic_launcher.png │ │ │ ├── ic_action_done_dark.png │ │ │ ├── ic_action_done_light.png │ │ │ ├── ic_action_discard_dark.png │ │ │ └── ic_action_discard_light.png │ │ ├── values │ │ │ ├── themes.xml │ │ │ └── multiimagechooser_strings_en.xml │ │ ├── anim │ │ │ └── image_pop_in.xml │ │ ├── drawable │ │ │ └── grid_background.xml │ │ ├── values-ja │ │ │ └── multiimagechooser_strings_ja.xml │ │ ├── values-ko │ │ │ └── multiimagechooser_strings_ko.xml │ │ ├── values-fr │ │ │ └── multiimagechooser_strings_fr.xml │ │ ├── values-es │ │ │ └── multiimagechooser_strings_es.xml │ │ ├── values-hu │ │ │ └── multiimagechooser_strings_hu.xml │ │ ├── values-de │ │ │ └── multiimagechooser_strings_de.xml │ │ └── layout │ │ │ ├── multiselectorgrid.xml │ │ │ ├── actionbar_custom_view_done_discard.xml │ │ │ ├── actionbar_done_button.xml │ │ │ └── actionbar_discard_button.xml │ └── src │ │ ├── ImageFetcher.java │ │ └── MultiImageChooserActivity.java │ └── com │ └── synconset │ └── ImagePicker │ ├── FakeR.java │ └── ImagePicker.java ├── .gitignore ├── package.json ├── LICENSE ├── BSD_LICENSE ├── www └── imagepicker.js ├── README.md ├── plugin.xml └── APACHE_LICENSE /src/ios/ELCImagePicker/Resources/Overlay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wymsee/cordova-imagePicker/HEAD/src/ios/ELCImagePicker/Resources/Overlay.png -------------------------------------------------------------------------------- /src/android/Library/res/drawable-hdpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wymsee/cordova-imagePicker/HEAD/src/android/Library/res/drawable-hdpi/icon.png -------------------------------------------------------------------------------- /src/android/Library/res/drawable-ldpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wymsee/cordova-imagePicker/HEAD/src/android/Library/res/drawable-ldpi/icon.png -------------------------------------------------------------------------------- /src/android/Library/res/drawable-mdpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wymsee/cordova-imagePicker/HEAD/src/android/Library/res/drawable-mdpi/icon.png -------------------------------------------------------------------------------- /src/ios/ELCImagePicker/Resources/Overlay@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wymsee/cordova-imagePicker/HEAD/src/ios/ELCImagePicker/Resources/Overlay@2x.png -------------------------------------------------------------------------------- /src/android/Library/res/drawable-hdpi/image_bg.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wymsee/cordova-imagePicker/HEAD/src/android/Library/res/drawable-hdpi/image_bg.9.png -------------------------------------------------------------------------------- /src/android/Library/res/drawable-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wymsee/cordova-imagePicker/HEAD/src/android/Library/res/drawable-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /src/android/Library/res/drawable-hdpi/loading_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wymsee/cordova-imagePicker/HEAD/src/android/Library/res/drawable-hdpi/loading_icon.png -------------------------------------------------------------------------------- /src/android/Library/res/drawable-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wymsee/cordova-imagePicker/HEAD/src/android/Library/res/drawable-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /src/android/Library/res/drawable-mdpi/ic_action_done_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wymsee/cordova-imagePicker/HEAD/src/android/Library/res/drawable-mdpi/ic_action_done_dark.png -------------------------------------------------------------------------------- /src/android/Library/res/drawable-mdpi/ic_action_done_light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wymsee/cordova-imagePicker/HEAD/src/android/Library/res/drawable-mdpi/ic_action_done_light.png -------------------------------------------------------------------------------- /src/android/Library/res/drawable-xhdpi/ic_action_done_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wymsee/cordova-imagePicker/HEAD/src/android/Library/res/drawable-xhdpi/ic_action_done_dark.png -------------------------------------------------------------------------------- /src/android/Library/res/drawable-xhdpi/ic_action_done_light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wymsee/cordova-imagePicker/HEAD/src/android/Library/res/drawable-xhdpi/ic_action_done_light.png -------------------------------------------------------------------------------- /src/android/Library/res/drawable-mdpi/ic_action_discard_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wymsee/cordova-imagePicker/HEAD/src/android/Library/res/drawable-mdpi/ic_action_discard_dark.png -------------------------------------------------------------------------------- /src/android/Library/res/drawable-mdpi/ic_action_discard_light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wymsee/cordova-imagePicker/HEAD/src/android/Library/res/drawable-mdpi/ic_action_discard_light.png -------------------------------------------------------------------------------- /src/android/Library/res/drawable-xhdpi/ic_action_discard_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wymsee/cordova-imagePicker/HEAD/src/android/Library/res/drawable-xhdpi/ic_action_discard_dark.png -------------------------------------------------------------------------------- /src/android/Library/res/drawable-xhdpi/ic_action_discard_light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wymsee/cordova-imagePicker/HEAD/src/android/Library/res/drawable-xhdpi/ic_action_discard_light.png -------------------------------------------------------------------------------- /src/android/Library/res/values/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /build 3 | # XCode (and ancestors) per-user config (very noisy, and not relevant) 4 | *.mode1 5 | *.mode1v3 6 | *.mode2v3 7 | *.perspective 8 | *.perspectivev3 9 | *.pbxuser 10 | *.zedstate 11 | # Xcode 4 12 | xcuserdata/ 13 | project.xcworkspace/ 14 | tags 15 | -------------------------------------------------------------------------------- /src/ios/ELCImagePicker/ELCAssetCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // AssetCell.h 3 | // 4 | // Created by ELC on 2/15/11. 5 | // Copyright 2011 ELC Technologies. All rights reserved. 6 | // 7 | 8 | #import 9 | 10 | 11 | @interface ELCAssetCell : UITableViewCell 12 | 13 | - (void)setAssets:(NSArray *)assets; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /src/ios/ELCImagePicker/ELCAssetPickerFilterDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // ELCAssetPickerFilterDelegate.h 3 | 4 | @class ELCAsset; 5 | @class ELCAssetTablePicker; 6 | 7 | @protocol ELCAssetPickerFilterDelegate 8 | 9 | // respond YES/NO to filter out (not show the asset) 10 | -(BOOL)assetTablePicker:(ELCAssetTablePicker *)picker isAssetFilteredOut:(ELCAsset *)elcAsset; 11 | 12 | @end -------------------------------------------------------------------------------- /src/android/Library/res/anim/image_pop_in.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | -------------------------------------------------------------------------------- /src/android/Library/res/drawable/grid_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 12 | -------------------------------------------------------------------------------- /src/ios/ELCImagePicker/ELCAssetSelectionDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // ELCAssetSelectionDelegate.h 3 | // ELCImagePickerDemo 4 | // 5 | // Created by JN on 9/6/12. 6 | // Copyright (c) 2012 ELC Technologies. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class ELCAsset; 12 | 13 | @protocol ELCAssetSelectionDelegate 14 | 15 | - (void)selectedAssets:(NSArray *)assets; 16 | - (BOOL)shouldSelectAsset:(ELCAsset *)asset previousCount:(NSUInteger)previousCount; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /src/ios/ELCImagePicker/ELCAsset.h: -------------------------------------------------------------------------------- 1 | // 2 | // Asset.h 3 | // 4 | // Created by ELC on 2/15/11. 5 | // Copyright 2011 ELC Technologies. All rights reserved. 6 | // 7 | 8 | #import 9 | #import 10 | 11 | @class ELCAsset; 12 | 13 | @protocol ELCAssetDelegate 14 | 15 | @optional 16 | - (void)assetSelected:(ELCAsset *)asset; 17 | - (BOOL)shouldSelectAsset:(ELCAsset *)asset; 18 | @end 19 | 20 | 21 | @interface ELCAsset : NSObject 22 | 23 | @property (nonatomic, strong) ALAsset *asset; 24 | @property (nonatomic, weak) id parent; 25 | @property (nonatomic, assign) BOOL selected; 26 | 27 | - (id)initWithAsset:(ALAsset *)asset; 28 | 29 | @end -------------------------------------------------------------------------------- /src/ios/SOSPicker.h: -------------------------------------------------------------------------------- 1 | // 2 | // SOSPicker.h 3 | // SyncOnSet 4 | // 5 | // Created by Christopher Sullivan on 10/25/13. 6 | // 7 | // 8 | 9 | #import 10 | #import "ELCAlbumPickerController.h" 11 | #import "ELCImagePickerController.h" 12 | 13 | @interface SOSPicker : CDVPlugin 14 | 15 | @property (copy) NSString* callbackId; 16 | 17 | - (void) getPictures:(CDVInvokedUrlCommand *)command; 18 | - (UIImage*)imageByScalingNotCroppingForSize:(UIImage*)anImage toSize:(CGSize)frameSize; 19 | 20 | @property (nonatomic, assign) NSInteger width; 21 | @property (nonatomic, assign) NSInteger height; 22 | @property (nonatomic, assign) NSInteger quality; 23 | 24 | @end 25 | -------------------------------------------------------------------------------- /src/android/Library/res/values-ja/multiimagechooser_strings_ja.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | MultiImageChooser 4 | 無料版 - 残りの画像: %d 5 | 画像データベースを開く際にエラーがありました。問題を報告してください。 6 | サムネイルをリクエスト中です。お待ちください。 7 | Processing Images 8 | This may take a few moments 9 | Limit reached 10 | You can only select %d photos at once. 11 | Cancel 12 | OK 13 | 14 | -------------------------------------------------------------------------------- /src/android/Library/res/values-ko/multiimagechooser_strings_ko.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | MultiImageChooser 4 | 무료 버전 - 남은 이미지: %d 5 | 이미지 데이터베이스를 여는 데 오류가 발생했습니다. 문제를 보고하세요. 6 | 썸네일 요청 중. 기다려주세요 7 | Processing Images 8 | This may take a few moments 9 | Limit reached 10 | You can only select %d photos at once. 11 | Cancel 12 | OK 13 | 14 | -------------------------------------------------------------------------------- /src/ios/ELCImagePicker/ELCAlbumPickerController.h: -------------------------------------------------------------------------------- 1 | // 2 | // AlbumPickerController.h 3 | // 4 | // Created by ELC on 2/15/11. 5 | // Copyright 2011 ELC Technologies. All rights reserved. 6 | // 7 | 8 | #import 9 | #import 10 | #import "ELCAssetSelectionDelegate.h" 11 | #import "ELCAssetPickerFilterDelegate.h" 12 | 13 | @interface ELCAlbumPickerController : UITableViewController 14 | 15 | @property (nonatomic, weak) id parent; 16 | @property (nonatomic, strong) NSMutableArray *assetGroups; 17 | @property (nonatomic, assign) BOOL singleSelection; 18 | @property (nonatomic, assign) BOOL immediateReturn; 19 | 20 | // optional, can be used to filter the assets displayed 21 | @property (nonatomic, weak) id assetPickerFilterDelegate; 22 | 23 | @end 24 | 25 | -------------------------------------------------------------------------------- /src/android/Library/res/values-fr/multiimagechooser_strings_fr.xml: -------------------------------------------------------------------------------- 1 | 2 | MultiImageChooser 3 | Version gratuite - Images restantes:%d" 4 | "Il y eu une erreur avec les images. Veuillez nous signaler le problème." 5 | Récupération des vignettes, soyez patient 6 | Processing Images 7 | This may take a few moments 8 | Limit reached 9 | You can only select %d photos at once. 10 | Annuler 11 | OK 12 | 13 | -------------------------------------------------------------------------------- /src/android/Library/res/values-es/multiimagechooser_strings_es.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | MultiImageChooser 4 | Solicitando miniaturas. Por favor, espere… 5 | Versión gratuita - Imágenes restantes: %d 6 | Error al abrir la base de datos de imágenes. 7 | Processing Images 8 | This may take a few moments 9 | Limit reached 10 | You can only select %d photos at once. 11 | Cancelar 12 | OK 13 | 14 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cordova-plugin-image-picker", 3 | "version": "1.1.3", 4 | "description": "This plugin allows selection of multiple images from the camera roll / gallery in a phonegap app", 5 | "cordova": { 6 | "id": "cordova-plugin-image-picker", 7 | "platforms": [ 8 | "ios", 9 | "android" 10 | ] 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "git+https://github.com/wymsee/cordova-imagePicker.git" 15 | }, 16 | "keywords": [ 17 | "ecosystem:cordova", 18 | "cordova-ios", 19 | "cordova-android" 20 | ], 21 | "engines": [ 22 | { 23 | "name": "cordova", 24 | "version": ">=3.0.0" 25 | } 26 | ], 27 | "author": "Wymsee", 28 | "license": "MIT", 29 | "bugs": { 30 | "url": "https://github.com/wymsee/cordova-imagePicker/issues" 31 | }, 32 | "homepage": "https://github.com/wymsee/cordova-imagePicker#readme" 33 | } 34 | -------------------------------------------------------------------------------- /src/android/Library/res/values-hu/multiimagechooser_strings_hu.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | MultiImageChooser 4 | Ingyenes verzió - hátralévő képek: %d 5 | Képadatbázis megnyitási hiba történt. Kérjük, jelentse a problémát. 6 | Miniatűrök lekérése, kérjük legyen türelemmel 7 | Processing Images 8 | This may take a few moments 9 | Limit reached 10 | You can only select %d photos at once. 11 | Cancel 12 | OK 13 | 14 | -------------------------------------------------------------------------------- /src/android/Library/res/values-de/multiimagechooser_strings_de.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | MultiImageChooser 4 | Kostenlose Version - Sie können noch %d Bilder wählen 5 | Beim Öffnen der Bilder-Datenbank kam es zu einem Fehler. 6 | Lade Vorschaubilder, bitte warten 7 | Verarbeite Bildauswahl 8 | Dies kann einen kurzen Augenblick dauern. 9 | Auswahllimit erreicht 10 | Sie können maximal %d Bilder auf einmal auswählen. 11 | Abbrechen 12 | OK 13 | 14 | -------------------------------------------------------------------------------- /src/android/Library/res/layout/multiselectorgrid.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 22 | 23 | -------------------------------------------------------------------------------- /src/android/Library/res/values/multiimagechooser_strings_en.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | MultiImageChooser 4 | Free version - Images left: %d 5 | There was an error opening the images database. Please report the problem. 6 | Requesting thumbnails, please be patient 7 | Processing Images 8 | This may take a few moments 9 | Limit reached 10 | You can only select %d photos at once. 11 | Cancel 12 | OK 13 | 14 | -------------------------------------------------------------------------------- /src/ios/ELCImagePicker/ELCAsset.m: -------------------------------------------------------------------------------- 1 | // 2 | // Asset.m 3 | // 4 | // Created by ELC on 2/15/11. 5 | // Copyright 2011 ELC Technologies. All rights reserved. 6 | // 7 | 8 | #import "ELCAsset.h" 9 | #import "ELCAssetTablePicker.h" 10 | 11 | @implementation ELCAsset 12 | 13 | //Using auto synthesizers 14 | 15 | - (id)initWithAsset:(ALAsset*)asset 16 | { 17 | self = [super init]; 18 | if (self) { 19 | self.asset = asset; 20 | _selected = NO; 21 | } 22 | return self; 23 | } 24 | 25 | - (void)toggleSelection 26 | { 27 | self.selected = !self.selected; 28 | } 29 | 30 | - (void)setSelected:(BOOL)selected 31 | { 32 | if (selected) { 33 | if ([_parent respondsToSelector:@selector(shouldSelectAsset:)]) { 34 | if (![_parent shouldSelectAsset:self]) { 35 | return; 36 | } 37 | } 38 | } 39 | _selected = selected; 40 | if (selected) { 41 | if (_parent != nil && [_parent respondsToSelector:@selector(assetSelected:)]) { 42 | [_parent assetSelected:self]; 43 | } 44 | } 45 | } 46 | 47 | 48 | @end 49 | 50 | -------------------------------------------------------------------------------- /src/ios/ELCImagePicker/ELCAssetTablePicker.h: -------------------------------------------------------------------------------- 1 | // 2 | // ELCAssetTablePicker.h 3 | // 4 | // Created by ELC on 2/15/11. 5 | // Copyright 2011 ELC Technologies. All rights reserved. 6 | // 7 | 8 | #import 9 | #import 10 | #import "ELCAsset.h" 11 | #import "ELCAssetSelectionDelegate.h" 12 | #import "ELCAssetPickerFilterDelegate.h" 13 | 14 | @interface ELCAssetTablePicker : UITableViewController 15 | 16 | @property (nonatomic, weak) id parent; 17 | @property (nonatomic, strong) ALAssetsGroup *assetGroup; 18 | @property (nonatomic, strong) NSMutableArray *elcAssets; 19 | @property (nonatomic, strong) IBOutlet UILabel *selectedAssetsLabel; 20 | @property (nonatomic, assign) BOOL singleSelection; 21 | @property (nonatomic, assign) BOOL immediateReturn; 22 | 23 | // optional, can be used to filter the assets displayed 24 | @property(nonatomic, weak) id assetPickerFilterDelegate; 25 | 26 | - (int)totalSelectedAssets; 27 | - (void)preparePhotos; 28 | 29 | - (void)doneAction:(id)sender; 30 | 31 | @end -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 CSullivan102 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | 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, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /src/android/Library/res/layout/actionbar_custom_view_done_discard.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /BSD_LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012, David Erosa 2 | 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 8 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 9 | 10 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE -------------------------------------------------------------------------------- /www/imagepicker.js: -------------------------------------------------------------------------------- 1 | /*global cordova,window,console*/ 2 | /** 3 | * An Image Picker plugin for Cordova 4 | * 5 | * Developed by Wymsee for Sync OnSet 6 | */ 7 | 8 | var ImagePicker = function() { 9 | 10 | }; 11 | 12 | /* 13 | * success - success callback 14 | * fail - error callback 15 | * options 16 | * .maximumImagesCount - max images to be selected, defaults to 15. If this is set to 1, 17 | * upon selection of a single image, the plugin will return it. 18 | * .width - width to resize image to (if one of height/width is 0, will resize to fit the 19 | * other while keeping aspect ratio, if both height and width are 0, the full size 20 | * image will be returned) 21 | * .height - height to resize image to 22 | * .quality - quality of resized image, defaults to 100 23 | */ 24 | ImagePicker.prototype.getPictures = function(success, fail, options) { 25 | if (!options) { 26 | options = {}; 27 | } 28 | 29 | var params = { 30 | maximumImagesCount: options.maximumImagesCount ? options.maximumImagesCount : 15, 31 | width: options.width ? options.width : 0, 32 | height: options.height ? options.height : 0, 33 | quality: options.quality ? options.quality : 100 34 | }; 35 | 36 | return cordova.exec(success, fail, "ImagePicker", "getPictures", [params]); 37 | }; 38 | 39 | window.imagePicker = new ImagePicker(); 40 | -------------------------------------------------------------------------------- /src/android/Library/res/layout/actionbar_done_button.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 23 | 24 | 34 | 35 | -------------------------------------------------------------------------------- /src/android/Library/res/layout/actionbar_discard_button.xml: -------------------------------------------------------------------------------- 1 | 16 | 22 | 33 | 34 | -------------------------------------------------------------------------------- /src/ios/ELCImagePicker/ELCImagePickerController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ELCImagePickerController.h 3 | // ELCImagePickerDemo 4 | // 5 | // Created by ELC on 9/9/10. 6 | // Copyright 2010 ELC Technologies. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "ELCAssetSelectionDelegate.h" 11 | 12 | @class ELCImagePickerController; 13 | @class ELCAlbumPickerController; 14 | 15 | @protocol ELCImagePickerControllerDelegate 16 | 17 | /** 18 | * Called with the picker the images were selected from, as well as an array of dictionary's 19 | * containing keys for ALAssetPropertyLocation, ALAssetPropertyType, 20 | * UIImagePickerControllerOriginalImage, and UIImagePickerControllerReferenceURL. 21 | * @param picker 22 | * @param info An NSArray containing dictionary's with the key UIImagePickerControllerOriginalImage, which is a rotated, and sized for the screen 'default representation' of the image selected. If you want to get the original image, use the UIImagePickerControllerReferenceURL key. 23 | */ 24 | - (void)elcImagePickerController:(ELCImagePickerController *)picker didFinishPickingMediaWithInfo:(NSArray *)info; 25 | 26 | /** 27 | * Called when image selection was cancelled, by tapping the 'Cancel' BarButtonItem. 28 | */ 29 | - (void)elcImagePickerControllerDidCancel:(ELCImagePickerController *)picker; 30 | 31 | @end 32 | 33 | @interface ELCImagePickerController : UINavigationController 34 | 35 | @property (nonatomic, weak) id imagePickerDelegate; 36 | @property (nonatomic, assign) NSInteger maximumImagesCount; 37 | 38 | /** 39 | * YES if the picker should return the original image, 40 | * or NO for an image suitable for displaying full screen on the device. 41 | */ 42 | @property (nonatomic, assign) BOOL returnsOriginalImage; 43 | 44 | - (id)initImagePicker; 45 | - (void)cancelImagePicker; 46 | 47 | @end 48 | 49 | -------------------------------------------------------------------------------- /src/android/com/synconset/ImagePicker/FakeR.java: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License 3 | 4 | Copyright (c) 2010 Matt Kane 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 7 | 8 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 9 | 10 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 11 | 12 | Code taken from: https://github.com/wildabeast/BarcodeScanner 13 | */ 14 | package com.synconset; 15 | 16 | import android.app.Activity; 17 | import android.content.Context; 18 | 19 | /** 20 | * R replacement for PhoneGap Build. 21 | * 22 | * ([^.\w])R\.(\w+)\.(\w+) 23 | * $1fakeR("$2", "$3") 24 | * 25 | * @author Maciej Nux Jaros 26 | */ 27 | public class FakeR { 28 | private Context context; 29 | private String packageName; 30 | 31 | public FakeR(Activity activity) { 32 | context = activity.getApplicationContext(); 33 | packageName = context.getPackageName(); 34 | } 35 | 36 | public FakeR(Context context) { 37 | this.context = context; 38 | packageName = context.getPackageName(); 39 | } 40 | 41 | public int getId(String group, String key) { 42 | return context.getResources().getIdentifier(key, group, packageName); 43 | } 44 | 45 | public static int getId(Context context, String group, String key) { 46 | return context.getResources().getIdentifier(key, group, context.getPackageName()); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/android/com/synconset/ImagePicker/ImagePicker.java: -------------------------------------------------------------------------------- 1 | /** 2 | * An Image Picker Plugin for Cordova/PhoneGap. 3 | */ 4 | package com.synconset; 5 | 6 | import org.apache.cordova.CallbackContext; 7 | import org.apache.cordova.CordovaPlugin; 8 | 9 | import org.json.JSONArray; 10 | import org.json.JSONException; 11 | import org.json.JSONObject; 12 | 13 | import java.util.ArrayList; 14 | 15 | import android.app.Activity; 16 | import android.content.Intent; 17 | import android.util.Log; 18 | 19 | public class ImagePicker extends CordovaPlugin { 20 | public static String TAG = "ImagePicker"; 21 | 22 | private CallbackContext callbackContext; 23 | private JSONObject params; 24 | 25 | public boolean execute(String action, final JSONArray args, final CallbackContext callbackContext) throws JSONException { 26 | this.callbackContext = callbackContext; 27 | this.params = args.getJSONObject(0); 28 | if (action.equals("getPictures")) { 29 | Intent intent = new Intent(cordova.getActivity(), MultiImageChooserActivity.class); 30 | int max = 20; 31 | int desiredWidth = 0; 32 | int desiredHeight = 0; 33 | int quality = 100; 34 | if (this.params.has("maximumImagesCount")) { 35 | max = this.params.getInt("maximumImagesCount"); 36 | } 37 | if (this.params.has("width")) { 38 | desiredWidth = this.params.getInt("width"); 39 | } 40 | if (this.params.has("height")) { 41 | desiredHeight = this.params.getInt("height"); 42 | } 43 | if (this.params.has("quality")) { 44 | quality = this.params.getInt("quality"); 45 | } 46 | intent.putExtra("MAX_IMAGES", max); 47 | intent.putExtra("WIDTH", desiredWidth); 48 | intent.putExtra("HEIGHT", desiredHeight); 49 | intent.putExtra("QUALITY", quality); 50 | if (this.cordova != null) { 51 | this.cordova.startActivityForResult((CordovaPlugin) this, intent, 0); 52 | } 53 | } 54 | return true; 55 | } 56 | 57 | public void onActivityResult(int requestCode, int resultCode, Intent data) { 58 | if (resultCode == Activity.RESULT_OK && data != null) { 59 | ArrayList fileNames = data.getStringArrayListExtra("MULTIPLEFILENAMES"); 60 | JSONArray res = new JSONArray(fileNames); 61 | this.callbackContext.success(res); 62 | } else if (resultCode == Activity.RESULT_CANCELED && data != null) { 63 | String error = data.getStringExtra("ERRORMESSAGE"); 64 | this.callbackContext.error(error); 65 | } else if (resultCode == Activity.RESULT_CANCELED) { 66 | JSONArray res = new JSONArray(); 67 | this.callbackContext.success(res); 68 | } else { 69 | this.callbackContext.error("No images selected"); 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/ios/ELCImagePicker/ELCImagePickerController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ELCImagePickerController.m 3 | // ELCImagePickerDemo 4 | // 5 | // Created by ELC on 9/9/10. 6 | // Copyright 2010 ELC Technologies. All rights reserved. 7 | // 8 | 9 | #import "ELCImagePickerController.h" 10 | #import "ELCAsset.h" 11 | #import "ELCAssetCell.h" 12 | #import "ELCAssetTablePicker.h" 13 | #import "ELCAlbumPickerController.h" 14 | #import 15 | 16 | @implementation ELCImagePickerController 17 | 18 | //Using auto synthesizers 19 | 20 | - (id)initImagePicker 21 | { 22 | ELCAlbumPickerController *albumPicker = [[ELCAlbumPickerController alloc] initWithStyle:UITableViewStylePlain]; 23 | 24 | self = [super initWithRootViewController:albumPicker]; 25 | if (self) { 26 | self.maximumImagesCount = 4; 27 | [albumPicker setParent:self]; 28 | } 29 | return self; 30 | } 31 | 32 | - (id)initWithRootViewController:(UIViewController *)rootViewController 33 | { 34 | self = [super initWithRootViewController:rootViewController]; 35 | if (self) { 36 | self.maximumImagesCount = 4; 37 | } 38 | return self; 39 | } 40 | 41 | - (void)cancelImagePicker 42 | { 43 | if ([_imagePickerDelegate respondsToSelector:@selector(elcImagePickerControllerDidCancel:)]) { 44 | [_imagePickerDelegate performSelector:@selector(elcImagePickerControllerDidCancel:) withObject:self]; 45 | } 46 | } 47 | 48 | - (BOOL)shouldSelectAsset:(ELCAsset *)asset previousCount:(NSUInteger)previousCount 49 | { 50 | BOOL shouldSelect = previousCount < self.maximumImagesCount; 51 | if (!shouldSelect) { 52 | NSString *title = [NSString stringWithFormat:NSLocalizedString(@"Maximum %d photos.", nil), self.maximumImagesCount]; 53 | NSString *message = [NSString stringWithFormat:NSLocalizedString(@"You can only select %d photos at a time.", nil), self.maximumImagesCount]; 54 | [[[UIAlertView alloc] initWithTitle:title 55 | message:message 56 | delegate:nil 57 | cancelButtonTitle:nil 58 | otherButtonTitles:NSLocalizedString(@"Okay", nil), nil] show]; 59 | } 60 | return shouldSelect; 61 | } 62 | 63 | - (void)selectedAssets:(NSArray *)assets 64 | { 65 | NSMutableArray *returnArray = [[NSMutableArray alloc] init]; 66 | 67 | for(ALAsset *asset in assets) { 68 | id obj = [asset valueForProperty:ALAssetPropertyType]; 69 | if (!obj) { 70 | continue; 71 | } 72 | NSMutableDictionary *workingDictionary = [[NSMutableDictionary alloc] init]; 73 | [workingDictionary setObject:asset forKey:@"ALAsset"]; 74 | [workingDictionary setObject:[[asset valueForProperty:ALAssetPropertyURLs] valueForKey:[[[asset valueForProperty:ALAssetPropertyURLs] allKeys] objectAtIndex:0]] forKey:UIImagePickerControllerReferenceURL]; 75 | 76 | [returnArray addObject:workingDictionary]; 77 | 78 | } 79 | if (_imagePickerDelegate != nil && [_imagePickerDelegate respondsToSelector:@selector(elcImagePickerController:didFinishPickingMediaWithInfo:)]) { 80 | [_imagePickerDelegate performSelector:@selector(elcImagePickerController:didFinishPickingMediaWithInfo:) withObject:self withObject:returnArray]; 81 | } else { 82 | [self popToRootViewControllerAnimated:NO]; 83 | } 84 | } 85 | 86 | - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation 87 | { 88 | if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) { 89 | return YES; 90 | } else { 91 | return toInterfaceOrientation != UIInterfaceOrientationPortraitUpsideDown; 92 | } 93 | } 94 | 95 | @end 96 | -------------------------------------------------------------------------------- /src/ios/ELCImagePicker/ELCAssetCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // AssetCell.m 3 | // 4 | // Created by ELC on 2/15/11. 5 | // Copyright 2011 ELC Technologies. All rights reserved. 6 | // 7 | 8 | #import "ELCAssetCell.h" 9 | #import "ELCAsset.h" 10 | 11 | @interface ELCAssetCell () 12 | 13 | @property (nonatomic, strong) NSArray *rowAssets; 14 | @property (nonatomic, strong) NSMutableArray *imageViewArray; 15 | @property (nonatomic, strong) NSMutableArray *overlayViewArray; 16 | 17 | @end 18 | 19 | @implementation ELCAssetCell 20 | 21 | //Using auto synthesizers 22 | 23 | - (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier 24 | { 25 | self = [super initWithStyle:UITableViewCellStyleDefault reuseIdentifier:reuseIdentifier]; 26 | if (self) { 27 | UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(cellTapped:)]; 28 | [self addGestureRecognizer:tapRecognizer]; 29 | 30 | NSMutableArray *mutableArray = [[NSMutableArray alloc] initWithCapacity:4]; 31 | self.imageViewArray = mutableArray; 32 | 33 | NSMutableArray *overlayArray = [[NSMutableArray alloc] initWithCapacity:4]; 34 | self.overlayViewArray = overlayArray; 35 | } 36 | return self; 37 | } 38 | 39 | - (void)setAssets:(NSArray *)assets 40 | { 41 | self.rowAssets = assets; 42 | for (UIImageView *view in _imageViewArray) { 43 | [view removeFromSuperview]; 44 | } 45 | for (UIImageView *view in _overlayViewArray) { 46 | [view removeFromSuperview]; 47 | } 48 | //set up a pointer here so we don't keep calling [UIImage imageNamed:] if creating overlays 49 | UIImage *overlayImage = nil; 50 | for (int i = 0; i < [_rowAssets count]; ++i) { 51 | 52 | ELCAsset *asset = [_rowAssets objectAtIndex:i]; 53 | 54 | if (i < [_imageViewArray count]) { 55 | UIImageView *imageView = [_imageViewArray objectAtIndex:i]; 56 | imageView.image = [UIImage imageWithCGImage:asset.asset.thumbnail]; 57 | } else { 58 | UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage imageWithCGImage:asset.asset.thumbnail]]; 59 | [_imageViewArray addObject:imageView]; 60 | } 61 | 62 | if (i < [_overlayViewArray count]) { 63 | UIImageView *overlayView = [_overlayViewArray objectAtIndex:i]; 64 | overlayView.hidden = asset.selected ? NO : YES; 65 | } else { 66 | if (overlayImage == nil) { 67 | overlayImage = [UIImage imageNamed:@"Overlay.png"]; 68 | } 69 | UIImageView *overlayView = [[UIImageView alloc] initWithImage:overlayImage]; 70 | [_overlayViewArray addObject:overlayView]; 71 | overlayView.hidden = asset.selected ? NO : YES; 72 | } 73 | } 74 | } 75 | 76 | - (void)cellTapped:(UITapGestureRecognizer *)tapRecognizer 77 | { 78 | CGPoint point = [tapRecognizer locationInView:self]; 79 | CGFloat totalWidth = self.rowAssets.count * 75 + (self.rowAssets.count - 1) * 4; 80 | CGFloat startX = (self.bounds.size.width - totalWidth) / 2; 81 | 82 | CGRect frame = CGRectMake(startX, 2, 75, 75); 83 | 84 | for (int i = 0; i < [_rowAssets count]; ++i) { 85 | if (CGRectContainsPoint(frame, point)) { 86 | ELCAsset *asset = [_rowAssets objectAtIndex:i]; 87 | asset.selected = !asset.selected; 88 | UIImageView *overlayView = [_overlayViewArray objectAtIndex:i]; 89 | overlayView.hidden = !asset.selected; 90 | break; 91 | } 92 | frame.origin.x = frame.origin.x + frame.size.width + 4; 93 | } 94 | } 95 | 96 | - (void)layoutSubviews 97 | { 98 | CGFloat totalWidth = self.rowAssets.count * 75 + (self.rowAssets.count - 1) * 4; 99 | CGFloat startX = (self.bounds.size.width - totalWidth) / 2; 100 | 101 | CGRect frame = CGRectMake(startX, 2, 75, 75); 102 | 103 | for (int i = 0; i < [_rowAssets count]; ++i) { 104 | UIImageView *imageView = [_imageViewArray objectAtIndex:i]; 105 | [imageView setFrame:frame]; 106 | [self addSubview:imageView]; 107 | 108 | UIImageView *overlayView = [_overlayViewArray objectAtIndex:i]; 109 | [overlayView setFrame:frame]; 110 | [self addSubview:overlayView]; 111 | 112 | frame.origin.x = frame.origin.x + frame.size.width + 4; 113 | } 114 | } 115 | 116 | 117 | @end 118 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | cordova-imagePicker 2 | =================== 3 | 4 | Cordova Plugin For Multiple Image Selection - implemented for iOS and Android 4.0 and above. 5 | 6 | ## Installing the plugin 7 | 8 | The plugin conforms to the Cordova plugin specification, it can be installed 9 | using the Cordova / Phonegap command line interface. 10 | 11 | phonegap plugin add cordova-plugin-image-picker 12 | 13 | cordova plugin add cordova-plugin-image-picker 14 | 15 | 16 | ## Using the plugin 17 | 18 | The plugin creates the object `window.imagePicker` with the method `getPictures(success, fail, options)` 19 | 20 | Example - Get Full Size Images (all default options): 21 | ```javascript 22 | window.imagePicker.getPictures( 23 | function(results) { 24 | for (var i = 0; i < results.length; i++) { 25 | console.log('Image URI: ' + results[i]); 26 | } 27 | }, function (error) { 28 | console.log('Error: ' + error); 29 | } 30 | ); 31 | ``` 32 | 33 | Example - Get at most 10 images scaled to width of 800: 34 | ```javascript 35 | window.imagePicker.getPictures( 36 | function(results) { 37 | for (var i = 0; i < results.length; i++) { 38 | console.log('Image URI: ' + results[i]); 39 | } 40 | }, function (error) { 41 | console.log('Error: ' + error); 42 | }, { 43 | maximumImagesCount: 10, 44 | width: 800 45 | } 46 | ); 47 | ``` 48 | 49 | ### Options 50 | 51 | options = { 52 | // max images to be selected, defaults to 15. If this is set to 1, upon 53 | // selection of a single image, the plugin will return it. 54 | maximumImagesCount: int, 55 | 56 | // max width and height to allow the images to be. Will keep aspect 57 | // ratio no matter what. So if both are 800, the returned image 58 | // will be at most 800 pixels wide and 800 pixels tall. If the width is 59 | // 800 and height 0 the image will be 800 pixels wide if the source 60 | // is at least that wide. 61 | width: int, 62 | height: int, 63 | 64 | // quality of resized image, defaults to 100 65 | quality: int (0-100) 66 | }; 67 | 68 | ### iOS 10 issues 69 | 70 | Starting from iOS 10, Apple started asking for specifying the reason for accessing the user’s photo library, therefore it's mandatory to add `NSPhotoLibraryUsageDescription` entry in the info.plist. 71 | 72 | [`NSPhotoLibraryUsageDescription`](https://developer.apple.com/library/mac/documentation/General/Reference/InfoPlistKeyReference/Articles/CocoaKeys.html#//apple_ref/doc/uid/TP40009251-SW17) describes the reason that the app accesses the user’s photo library. When the system prompts the user to allow access, this string is displayed as part of the dialog box. In order to add this entry you must pass the variable `PHOTO_LIBRARY_USAGE_DESCRIPTION` on plugin install. 73 | 74 | Example: 75 | 76 | `cordova plugin add cordova-plugin-image-picker --variable PHOTO_LIBRARY_USAGE_DESCRIPTION="your message"` 77 | 78 | Empty string will be added as value if you dont pass the variable 79 | 80 | ### Note for Android Use 81 | 82 | The plugin returns images that are stored in a temporary directory. These images will often not be deleted automatically though. The files should be moved or deleted after you get their filepaths in javascript. 83 | 84 | ## Libraries used 85 | 86 | #### ELCImagePicker 87 | 88 | For iOS this plugin uses the ELCImagePickerController, with slight modifications for the iOS image picker. ELCImagePicker uses the MIT License which can be found in the file LICENSE. 89 | 90 | https://github.com/B-Sides/ELCImagePickerController 91 | 92 | #### MultiImageChooser 93 | 94 | For Android this plugin uses MultiImageChooser, with modifications. MultiImageChooser uses the BSD 2-Clause License which can be found in the file BSD_LICENSE. Some code inside MultImageChooser is licensed under the Apache license which can be found in the file APACHE_LICENSE. 95 | 96 | https://github.com/derosa/MultiImageChooser 97 | 98 | #### FakeR 99 | 100 | Code(FakeR) was also taken from the phonegap BarCodeScanner plugin. This code uses the MIT license. 101 | 102 | https://github.com/wildabeast/BarcodeScanner 103 | 104 | ## License 105 | 106 | The MIT License 107 | 108 | Permission is hereby granted, free of charge, to any person obtaining a copy 109 | of this software and associated documentation files (the "Software"), to deal 110 | in the Software without restriction, including without limitation the rights 111 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 112 | copies of the Software, and to permit persons to whom the Software is 113 | furnished to do so, subject to the following conditions: 114 | 115 | The above copyright notice and this permission notice shall be included in 116 | all copies or substantial portions of the Software. 117 | 118 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 119 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 120 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 121 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 122 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 123 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 124 | THE SOFTWARE. 125 | -------------------------------------------------------------------------------- /src/ios/ELCImagePicker/ELCAlbumPickerController.m: -------------------------------------------------------------------------------- 1 | // 2 | // AlbumPickerController.m 3 | // 4 | // Created by ELC on 2/15/11. 5 | // Copyright 2011 ELC Technologies. All rights reserved. 6 | // 7 | 8 | #import "ELCAlbumPickerController.h" 9 | #import "ELCImagePickerController.h" 10 | #import "ELCAssetTablePicker.h" 11 | 12 | @interface ELCAlbumPickerController () 13 | 14 | @property (nonatomic, strong) ALAssetsLibrary *library; 15 | 16 | @end 17 | 18 | @implementation ELCAlbumPickerController 19 | 20 | //Using auto synthesizers 21 | 22 | #pragma mark - 23 | #pragma mark View lifecycle 24 | 25 | - (void)viewDidLoad 26 | { 27 | [super viewDidLoad]; 28 | 29 | [self.navigationItem setTitle:@"Loading..."]; 30 | 31 | UIBarButtonItem *cancelButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel target:self.parent action:@selector(cancelImagePicker)]; 32 | [self.navigationItem setRightBarButtonItem:cancelButton]; 33 | 34 | NSMutableArray *tempArray = [[NSMutableArray alloc] init]; 35 | self.assetGroups = tempArray; 36 | 37 | ALAssetsLibrary *assetLibrary = [[ALAssetsLibrary alloc] init]; 38 | self.library = assetLibrary; 39 | 40 | // Load Albums into assetGroups 41 | dispatch_async(dispatch_get_main_queue(), ^ 42 | { 43 | @autoreleasepool { 44 | 45 | // Group enumerator Block 46 | void (^assetGroupEnumerator)(ALAssetsGroup *, BOOL *) = ^(ALAssetsGroup *group, BOOL *stop) 47 | { 48 | if (group == nil) { 49 | return; 50 | } 51 | 52 | // added fix for camera albums order 53 | NSString *sGroupPropertyName = (NSString *)[group valueForProperty:ALAssetsGroupPropertyName]; 54 | NSUInteger nType = [[group valueForProperty:ALAssetsGroupPropertyType] intValue]; 55 | 56 | if ([[sGroupPropertyName lowercaseString] isEqualToString:@"camera roll"] && nType == ALAssetsGroupSavedPhotos) { 57 | [self.assetGroups insertObject:group atIndex:0]; 58 | } 59 | else { 60 | [self.assetGroups addObject:group]; 61 | } 62 | 63 | // Reload albums 64 | [self performSelectorOnMainThread:@selector(reloadTableView) withObject:nil waitUntilDone:YES]; 65 | }; 66 | 67 | // Group Enumerator Failure Block 68 | void (^assetGroupEnumberatorFailure)(NSError *) = ^(NSError *error) { 69 | 70 | UIAlertView * alert = [[UIAlertView alloc] initWithTitle:@"Error" message:[NSString stringWithFormat:@"Album Error: %@ - %@", [error localizedDescription], [error localizedRecoverySuggestion]] delegate:nil cancelButtonTitle:@"Ok" otherButtonTitles:nil]; 71 | [alert show]; 72 | 73 | NSLog(@"A problem occured %@", [error description]); 74 | }; 75 | 76 | // Enumerate Albums 77 | [self.library enumerateGroupsWithTypes:ALAssetsGroupAll 78 | usingBlock:assetGroupEnumerator 79 | failureBlock:assetGroupEnumberatorFailure]; 80 | 81 | } 82 | }); 83 | } 84 | 85 | - (void)reloadTableView 86 | { 87 | [self.tableView reloadData]; 88 | [self.navigationItem setTitle:@"Select an Album"]; 89 | } 90 | 91 | - (BOOL)shouldSelectAsset:(ELCAsset *)asset previousCount:(NSUInteger)previousCount 92 | { 93 | return [self.parent shouldSelectAsset:asset previousCount:previousCount]; 94 | } 95 | 96 | - (void)selectedAssets:(NSArray*)assets 97 | { 98 | [_parent selectedAssets:assets]; 99 | } 100 | 101 | #pragma mark - 102 | #pragma mark Table view data source 103 | 104 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView 105 | { 106 | // Return the number of sections. 107 | return 1; 108 | } 109 | 110 | 111 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 112 | { 113 | // Return the number of rows in the section. 114 | return [self.assetGroups count]; 115 | } 116 | 117 | 118 | // Customize the appearance of table view cells. 119 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 120 | { 121 | static NSString *CellIdentifier = @"Cell"; 122 | 123 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; 124 | if (cell == nil) { 125 | cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier]; 126 | } 127 | 128 | // Get count 129 | ALAssetsGroup *g = (ALAssetsGroup*)[self.assetGroups objectAtIndex:indexPath.row]; 130 | [g setAssetsFilter:[ALAssetsFilter allPhotos]]; 131 | NSInteger gCount = [g numberOfAssets]; 132 | 133 | cell.textLabel.text = [NSString stringWithFormat:@"%@ (%ld)",[g valueForProperty:ALAssetsGroupPropertyName], (long)gCount]; 134 | [cell.imageView setImage:[UIImage imageWithCGImage:[(ALAssetsGroup*)[self.assetGroups objectAtIndex:indexPath.row] posterImage]]]; 135 | [cell setAccessoryType:UITableViewCellAccessoryDisclosureIndicator]; 136 | 137 | return cell; 138 | } 139 | 140 | #pragma mark - 141 | #pragma mark Table view delegate 142 | 143 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 144 | { 145 | ELCAssetTablePicker *picker = [[ELCAssetTablePicker alloc] initWithNibName: nil bundle: nil]; 146 | picker.parent = self; 147 | 148 | picker.assetGroup = [self.assetGroups objectAtIndex:indexPath.row]; 149 | [picker.assetGroup setAssetsFilter:[ALAssetsFilter allPhotos]]; 150 | 151 | picker.assetPickerFilterDelegate = self.assetPickerFilterDelegate; 152 | picker.immediateReturn = self.immediateReturn; 153 | picker.singleSelection = self.singleSelection; 154 | 155 | [self.navigationController pushViewController:picker animated:YES]; 156 | } 157 | 158 | - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath 159 | { 160 | return 57; 161 | } 162 | 163 | @end 164 | 165 | -------------------------------------------------------------------------------- /plugin.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | ImagePicker 8 | 9 | 10 | This plugin allows selection of multiple images from the camera roll / gallery in a phonegap app 11 | 12 | 13 | MIT 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | $PHOTO_LIBRARY_USAGE_DESCRIPTION 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | -------------------------------------------------------------------------------- /src/ios/SOSPicker.m: -------------------------------------------------------------------------------- 1 | // 2 | // SOSPicker.m 3 | // SyncOnSet 4 | // 5 | // Created by Christopher Sullivan on 10/25/13. 6 | // 7 | // 8 | 9 | #import "SOSPicker.h" 10 | #import "ELCAlbumPickerController.h" 11 | #import "ELCImagePickerController.h" 12 | #import "ELCAssetTablePicker.h" 13 | 14 | #define CDV_PHOTO_PREFIX @"cdv_photo_" 15 | 16 | @implementation SOSPicker 17 | 18 | @synthesize callbackId; 19 | 20 | - (void) getPictures:(CDVInvokedUrlCommand *)command { 21 | NSDictionary *options = [command.arguments objectAtIndex: 0]; 22 | 23 | NSInteger maximumImagesCount = [[options objectForKey:@"maximumImagesCount"] integerValue]; 24 | self.width = [[options objectForKey:@"width"] integerValue]; 25 | self.height = [[options objectForKey:@"height"] integerValue]; 26 | self.quality = [[options objectForKey:@"quality"] integerValue]; 27 | 28 | // Create the an album controller and image picker 29 | ELCAlbumPickerController *albumController = [[ELCAlbumPickerController alloc] init]; 30 | 31 | if (maximumImagesCount == 1) { 32 | albumController.immediateReturn = true; 33 | albumController.singleSelection = true; 34 | } else { 35 | albumController.immediateReturn = false; 36 | albumController.singleSelection = false; 37 | } 38 | 39 | ELCImagePickerController *imagePicker = [[ELCImagePickerController alloc] initWithRootViewController:albumController]; 40 | imagePicker.maximumImagesCount = maximumImagesCount; 41 | imagePicker.returnsOriginalImage = 1; 42 | imagePicker.imagePickerDelegate = self; 43 | 44 | albumController.parent = imagePicker; 45 | self.callbackId = command.callbackId; 46 | // Present modally 47 | [self.viewController presentViewController:imagePicker 48 | animated:YES 49 | completion:nil]; 50 | } 51 | 52 | 53 | - (void)elcImagePickerController:(ELCImagePickerController *)picker didFinishPickingMediaWithInfo:(NSArray *)info { 54 | CDVPluginResult* result = nil; 55 | NSMutableArray *resultStrings = [[NSMutableArray alloc] init]; 56 | NSData* data = nil; 57 | NSString* docsPath = [NSTemporaryDirectory()stringByStandardizingPath]; 58 | NSError* err = nil; 59 | NSFileManager* fileMgr = [[NSFileManager alloc] init]; 60 | NSString* filePath; 61 | ALAsset* asset = nil; 62 | UIImageOrientation orientation = UIImageOrientationUp;; 63 | CGSize targetSize = CGSizeMake(self.width, self.height); 64 | for (NSDictionary *dict in info) { 65 | asset = [dict objectForKey:@"ALAsset"]; 66 | // From ELCImagePickerController.m 67 | 68 | int i = 1; 69 | do { 70 | filePath = [NSString stringWithFormat:@"%@/%@%03d.%@", docsPath, CDV_PHOTO_PREFIX, i++, @"jpg"]; 71 | } while ([fileMgr fileExistsAtPath:filePath]); 72 | 73 | @autoreleasepool { 74 | ALAssetRepresentation *assetRep = [asset defaultRepresentation]; 75 | CGImageRef imgRef = NULL; 76 | 77 | //defaultRepresentation returns image as it appears in photo picker, rotated and sized, 78 | //so use UIImageOrientationUp when creating our image below. 79 | if (picker.returnsOriginalImage) { 80 | imgRef = [assetRep fullResolutionImage]; 81 | orientation = [assetRep orientation]; 82 | } else { 83 | imgRef = [assetRep fullScreenImage]; 84 | } 85 | 86 | UIImage* image = [UIImage imageWithCGImage:imgRef scale:1.0f orientation:orientation]; 87 | if (self.width == 0 && self.height == 0) { 88 | data = UIImageJPEGRepresentation(image, self.quality/100.0f); 89 | } else { 90 | UIImage* scaledImage = [self imageByScalingNotCroppingForSize:image toSize:targetSize]; 91 | data = UIImageJPEGRepresentation(scaledImage, self.quality/100.0f); 92 | } 93 | 94 | if (![data writeToFile:filePath options:NSAtomicWrite error:&err]) { 95 | result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsString:[err localizedDescription]]; 96 | break; 97 | } else { 98 | [resultStrings addObject:[[NSURL fileURLWithPath:filePath] absoluteString]]; 99 | } 100 | } 101 | 102 | } 103 | 104 | if (nil == result) { 105 | result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsArray:resultStrings]; 106 | } 107 | 108 | [self.viewController dismissViewControllerAnimated:YES completion:nil]; 109 | [self.commandDelegate sendPluginResult:result callbackId:self.callbackId]; 110 | } 111 | 112 | - (void)elcImagePickerControllerDidCancel:(ELCImagePickerController *)picker { 113 | [self.viewController dismissViewControllerAnimated:YES completion:nil]; 114 | CDVPluginResult* pluginResult = nil; 115 | NSArray* emptyArray = [NSArray array]; 116 | pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsArray:emptyArray]; 117 | [self.commandDelegate sendPluginResult:pluginResult callbackId:self.callbackId]; 118 | } 119 | 120 | - (UIImage*)imageByScalingNotCroppingForSize:(UIImage*)anImage toSize:(CGSize)frameSize 121 | { 122 | UIImage* sourceImage = anImage; 123 | UIImage* newImage = nil; 124 | CGSize imageSize = sourceImage.size; 125 | CGFloat width = imageSize.width; 126 | CGFloat height = imageSize.height; 127 | CGFloat targetWidth = frameSize.width; 128 | CGFloat targetHeight = frameSize.height; 129 | CGFloat scaleFactor = 0.0; 130 | CGSize scaledSize = frameSize; 131 | 132 | if (CGSizeEqualToSize(imageSize, frameSize) == NO) { 133 | CGFloat widthFactor = targetWidth / width; 134 | CGFloat heightFactor = targetHeight / height; 135 | 136 | // opposite comparison to imageByScalingAndCroppingForSize in order to contain the image within the given bounds 137 | if (widthFactor == 0.0) { 138 | scaleFactor = heightFactor; 139 | } else if (heightFactor == 0.0) { 140 | scaleFactor = widthFactor; 141 | } else if (widthFactor > heightFactor) { 142 | scaleFactor = heightFactor; // scale to fit height 143 | } else { 144 | scaleFactor = widthFactor; // scale to fit width 145 | } 146 | scaledSize = CGSizeMake(width * scaleFactor, height * scaleFactor); 147 | } 148 | 149 | UIGraphicsBeginImageContext(scaledSize); // this will resize 150 | 151 | [sourceImage drawInRect:CGRectMake(0, 0, scaledSize.width, scaledSize.height)]; 152 | 153 | newImage = UIGraphicsGetImageFromCurrentImageContext(); 154 | if (newImage == nil) { 155 | NSLog(@"could not scale image"); 156 | } 157 | 158 | // pop the context to get back to the default 159 | UIGraphicsEndImageContext(); 160 | return newImage; 161 | } 162 | 163 | @end 164 | -------------------------------------------------------------------------------- /src/ios/ELCImagePicker/ELCAssetTablePicker.m: -------------------------------------------------------------------------------- 1 | // 2 | // ELCAssetTablePicker.m 3 | // 4 | // Created by ELC on 2/15/11. 5 | // Copyright 2011 ELC Technologies. All rights reserved. 6 | // 7 | 8 | #import "ELCAssetTablePicker.h" 9 | #import "ELCAssetCell.h" 10 | #import "ELCAsset.h" 11 | #import "ELCAlbumPickerController.h" 12 | 13 | @interface ELCAssetTablePicker () 14 | 15 | @property (nonatomic, assign) int columns; 16 | 17 | @end 18 | 19 | @implementation ELCAssetTablePicker 20 | 21 | //Using auto synthesizers 22 | 23 | - (id)init 24 | { 25 | self = [super init]; 26 | if (self) { 27 | //Sets a reasonable default bigger then 0 for columns 28 | //So that we don't have a divide by 0 scenario 29 | self.columns = 4; 30 | } 31 | return self; 32 | } 33 | 34 | - (void)viewDidLoad 35 | { 36 | [self.tableView setSeparatorStyle:UITableViewCellSeparatorStyleNone]; 37 | [self.tableView setAllowsSelection:NO]; 38 | 39 | NSMutableArray *tempArray = [[NSMutableArray alloc] init]; 40 | self.elcAssets = tempArray; 41 | 42 | if (self.immediateReturn) { 43 | 44 | } else { 45 | UIBarButtonItem *doneButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(doneAction:)]; 46 | [self.navigationItem setRightBarButtonItem:doneButtonItem]; 47 | [self.navigationItem setTitle:@"Loading..."]; 48 | } 49 | 50 | [self performSelectorInBackground:@selector(preparePhotos) withObject:nil]; 51 | } 52 | 53 | - (void)viewWillAppear:(BOOL)animated 54 | { 55 | [super viewWillAppear:animated]; 56 | self.columns = self.view.bounds.size.width / 80; 57 | } 58 | 59 | - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation 60 | { 61 | return YES; 62 | } 63 | 64 | - (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation 65 | { 66 | [super didRotateFromInterfaceOrientation:fromInterfaceOrientation]; 67 | self.columns = self.view.bounds.size.width / 80; 68 | [self.tableView reloadData]; 69 | } 70 | 71 | - (void)preparePhotos 72 | { 73 | @autoreleasepool { 74 | 75 | [self.assetGroup enumerateAssetsUsingBlock:^(ALAsset *result, NSUInteger index, BOOL *stop) { 76 | 77 | if (result == nil) { 78 | return; 79 | } 80 | 81 | ELCAsset *elcAsset = [[ELCAsset alloc] initWithAsset:result]; 82 | [elcAsset setParent:self]; 83 | 84 | BOOL isAssetFiltered = NO; 85 | if (self.assetPickerFilterDelegate && 86 | [self.assetPickerFilterDelegate respondsToSelector:@selector(assetTablePicker:isAssetFilteredOut:)]) 87 | { 88 | isAssetFiltered = [self.assetPickerFilterDelegate assetTablePicker:self isAssetFilteredOut:(ELCAsset*)elcAsset]; 89 | } 90 | 91 | if (!isAssetFiltered) { 92 | [self.elcAssets addObject:elcAsset]; 93 | } 94 | 95 | }]; 96 | 97 | dispatch_sync(dispatch_get_main_queue(), ^{ 98 | [self.tableView reloadData]; 99 | // scroll to bottom 100 | long section = [self numberOfSectionsInTableView:self.tableView] - 1; 101 | long row = [self tableView:self.tableView numberOfRowsInSection:section] - 1; 102 | if (section >= 0 && row >= 0) { 103 | NSIndexPath *ip = [NSIndexPath indexPathForRow:row 104 | inSection:section]; 105 | [self.tableView scrollToRowAtIndexPath:ip 106 | atScrollPosition:UITableViewScrollPositionBottom 107 | animated:NO]; 108 | } 109 | 110 | [self.navigationItem setTitle:self.singleSelection ? @"Pick Photo" : @"Pick Photos"]; 111 | }); 112 | } 113 | } 114 | 115 | - (void)doneAction:(id)sender 116 | { 117 | NSMutableArray *selectedAssetsImages = [[NSMutableArray alloc] init]; 118 | 119 | for (ELCAsset *elcAsset in self.elcAssets) { 120 | if ([elcAsset selected]) { 121 | [selectedAssetsImages addObject:[elcAsset asset]]; 122 | } 123 | } 124 | [self.parent selectedAssets:selectedAssetsImages]; 125 | } 126 | 127 | 128 | - (BOOL)shouldSelectAsset:(ELCAsset *)asset 129 | { 130 | NSUInteger selectionCount = 0; 131 | for (ELCAsset *elcAsset in self.elcAssets) { 132 | if (elcAsset.selected) selectionCount++; 133 | } 134 | BOOL shouldSelect = YES; 135 | if ([self.parent respondsToSelector:@selector(shouldSelectAsset:previousCount:)]) { 136 | shouldSelect = [self.parent shouldSelectAsset:asset previousCount:selectionCount]; 137 | } 138 | return shouldSelect; 139 | } 140 | 141 | - (void)assetSelected:(ELCAsset *)asset 142 | { 143 | if (self.singleSelection) { 144 | 145 | for (ELCAsset *elcAsset in self.elcAssets) { 146 | if (asset != elcAsset) { 147 | elcAsset.selected = NO; 148 | } 149 | } 150 | } 151 | if (self.immediateReturn) { 152 | NSArray *singleAssetArray = @[asset.asset]; 153 | [(NSObject *)self.parent performSelector:@selector(selectedAssets:) withObject:singleAssetArray afterDelay:0]; 154 | } 155 | } 156 | 157 | #pragma mark UITableViewDataSource Delegate Methods 158 | 159 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView 160 | { 161 | // Return the number of sections. 162 | return 1; 163 | } 164 | 165 | 166 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 167 | { 168 | if (self.columns <= 0) { //Sometimes called before we know how many columns we have 169 | self.columns = 4; 170 | } 171 | NSInteger numRows = ceil([self.elcAssets count] / (float)self.columns); 172 | return numRows; 173 | } 174 | 175 | - (NSArray *)assetsForIndexPath:(NSIndexPath *)path 176 | { 177 | long index = path.row * self.columns; 178 | long length = MIN(self.columns, [self.elcAssets count] - index); 179 | return [self.elcAssets subarrayWithRange:NSMakeRange(index, length)]; 180 | } 181 | 182 | // Customize the appearance of table view cells. 183 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 184 | { 185 | static NSString *CellIdentifier = @"Cell"; 186 | 187 | ELCAssetCell *cell = (ELCAssetCell*)[tableView dequeueReusableCellWithIdentifier:CellIdentifier]; 188 | 189 | if (cell == nil) { 190 | cell = [[ELCAssetCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier]; 191 | } 192 | 193 | [cell setAssets:[self assetsForIndexPath:indexPath]]; 194 | 195 | return cell; 196 | } 197 | 198 | - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath 199 | { 200 | return 79; 201 | } 202 | 203 | - (int)totalSelectedAssets 204 | { 205 | int count = 0; 206 | 207 | for (ELCAsset *asset in self.elcAssets) { 208 | if (asset.selected) { 209 | count++; 210 | } 211 | } 212 | 213 | return count; 214 | } 215 | 216 | 217 | @end 218 | -------------------------------------------------------------------------------- /APACHE_LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /src/android/Library/src/ImageFetcher.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2010 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.synconset; 18 | 19 | import java.lang.ref.SoftReference; 20 | import java.lang.ref.WeakReference; 21 | import java.util.HashMap; 22 | import java.util.LinkedHashMap; 23 | import java.util.concurrent.ConcurrentHashMap; 24 | import java.util.concurrent.ExecutorService; 25 | import java.util.concurrent.Executors; 26 | import java.util.concurrent.RejectedExecutionException; 27 | 28 | import android.content.Context; 29 | import android.graphics.Bitmap; 30 | import android.graphics.Color; 31 | import android.graphics.drawable.ColorDrawable; 32 | import android.graphics.drawable.Drawable; 33 | import android.graphics.Matrix; 34 | import android.os.AsyncTask; 35 | import android.os.Build; 36 | import android.os.Handler; 37 | import android.provider.MediaStore; 38 | import android.util.Log; 39 | import android.view.View; 40 | import android.view.animation.Animation; 41 | import android.view.animation.AnimationUtils; 42 | import android.widget.ImageView; 43 | 44 | /** 45 | * This helper class download images from the Internet and binds those with the 46 | * provided ImageView. 47 | * 48 | *

49 | * It requires the INTERNET permission, which should be added to your 50 | * application's manifest file. 51 | *

52 | * 53 | * A local cache of downloaded images is maintained internally to improve 54 | * performance. 55 | */ 56 | public class ImageFetcher { 57 | 58 | private int colWidth; 59 | private long origId; 60 | private ExecutorService executor; 61 | 62 | public ImageFetcher() { 63 | executor = Executors.newCachedThreadPool(); 64 | } 65 | 66 | public void fetch(Integer id, ImageView imageView, int colWidth, int rotate) { 67 | resetPurgeTimer(); 68 | this.colWidth = colWidth; 69 | this.origId = id; 70 | Bitmap bitmap = getBitmapFromCache(id); 71 | 72 | if (bitmap == null) { 73 | forceDownload(id, imageView, rotate); 74 | } else { 75 | cancelPotentialDownload(id, imageView); 76 | imageView.setImageBitmap(bitmap); 77 | } 78 | } 79 | 80 | /** 81 | * Same as download but the image is always downloaded and the cache is not 82 | * used. Kept private at the moment as its interest is not clear. 83 | */ 84 | private void forceDownload(Integer position, ImageView imageView, int rotate) { 85 | if (position == null) { 86 | imageView.setImageDrawable(null); 87 | return; 88 | } 89 | 90 | if (cancelPotentialDownload(position, imageView)) { 91 | BitmapFetcherTask task = new BitmapFetcherTask(imageView.getContext(), imageView, rotate); 92 | DownloadedDrawable downloadedDrawable = new DownloadedDrawable(imageView.getContext(), task, origId); 93 | imageView.setImageDrawable(downloadedDrawable); 94 | imageView.setMinimumHeight(colWidth); 95 | 96 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { 97 | task.executeOnExecutor(executor, position); 98 | } else { 99 | try { 100 | task.execute(position); 101 | } catch (RejectedExecutionException e) { 102 | // Oh :( 103 | } 104 | } 105 | 106 | } 107 | } 108 | 109 | /** 110 | * Returns true if the current download has been canceled or if there was no 111 | * download in progress on this image view. Returns false if the download in 112 | * progress deals with the same url. The download is not stopped in that 113 | * case. 114 | */ 115 | private static boolean cancelPotentialDownload(Integer position, ImageView imageView) { 116 | BitmapFetcherTask bitmapDownloaderTask = getBitmapDownloaderTask(imageView); 117 | long origId = getOrigId(imageView); 118 | 119 | if (bitmapDownloaderTask != null) { 120 | Integer bitmapPosition = bitmapDownloaderTask.position; 121 | if ((bitmapPosition == null) || (!bitmapPosition.equals(position))) { 122 | // Log.d("DAVID", "Canceling..."); 123 | MediaStore.Images.Thumbnails.cancelThumbnailRequest(imageView.getContext().getContentResolver(), 124 | origId, 12345); 125 | bitmapDownloaderTask.cancel(true); 126 | } else { 127 | return false; 128 | } 129 | } 130 | return true; 131 | } 132 | 133 | /** 134 | * @param imageView 135 | * Any imageView 136 | * @return Retrieve the currently active download task (if any) associated 137 | * with this imageView. null if there is no such task. 138 | */ 139 | private static BitmapFetcherTask getBitmapDownloaderTask(ImageView imageView) { 140 | if (imageView != null) { 141 | Drawable drawable = imageView.getDrawable(); 142 | if (drawable instanceof DownloadedDrawable) { 143 | DownloadedDrawable downloadedDrawable = (DownloadedDrawable) drawable; 144 | return downloadedDrawable.getBitmapDownloaderTask(); 145 | } 146 | } 147 | return null; 148 | } 149 | 150 | private static long getOrigId(ImageView imageView) { 151 | if (imageView != null) { 152 | Drawable drawable = imageView.getDrawable(); 153 | if (drawable instanceof DownloadedDrawable) { 154 | DownloadedDrawable downloadedDrawable = (DownloadedDrawable) drawable; 155 | return downloadedDrawable.getOrigId(); 156 | } 157 | } 158 | return -1; 159 | } 160 | 161 | /** 162 | * The actual AsyncTask that will asynchronously download the image. 163 | */ 164 | class BitmapFetcherTask extends AsyncTask { 165 | private Integer position; 166 | private final WeakReference imageViewReference; 167 | private final Context mContext; 168 | private final int rotate; 169 | 170 | public BitmapFetcherTask(Context context, ImageView imageView, int rotate) { 171 | imageViewReference = new WeakReference(imageView); 172 | mContext = context; 173 | this.rotate = rotate; 174 | } 175 | 176 | /** 177 | * Actual download method. 178 | */ 179 | @Override 180 | protected Bitmap doInBackground(Integer... params) { 181 | try { 182 | position = params[0]; 183 | if (isCancelled()) { 184 | return null; 185 | } 186 | Bitmap thumb = MediaStore.Images.Thumbnails.getThumbnail(mContext.getContentResolver(), position, 12345, 187 | MediaStore.Images.Thumbnails.MINI_KIND, null); 188 | if (isCancelled()) { 189 | return null; 190 | } 191 | if (thumb == null) { 192 | return null; 193 | } else { 194 | if (isCancelled()) { 195 | return null; 196 | } else { 197 | if (rotate != 0) { 198 | Matrix matrix = new Matrix(); 199 | matrix.setRotate(rotate); 200 | thumb = Bitmap.createBitmap(thumb, 0, 0, thumb.getWidth(), thumb.getHeight(), matrix, true); 201 | } 202 | return thumb; 203 | } 204 | } 205 | }catch(OutOfMemoryError error) { 206 | clearCache(); 207 | return null; 208 | } 209 | 210 | } 211 | 212 | private void setInvisible() { 213 | // Log.d("COLLAGE", "Setting something invisible..."); 214 | if (imageViewReference != null) { 215 | final ImageView imageView = imageViewReference.get(); 216 | BitmapFetcherTask bitmapDownloaderTask = getBitmapDownloaderTask(imageView); 217 | if (this == bitmapDownloaderTask) { 218 | imageView.setVisibility(View.GONE); 219 | imageView.setClickable(false); 220 | imageView.setEnabled(false); 221 | } 222 | } 223 | } 224 | 225 | /** 226 | * Once the image is downloaded, associates it to the imageView 227 | */ 228 | @Override 229 | protected void onPostExecute(Bitmap bitmap) { 230 | if (isCancelled()) { 231 | bitmap = null; 232 | } 233 | addBitmapToCache(position, bitmap); 234 | if (imageViewReference != null) { 235 | ImageView imageView = imageViewReference.get(); 236 | BitmapFetcherTask bitmapDownloaderTask = getBitmapDownloaderTask(imageView); 237 | if (this == bitmapDownloaderTask) { 238 | imageView.setImageBitmap(bitmap); 239 | Animation anim = AnimationUtils.loadAnimation(imageView.getContext(), android.R.anim.fade_in); 240 | imageView.setAnimation(anim); 241 | anim.start(); 242 | } 243 | } else { 244 | setInvisible(); 245 | } 246 | } 247 | } 248 | 249 | /** 250 | * A fake Drawable that will be attached to the imageView while the download 251 | * is in progress. 252 | * 253 | *

254 | * Contains a reference to the actual download task, so that a download task 255 | * can be stopped if a new binding is required, and makes sure that only the 256 | * last started download process can bind its result, independently of the 257 | * download finish order. 258 | *

259 | */ 260 | static class DownloadedDrawable extends ColorDrawable { 261 | private final WeakReference bitmapDownloaderTaskReference; 262 | private long origId; 263 | 264 | public DownloadedDrawable(Context mContext, BitmapFetcherTask bitmapDownloaderTask, long origId) { 265 | super(Color.TRANSPARENT); 266 | bitmapDownloaderTaskReference = new WeakReference(bitmapDownloaderTask); 267 | this.origId = origId; 268 | } 269 | 270 | public long getOrigId() { 271 | return origId; 272 | } 273 | 274 | public BitmapFetcherTask getBitmapDownloaderTask() { 275 | return bitmapDownloaderTaskReference.get(); 276 | } 277 | } 278 | 279 | /* 280 | * Cache-related fields and methods. 281 | * 282 | * We use a hard and a soft cache. A soft reference cache is too aggressively cleared by the 283 | * Garbage Collector. 284 | */ 285 | 286 | private static final int HARD_CACHE_CAPACITY = 100; 287 | private static final int DELAY_BEFORE_PURGE = 10 * 1000; // in milliseconds 288 | 289 | // Hard cache, with a fixed maximum capacity and a life duration 290 | private final HashMap sHardBitmapCache = new LinkedHashMap( 291 | HARD_CACHE_CAPACITY / 2, 0.75f, true) { 292 | @Override 293 | protected boolean removeEldestEntry(LinkedHashMap.Entry eldest) { 294 | if (size() > HARD_CACHE_CAPACITY) { 295 | // Entries push-out of hard reference cache are transferred to 296 | // soft reference cache 297 | sSoftBitmapCache.put(eldest.getKey(), new SoftReference(eldest.getValue())); 298 | return true; 299 | } else 300 | return false; 301 | } 302 | }; 303 | 304 | // Soft cache for bitmaps kicked out of hard cache 305 | private final static ConcurrentHashMap> sSoftBitmapCache = new ConcurrentHashMap>( 306 | HARD_CACHE_CAPACITY / 2); 307 | 308 | private final Handler purgeHandler = new Handler(); 309 | 310 | private final Runnable purger = new Runnable() { 311 | public void run() { 312 | clearCache(); 313 | } 314 | }; 315 | 316 | /** 317 | * Adds this bitmap to the cache. 318 | * 319 | * @param bitmap 320 | * The newly downloaded bitmap. 321 | */ 322 | private void addBitmapToCache(Integer position, Bitmap bitmap) { 323 | if (bitmap != null) { 324 | synchronized (sHardBitmapCache) { 325 | sHardBitmapCache.put(position, bitmap); 326 | } 327 | } 328 | } 329 | 330 | /** 331 | * @param position 332 | * The URL of the image that will be retrieved from the cache. 333 | * @return The cached bitmap or null if it was not found. 334 | */ 335 | private Bitmap getBitmapFromCache(Integer position) { 336 | // First try the hard reference cache 337 | synchronized (sHardBitmapCache) { 338 | final Bitmap bitmap = sHardBitmapCache.get(position); 339 | if (bitmap != null) { 340 | // Log.d("CACHE ****** ", "Hard hit!"); 341 | // Bitmap found in hard cache 342 | // Move element to first position, so that it is removed last 343 | return bitmap; 344 | } 345 | } 346 | 347 | // Then try the soft reference cache 348 | SoftReference bitmapReference = sSoftBitmapCache.get(position); 349 | if (bitmapReference != null) { 350 | final Bitmap bitmap = bitmapReference.get(); 351 | if (bitmap != null) { 352 | // Bitmap found in soft cache 353 | // Log.d("CACHE ****** ", "Soft hit!"); 354 | return bitmap; 355 | } else { 356 | // Soft reference has been Garbage Collected 357 | sSoftBitmapCache.remove(position); 358 | } 359 | } 360 | 361 | return null; 362 | } 363 | 364 | /** 365 | * Clears the image cache used internally to improve performance. Note that 366 | * for memory efficiency reasons, the cache will automatically be cleared 367 | * after a certain inactivity delay. 368 | */ 369 | public void clearCache() { 370 | sHardBitmapCache.clear(); 371 | sSoftBitmapCache.clear(); 372 | } 373 | 374 | /** 375 | * Allow a new delay before the automatic cache clear is done. 376 | */ 377 | private void resetPurgeTimer() { 378 | // purgeHandler.removeCallbacks(purger); 379 | // purgeHandler.postDelayed(purger, DELAY_BEFORE_PURGE); 380 | } 381 | } 382 | -------------------------------------------------------------------------------- /src/ios/ELCImagePicker/Resources/ELCAlbumPickerController.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 1024 5 | 10F569 6 | 804 7 | 1038.29 8 | 461.00 9 | 10 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 11 | 123 12 | 13 | 14 | YES 15 | 16 | 17 | 18 | YES 19 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 20 | 21 | 22 | YES 23 | 24 | YES 25 | 26 | 27 | YES 28 | 29 | 30 | 31 | YES 32 | 33 | IBFilesOwner 34 | IBCocoaTouchFramework 35 | 36 | 37 | IBFirstResponder 38 | IBCocoaTouchFramework 39 | 40 | 41 | 42 | 274 43 | {320, 416} 44 | 45 | 46 | 3 47 | MQA 48 | 49 | NO 50 | YES 51 | NO 52 | 53 | 54 | NO 55 | 56 | IBCocoaTouchFramework 57 | NO 58 | 1 59 | 0 60 | YES 61 | 44 62 | 22 63 | 22 64 | 65 | 66 | 67 | 68 | YES 69 | 70 | 71 | view 72 | 73 | 74 | 75 | 5 76 | 77 | 78 | 79 | dataSource 80 | 81 | 82 | 83 | 6 84 | 85 | 86 | 87 | delegate 88 | 89 | 90 | 91 | 7 92 | 93 | 94 | 95 | 96 | YES 97 | 98 | 0 99 | 100 | 101 | 102 | 103 | 104 | -1 105 | 106 | 107 | File's Owner 108 | 109 | 110 | -2 111 | 112 | 113 | 114 | 115 | 4 116 | 117 | 118 | 119 | 120 | 121 | 122 | YES 123 | 124 | YES 125 | -1.CustomClassName 126 | -2.CustomClassName 127 | 4.IBEditorWindowLastContentRect 128 | 4.IBPluginDependency 129 | 130 | 131 | YES 132 | AlbumPickerController 133 | UIResponder 134 | {{329, 504}, {320, 480}} 135 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 136 | 137 | 138 | 139 | YES 140 | 141 | 142 | YES 143 | 144 | 145 | 146 | 147 | YES 148 | 149 | 150 | YES 151 | 152 | 153 | 154 | 7 155 | 156 | 157 | 158 | YES 159 | 160 | AlbumPickerController 161 | UITableViewController 162 | 163 | IBProjectSource 164 | Classes/AlbumPickerController.h 165 | 166 | 167 | 168 | 169 | YES 170 | 171 | NSObject 172 | 173 | IBFrameworkSource 174 | Foundation.framework/Headers/NSError.h 175 | 176 | 177 | 178 | NSObject 179 | 180 | IBFrameworkSource 181 | Foundation.framework/Headers/NSFileManager.h 182 | 183 | 184 | 185 | NSObject 186 | 187 | IBFrameworkSource 188 | Foundation.framework/Headers/NSKeyValueCoding.h 189 | 190 | 191 | 192 | NSObject 193 | 194 | IBFrameworkSource 195 | Foundation.framework/Headers/NSKeyValueObserving.h 196 | 197 | 198 | 199 | NSObject 200 | 201 | IBFrameworkSource 202 | Foundation.framework/Headers/NSKeyedArchiver.h 203 | 204 | 205 | 206 | NSObject 207 | 208 | IBFrameworkSource 209 | Foundation.framework/Headers/NSObject.h 210 | 211 | 212 | 213 | NSObject 214 | 215 | IBFrameworkSource 216 | Foundation.framework/Headers/NSRunLoop.h 217 | 218 | 219 | 220 | NSObject 221 | 222 | IBFrameworkSource 223 | Foundation.framework/Headers/NSThread.h 224 | 225 | 226 | 227 | NSObject 228 | 229 | IBFrameworkSource 230 | Foundation.framework/Headers/NSURL.h 231 | 232 | 233 | 234 | NSObject 235 | 236 | IBFrameworkSource 237 | Foundation.framework/Headers/NSURLConnection.h 238 | 239 | 240 | 241 | NSObject 242 | 243 | IBFrameworkSource 244 | UIKit.framework/Headers/UIAccessibility.h 245 | 246 | 247 | 248 | NSObject 249 | 250 | IBFrameworkSource 251 | UIKit.framework/Headers/UINibLoading.h 252 | 253 | 254 | 255 | NSObject 256 | 257 | IBFrameworkSource 258 | UIKit.framework/Headers/UIResponder.h 259 | 260 | 261 | 262 | UIResponder 263 | NSObject 264 | 265 | 266 | 267 | UIScrollView 268 | UIView 269 | 270 | IBFrameworkSource 271 | UIKit.framework/Headers/UIScrollView.h 272 | 273 | 274 | 275 | UISearchBar 276 | UIView 277 | 278 | IBFrameworkSource 279 | UIKit.framework/Headers/UISearchBar.h 280 | 281 | 282 | 283 | UISearchDisplayController 284 | NSObject 285 | 286 | IBFrameworkSource 287 | UIKit.framework/Headers/UISearchDisplayController.h 288 | 289 | 290 | 291 | UITableView 292 | UIScrollView 293 | 294 | IBFrameworkSource 295 | UIKit.framework/Headers/UITableView.h 296 | 297 | 298 | 299 | UITableViewController 300 | UIViewController 301 | 302 | IBFrameworkSource 303 | UIKit.framework/Headers/UITableViewController.h 304 | 305 | 306 | 307 | UIView 308 | 309 | IBFrameworkSource 310 | UIKit.framework/Headers/UITextField.h 311 | 312 | 313 | 314 | UIView 315 | UIResponder 316 | 317 | IBFrameworkSource 318 | UIKit.framework/Headers/UIView.h 319 | 320 | 321 | 322 | UIViewController 323 | 324 | IBFrameworkSource 325 | UIKit.framework/Headers/UINavigationController.h 326 | 327 | 328 | 329 | UIViewController 330 | 331 | IBFrameworkSource 332 | UIKit.framework/Headers/UIPopoverController.h 333 | 334 | 335 | 336 | UIViewController 337 | 338 | IBFrameworkSource 339 | UIKit.framework/Headers/UISplitViewController.h 340 | 341 | 342 | 343 | UIViewController 344 | 345 | IBFrameworkSource 346 | UIKit.framework/Headers/UITabBarController.h 347 | 348 | 349 | 350 | UIViewController 351 | UIResponder 352 | 353 | IBFrameworkSource 354 | UIKit.framework/Headers/UIViewController.h 355 | 356 | 357 | 358 | 359 | 0 360 | IBCocoaTouchFramework 361 | 362 | com.apple.InterfaceBuilder.CocoaTouchPlugin.iPhoneOS 363 | 364 | 365 | 366 | com.apple.InterfaceBuilder.CocoaTouchPlugin.InterfaceBuilder3 367 | 368 | 369 | YES 370 | ../ELCImagePickerDemo.xcodeproj 371 | 3 372 | 123 373 | 374 | 375 | -------------------------------------------------------------------------------- /src/ios/ELCImagePicker/Resources/ELCAssetTablePicker.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 1024 5 | 10F569 6 | 804 7 | 1038.29 8 | 461.00 9 | 10 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 11 | 123 12 | 13 | 14 | YES 15 | 16 | 17 | 18 | YES 19 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 20 | 21 | 22 | YES 23 | 24 | YES 25 | 26 | 27 | YES 28 | 29 | 30 | 31 | YES 32 | 33 | IBFilesOwner 34 | IBCocoaTouchFramework 35 | 36 | 37 | IBFirstResponder 38 | IBCocoaTouchFramework 39 | 40 | 41 | 42 | 274 43 | {320, 436} 44 | 45 | 46 | 3 47 | MQA 48 | 49 | YES 50 | 51 | NO 52 | 53 | IBCocoaTouchFramework 54 | YES 55 | 1 56 | 0 57 | YES 58 | 44 59 | 22 60 | 22 61 | 62 | 63 | 64 | 65 | YES 66 | 67 | 68 | view 69 | 70 | 71 | 72 | 3 73 | 74 | 75 | 76 | dataSource 77 | 78 | 79 | 80 | 4 81 | 82 | 83 | 84 | delegate 85 | 86 | 87 | 88 | 5 89 | 90 | 91 | 92 | 93 | YES 94 | 95 | 0 96 | 97 | 98 | 99 | 100 | 101 | -1 102 | 103 | 104 | File's Owner 105 | 106 | 107 | -2 108 | 109 | 110 | 111 | 112 | 2 113 | 114 | 115 | 116 | 117 | 118 | 119 | YES 120 | 121 | YES 122 | -1.CustomClassName 123 | -2.CustomClassName 124 | 2.IBEditorWindowLastContentRect 125 | 2.IBPluginDependency 126 | 127 | 128 | YES 129 | AssetTablePicker 130 | UIResponder 131 | {{0, 526}, {320, 480}} 132 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 133 | 134 | 135 | 136 | YES 137 | 138 | 139 | YES 140 | 141 | 142 | 143 | 144 | YES 145 | 146 | 147 | YES 148 | 149 | 150 | 151 | 5 152 | 153 | 154 | 155 | YES 156 | 157 | AssetTablePicker 158 | UITableViewController 159 | 160 | dismiss: 161 | id 162 | 163 | 164 | dismiss: 165 | 166 | dismiss: 167 | id 168 | 169 | 170 | 171 | YES 172 | 173 | YES 174 | parent 175 | selectedAssetsLabel 176 | 177 | 178 | YES 179 | id 180 | UILabel 181 | 182 | 183 | 184 | YES 185 | 186 | YES 187 | parent 188 | selectedAssetsLabel 189 | 190 | 191 | YES 192 | 193 | parent 194 | id 195 | 196 | 197 | selectedAssetsLabel 198 | UILabel 199 | 200 | 201 | 202 | 203 | IBProjectSource 204 | Classes/ELCImagePickerController.h 205 | 206 | 207 | 208 | 209 | YES 210 | 211 | NSObject 212 | 213 | IBFrameworkSource 214 | Foundation.framework/Headers/NSError.h 215 | 216 | 217 | 218 | NSObject 219 | 220 | IBFrameworkSource 221 | Foundation.framework/Headers/NSFileManager.h 222 | 223 | 224 | 225 | NSObject 226 | 227 | IBFrameworkSource 228 | Foundation.framework/Headers/NSKeyValueCoding.h 229 | 230 | 231 | 232 | NSObject 233 | 234 | IBFrameworkSource 235 | Foundation.framework/Headers/NSKeyValueObserving.h 236 | 237 | 238 | 239 | NSObject 240 | 241 | IBFrameworkSource 242 | Foundation.framework/Headers/NSKeyedArchiver.h 243 | 244 | 245 | 246 | NSObject 247 | 248 | IBFrameworkSource 249 | Foundation.framework/Headers/NSObject.h 250 | 251 | 252 | 253 | NSObject 254 | 255 | IBFrameworkSource 256 | Foundation.framework/Headers/NSRunLoop.h 257 | 258 | 259 | 260 | NSObject 261 | 262 | IBFrameworkSource 263 | Foundation.framework/Headers/NSThread.h 264 | 265 | 266 | 267 | NSObject 268 | 269 | IBFrameworkSource 270 | Foundation.framework/Headers/NSURL.h 271 | 272 | 273 | 274 | NSObject 275 | 276 | IBFrameworkSource 277 | Foundation.framework/Headers/NSURLConnection.h 278 | 279 | 280 | 281 | NSObject 282 | 283 | IBFrameworkSource 284 | UIKit.framework/Headers/UIAccessibility.h 285 | 286 | 287 | 288 | NSObject 289 | 290 | IBFrameworkSource 291 | UIKit.framework/Headers/UINibLoading.h 292 | 293 | 294 | 295 | NSObject 296 | 297 | IBFrameworkSource 298 | UIKit.framework/Headers/UIResponder.h 299 | 300 | 301 | 302 | UILabel 303 | UIView 304 | 305 | IBFrameworkSource 306 | UIKit.framework/Headers/UILabel.h 307 | 308 | 309 | 310 | UIResponder 311 | NSObject 312 | 313 | 314 | 315 | UIScrollView 316 | UIView 317 | 318 | IBFrameworkSource 319 | UIKit.framework/Headers/UIScrollView.h 320 | 321 | 322 | 323 | UISearchBar 324 | UIView 325 | 326 | IBFrameworkSource 327 | UIKit.framework/Headers/UISearchBar.h 328 | 329 | 330 | 331 | UISearchDisplayController 332 | NSObject 333 | 334 | IBFrameworkSource 335 | UIKit.framework/Headers/UISearchDisplayController.h 336 | 337 | 338 | 339 | UITableView 340 | UIScrollView 341 | 342 | IBFrameworkSource 343 | UIKit.framework/Headers/UITableView.h 344 | 345 | 346 | 347 | UITableViewController 348 | UIViewController 349 | 350 | IBFrameworkSource 351 | UIKit.framework/Headers/UITableViewController.h 352 | 353 | 354 | 355 | UIView 356 | 357 | IBFrameworkSource 358 | UIKit.framework/Headers/UITextField.h 359 | 360 | 361 | 362 | UIView 363 | UIResponder 364 | 365 | IBFrameworkSource 366 | UIKit.framework/Headers/UIView.h 367 | 368 | 369 | 370 | UIViewController 371 | 372 | IBFrameworkSource 373 | UIKit.framework/Headers/UINavigationController.h 374 | 375 | 376 | 377 | UIViewController 378 | 379 | IBFrameworkSource 380 | UIKit.framework/Headers/UIPopoverController.h 381 | 382 | 383 | 384 | UIViewController 385 | 386 | IBFrameworkSource 387 | UIKit.framework/Headers/UISplitViewController.h 388 | 389 | 390 | 391 | UIViewController 392 | 393 | IBFrameworkSource 394 | UIKit.framework/Headers/UITabBarController.h 395 | 396 | 397 | 398 | UIViewController 399 | UIResponder 400 | 401 | IBFrameworkSource 402 | UIKit.framework/Headers/UIViewController.h 403 | 404 | 405 | 406 | 407 | 0 408 | IBCocoaTouchFramework 409 | 410 | com.apple.InterfaceBuilder.CocoaTouchPlugin.iPhoneOS 411 | 412 | 413 | 414 | com.apple.InterfaceBuilder.CocoaTouchPlugin.InterfaceBuilder3 415 | 416 | 417 | YES 418 | ../ELCImagePickerDemo.xcodeproj 419 | 3 420 | 123 421 | 422 | 423 | -------------------------------------------------------------------------------- /src/ios/ELCImagePicker/Resources/ELCAssetPicker.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 1024 5 | 10F569 6 | 804 7 | 1038.29 8 | 461.00 9 | 10 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 11 | 123 12 | 13 | 14 | YES 15 | 16 | 17 | 18 | YES 19 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 20 | 21 | 22 | YES 23 | 24 | YES 25 | 26 | 27 | YES 28 | 29 | 30 | 31 | YES 32 | 33 | IBFilesOwner 34 | IBCocoaTouchFramework 35 | 36 | 37 | IBFirstResponder 38 | IBCocoaTouchFramework 39 | 40 | 41 | 42 | 274 43 | 44 | YES 45 | 46 | 47 | 268 48 | {320, 416} 49 | 50 | 51 | 1 52 | MSAxIDEAA 53 | 54 | YES 55 | YES 56 | IBCocoaTouchFramework 57 | 58 | 59 | {320, 416} 60 | 61 | 62 | 3 63 | MQA 64 | 65 | 2 66 | 67 | 68 | 69 | 70 | NO 71 | 72 | IBCocoaTouchFramework 73 | 74 | 75 | 76 | 77 | YES 78 | 79 | 80 | view 81 | 82 | 83 | 84 | 3 85 | 86 | 87 | 88 | scrollview 89 | 90 | 91 | 92 | 7 93 | 94 | 95 | 96 | 97 | YES 98 | 99 | 0 100 | 101 | 102 | 103 | 104 | 105 | 1 106 | 107 | 108 | YES 109 | 110 | 111 | 112 | 113 | 114 | -1 115 | 116 | 117 | File's Owner 118 | 119 | 120 | -2 121 | 122 | 123 | 124 | 125 | 6 126 | 127 | 128 | YES 129 | 130 | 131 | 132 | 133 | 134 | 135 | YES 136 | 137 | YES 138 | -1.CustomClassName 139 | -2.CustomClassName 140 | 1.IBEditorWindowLastContentRect 141 | 1.IBPluginDependency 142 | 6.IBPluginDependency 143 | 6.IBViewBoundsToFrameTransform 144 | 145 | 146 | YES 147 | AssetPicker 148 | UIResponder 149 | {{575, 376}, {320, 480}} 150 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 151 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 152 | 153 | P4AAAL+AAAAAAAAAw88AAA 154 | 155 | 156 | 157 | 158 | YES 159 | 160 | 161 | YES 162 | 163 | 164 | 165 | 166 | YES 167 | 168 | 169 | YES 170 | 171 | 172 | 173 | 15 174 | 175 | 176 | 177 | YES 178 | 179 | AssetPicker 180 | UIViewController 181 | 182 | dismiss: 183 | id 184 | 185 | 186 | dismiss: 187 | 188 | dismiss: 189 | id 190 | 191 | 192 | 193 | YES 194 | 195 | YES 196 | parent 197 | scrollview 198 | selectedAssetsLabel 199 | 200 | 201 | YES 202 | id 203 | UIScrollView 204 | UILabel 205 | 206 | 207 | 208 | YES 209 | 210 | YES 211 | parent 212 | scrollview 213 | selectedAssetsLabel 214 | 215 | 216 | YES 217 | 218 | parent 219 | id 220 | 221 | 222 | scrollview 223 | UIScrollView 224 | 225 | 226 | selectedAssetsLabel 227 | UILabel 228 | 229 | 230 | 231 | 232 | IBProjectSource 233 | Classes/ELCImagePickerController.h 234 | 235 | 236 | 237 | 238 | YES 239 | 240 | NSObject 241 | 242 | IBFrameworkSource 243 | Foundation.framework/Headers/NSError.h 244 | 245 | 246 | 247 | NSObject 248 | 249 | IBFrameworkSource 250 | Foundation.framework/Headers/NSFileManager.h 251 | 252 | 253 | 254 | NSObject 255 | 256 | IBFrameworkSource 257 | Foundation.framework/Headers/NSKeyValueCoding.h 258 | 259 | 260 | 261 | NSObject 262 | 263 | IBFrameworkSource 264 | Foundation.framework/Headers/NSKeyValueObserving.h 265 | 266 | 267 | 268 | NSObject 269 | 270 | IBFrameworkSource 271 | Foundation.framework/Headers/NSKeyedArchiver.h 272 | 273 | 274 | 275 | NSObject 276 | 277 | IBFrameworkSource 278 | Foundation.framework/Headers/NSObject.h 279 | 280 | 281 | 282 | NSObject 283 | 284 | IBFrameworkSource 285 | Foundation.framework/Headers/NSRunLoop.h 286 | 287 | 288 | 289 | NSObject 290 | 291 | IBFrameworkSource 292 | Foundation.framework/Headers/NSThread.h 293 | 294 | 295 | 296 | NSObject 297 | 298 | IBFrameworkSource 299 | Foundation.framework/Headers/NSURL.h 300 | 301 | 302 | 303 | NSObject 304 | 305 | IBFrameworkSource 306 | Foundation.framework/Headers/NSURLConnection.h 307 | 308 | 309 | 310 | NSObject 311 | 312 | IBFrameworkSource 313 | UIKit.framework/Headers/UIAccessibility.h 314 | 315 | 316 | 317 | NSObject 318 | 319 | IBFrameworkSource 320 | UIKit.framework/Headers/UINibLoading.h 321 | 322 | 323 | 324 | NSObject 325 | 326 | IBFrameworkSource 327 | UIKit.framework/Headers/UIResponder.h 328 | 329 | 330 | 331 | UILabel 332 | UIView 333 | 334 | IBFrameworkSource 335 | UIKit.framework/Headers/UILabel.h 336 | 337 | 338 | 339 | UIResponder 340 | NSObject 341 | 342 | 343 | 344 | UIScrollView 345 | UIView 346 | 347 | IBFrameworkSource 348 | UIKit.framework/Headers/UIScrollView.h 349 | 350 | 351 | 352 | UISearchBar 353 | UIView 354 | 355 | IBFrameworkSource 356 | UIKit.framework/Headers/UISearchBar.h 357 | 358 | 359 | 360 | UISearchDisplayController 361 | NSObject 362 | 363 | IBFrameworkSource 364 | UIKit.framework/Headers/UISearchDisplayController.h 365 | 366 | 367 | 368 | UIView 369 | 370 | IBFrameworkSource 371 | UIKit.framework/Headers/UITextField.h 372 | 373 | 374 | 375 | UIView 376 | UIResponder 377 | 378 | IBFrameworkSource 379 | UIKit.framework/Headers/UIView.h 380 | 381 | 382 | 383 | UIViewController 384 | 385 | IBFrameworkSource 386 | UIKit.framework/Headers/UINavigationController.h 387 | 388 | 389 | 390 | UIViewController 391 | 392 | IBFrameworkSource 393 | UIKit.framework/Headers/UIPopoverController.h 394 | 395 | 396 | 397 | UIViewController 398 | 399 | IBFrameworkSource 400 | UIKit.framework/Headers/UISplitViewController.h 401 | 402 | 403 | 404 | UIViewController 405 | 406 | IBFrameworkSource 407 | UIKit.framework/Headers/UITabBarController.h 408 | 409 | 410 | 411 | UIViewController 412 | UIResponder 413 | 414 | IBFrameworkSource 415 | UIKit.framework/Headers/UIViewController.h 416 | 417 | 418 | 419 | 420 | 0 421 | IBCocoaTouchFramework 422 | 423 | com.apple.InterfaceBuilder.CocoaTouchPlugin.iPhoneOS 424 | 425 | 426 | 427 | com.apple.InterfaceBuilder.CocoaTouchPlugin.InterfaceBuilder3 428 | 429 | 430 | YES 431 | ../ELCImagePickerDemo.xcodeproj 432 | 3 433 | 123 434 | 435 | 436 | -------------------------------------------------------------------------------- /src/android/Library/src/MultiImageChooserActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012, David Erosa 3 | * 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * Redistributions of source code must retain the above copyright notice, 10 | * this list of conditions and the following disclaimer. 11 | * Redistributions in binary form must reproduce the above copyright notice, 12 | * this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 19 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 20 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 21 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 22 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 23 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDIN G NEGLIGENCE OR OTHERWISE) 24 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 25 | * POSSIBILITY OF SUCH DAMAGE 26 | * 27 | * Code modified by Andrew Stephan for Sync OnSet 28 | * 29 | */ 30 | 31 | package com.synconset; 32 | 33 | import java.net.URI; 34 | import java.io.File; 35 | import java.io.FileOutputStream; 36 | import java.io.IOException; 37 | import java.io.OutputStream; 38 | import java.util.ArrayList; 39 | import java.util.HashMap; 40 | import java.util.Iterator; 41 | import java.util.Map; 42 | import java.util.Map.Entry; 43 | import java.util.Set; 44 | 45 | import com.synconset.FakeR; 46 | import android.app.Activity; 47 | import android.app.ActionBar; 48 | import android.app.AlertDialog; 49 | import android.app.LoaderManager; 50 | import android.app.ProgressDialog; 51 | import android.content.Context; 52 | import android.content.CursorLoader; 53 | import android.content.DialogInterface; 54 | import android.content.Intent; 55 | import android.content.Loader; 56 | import android.database.Cursor; 57 | import android.graphics.Bitmap; 58 | import android.graphics.BitmapFactory; 59 | import android.graphics.Color; 60 | import android.graphics.Matrix; 61 | import android.net.Uri; 62 | import android.os.AsyncTask; 63 | import android.os.Bundle; 64 | import android.provider.MediaStore; 65 | import android.util.Log; 66 | import android.util.SparseBooleanArray; 67 | import android.view.Display; 68 | import android.view.LayoutInflater; 69 | import android.view.View; 70 | import android.view.ViewGroup; 71 | import android.widget.AbsListView; 72 | import android.widget.AbsListView.OnScrollListener; 73 | import android.widget.AdapterView; 74 | import android.widget.AdapterView.OnItemClickListener; 75 | import android.widget.BaseAdapter; 76 | import android.widget.GridView; 77 | import android.widget.ImageView; 78 | import android.widget.TextView; 79 | 80 | public class MultiImageChooserActivity extends Activity implements OnItemClickListener, 81 | LoaderManager.LoaderCallbacks { 82 | private static final String TAG = "ImagePicker"; 83 | 84 | public static final int NOLIMIT = -1; 85 | public static final String MAX_IMAGES_KEY = "MAX_IMAGES"; 86 | public static final String WIDTH_KEY = "WIDTH"; 87 | public static final String HEIGHT_KEY = "HEIGHT"; 88 | public static final String QUALITY_KEY = "QUALITY"; 89 | 90 | private ImageAdapter ia; 91 | 92 | private Cursor imagecursor, actualimagecursor; 93 | private int image_column_index, image_column_orientation, actual_image_column_index, orientation_column_index; 94 | private int colWidth; 95 | 96 | private static final int CURSORLOADER_THUMBS = 0; 97 | private static final int CURSORLOADER_REAL = 1; 98 | 99 | private Map fileNames = new HashMap(); 100 | 101 | private SparseBooleanArray checkStatus = new SparseBooleanArray(); 102 | 103 | private int maxImages; 104 | private int maxImageCount; 105 | 106 | private int desiredWidth; 107 | private int desiredHeight; 108 | private int quality; 109 | 110 | private GridView gridView; 111 | 112 | private final ImageFetcher fetcher = new ImageFetcher(); 113 | 114 | private int selectedColor = 0xff32b2e1; 115 | private boolean shouldRequestThumb = true; 116 | 117 | private FakeR fakeR; 118 | 119 | private ProgressDialog progress; 120 | 121 | @Override 122 | public void onCreate(Bundle savedInstanceState) { 123 | super.onCreate(savedInstanceState); 124 | fakeR = new FakeR(this); 125 | setContentView(fakeR.getId("layout", "multiselectorgrid")); 126 | fileNames.clear(); 127 | 128 | maxImages = getIntent().getIntExtra(MAX_IMAGES_KEY, NOLIMIT); 129 | desiredWidth = getIntent().getIntExtra(WIDTH_KEY, 0); 130 | desiredHeight = getIntent().getIntExtra(HEIGHT_KEY, 0); 131 | quality = getIntent().getIntExtra(QUALITY_KEY, 0); 132 | maxImageCount = maxImages; 133 | 134 | Display display = getWindowManager().getDefaultDisplay(); 135 | int width = display.getWidth(); 136 | 137 | colWidth = width / 4; 138 | 139 | gridView = (GridView) findViewById(fakeR.getId("id", "gridview")); 140 | gridView.setOnItemClickListener(this); 141 | gridView.setOnScrollListener(new OnScrollListener() { 142 | private int lastFirstItem = 0; 143 | private long timestamp = System.currentTimeMillis(); 144 | 145 | @Override 146 | public void onScrollStateChanged(AbsListView view, int scrollState) { 147 | if (scrollState == SCROLL_STATE_IDLE) { 148 | shouldRequestThumb = true; 149 | ia.notifyDataSetChanged(); 150 | } 151 | } 152 | 153 | @Override 154 | public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { 155 | float dt = System.currentTimeMillis() - timestamp; 156 | if (firstVisibleItem != lastFirstItem) { 157 | double speed = 1 / dt * 1000; 158 | lastFirstItem = firstVisibleItem; 159 | timestamp = System.currentTimeMillis(); 160 | 161 | // Limit if we go faster than a page a second 162 | shouldRequestThumb = speed < visibleItemCount; 163 | } 164 | } 165 | }); 166 | 167 | ia = new ImageAdapter(this); 168 | gridView.setAdapter(ia); 169 | 170 | LoaderManager.enableDebugLogging(false); 171 | getLoaderManager().initLoader(CURSORLOADER_THUMBS, null, this); 172 | getLoaderManager().initLoader(CURSORLOADER_REAL, null, this); 173 | setupHeader(); 174 | updateAcceptButton(); 175 | progress = new ProgressDialog(this); 176 | progress.setTitle("Processing Images"); 177 | progress.setMessage("This may take a few moments"); 178 | } 179 | 180 | @Override 181 | public void onItemClick(AdapterView arg0, View view, int position, long id) { 182 | String name = getImageName(position); 183 | int rotation = getImageRotation(position); 184 | 185 | if (name == null) { 186 | return; 187 | } 188 | boolean isChecked = !isChecked(position); 189 | if (maxImages == 0 && isChecked) { 190 | isChecked = false; 191 | AlertDialog.Builder builder = new AlertDialog.Builder(this); 192 | builder.setTitle("Maximum " + maxImageCount + " Photos"); 193 | builder.setMessage("You can only select " + maxImageCount + " photos at a time."); 194 | builder.setPositiveButton("OK", new DialogInterface.OnClickListener() { 195 | public void onClick(DialogInterface dialog, int which) { 196 | dialog.cancel(); 197 | } 198 | }); 199 | AlertDialog alert = builder.create(); 200 | alert.show(); 201 | } else if (isChecked) { 202 | fileNames.put(name, new Integer(rotation)); 203 | if (maxImageCount == 1) { 204 | this.selectClicked(null); 205 | } else { 206 | maxImages--; 207 | ImageView imageView = (ImageView)view; 208 | if (android.os.Build.VERSION.SDK_INT>=16) { 209 | imageView.setImageAlpha(128); 210 | } else { 211 | imageView.setAlpha(128); 212 | } 213 | view.setBackgroundColor(selectedColor); 214 | } 215 | } else { 216 | fileNames.remove(name); 217 | maxImages++; 218 | ImageView imageView = (ImageView)view; 219 | if (android.os.Build.VERSION.SDK_INT>=16) { 220 | imageView.setImageAlpha(255); 221 | } else { 222 | imageView.setAlpha(255); 223 | } 224 | view.setBackgroundColor(Color.TRANSPARENT); 225 | } 226 | 227 | checkStatus.put(position, isChecked); 228 | updateAcceptButton(); 229 | } 230 | 231 | @Override 232 | public Loader onCreateLoader(int cursorID, Bundle arg1) { 233 | CursorLoader cl = null; 234 | 235 | ArrayList img = new ArrayList(); 236 | switch (cursorID) { 237 | 238 | case CURSORLOADER_THUMBS: 239 | img.add(MediaStore.Images.Media._ID); 240 | img.add(MediaStore.Images.Media.ORIENTATION); 241 | break; 242 | case CURSORLOADER_REAL: 243 | img.add(MediaStore.Images.Thumbnails.DATA); 244 | img.add(MediaStore.Images.Media.ORIENTATION); 245 | break; 246 | default: 247 | break; 248 | } 249 | 250 | cl = new CursorLoader(MultiImageChooserActivity.this, MediaStore.Images.Media.EXTERNAL_CONTENT_URI, 251 | img.toArray(new String[img.size()]), null, null, "DATE_MODIFIED DESC"); 252 | return cl; 253 | } 254 | 255 | @Override 256 | public void onLoadFinished(Loader loader, Cursor cursor) { 257 | if (cursor == null) { 258 | // NULL cursor. This usually means there's no image database yet.... 259 | return; 260 | } 261 | 262 | switch (loader.getId()) { 263 | case CURSORLOADER_THUMBS: 264 | imagecursor = cursor; 265 | image_column_index = imagecursor.getColumnIndex(MediaStore.Images.Media._ID); 266 | image_column_orientation = imagecursor.getColumnIndex(MediaStore.Images.Media.ORIENTATION); 267 | ia.notifyDataSetChanged(); 268 | break; 269 | case CURSORLOADER_REAL: 270 | actualimagecursor = cursor; 271 | String[] columns = actualimagecursor.getColumnNames(); 272 | actual_image_column_index = actualimagecursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA); 273 | orientation_column_index = actualimagecursor.getColumnIndexOrThrow(MediaStore.Images.Media.ORIENTATION); 274 | break; 275 | default: 276 | break; 277 | } 278 | } 279 | 280 | @Override 281 | public void onLoaderReset(Loader loader) { 282 | if (loader.getId() == CURSORLOADER_THUMBS) { 283 | imagecursor = null; 284 | } else if (loader.getId() == CURSORLOADER_REAL) { 285 | actualimagecursor = null; 286 | } 287 | } 288 | 289 | public void cancelClicked(View ignored) { 290 | setResult(RESULT_CANCELED); 291 | finish(); 292 | } 293 | 294 | public void selectClicked(View ignored) { 295 | ((TextView) getActionBar().getCustomView().findViewById(fakeR.getId("id", "actionbar_done_textview"))).setEnabled(false); 296 | getActionBar().getCustomView().findViewById(fakeR.getId("id", "actionbar_done")).setEnabled(false); 297 | progress.show(); 298 | Intent data = new Intent(); 299 | if (fileNames.isEmpty()) { 300 | this.setResult(RESULT_CANCELED); 301 | progress.dismiss(); 302 | finish(); 303 | } else { 304 | new ResizeImagesTask().execute(fileNames.entrySet()); 305 | } 306 | } 307 | 308 | 309 | /********************* 310 | * Helper Methods 311 | ********************/ 312 | private void updateAcceptButton() { 313 | ((TextView) getActionBar().getCustomView().findViewById(fakeR.getId("id", "actionbar_done_textview"))) 314 | .setEnabled(fileNames.size() != 0); 315 | getActionBar().getCustomView().findViewById(fakeR.getId("id", "actionbar_done")).setEnabled(fileNames.size() != 0); 316 | } 317 | 318 | private void setupHeader() { 319 | // From Roman Nkk's code 320 | // https://plus.google.com/113735310430199015092/posts/R49wVvcDoEW 321 | // Inflate a "Done/Discard" custom action bar view 322 | /* 323 | * Copyright 2013 The Android Open Source Project 324 | * 325 | * Licensed under the Apache License, Version 2.0 (the "License"); 326 | * you may not use this file except in compliance with the License. 327 | * You may obtain a copy of the License at 328 | * 329 | * http://www.apache.org/licenses/LICENSE-2.0 330 | * 331 | * Unless required by applicable law or agreed to in writing, software 332 | * distributed under the License is distributed on an "AS IS" BASIS, 333 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 334 | * See the License for the specific language governing permissions and 335 | * limitations under the License. 336 | */ 337 | LayoutInflater inflater = (LayoutInflater) getActionBar().getThemedContext().getSystemService( 338 | LAYOUT_INFLATER_SERVICE); 339 | final View customActionBarView = inflater.inflate(fakeR.getId("layout", "actionbar_custom_view_done_discard"), null); 340 | customActionBarView.findViewById(fakeR.getId("id", "actionbar_done")).setOnClickListener(new View.OnClickListener() { 341 | @Override 342 | public void onClick(View v) { 343 | // "Done" 344 | selectClicked(null); 345 | } 346 | }); 347 | customActionBarView.findViewById(fakeR.getId("id", "actionbar_discard")).setOnClickListener(new View.OnClickListener() { 348 | @Override 349 | public void onClick(View v) { 350 | finish(); 351 | } 352 | }); 353 | 354 | // Show the custom action bar view and hide the normal Home icon and title. 355 | final ActionBar actionBar = getActionBar(); 356 | actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM, ActionBar.DISPLAY_SHOW_CUSTOM 357 | | ActionBar.DISPLAY_SHOW_HOME | ActionBar.DISPLAY_SHOW_TITLE); 358 | actionBar.setCustomView(customActionBarView, new ActionBar.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 359 | ViewGroup.LayoutParams.MATCH_PARENT)); 360 | } 361 | 362 | private String getImageName(int position) { 363 | actualimagecursor.moveToPosition(position); 364 | String name = null; 365 | 366 | try { 367 | name = actualimagecursor.getString(actual_image_column_index); 368 | } catch (Exception e) { 369 | return null; 370 | } 371 | return name; 372 | } 373 | 374 | private int getImageRotation(int position) { 375 | actualimagecursor.moveToPosition(position); 376 | int rotation = 0; 377 | 378 | try { 379 | rotation = actualimagecursor.getInt(orientation_column_index); 380 | } catch (Exception e) { 381 | return rotation; 382 | } 383 | return rotation; 384 | } 385 | 386 | public boolean isChecked(int position) { 387 | boolean ret = checkStatus.get(position); 388 | return ret; 389 | } 390 | 391 | 392 | /********************* 393 | * Nested Classes 394 | ********************/ 395 | private class SquareImageView extends ImageView { 396 | public SquareImageView(Context context) { 397 | super(context); 398 | } 399 | 400 | @Override 401 | public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 402 | super.onMeasure(widthMeasureSpec, widthMeasureSpec); 403 | } 404 | } 405 | 406 | 407 | private class ImageAdapter extends BaseAdapter { 408 | private final Bitmap mPlaceHolderBitmap; 409 | 410 | public ImageAdapter(Context c) { 411 | Bitmap tmpHolderBitmap = BitmapFactory.decodeResource(getResources(), fakeR.getId("drawable", "loading_icon")); 412 | mPlaceHolderBitmap = Bitmap.createScaledBitmap(tmpHolderBitmap, colWidth, colWidth, false); 413 | if (tmpHolderBitmap != mPlaceHolderBitmap) { 414 | tmpHolderBitmap.recycle(); 415 | tmpHolderBitmap = null; 416 | } 417 | } 418 | 419 | public int getCount() { 420 | if (imagecursor != null) { 421 | return imagecursor.getCount(); 422 | } else { 423 | return 0; 424 | } 425 | } 426 | 427 | public Object getItem(int position) { 428 | return position; 429 | } 430 | 431 | public long getItemId(int position) { 432 | return position; 433 | } 434 | 435 | // create a new ImageView for each item referenced by the Adapter 436 | public View getView(int pos, View convertView, ViewGroup parent) { 437 | 438 | if (convertView == null) { 439 | ImageView temp = new SquareImageView(MultiImageChooserActivity.this); 440 | temp.setScaleType(ImageView.ScaleType.CENTER_CROP); 441 | convertView = (View)temp; 442 | } 443 | 444 | ImageView imageView = (ImageView)convertView; 445 | imageView.setImageBitmap(null); 446 | 447 | final int position = pos; 448 | 449 | if (!imagecursor.moveToPosition(position)) { 450 | return imageView; 451 | } 452 | 453 | if (image_column_index == -1) { 454 | return imageView; 455 | } 456 | 457 | final int id = imagecursor.getInt(image_column_index); 458 | final int rotate = imagecursor.getInt(image_column_orientation); 459 | if (isChecked(pos)) { 460 | if (android.os.Build.VERSION.SDK_INT>=16) { 461 | imageView.setImageAlpha(128); 462 | } else { 463 | imageView.setAlpha(128); 464 | } 465 | imageView.setBackgroundColor(selectedColor); 466 | } else { 467 | if (android.os.Build.VERSION.SDK_INT>=16) { 468 | imageView.setImageAlpha(255); 469 | } else { 470 | imageView.setAlpha(255); 471 | } 472 | imageView.setBackgroundColor(Color.TRANSPARENT); 473 | } 474 | if (shouldRequestThumb) { 475 | fetcher.fetch(Integer.valueOf(id), imageView, colWidth, rotate); 476 | } 477 | 478 | return imageView; 479 | } 480 | } 481 | 482 | 483 | private class ResizeImagesTask extends AsyncTask>, Void, ArrayList> { 484 | private Exception asyncTaskError = null; 485 | 486 | @Override 487 | protected ArrayList doInBackground(Set>... fileSets) { 488 | Set> fileNames = fileSets[0]; 489 | ArrayList al = new ArrayList(); 490 | try { 491 | Iterator> i = fileNames.iterator(); 492 | Bitmap bmp; 493 | while(i.hasNext()) { 494 | Entry imageInfo = i.next(); 495 | File file = new File(imageInfo.getKey()); 496 | int rotate = imageInfo.getValue().intValue(); 497 | BitmapFactory.Options options = new BitmapFactory.Options(); 498 | options.inSampleSize = 1; 499 | options.inJustDecodeBounds = true; 500 | BitmapFactory.decodeFile(file.getAbsolutePath(), options); 501 | int width = options.outWidth; 502 | int height = options.outHeight; 503 | float scale = calculateScale(width, height); 504 | if (scale < 1) { 505 | int finalWidth = (int)(width * scale); 506 | int finalHeight = (int)(height * scale); 507 | int inSampleSize = calculateInSampleSize(options, finalWidth, finalHeight); 508 | options = new BitmapFactory.Options(); 509 | options.inSampleSize = inSampleSize; 510 | try { 511 | bmp = this.tryToGetBitmap(file, options, rotate, true); 512 | } catch (OutOfMemoryError e) { 513 | options.inSampleSize = calculateNextSampleSize(options.inSampleSize); 514 | try { 515 | bmp = this.tryToGetBitmap(file, options, rotate, false); 516 | } catch (OutOfMemoryError e2) { 517 | throw new IOException("Unable to load image into memory."); 518 | } 519 | } 520 | } else { 521 | try { 522 | bmp = this.tryToGetBitmap(file, null, rotate, false); 523 | } catch(OutOfMemoryError e) { 524 | options = new BitmapFactory.Options(); 525 | options.inSampleSize = 2; 526 | try { 527 | bmp = this.tryToGetBitmap(file, options, rotate, false); 528 | } catch(OutOfMemoryError e2) { 529 | options = new BitmapFactory.Options(); 530 | options.inSampleSize = 4; 531 | try { 532 | bmp = this.tryToGetBitmap(file, options, rotate, false); 533 | } catch (OutOfMemoryError e3) { 534 | throw new IOException("Unable to load image into memory."); 535 | } 536 | } 537 | } 538 | } 539 | 540 | file = this.storeImage(bmp, file.getName()); 541 | al.add(Uri.fromFile(file).toString()); 542 | } 543 | return al; 544 | } catch(IOException e) { 545 | try { 546 | asyncTaskError = e; 547 | for (int i = 0; i < al.size(); i++) { 548 | URI uri = new URI(al.get(i)); 549 | File file = new File(uri); 550 | file.delete(); 551 | } 552 | } catch(Exception exception) { 553 | // the finally does what we want to do 554 | } finally { 555 | return new ArrayList(); 556 | } 557 | } 558 | } 559 | 560 | @Override 561 | protected void onPostExecute(ArrayList al) { 562 | Intent data = new Intent(); 563 | 564 | if (asyncTaskError != null) { 565 | Bundle res = new Bundle(); 566 | res.putString("ERRORMESSAGE", asyncTaskError.getMessage()); 567 | data.putExtras(res); 568 | setResult(RESULT_CANCELED, data); 569 | } else if (al.size() > 0) { 570 | Bundle res = new Bundle(); 571 | res.putStringArrayList("MULTIPLEFILENAMES", al); 572 | if (imagecursor != null) { 573 | res.putInt("TOTALFILES", imagecursor.getCount()); 574 | } 575 | data.putExtras(res); 576 | setResult(RESULT_OK, data); 577 | } else { 578 | setResult(RESULT_CANCELED, data); 579 | } 580 | 581 | progress.dismiss(); 582 | finish(); 583 | } 584 | 585 | private Bitmap tryToGetBitmap(File file, BitmapFactory.Options options, int rotate, boolean shouldScale) throws IOException, OutOfMemoryError { 586 | Bitmap bmp; 587 | if (options == null) { 588 | bmp = BitmapFactory.decodeFile(file.getAbsolutePath()); 589 | } else { 590 | bmp = BitmapFactory.decodeFile(file.getAbsolutePath(), options); 591 | } 592 | if (bmp == null) { 593 | throw new IOException("The image file could not be opened."); 594 | } 595 | if (options != null && shouldScale) { 596 | float scale = calculateScale(options.outWidth, options.outHeight); 597 | bmp = this.getResizedBitmap(bmp, scale); 598 | } 599 | if (rotate != 0) { 600 | Matrix matrix = new Matrix(); 601 | matrix.setRotate(rotate); 602 | bmp = Bitmap.createBitmap(bmp, 0, 0, bmp.getWidth(), bmp.getHeight(), matrix, true); 603 | } 604 | return bmp; 605 | } 606 | 607 | /* 608 | * The following functions are originally from 609 | * https://github.com/raananw/PhoneGap-Image-Resizer 610 | * 611 | * They have been modified by Andrew Stephan for Sync OnSet 612 | * 613 | * The software is open source, MIT Licensed. 614 | * Copyright (C) 2012, webXells GmbH All Rights Reserved. 615 | */ 616 | private File storeImage(Bitmap bmp, String fileName) throws IOException { 617 | int index = fileName.lastIndexOf('.'); 618 | String name = fileName.substring(0, index); 619 | String ext = fileName.substring(index); 620 | File file = File.createTempFile("tmp_" + name, ext); 621 | OutputStream outStream = new FileOutputStream(file); 622 | if (ext.compareToIgnoreCase(".png") == 0) { 623 | bmp.compress(Bitmap.CompressFormat.PNG, quality, outStream); 624 | } else { 625 | bmp.compress(Bitmap.CompressFormat.JPEG, quality, outStream); 626 | } 627 | outStream.flush(); 628 | outStream.close(); 629 | return file; 630 | } 631 | 632 | private Bitmap getResizedBitmap(Bitmap bm, float factor) { 633 | int width = bm.getWidth(); 634 | int height = bm.getHeight(); 635 | // create a matrix for the manipulation 636 | Matrix matrix = new Matrix(); 637 | // resize the bit map 638 | matrix.postScale(factor, factor); 639 | // recreate the new Bitmap 640 | Bitmap resizedBitmap = Bitmap.createBitmap(bm, 0, 0, width, height, matrix, false); 641 | return resizedBitmap; 642 | } 643 | } 644 | 645 | private int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) { 646 | // Raw height and width of image 647 | final int height = options.outHeight; 648 | final int width = options.outWidth; 649 | int inSampleSize = 1; 650 | 651 | if (height > reqHeight || width > reqWidth) { 652 | final int halfHeight = height / 2; 653 | final int halfWidth = width / 2; 654 | 655 | // Calculate the largest inSampleSize value that is a power of 2 and keeps both 656 | // height and width larger than the requested height and width. 657 | while ((halfHeight / inSampleSize) > reqHeight && (halfWidth / inSampleSize) > reqWidth) { 658 | inSampleSize *= 2; 659 | } 660 | } 661 | 662 | return inSampleSize; 663 | } 664 | 665 | private int calculateNextSampleSize(int sampleSize) { 666 | double logBaseTwo = (int)(Math.log(sampleSize) / Math.log(2)); 667 | return (int)Math.pow(logBaseTwo + 1, 2); 668 | } 669 | 670 | private float calculateScale(int width, int height) { 671 | float widthScale = 1.0f; 672 | float heightScale = 1.0f; 673 | float scale = 1.0f; 674 | if (desiredWidth > 0 || desiredHeight > 0) { 675 | if (desiredHeight == 0 && desiredWidth < width) { 676 | scale = (float)desiredWidth/width; 677 | } else if (desiredWidth == 0 && desiredHeight < height) { 678 | scale = (float)desiredHeight/height; 679 | } else { 680 | if (desiredWidth > 0 && desiredWidth < width) { 681 | widthScale = (float)desiredWidth/width; 682 | } 683 | if (desiredHeight > 0 && desiredHeight < height) { 684 | heightScale = (float)desiredHeight/height; 685 | } 686 | if (widthScale < heightScale) { 687 | scale = widthScale; 688 | } else { 689 | scale = heightScale; 690 | } 691 | } 692 | } 693 | 694 | return scale; 695 | } 696 | } 697 | --------------------------------------------------------------------------------