#2 Support for iCloud photos on iOS - modifications

This commit is contained in:
EddyVerbruggen
2015-10-29 22:04:55 +01:00
parent b88f7107f7
commit 3900d47c4e
7 changed files with 245 additions and 157 deletions
+53
View File
@@ -0,0 +1,53 @@
<!DOCTYPE html>
<html>
<head>
<meta name="format-detection" content="telephone=no">
<meta name="msapplication-tap-highlight" content="no">
<meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width">
<link rel="stylesheet" type="text/css" href="css/index.css">
<title>ImagePicker demo</title>
</head>
<body>
<div class="app">
<h1>ImagePicker demo</h1>
<div id="deviceready" class="blink">
<p class="event listening">Connecting to Device</p>
<p class="event received">Device is Ready</p>
<button onclick="getPics()">get pics</button>
</div>
</div>
<script type="text/javascript" src="cordova.js"></script>
<script type="text/javascript" src="js/index.js"></script>
<script>
function getPics() {
imagePicker.getPictures(
function (result) {
var content = '';
for (var i = 0; i < result.length; i++) {
content += '<img src="' + result[i] + '" style="max-width:200px"/>';
//content += '<img src="data:image/jpg;base64,'+result[i]+'" style="max-width:200px"/>';
}
document.getElementById("imageOutput").innerHTML = content;
}, function (error) {
alert('Error: ' + error);
}, {
// if no title is passed, the plugin should use a sane default (preferrably the same as it was, so check the old one.. there are screenshots in the marketplace doc)
maximumImagesCount: 10,
title: 'Select pix',
message: 'Pick max 10 items', // optional default no helper message above the picker UI
// be careful with these options as they require additional processing
width: 400,
quality: 80
// outputType: imagePicker.OutputType.BASE64_STRING
}
);
}
</script>
<div id="imageOutput">
</div>
</body>
</html>
+12 -13
View File
@@ -13,7 +13,7 @@
<license>MIT</license>
<engines>
<engine name="cordova" version=">=3.0.0" />
<engine name="cordova" version=">=3.5.0" />
</engines>
<js-module src="www/imagepicker.js" name="ImagePicker">
@@ -31,8 +31,8 @@
<header-file src="src/ios/SOSPicker.h" />
<source-file src="src/ios/SOSPicker.m" />
<header-file src="src/ios/GMImagePicker/UIImage+fixOrientation.h" />
<source-file src="src/ios/GMImagePicker/UIImage+fixOrientation.m" />
<header-file src="src/ios/GMImagePicker/UIImage+fixOrientation.h" />
<source-file src="src/ios/GMImagePicker/UIImage+fixOrientation.m" />
<header-file src="src/ios/GMImagePicker/GMAlbumsViewCell.h" />
<source-file src="src/ios/GMImagePicker/GMAlbumsViewCell.m" />
@@ -76,6 +76,9 @@
<asset src="src/ios/GMImagePicker/it.lproj/GMImagePicker.strings" target="res/strings-it/GMImagePicker.strings" />
<asset src="src/ios/GMImagePicker/pt.lproj/GMImagePicker.strings" target="res/strings-pt/GMImagePicker.strings" />
<!-- NOTE that MRProgress (and its dependency AFNetworking) have been added for a semi-useful
progress indicator. We may remove that if we have trouble with these files for some reason. -->
<!-- AF Networking -->
<source-file src="src/ios/GMImagePicker/AFNetworking/LICENSE" target-dir="resources/AFNetworking" />
<source-file src="src/ios/GMImagePicker/AFNetworking/README.md" target-dir="resources/AFNetworking" />
@@ -187,11 +190,11 @@
<header-file src="src/ios/GMImagePicker/mrprogress/Utils/MRWeakProxy.h" target-dir="mrprogress" />
<source-file src="src/ios/GMImagePicker/mrprogress/Utils/MRWeakProxy.m" target-dir="mrprogress" />
<framework src="Accelerate.framework" />
<framework src="Security.framework" />
<framework src="Photos.framework" />
<framework src="SystemConfiguration.framework" />
<framework src="QuartzCore.framework" />
<framework src="Accelerate.framework" />
<framework src="Security.framework" />
<framework src="Photos.framework" />
<framework src="SystemConfiguration.framework" />
<framework src="QuartzCore.framework" />
</platform>
@@ -217,8 +220,7 @@
<source-file src="src/android/Library/src/ImageFetcher.java" target-dir="src/com/synconset"/>
<source-file src="src/android/Library/src/MultiImageChooserActivity.java" target-dir="src/com/synconset"/>
<source-file src="src/android/Library/src/FileNameItem.java" target-dir="src/com/synconset"/>
<source-file src="src/android/Library/res/anim/image_pop_in.xml" target-dir="res/anim"/>
<source-file src="src/android/Library/res/drawable/grid_background.xml" target-dir="res/drawable"/>
<source-file src="src/android/Library/res/drawable-hdpi/image_bg.9.png" target-dir="res/drawable-hdpi"/>
@@ -248,8 +250,5 @@
<source-file src="src/android/Library/res/values-ko/multiimagechooser_strings_ko.xml" target-dir="res/values-ko"/>
<framework src="src/android/ignorelinterrors.gradle" custom="true" type="gradleReference"/>
<source-file src="src/android/Library/libs/commons-io-2.4.jar" target-dir="libs" />
<source-file src="src/android/Library/libs/gson-2.3.1.jar" target-dir="libs" />
</platform>
</plugin>
@@ -64,7 +64,6 @@ import android.os.AsyncTask;
import android.os.Bundle;
import android.provider.MediaStore;
import android.util.Base64;
import android.util.Log;
import android.util.SparseBooleanArray;
import android.view.Display;
import android.view.LayoutInflater;
@@ -105,7 +104,7 @@ public class MultiImageChooserActivity extends Activity implements OnItemClickLi
private int maxImages;
private int maxImageCount;
private int desiredWidth;
private int desiredHeight;
private int quality;
@@ -117,9 +116,9 @@ public class MultiImageChooserActivity extends Activity implements OnItemClickLi
private int selectedColor = 0xff32b2e1;
private boolean shouldRequestThumb = true;
private FakeR fakeR;
private ProgressDialog progress;
@Override
@@ -138,7 +137,7 @@ public class MultiImageChooserActivity extends Activity implements OnItemClickLi
Display display = getWindowManager().getDefaultDisplay();
int width = display.getWidth();
colWidth = width / 4;
gridView = (GridView) findViewById(fakeR.getId("id", "gridview"));
@@ -181,7 +180,7 @@ public class MultiImageChooserActivity extends Activity implements OnItemClickLi
progress.setTitle("Processing Images");
progress.setMessage("This may take a few moments");
}
@Override
public void onItemClick(AdapterView<?> arg0, View view, int position, long id) {
String name = getImageName(position);
@@ -197,7 +196,7 @@ public class MultiImageChooserActivity extends Activity implements OnItemClickLi
builder.setTitle("Maximum " + maxImageCount + " Photos");
builder.setMessage("You can only select " + maxImageCount + " photos at a time.");
builder.setPositiveButton("OK", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
}
});
@@ -290,7 +289,7 @@ public class MultiImageChooserActivity extends Activity implements OnItemClickLi
actualimagecursor = null;
}
}
public void cancelClicked(View ignored) {
setResult(RESULT_CANCELED);
finish();
@@ -309,8 +308,8 @@ public class MultiImageChooserActivity extends Activity implements OnItemClickLi
new ResizeImagesTask().execute(fileNames.entrySet());
}
}
/*********************
* Helper Methods
********************/
@@ -375,7 +374,7 @@ public class MultiImageChooserActivity extends Activity implements OnItemClickLi
}
return name;
}
private int getImageRotation(int position) {
actualimagecursor.moveToPosition(position);
int rotation = 0;
@@ -387,13 +386,13 @@ public class MultiImageChooserActivity extends Activity implements OnItemClickLi
}
return rotation;
}
public boolean isChecked(int position) {
boolean ret = checkStatus.get(position);
return ret;
}
/*********************
* Nested Classes
********************/
@@ -407,8 +406,8 @@ public class MultiImageChooserActivity extends Activity implements OnItemClickLi
super.onMeasure(widthMeasureSpec, widthMeasureSpec);
}
}
private class ImageAdapter extends BaseAdapter {
private final Bitmap mPlaceHolderBitmap;
@@ -465,14 +464,14 @@ public class MultiImageChooserActivity extends Activity implements OnItemClickLi
if (android.os.Build.VERSION.SDK_INT>=16) {
imageView.setImageAlpha(128);
} else {
imageView.setAlpha(128);
imageView.setAlpha(128);
}
imageView.setBackgroundColor(selectedColor);
} else {
if (android.os.Build.VERSION.SDK_INT>=16) {
imageView.setImageAlpha(255);
} else {
imageView.setAlpha(255);
imageView.setAlpha(255);
}
imageView.setBackgroundColor(Color.TRANSPARENT);
}
@@ -483,8 +482,8 @@ public class MultiImageChooserActivity extends Activity implements OnItemClickLi
return imageView;
}
}
private class ResizeImagesTask extends AsyncTask<Set<Entry<String, Integer>>, Void, ArrayList<String>> {
private Exception asyncTaskError = null;
@@ -544,7 +543,7 @@ public class MultiImageChooserActivity extends Activity implements OnItemClickLi
if(outputType == OutputType.FILE_URI) {
file = this.storeImage(bmp, file.getName());
al.add(Uri.fromFile(file).toString());
al.add(Uri.fromFile(file).toString());
} else if (outputType == OutputType.BASE64_STRING){
al.add(getBase64OfImage(bmp));
}
@@ -558,14 +557,12 @@ public class MultiImageChooserActivity extends Activity implements OnItemClickLi
File file = new File(uri);
file.delete();
}
} catch(Exception exception) {
// the finally does what we want to do
} finally {
return new ArrayList<String>();
} catch(Exception ignore) {
}
return new ArrayList<String>();
}
}
@Override
protected void onPostExecute(ArrayList<String> al) {
Intent data = new Intent();
@@ -612,11 +609,11 @@ public class MultiImageChooserActivity extends Activity implements OnItemClickLi
}
return bmp;
}
/*
* The following functions are originally from
* https://github.com/raananw/PhoneGap-Image-Resizer
*
*
* They have been modified by Andrew Stephan for Sync OnSet
*
* The software is open source, MIT Licensed.
@@ -637,7 +634,7 @@ public class MultiImageChooserActivity extends Activity implements OnItemClickLi
outStream.close();
return file;
}
private Bitmap getResizedBitmap(Bitmap bm, float factor) {
int width = bm.getWidth();
int height = bm.getHeight();
@@ -646,35 +643,34 @@ public class MultiImageChooserActivity extends Activity implements OnItemClickLi
// resize the bit map
matrix.postScale(factor, factor);
// recreate the new Bitmap
Bitmap resizedBitmap = Bitmap.createBitmap(bm, 0, 0, width, height, matrix, false);
return resizedBitmap;
return Bitmap.createBitmap(bm, 0, 0, width, height, matrix, false);
}
private String getBase64OfImage(Bitmap bm) {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
bm.compress(Bitmap.CompressFormat.JPEG, 100, byteArrayOutputStream);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
bm.compress(Bitmap.CompressFormat.JPEG, quality, byteArrayOutputStream);
byte[] byteArray = byteArrayOutputStream .toByteArray();
return Base64.encodeToString(byteArray, Base64.DEFAULT);
}
}
private int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
final int halfHeight = height / 2;
final int halfWidth = width / 2;
// Calculate the largest inSampleSize value that is a power of 2 and keeps both
// height and width larger than the requested height and width.
while ((halfHeight / inSampleSize) > reqHeight && (halfWidth / inSampleSize) > reqWidth) {
inSampleSize *= 2;
}
}
return inSampleSize;
}
@@ -682,7 +678,7 @@ public class MultiImageChooserActivity extends Activity implements OnItemClickLi
double logBaseTwo = (int)(Math.log(sampleSize) / Math.log(2));
return (int)Math.pow(logBaseTwo + 1, 2);
}
private float calculateScale(int width, int height) {
float widthScale = 1.0f;
float heightScale = 1.0f;
@@ -706,7 +702,7 @@ public class MultiImageChooserActivity extends Activity implements OnItemClickLi
}
}
}
return scale;
}
+1 -1
View File
@@ -140,7 +140,7 @@ static UIColor *disabledColor;
_fetch.textColor = titleColor;
_fetch.textAlignment = NSTextAlignmentCenter;
_fetch.frame = CGRectMake(0, 0, self.bounds.size.width, self.bounds.size.height);
_fetch.text = @"fetching";
_fetch.text = @""; // removed 'fetching'
[self addSubview:_fetch];
}
+2 -1
View File
@@ -403,7 +403,8 @@ NSString * const GMGridViewCellIdentifier = @"GMGridViewCellIdentifier";
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
fetch_item.be_saving_img_thumb = false;
// TODO pass in quality
if ( ![ UIImageJPEGRepresentation(result, 1.0f ) writeToFile:filePath atomically:YES ] ) {
return;
}
+141 -102
View File
@@ -7,9 +7,10 @@
//
#import "SOSPicker.h"
#import "ELCAlbumPickerController.h"
#import "ELCImagePickerController.h"
#import "ELCAssetTablePicker.h"
#import "GMImagePickerController.h"
#import "GMFetchItem.h"
#define CDV_PHOTO_PREFIX @"cdv_photo_"
@@ -18,114 +19,51 @@ typedef enum : NSUInteger {
BASE64_STRING = 1
} SOSPickerOutputType;
@implementation SOSPicker
@interface SOSPicker () <GMImagePickerControllerDelegate>
@end
@implementation SOSPicker
@synthesize callbackId;
- (void) getPictures:(CDVInvokedUrlCommand *)command {
NSDictionary *options = [command.arguments objectAtIndex: 0];
NSInteger maximumImagesCount = [[options objectForKey:@"maximumImagesCount"] integerValue];
self.width = [[options objectForKey:@"width"] integerValue];
self.height = [[options objectForKey:@"height"] integerValue];
self.quality = [[options objectForKey:@"quality"] integerValue];
NSDictionary *options = [command.arguments objectAtIndex: 0];
self.outputType = [[options objectForKey:@"outputType"] integerValue];
BOOL allow_video = [[options objectForKey:@"allow_video" ] boolValue ];
NSString * title = [options objectForKey:@"title"];
NSString * message = [options objectForKey:@"message"];
if (message == (id)[NSNull null]) {
message = nil;
}
self.width = [[options objectForKey:@"width"] integerValue];
self.height = [[options objectForKey:@"height"] integerValue];
self.quality = [[options objectForKey:@"quality"] integerValue];
// Create the an album controller and image picker
ELCAlbumPickerController *albumController = [[ELCAlbumPickerController alloc] init];
if (maximumImagesCount == 1) {
albumController.immediateReturn = true;
albumController.singleSelection = true;
} else {
albumController.immediateReturn = false;
albumController.singleSelection = false;
}
ELCImagePickerController *imagePicker = [[ELCImagePickerController alloc] initWithRootViewController:albumController];
imagePicker.maximumImagesCount = maximumImagesCount;
imagePicker.returnsOriginalImage = 1;
imagePicker.imagePickerDelegate = self;
albumController.parent = imagePicker;
self.callbackId = command.callbackId;
// Present modally
[self.viewController presentViewController:imagePicker
animated:YES
completion:nil];
self.callbackId = command.callbackId;
[self launchGMImagePicker:allow_video title:title message:message];
}
- (void)elcImagePickerController:(ELCImagePickerController *)picker didFinishPickingMediaWithInfo:(NSArray *)info {
CDVPluginResult* result = nil;
NSMutableArray *resultStrings = [[NSMutableArray alloc] init];
NSData* data = nil;
NSString* docsPath = [NSTemporaryDirectory()stringByStandardizingPath];
NSError* err = nil;
NSFileManager* fileMgr = [[NSFileManager alloc] init];
NSString* filePath;
ALAsset* asset = nil;
UIImageOrientation orientation = UIImageOrientationUp;;
CGSize targetSize = CGSizeMake(self.width, self.height);
for (NSDictionary *dict in info) {
asset = [dict objectForKey:@"ALAsset"];
// From ELCImagePickerController.m
int i = 1;
do {
filePath = [NSString stringWithFormat:@"%@/%@%03d.%@", docsPath, CDV_PHOTO_PREFIX, i++, @"jpg"];
} while ([fileMgr fileExistsAtPath:filePath]);
@autoreleasepool {
ALAssetRepresentation *assetRep = [asset defaultRepresentation];
CGImageRef imgRef = NULL;
//defaultRepresentation returns image as it appears in photo picker, rotated and sized,
//so use UIImageOrientationUp when creating our image below.
if (picker.returnsOriginalImage) {
imgRef = [assetRep fullResolutionImage];
orientation = [assetRep orientation];
} else {
imgRef = [assetRep fullScreenImage];
}
UIImage* image = [UIImage imageWithCGImage:imgRef scale:1.0f orientation:orientation];
if (self.width == 0 && self.height == 0) {
data = UIImageJPEGRepresentation(image, self.quality/100.0f);
} else {
UIImage* scaledImage = [self imageByScalingNotCroppingForSize:image toSize:targetSize];
data = UIImageJPEGRepresentation(scaledImage, self.quality/100.0f);
}
if (![data writeToFile:filePath options:NSAtomicWrite error:&err]) {
result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsString:[err localizedDescription]];
break;
} else {
if(self.outputType == BASE64_STRING){
[resultStrings addObject:[data base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength]];
} else {
[resultStrings addObject:[[NSURL fileURLWithPath:filePath] absoluteString]];
}
}
}
}
if (nil == result) {
result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsArray:resultStrings];
}
[self.viewController dismissViewControllerAnimated:YES completion:nil];
[self.commandDelegate sendPluginResult:result callbackId:self.callbackId];
- (void)launchGMImagePicker:(bool)allow_video title:(NSString *)title message:(NSString *)message
{
GMImagePickerController *picker = [[GMImagePickerController alloc] init:allow_video];
picker.delegate = self;
picker.title = title;
picker.customNavigationBarPrompt = message;
picker.colsInPortrait = 4;
picker.colsInLandscape = 6;
picker.minimumInteritemSpacing = 2.0;
picker.modalPresentationStyle = UIModalPresentationPopover;
UIPopoverPresentationController *popPC = picker.popoverPresentationController;
popPC.permittedArrowDirections = UIPopoverArrowDirectionAny;
popPC.sourceView = picker.view;
//popPC.sourceRect = nil;
[self.viewController showViewController:picker sender:nil];
}
- (void)elcImagePickerControllerDidCancel:(ELCImagePickerController *)picker {
[self.viewController dismissViewControllerAnimated:YES completion:nil];
CDVPluginResult* pluginResult = nil;
NSArray* emptyArray = [NSArray array];
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsArray:emptyArray];
[self.commandDelegate sendPluginResult:pluginResult callbackId:self.callbackId];
}
- (UIImage*)imageByScalingNotCroppingForSize:(UIImage*)anImage toSize:(CGSize)frameSize
{
@@ -170,4 +108,105 @@ typedef enum : NSUInteger {
return newImage;
}
@end
#pragma mark - UIImagePickerControllerDelegate
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
[picker.presentingViewController dismissViewControllerAnimated:YES completion:nil];
NSLog(@"UIImagePickerController: User finished picking assets");
}
- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker
{
[picker.presentingViewController dismissViewControllerAnimated:YES completion:nil];
NSLog(@"UIImagePickerController: User pressed cancel button");
}
#pragma mark - GMImagePickerControllerDelegate
- (void)assetsPickerController:(GMImagePickerController *)picker didFinishPickingAssets:(NSArray *)fetchArray
{
[picker.presentingViewController dismissViewControllerAnimated:YES completion:nil];
NSLog(@"GMImagePicker: User finished picking assets. Number of selected items is: %lu", (unsigned long)fetchArray.count);
NSMutableArray * result_all = [[NSMutableArray alloc] init];
CGSize targetSize = CGSizeMake(self.width, self.height);
NSFileManager* fileMgr = [[NSFileManager alloc] init];
NSString* docsPath = [NSTemporaryDirectory()stringByStandardizingPath];
NSError* err = nil;
int i = 1;
NSString* filePath;
CDVPluginResult* result = nil;
for (GMFetchItem *item in fetchArray) {
if ( !item.image_fullsize ) {
continue;
}
do {
filePath = [NSString stringWithFormat:@"%@/%@%03d.%@", docsPath, CDV_PHOTO_PREFIX, i++, @"jpg"];
} while ([fileMgr fileExistsAtPath:filePath]);
NSData* data = nil;
if (self.width == 0 && self.height == 0) {
// no scaling required
if (self.outputType == BASE64_STRING){
UIImage* image = [UIImage imageNamed:item.image_fullsize];
[result_all addObject:[UIImageJPEGRepresentation(image, self.quality/100.0f) base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength]];
} else {
if (self.quality == 100) {
// no scaling, no downsampling, this is the fastest option
[result_all addObject:item.image_fullsize];
} else {
// resample first
UIImage* image = [UIImage imageNamed:item.image_fullsize];
data = UIImageJPEGRepresentation(image, self.quality/100.0f);
if (![data writeToFile:filePath options:NSAtomicWrite error:&err]) {
result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsString:[err localizedDescription]];
break;
} else {
[result_all addObject:[[NSURL fileURLWithPath:filePath] absoluteString]];
}
}
}
} else {
// scale
UIImage* image = [UIImage imageNamed:item.image_fullsize];
UIImage* scaledImage = [self imageByScalingNotCroppingForSize:image toSize:targetSize];
data = UIImageJPEGRepresentation(scaledImage, self.quality/100.0f);
if (![data writeToFile:filePath options:NSAtomicWrite error:&err]) {
result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsString:[err localizedDescription]];
break;
} else {
if(self.outputType == BASE64_STRING){
[result_all addObject:[data base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength]];
} else {
[result_all addObject:[[NSURL fileURLWithPath:filePath] absoluteString]];
}
}
}
}
if (result == nil) {
result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsArray:result_all];
}
[self.viewController dismissViewControllerAnimated:YES completion:nil];
[self.commandDelegate sendPluginResult:result callbackId:self.callbackId];
}
//Optional implementation:
-(void)assetsPickerControllerDidCancel:(GMImagePickerController *)picker
{
NSLog(@"GMImagePicker: User pressed cancel button");
}
@end
+2 -2
View File
@@ -51,8 +51,8 @@ ImagePicker.prototype.getPictures = function(success, fail, options) {
height: options.height ? options.height : 0,
quality: options.quality ? options.quality : 100,
allow_video: options.allow_video ? options.allow_video : false,
title: options.title ? options.title : 'Custom Title',
message: options.message ? options.message : 'Custom helper message',
title: options.title ? options.title : 'Select an Album', // the default is the message of the old plugin impl
message: options.message ? options.message : null, // the old plugin impl didn't have it, so passing null by default
outputType: options.outputType ? options.outputType : this.OutputType.FILE_URI
};