Skip to content

Commit

Permalink
Merge branch 'getLibrary-PartialResult'
Browse files Browse the repository at this point in the history
# Conflicts:
#	README.md
  • Loading branch information
viskin committed Nov 18, 2016
2 parents 87ba1ac + 2a76f5b commit 8866f07
Show file tree
Hide file tree
Showing 8 changed files with 189 additions and 129 deletions.
2 changes: 1 addition & 1 deletion PhotoLibrary.d.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
declare module PhotoLibraryCordova {

export interface Plugin {
getLibrary(success: (result: LibraryItem[]) => void, error: (err: any) => void, options: GetLibraryOptions): void;
getLibrary(success: (result: LibraryItem[]) => void, error: (err: any) => void, options: GetLibraryOptions, partialCallback: (result: string) => void): void;

getThumbnailURL(photoId: string, success: (result: string) => void, error: (err: any) => void, options: GetThumbnailOptions): void;
getThumbnailURL(libraryItem: LibraryItem, success: (result: string) => void, error: (err: any) => void, options: GetThumbnailOptions): void;
Expand Down
11 changes: 8 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,14 @@ cordova.plugins.photoLibrary.getLibrary(
function (err) {
console.log('Error occured');
},
{
{ // optional options
thumbnailWidth: 512,
thumbnailHeight: 384,
quality: 0.8
},
function partialCallback(partialLibrary) { // optional
// If this callback provided and loading library takes time, it will be called each 0.5 seconds with
// library that filled up to this time. You can start displaying photos to user right then.
}
);
```
Expand Down Expand Up @@ -130,7 +134,7 @@ cordova.plugins.photoLibrary.getThumbnailURL(
function (err) {
console.log('Error occured');
},
{
{ // optional options
thumbnailWidth: 512,
thumbnailHeight: 384,
quality: 0.8
Expand Down Expand Up @@ -160,7 +164,7 @@ cordova.plugins.photoLibrary.getThumbnail(
function (err) {
console.log('Error occured');
},
{
{ // optional options
thumbnailWidth: 512,
thumbnailHeight: 384,
quality: 0.8
Expand Down Expand Up @@ -253,6 +257,7 @@ TypeScript definitions are provided in [PhotoLibrary.d.ts](https://github.com/te
- Provide cancellation mechanism for long-running operations, like getLibrary.
- Pre-fetching data to file-based cache on app start can improve responsiveness dramatically. Just this caching should occur as low-priority thread. Cache can be updated
by system photo libraries events.
- partialCallback currently implemented only for iOS platform. android and browser platform implementations needed.

# References

Expand Down
43 changes: 38 additions & 5 deletions src/android/PhotoLibrary.java
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,34 @@ public void run() {
return;
}

ArrayList<JSONObject> library = service.getLibrary(getContext());
callbackContext.success(new JSONArray(library));
service.getLibrary(getContext(), new PhotoLibraryService.MyRunnable() { // partialCallback
@Override
public void run(ArrayList<JSONObject> library) {
try {

JSONObject result = createGetLibraryResult(library, true);
PluginResult pluginResult = new PluginResult(PluginResult.Status.OK, result);
callbackContext.sendPluginResult(pluginResult);

} catch (Exception e) {
e.printStackTrace();
callbackContext.error(e.getMessage());
}
}
}, new PhotoLibraryService.MyRunnable() { // completion
@Override
public void run(ArrayList<JSONObject> library) {
try {

JSONObject result = createGetLibraryResult(library, false);
callbackContext.success(result);

} catch (Exception e) {
e.printStackTrace();
callbackContext.error(e.getMessage());
}
}
});

} catch (Exception e) {
e.printStackTrace();
Expand All @@ -89,7 +115,7 @@ public void run() {
}

PhotoLibraryService.PictureData thumbnail = service.getThumbnail(getContext(), photoId, thumbnailWidth, thumbnailHeight, quality);
callbackContext.sendPluginResult(createPluginResult(PluginResult.Status.OK, thumbnail));
callbackContext.sendPluginResult(createMultipartPluginResult(PluginResult.Status.OK, thumbnail));

} catch (Exception e) {
e.printStackTrace();
Expand All @@ -113,7 +139,7 @@ public void run() {
}

PhotoLibraryService.PictureData photo = service.getPhoto(getContext(), photoId);
callbackContext.sendPluginResult(createPluginResult(PluginResult.Status.OK, photo));
callbackContext.sendPluginResult(createMultipartPluginResult(PluginResult.Status.OK, photo));

} catch (Exception e) {
e.printStackTrace();
Expand Down Expand Up @@ -307,7 +333,7 @@ private Context getContext() {

}

private PluginResult createPluginResult(PluginResult.Status status, PhotoLibraryService.PictureData pictureData) {
private PluginResult createMultipartPluginResult(PluginResult.Status status, PhotoLibraryService.PictureData pictureData) {

return new PluginResult(status,
Arrays.asList(
Expand All @@ -331,4 +357,11 @@ private void requestAuthorization(boolean read, boolean write) {
cordova.requestPermissions(this, REQUEST_AUTHORIZATION_REQ_CODE, permissions.toArray(new String[0]));
}

private static JSONObject createGetLibraryResult(ArrayList<JSONObject> library, boolean isPartial) throws JSONException {
JSONObject result = new JSONObject();
result.put("isPartial", isPartial);
result.put("library", new JSONArray(library));
return result;
}

}
81 changes: 25 additions & 56 deletions src/android/PhotoLibraryService.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,9 @@
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.media.ExifInterface;
import android.media.ThumbnailUtils;
import android.net.Uri;
import android.os.Environment;
import android.provider.MediaStore;
Expand Down Expand Up @@ -62,9 +61,12 @@ public static PhotoLibraryService getInstance() {
return instance;
}

public ArrayList<JSONObject> getLibrary(Context context) throws JSONException {
public void getLibrary(Context context, MyRunnable partialCallback, MyRunnable completion) throws JSONException {

return queryLibrary(context, "");
// TODO: make use of partialCallback

String whereClause = "";
completion.run(queryLibrary(context, whereClause));

}

Expand All @@ -75,14 +77,6 @@ public PictureData getThumbnail(Context context, String photoId, int thumbnailWi
String imageURL = getImageURL(photoId);
File imageFile = new File(imageURL);

// if image is rotated by 90 or 270 degrees, swap provided width and height
boolean swapDimensions = getSwapFromPhotoID(photoId);
if (swapDimensions) {
int tempWidth = thumbnailWidth;
thumbnailWidth = thumbnailHeight;
thumbnailHeight = tempWidth;
}

// TODO: maybe it never worth using MediaStore.Images.Thumbnails.getThumbnail, as it returns sizes less than 512x384?
if (thumbnailWidth == 512 && thumbnailHeight == 384) { // In such case, thumbnail will be cached by MediaStore
int imageId = getImageId(photoId);
Expand Down Expand Up @@ -112,25 +106,24 @@ public PictureData getThumbnail(Context context, String photoId, int thumbnailWi

if (bitmap != null) {

// resize to exact size needed
Bitmap resizedBitmap = resizeBitmap(bitmap, thumbnailWidth, thumbnailHeight);
if (bitmap != resizedBitmap) {
// correct image orientation
int orientation = getImageOrientation(imageFile);
Bitmap rotatedBitmap = rotateImage(bitmap, orientation);
if (bitmap != rotatedBitmap) {
bitmap.recycle();
}

// correct image orientation
int orientation = getImageOrientation(imageFile);
Bitmap rotatedBitmap = rotateImage(resizedBitmap, orientation);
if (resizedBitmap != rotatedBitmap) {
resizedBitmap.recycle();
Bitmap thumbnailBitmap = ThumbnailUtils.extractThumbnail(rotatedBitmap, thumbnailWidth, thumbnailHeight);
if (rotatedBitmap != thumbnailBitmap) {
rotatedBitmap.recycle();
}

// TODO: cache bytes for performance

byte[] bytes = getJpegBytesFromBitmap(rotatedBitmap, quality);
byte[] bytes = getJpegBytesFromBitmap(thumbnailBitmap, quality);
String mimeType = "image/jpeg";

rotatedBitmap.recycle();
thumbnailBitmap.recycle();

return new PictureData(bytes, mimeType);

Expand Down Expand Up @@ -317,26 +310,22 @@ private ArrayList<JSONObject> queryLibrary(Context context, String whereClause)
System.err.println(queryResult);
} else {

boolean swapDimensions = false;

// swap width and height if needed
try {
int orientation = getImageOrientation(new File(queryResult.getString("nativeURL")));
if (isOrientationSwapsDimensions(orientation)) { // swap width and height
int tempWidth = queryResult.getInt("width");
queryResult.put("width", queryResult.getInt("height"));
queryResult.put("height", tempWidth);
swapDimensions = true;
}
} catch (IOException e) {
// Do nothing
}

// photoId is in format "imageid;imageurl;" or "imageid;imageurl;swap"
// photoId is in format "imageid;imageurl"
queryResult.put("id",
queryResult.get("id") + ";" +
queryResult.get("nativeURL") + ";" +
(swapDimensions ? "swap" : ""));
queryResult.get("nativeURL"));

results.add(queryResult);
}
Expand Down Expand Up @@ -441,33 +430,6 @@ private static String getImageURL(String photoId) {
return photoId.split(";")[1];
}

// photoId is in format "imageid;imageurl;[swap]"
private static boolean getSwapFromPhotoID(String photoId) {
String[] split = photoId.split(";");
return split.length >=3 && split[2].equals("swap");
}

// from http://stackoverflow.com/a/15441311/1691132
private static Bitmap resizeBitmap(Bitmap bitmap, int width, int height) {

if (bitmap.getWidth() == width && bitmap.getHeight() == height) {
return bitmap;
}

Bitmap result = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
float originalWidth = bitmap.getWidth(), originalHeight = bitmap.getHeight();
Canvas canvas = new Canvas(result);
float scale = width/originalWidth;
float xTranslation = 0.0f, yTranslation = (height - originalHeight * scale)/2.0f;
Matrix transformation = new Matrix();
transformation.postTranslate(xTranslation, yTranslation);
transformation.preScale(scale, scale);
Paint paint = new Paint();
paint.setFilterBitmap(true);
canvas.drawBitmap(bitmap, transformation, paint);
return result;
}

private static int getImageOrientation(File imageFile) throws IOException {

ExifInterface exif = new ExifInterface(imageFile.getAbsolutePath());
Expand All @@ -477,13 +439,14 @@ private static int getImageOrientation(File imageFile) throws IOException {

}

// see http://www.daveperrett.com/articles/2012/07/28/exif-orientation-handling-is-a-ghetto/
private static Bitmap rotateImage(Bitmap source, int orientation) {

Matrix matrix = new Matrix();

switch (orientation) {
case ExifInterface.ORIENTATION_NORMAL: // 1
return source;
return source;
case ExifInterface.ORIENTATION_FLIP_HORIZONTAL: // 2
matrix.setScale(-1, 1);
break;
Expand Down Expand Up @@ -619,4 +582,10 @@ private void saveMedia(CordovaInterface cordova, String url, String album, Map<S

}

public interface MyRunnable {

void run(ArrayList<JSONObject> data);

}

}
2 changes: 1 addition & 1 deletion src/browser/PhotoLibraryProxy.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ var photoLibraryProxy = {
files2Library(files).then(lib => {
library = lib;
removeFilesElement(filesElement);
success(library);
success({ library: library, isPartial: false });
});

}, false);
Expand Down
Loading

0 comments on commit 8866f07

Please sign in to comment.