From d3192a8e5a20fdc82c5d7c2a647fb6b49429373a Mon Sep 17 00:00:00 2001 From: Thomas Welfley Date: Fri, 29 Jun 2018 15:11:21 -0700 Subject: [PATCH 01/12] Remove unused react import --- index.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/index.js b/index.js index 44044f0..f613ce4 100644 --- a/index.js +++ b/index.js @@ -1,6 +1,3 @@ - -import React from 'react' - import { NativeModules } from 'react-native' const RNGooglePlacesNative = NativeModules.RNGooglePlaces From 258194cf507ae8c502f4c429b54cdd6a1830aebd Mon Sep 17 00:00:00 2001 From: Thomas Welfley Date: Fri, 29 Jun 2018 15:12:18 -0700 Subject: [PATCH 02/12] index.js: Add missing EOF newline Files should always end with new lines to avoid introducing extraneous diffs. --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index f613ce4..befba2f 100644 --- a/index.js +++ b/index.js @@ -51,4 +51,4 @@ class RNGooglePlaces { } } -export default new RNGooglePlaces() \ No newline at end of file +export default new RNGooglePlaces() From e030a82458f973806d27001bef4b14522ac3a4f7 Mon Sep 17 00:00:00 2001 From: Thomas Welfley Date: Fri, 29 Jun 2018 15:20:22 -0700 Subject: [PATCH 03/12] Android: Add getPlacePhoto and getPlacePhotos This introduces preliminary support for 1. retrieving photo metadata for a place and 2. persisting the associated photos to disk where they can be accessed via react-native using an `` tag. Example usage via JS: ```javascript const placePhotos = await RNGooglePlaces.getPlacePhotos(placeId) const firstPhotoUri = await RNGooglePlaces.getPlacePhoto(placePhotos[0]) console.info(firstPhotoUri) ``` --- .../rngoogleplaces/RNGooglePlacesModule.java | 132 +++++++++++++++++- index.js | 9 ++ 2 files changed, 139 insertions(+), 2 deletions(-) diff --git a/android/src/main/java/com/arttitude360/reactnative/rngoogleplaces/RNGooglePlacesModule.java b/android/src/main/java/com/arttitude360/reactnative/rngoogleplaces/RNGooglePlacesModule.java index 0c354e2..14068b2 100644 --- a/android/src/main/java/com/arttitude360/reactnative/rngoogleplaces/RNGooglePlacesModule.java +++ b/android/src/main/java/com/arttitude360/reactnative/rngoogleplaces/RNGooglePlacesModule.java @@ -3,6 +3,8 @@ import android.app.Activity; import android.content.Intent; import android.content.IntentSender; +import android.graphics.Bitmap; +import android.net.Uri; import android.os.Bundle; import android.text.TextUtils; import android.util.Log; @@ -34,6 +36,10 @@ import com.google.android.gms.location.places.PlaceBuffer; import com.google.android.gms.location.places.PlaceLikelihood; import com.google.android.gms.location.places.PlaceLikelihoodBuffer; +import com.google.android.gms.location.places.PlacePhotoMetadata; +import com.google.android.gms.location.places.PlacePhotoMetadataBuffer; +import com.google.android.gms.location.places.PlacePhotoMetadataResult; +import com.google.android.gms.location.places.PlacePhotoResult; import com.google.android.gms.location.places.Places; import com.google.android.gms.location.places.ui.PlaceAutocomplete; import com.google.android.gms.location.places.ui.PlacePicker; @@ -41,10 +47,13 @@ import com.google.android.gms.maps.model.LatLng; import com.google.maps.android.SphericalUtil; -import java.util.Objects; +import java.io.IOException; +import java.io.FileOutputStream; +import java.io.File; +import java.util.concurrent.TimeUnit; import java.util.ArrayList; import java.util.List; -import java.util.concurrent.TimeUnit; +import java.util.Objects; public class RNGooglePlacesModule extends ReactContextBaseJavaModule implements ActivityEventListener, GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener { @@ -391,6 +400,81 @@ public void onResult(PlaceLikelihoodBuffer likelyPlaces) { }); } + @ReactMethod + public void getPlacePhotos(final String placeID, final Promise promise) { + this.pendingPromise = promise; + + if (this.isClientDisconnected()) return; + + Places.GeoDataApi.getPlacePhotos(mGoogleApiClient, placeID).setResultCallback(new ResultCallback() { + @Override + public void onResult(PlacePhotoMetadataResult placePhotosResult) { + if (!placePhotosResult.getStatus().isSuccess()) { + promise.reject( + "E_PLACE_PHOTOS_ERROR", + new Error("Error making place lookup API call: " + placePhotosResult.getStatus().toString())); + return; + } + + PlacePhotoMetadataBuffer placePhotos = placePhotosResult.getPhotoMetadata(); + WritableArray photosList = Arguments.createArray(); + + if (placePhotos.getCount() > 0) { + photosList = photosArray(placeID, placePhotos); + } + + placePhotos.release(); + promise.resolve(photosList); + } + }); + } + + @ReactMethod + public void getPlacePhoto(final String placeID, final Integer index, final Promise promise) { + this.pendingPromise = promise; + + if (this.isClientDisconnected()) return; + + Places.GeoDataApi.getPlacePhotos(mGoogleApiClient, placeID).setResultCallback(new ResultCallback() { + @Override + public void onResult(PlacePhotoMetadataResult placePhotosResult) { + if (!placePhotosResult.getStatus().isSuccess()) { + promise.reject( + "E_PLACE_PHOTOS_ERROR", + new Error("Error making place lookup API call: " + placePhotosResult.getStatus().toString())); + return; + } + + PlacePhotoMetadataBuffer placePhotos = placePhotosResult.getPhotoMetadata(); + PlacePhotoMetadata photoMeta = placePhotos.get(index); + getPhotoUriFromMeta(photoMeta, promise); + placePhotos.release(); + } + }); + } + + private void getPhotoUriFromMeta(PlacePhotoMetadata photoMeta, final Promise promise) { + photoMeta.getPhoto(mGoogleApiClient).setResultCallback(new ResultCallback() { + @Override + public void onResult(PlacePhotoResult placePhoto) { + if (!placePhoto.getStatus().isSuccess()) { + promise.reject( + "E_PLACE_PHOTOS_ERROR", + new Error("Error retrieving place photo API call: " + placePhoto.getStatus().toString())); + return; + } + + Bitmap photoData = placePhoto.getBitmap(); + + try { + promise.resolve(getURIForBitmap(photoData)); + } catch (Exception e) { + promise.reject("E_PHOTO_PERSIST_ERROR", "Error saving photo: " + e.getMessage()); + } + } + }); + } + private WritableArray processLookupByIDsPlaces(final PlaceBuffer places) { WritableArray resultList = new WritableNativeArray(); @@ -454,6 +538,50 @@ private WritableMap propertiesMapForPlace(Place place) { return map; } + private WritableArray photosArray(final String placeID, final PlacePhotoMetadataBuffer photos) { + WritableArray resultList = new WritableNativeArray(); + + for (PlacePhotoMetadata photo : photos) { + resultList.pushMap(propertiesMapForPhotoMetadata(placeID, photo)); + } + + return resultList; + } + + private WritableMap propertiesMapForPhotoMetadata(final String placeID, final PlacePhotoMetadata photo) { + WritableMap map = Arguments.createMap(); + CharSequence attributions = photo.getAttributions(); + + map.putString("placeID", placeID); + + if (!TextUtils.isEmpty(attributions)) { + map.putString("attributions", attributions.toString()); + } + + map.putInt("maxWidth", photo.getMaxWidth()); + map.putInt("maxHeight", photo.getMaxHeight()); + + return map; + } + + private String getURIForBitmap(Bitmap bitmap) throws IOException { + Activity currentActivity = this.reactContext.getCurrentActivity(); + // We should probably use getCacheDir() instead of getFilesDir(), + // but I can't seem to successfully read from the files I attempt to + // write to this directory from RN + File file = File.createTempFile( + "gplacephoto", ".jpg", currentActivity.getFilesDir()); + FileOutputStream out = currentActivity.openFileOutput( + file.getName(), this.reactContext.MODE_PRIVATE); + + bitmap.compress(Bitmap.CompressFormat.JPEG, 100, out); + + out.flush(); + out.close(); + + return Uri.fromFile(file).toString(); + } + private AutocompleteFilter getFilterType(String type, String country) { AutocompleteFilter mappedFilter; diff --git a/index.js b/index.js index befba2f..86f6d3c 100644 --- a/index.js +++ b/index.js @@ -49,6 +49,15 @@ class RNGooglePlaces { getCurrentPlace() { return RNGooglePlacesNative.getCurrentPlace() } + + async getPlacePhotos(placeID) { + const photos = await RNGooglePlacesNative.getPlacePhotos(placeID) + return photos.map((photo, idx) => ({photoID: idx, ...photo})) + } + + getPlacePhoto({placeID, photoID}) { + return RNGooglePlacesNative.getPlacePhoto(placeID, photoID) + } } export default new RNGooglePlaces() From 2f03075de0356725aa4e2d6f802a5e71ec42faa2 Mon Sep 17 00:00:00 2001 From: Thomas Welfley Date: Sun, 1 Jul 2018 10:11:33 -0700 Subject: [PATCH 04/12] getPlacePhoto: write image to cache directory Place photos shouldn't be persisted indefinitely. We write them to a cache directory so that the OS knows that it can clean these files up if necessary. This also removes specifying context.MODE_PRIVATE when creating the FileOutputStream, which doesn't seem necessary. --- .../reactnative/rngoogleplaces/RNGooglePlacesModule.java | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/android/src/main/java/com/arttitude360/reactnative/rngoogleplaces/RNGooglePlacesModule.java b/android/src/main/java/com/arttitude360/reactnative/rngoogleplaces/RNGooglePlacesModule.java index 14068b2..c9d7fd0 100644 --- a/android/src/main/java/com/arttitude360/reactnative/rngoogleplaces/RNGooglePlacesModule.java +++ b/android/src/main/java/com/arttitude360/reactnative/rngoogleplaces/RNGooglePlacesModule.java @@ -566,13 +566,9 @@ private WritableMap propertiesMapForPhotoMetadata(final String placeID, final Pl private String getURIForBitmap(Bitmap bitmap) throws IOException { Activity currentActivity = this.reactContext.getCurrentActivity(); - // We should probably use getCacheDir() instead of getFilesDir(), - // but I can't seem to successfully read from the files I attempt to - // write to this directory from RN File file = File.createTempFile( - "gplacephoto", ".jpg", currentActivity.getFilesDir()); - FileOutputStream out = currentActivity.openFileOutput( - file.getName(), this.reactContext.MODE_PRIVATE); + "gPlacePhoto", ".jpg", currentActivity.getCacheDir()); + FileOutputStream out = new FileOutputStream(file); bitmap.compress(Bitmap.CompressFormat.JPEG, 100, out); From 7f6a8ef8d22aa1ca72ed937a60a82a95dd95ab09 Mon Sep 17 00:00:00 2001 From: Thomas Welfley Date: Sun, 1 Jul 2018 10:12:51 -0700 Subject: [PATCH 05/12] getURIForBitmap: return a URI instead of String This makes the return type consistent with the method's current naming. --- .../reactnative/rngoogleplaces/RNGooglePlacesModule.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/android/src/main/java/com/arttitude360/reactnative/rngoogleplaces/RNGooglePlacesModule.java b/android/src/main/java/com/arttitude360/reactnative/rngoogleplaces/RNGooglePlacesModule.java index c9d7fd0..3cd63a8 100644 --- a/android/src/main/java/com/arttitude360/reactnative/rngoogleplaces/RNGooglePlacesModule.java +++ b/android/src/main/java/com/arttitude360/reactnative/rngoogleplaces/RNGooglePlacesModule.java @@ -467,7 +467,7 @@ public void onResult(PlacePhotoResult placePhoto) { Bitmap photoData = placePhoto.getBitmap(); try { - promise.resolve(getURIForBitmap(photoData)); + promise.resolve(getURIForBitmap(photoData).toString()); } catch (Exception e) { promise.reject("E_PHOTO_PERSIST_ERROR", "Error saving photo: " + e.getMessage()); } @@ -564,7 +564,7 @@ private WritableMap propertiesMapForPhotoMetadata(final String placeID, final Pl return map; } - private String getURIForBitmap(Bitmap bitmap) throws IOException { + private URI getURIForBitmap(Bitmap bitmap) throws IOException { Activity currentActivity = this.reactContext.getCurrentActivity(); File file = File.createTempFile( "gPlacePhoto", ".jpg", currentActivity.getCacheDir()); @@ -575,7 +575,7 @@ private String getURIForBitmap(Bitmap bitmap) throws IOException { out.flush(); out.close(); - return Uri.fromFile(file).toString(); + return file.toURI(); } private AutocompleteFilter getFilterType(String type, String country) { From d0c5aa1b790a00ca06454bbd211850fa125ba67f Mon Sep 17 00:00:00 2001 From: Thomas Welfley Date: Mon, 2 Jul 2018 12:40:29 -0700 Subject: [PATCH 06/12] getUriForBitmap: correct invalid return type File.toURI returns a URI, not a Uri. --- .../reactnative/rngoogleplaces/RNGooglePlacesModule.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/android/src/main/java/com/arttitude360/reactnative/rngoogleplaces/RNGooglePlacesModule.java b/android/src/main/java/com/arttitude360/reactnative/rngoogleplaces/RNGooglePlacesModule.java index 3cd63a8..2553efe 100644 --- a/android/src/main/java/com/arttitude360/reactnative/rngoogleplaces/RNGooglePlacesModule.java +++ b/android/src/main/java/com/arttitude360/reactnative/rngoogleplaces/RNGooglePlacesModule.java @@ -467,7 +467,7 @@ public void onResult(PlacePhotoResult placePhoto) { Bitmap photoData = placePhoto.getBitmap(); try { - promise.resolve(getURIForBitmap(photoData).toString()); + promise.resolve(getUriForBitmap(photoData).toString()); } catch (Exception e) { promise.reject("E_PHOTO_PERSIST_ERROR", "Error saving photo: " + e.getMessage()); } @@ -564,7 +564,7 @@ private WritableMap propertiesMapForPhotoMetadata(final String placeID, final Pl return map; } - private URI getURIForBitmap(Bitmap bitmap) throws IOException { + private Uri getUriForBitmap(Bitmap bitmap) throws IOException { Activity currentActivity = this.reactContext.getCurrentActivity(); File file = File.createTempFile( "gPlacePhoto", ".jpg", currentActivity.getCacheDir()); @@ -575,7 +575,7 @@ private URI getURIForBitmap(Bitmap bitmap) throws IOException { out.flush(); out.close(); - return file.toURI(); + return Uri.fromFile(file); } private AutocompleteFilter getFilterType(String type, String country) { From 4cc2e39e94dfc564e60900059e52cda3192b7873 Mon Sep 17 00:00:00 2001 From: Thomas Welfley Date: Fri, 6 Jul 2018 10:42:36 -0700 Subject: [PATCH 07/12] Make use of ResultCallbacks and ResultTransform ResultCallbacks (vs ResultCallback) allows us to factor out status checks because we can define specific handlers for success/failure cases. ResultTransform allows us to neatly chain API calls without needing to pass promise references down deeply into multiple methods. --- .../rngoogleplaces/RNGooglePlacesModule.java | 100 ++++++++---------- 1 file changed, 47 insertions(+), 53 deletions(-) diff --git a/android/src/main/java/com/arttitude360/reactnative/rngoogleplaces/RNGooglePlacesModule.java b/android/src/main/java/com/arttitude360/reactnative/rngoogleplaces/RNGooglePlacesModule.java index 2553efe..f84e881 100644 --- a/android/src/main/java/com/arttitude360/reactnative/rngoogleplaces/RNGooglePlacesModule.java +++ b/android/src/main/java/com/arttitude360/reactnative/rngoogleplaces/RNGooglePlacesModule.java @@ -28,6 +28,8 @@ import com.google.android.gms.common.api.GoogleApiClient; import com.google.android.gms.common.api.PendingResult; import com.google.android.gms.common.api.ResultCallback; +import com.google.android.gms.common.api.ResultCallbacks; +import com.google.android.gms.common.api.ResultTransform; import com.google.android.gms.common.api.Status; import com.google.android.gms.location.places.AutocompleteFilter; import com.google.android.gms.location.places.AutocompletePrediction; @@ -406,27 +408,28 @@ public void getPlacePhotos(final String placeID, final Promise promise) { if (this.isClientDisconnected()) return; - Places.GeoDataApi.getPlacePhotos(mGoogleApiClient, placeID).setResultCallback(new ResultCallback() { - @Override - public void onResult(PlacePhotoMetadataResult placePhotosResult) { - if (!placePhotosResult.getStatus().isSuccess()) { - promise.reject( - "E_PLACE_PHOTOS_ERROR", - new Error("Error making place lookup API call: " + placePhotosResult.getStatus().toString())); - return; - } + Places.GeoDataApi.getPlacePhotos(mGoogleApiClient, placeID) + .setResultCallback(new ResultCallbacks() { + @Override + public void onSuccess(PlacePhotoMetadataResult placePhotosResult) { + PlacePhotoMetadataBuffer placePhotos = placePhotosResult.getPhotoMetadata(); + WritableArray photosList = Arguments.createArray(); - PlacePhotoMetadataBuffer placePhotos = placePhotosResult.getPhotoMetadata(); - WritableArray photosList = Arguments.createArray(); + if (placePhotos.getCount() > 0) { + photosList = photosArray(placeID, placePhotos); + } - if (placePhotos.getCount() > 0) { - photosList = photosArray(placeID, placePhotos); + placePhotos.release(); + promise.resolve(photosList); } - placePhotos.release(); - promise.resolve(photosList); - } - }); + @Override + public void onFailure(Status status) { + promise.reject( + "E_PLACE_PHOTOS_ERROR", + new Error("Error making place photo meta API call: " + status.toString())); + } + }); } @ReactMethod @@ -435,44 +438,35 @@ public void getPlacePhoto(final String placeID, final Integer index, final Promi if (this.isClientDisconnected()) return; - Places.GeoDataApi.getPlacePhotos(mGoogleApiClient, placeID).setResultCallback(new ResultCallback() { - @Override - public void onResult(PlacePhotoMetadataResult placePhotosResult) { - if (!placePhotosResult.getStatus().isSuccess()) { - promise.reject( - "E_PLACE_PHOTOS_ERROR", - new Error("Error making place lookup API call: " + placePhotosResult.getStatus().toString())); - return; - } + Places.GeoDataApi.getPlacePhotos(mGoogleApiClient, placeID) + .then(new ResultTransform() { + @Override + public PendingResult onSuccess(PlacePhotoMetadataResult placePhotosResult) { + PlacePhotoMetadataBuffer placePhotos = placePhotosResult.getPhotoMetadata(); + PlacePhotoMetadata photoMeta = placePhotos.get(index); - PlacePhotoMetadataBuffer placePhotos = placePhotosResult.getPhotoMetadata(); - PlacePhotoMetadata photoMeta = placePhotos.get(index); - getPhotoUriFromMeta(photoMeta, promise); - placePhotos.release(); - } - }); - } + return photoMeta.getPhoto(mGoogleApiClient); + } + }) + .andFinally(new ResultCallbacks() { + @Override + public void onSuccess(PlacePhotoResult placePhotoResult) { + Bitmap photoData = placePhotoResult.getBitmap(); - private void getPhotoUriFromMeta(PlacePhotoMetadata photoMeta, final Promise promise) { - photoMeta.getPhoto(mGoogleApiClient).setResultCallback(new ResultCallback() { - @Override - public void onResult(PlacePhotoResult placePhoto) { - if (!placePhoto.getStatus().isSuccess()) { - promise.reject( - "E_PLACE_PHOTOS_ERROR", - new Error("Error retrieving place photo API call: " + placePhoto.getStatus().toString())); - return; - } - - Bitmap photoData = placePhoto.getBitmap(); - - try { - promise.resolve(getUriForBitmap(photoData).toString()); - } catch (Exception e) { - promise.reject("E_PHOTO_PERSIST_ERROR", "Error saving photo: " + e.getMessage()); - } - } - }); + try { + promise.resolve(getUriForBitmap(photoData).toString()); + } catch (Exception e) { + promise.reject("E_PHOTO_PERSIST_ERROR", "Error saving photo: " + e.getMessage()); + } + } + + @Override + public void onFailure(Status status) { + promise.reject( + "E_PLACE_PHOTOS_ERROR", + new Error("Error retrieving place photo: " + status.toString())); + } + }); } private WritableArray processLookupByIDsPlaces(final PlaceBuffer places) { From a255a499b8ff9d4b34edb222bd1bf2a09c8cf50b Mon Sep 17 00:00:00 2001 From: Thomas Welfley Date: Fri, 6 Jul 2018 11:23:40 -0700 Subject: [PATCH 08/12] Add getScaledPhoto support for Android --- .../rngoogleplaces/RNGooglePlacesModule.java | 56 +++++++++++-------- index.js | 4 ++ 2 files changed, 37 insertions(+), 23 deletions(-) diff --git a/android/src/main/java/com/arttitude360/reactnative/rngoogleplaces/RNGooglePlacesModule.java b/android/src/main/java/com/arttitude360/reactnative/rngoogleplaces/RNGooglePlacesModule.java index f84e881..15ac9c0 100644 --- a/android/src/main/java/com/arttitude360/reactnative/rngoogleplaces/RNGooglePlacesModule.java +++ b/android/src/main/java/com/arttitude360/reactnative/rngoogleplaces/RNGooglePlacesModule.java @@ -6,6 +6,7 @@ import android.graphics.Bitmap; import android.net.Uri; import android.os.Bundle; +import android.support.annotation.Nullable; import android.text.TextUtils; import android.util.Log; @@ -434,39 +435,48 @@ public void onFailure(Status status) { @ReactMethod public void getPlacePhoto(final String placeID, final Integer index, final Promise promise) { + this.getScaledPlacePhoto(placeID, index, null, null, promise); + } + + @ReactMethod + public void getScaledPlacePhoto(final String placeID, final Integer index, @Nullable final Integer width, @Nullable final Integer height, final Promise promise) { this.pendingPromise = promise; if (this.isClientDisconnected()) return; Places.GeoDataApi.getPlacePhotos(mGoogleApiClient, placeID) - .then(new ResultTransform() { - @Override - public PendingResult onSuccess(PlacePhotoMetadataResult placePhotosResult) { - PlacePhotoMetadataBuffer placePhotos = placePhotosResult.getPhotoMetadata(); - PlacePhotoMetadata photoMeta = placePhotos.get(index); + .then(new ResultTransform() { + @Override + public PendingResult onSuccess(PlacePhotoMetadataResult placePhotosResult) { + PlacePhotoMetadataBuffer placePhotos = placePhotosResult.getPhotoMetadata(); + PlacePhotoMetadata photoMeta = placePhotos.get(index); - return photoMeta.getPhoto(mGoogleApiClient); + if (width != null && height != null) { + return photoMeta.getScaledPhoto(mGoogleApiClient, width, height); } - }) - .andFinally(new ResultCallbacks() { - @Override - public void onSuccess(PlacePhotoResult placePhotoResult) { - Bitmap photoData = placePhotoResult.getBitmap(); - try { - promise.resolve(getUriForBitmap(photoData).toString()); - } catch (Exception e) { - promise.reject("E_PHOTO_PERSIST_ERROR", "Error saving photo: " + e.getMessage()); - } - } + return photoMeta.getPhoto(mGoogleApiClient); + } + }) + .andFinally(new ResultCallbacks() { + @Override + public void onSuccess(PlacePhotoResult placePhotoResult) { + Bitmap photoData = placePhotoResult.getBitmap(); - @Override - public void onFailure(Status status) { - promise.reject( - "E_PLACE_PHOTOS_ERROR", - new Error("Error retrieving place photo: " + status.toString())); + try { + promise.resolve(getUriForBitmap(photoData).toString()); + } catch (Exception e) { + promise.reject("E_PHOTO_PERSIST_ERROR", "Error saving photo: " + e.getMessage()); } - }); + } + + @Override + public void onFailure(Status status) { + promise.reject( + "E_PLACE_PHOTOS_ERROR", + new Error("Error retrieving place photo: " + status.toString())); + } + }); } private WritableArray processLookupByIDsPlaces(final PlaceBuffer places) { diff --git a/index.js b/index.js index 86f6d3c..52a161d 100644 --- a/index.js +++ b/index.js @@ -58,6 +58,10 @@ class RNGooglePlaces { getPlacePhoto({placeID, photoID}) { return RNGooglePlacesNative.getPlacePhoto(placeID, photoID) } + + getScaledPlacePhoto({placeID, photoID}, width, height) { + return RNGooglePlacesNative.getScaledPlacePhoto(placeID, photoID, width, height) + } } export default new RNGooglePlaces() From 6724345e8e3018214e47073a3cb768a5e41828bc Mon Sep 17 00:00:00 2001 From: Thomas Welfley Date: Fri, 6 Jul 2018 11:31:14 -0700 Subject: [PATCH 09/12] Rename getPlacePhotos to getPlacePhotoMetadata --- .../reactnative/rngoogleplaces/RNGooglePlacesModule.java | 2 +- index.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/android/src/main/java/com/arttitude360/reactnative/rngoogleplaces/RNGooglePlacesModule.java b/android/src/main/java/com/arttitude360/reactnative/rngoogleplaces/RNGooglePlacesModule.java index 15ac9c0..566d3f1 100644 --- a/android/src/main/java/com/arttitude360/reactnative/rngoogleplaces/RNGooglePlacesModule.java +++ b/android/src/main/java/com/arttitude360/reactnative/rngoogleplaces/RNGooglePlacesModule.java @@ -404,7 +404,7 @@ public void onResult(PlaceLikelihoodBuffer likelyPlaces) { } @ReactMethod - public void getPlacePhotos(final String placeID, final Promise promise) { + public void getPlacePhotosMetadata(final String placeID, final Promise promise) { this.pendingPromise = promise; if (this.isClientDisconnected()) return; diff --git a/index.js b/index.js index 52a161d..c02e92c 100644 --- a/index.js +++ b/index.js @@ -50,8 +50,8 @@ class RNGooglePlaces { return RNGooglePlacesNative.getCurrentPlace() } - async getPlacePhotos(placeID) { - const photos = await RNGooglePlacesNative.getPlacePhotos(placeID) + async getPlacePhotosMetadata(placeID) { + const photos = await RNGooglePlacesNative.getPlacePhotosMetadata(placeID) return photos.map((photo, idx) => ({photoID: idx, ...photo})) } From a87467c824701b81830518c47bb08d68802cdbed Mon Sep 17 00:00:00 2001 From: Thomas Welfley Date: Fri, 6 Jul 2018 15:06:43 -0700 Subject: [PATCH 10/12] getPlacePhotosMetadata: simplify 0 photos case --- .../rngoogleplaces/RNGooglePlacesModule.java | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/android/src/main/java/com/arttitude360/reactnative/rngoogleplaces/RNGooglePlacesModule.java b/android/src/main/java/com/arttitude360/reactnative/rngoogleplaces/RNGooglePlacesModule.java index 566d3f1..a54cc21 100644 --- a/android/src/main/java/com/arttitude360/reactnative/rngoogleplaces/RNGooglePlacesModule.java +++ b/android/src/main/java/com/arttitude360/reactnative/rngoogleplaces/RNGooglePlacesModule.java @@ -414,11 +414,7 @@ public void getPlacePhotosMetadata(final String placeID, final Promise promise) @Override public void onSuccess(PlacePhotoMetadataResult placePhotosResult) { PlacePhotoMetadataBuffer placePhotos = placePhotosResult.getPhotoMetadata(); - WritableArray photosList = Arguments.createArray(); - - if (placePhotos.getCount() > 0) { - photosList = photosArray(placeID, placePhotos); - } + WritableArray photosList = photosArray(placeID, placePhotos); placePhotos.release(); promise.resolve(photosList); @@ -427,8 +423,8 @@ public void onSuccess(PlacePhotoMetadataResult placePhotosResult) { @Override public void onFailure(Status status) { promise.reject( - "E_PLACE_PHOTOS_ERROR", - new Error("Error making place photo meta API call: " + status.toString())); + "E_PLACE_PHOTOS_ERROR", + new Error("Error making place photo meta API call: " + status.toString())); } }); } From a6c69540d8c5fafc586872bf276d5981f3091fb0 Mon Sep 17 00:00:00 2001 From: Thomas Welfley Date: Fri, 6 Jul 2018 15:08:55 -0700 Subject: [PATCH 11/12] getScaledPhoto: Call .release() on buffer I'd believed that this wasn't required when using .then(...).andFinally(...) pattern, but according to the console warnings this was resulting in, it is indeed required. --- .../rngoogleplaces/RNGooglePlacesModule.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/android/src/main/java/com/arttitude360/reactnative/rngoogleplaces/RNGooglePlacesModule.java b/android/src/main/java/com/arttitude360/reactnative/rngoogleplaces/RNGooglePlacesModule.java index a54cc21..44cbcea 100644 --- a/android/src/main/java/com/arttitude360/reactnative/rngoogleplaces/RNGooglePlacesModule.java +++ b/android/src/main/java/com/arttitude360/reactnative/rngoogleplaces/RNGooglePlacesModule.java @@ -446,12 +446,16 @@ public void getScaledPlacePhoto(final String placeID, final Integer index, @Null public PendingResult onSuccess(PlacePhotoMetadataResult placePhotosResult) { PlacePhotoMetadataBuffer placePhotos = placePhotosResult.getPhotoMetadata(); PlacePhotoMetadata photoMeta = placePhotos.get(index); + PendingResult pendingPhotoResult; if (width != null && height != null) { - return photoMeta.getScaledPhoto(mGoogleApiClient, width, height); + pendingPhotoResult = photoMeta.getScaledPhoto(mGoogleApiClient, width, height); } - return photoMeta.getPhoto(mGoogleApiClient); + pendingPhotoResult = photoMeta.getPhoto(mGoogleApiClient); + placePhotos.release(); + + return pendingPhotoResult; } }) .andFinally(new ResultCallbacks() { @@ -462,7 +466,7 @@ public void onSuccess(PlacePhotoResult placePhotoResult) { try { promise.resolve(getUriForBitmap(photoData).toString()); } catch (Exception e) { - promise.reject("E_PHOTO_PERSIST_ERROR", "Error saving photo: " + e.getMessage()); + promise.reject("E_PHOTO_PERSIST_ERROR", e); } } From 669cf38a0ab12bfb1eb0cdebbb7f19f6ff153024 Mon Sep 17 00:00:00 2001 From: Thomas Welfley Date: Fri, 6 Jul 2018 15:09:21 -0700 Subject: [PATCH 12/12] Add getPlacePhotos support --- .../rngoogleplaces/RNGooglePlacesModule.java | 68 +++++++++++++++++++ index.js | 5 ++ 2 files changed, 73 insertions(+) diff --git a/android/src/main/java/com/arttitude360/reactnative/rngoogleplaces/RNGooglePlacesModule.java b/android/src/main/java/com/arttitude360/reactnative/rngoogleplaces/RNGooglePlacesModule.java index 44cbcea..5f3cf55 100644 --- a/android/src/main/java/com/arttitude360/reactnative/rngoogleplaces/RNGooglePlacesModule.java +++ b/android/src/main/java/com/arttitude360/reactnative/rngoogleplaces/RNGooglePlacesModule.java @@ -26,6 +26,8 @@ import com.google.android.gms.common.GoogleApiAvailability; import com.google.android.gms.common.GooglePlayServicesNotAvailableException; import com.google.android.gms.common.GooglePlayServicesRepairableException; +import com.google.android.gms.common.api.Batch; +import com.google.android.gms.common.api.BatchResult; import com.google.android.gms.common.api.GoogleApiClient; import com.google.android.gms.common.api.PendingResult; import com.google.android.gms.common.api.ResultCallback; @@ -403,6 +405,72 @@ public void onResult(PlaceLikelihoodBuffer likelyPlaces) { }); } + @ReactMethod + public void getPlacePhotos(final String placeID, final Promise promise) { + final List photoMetaList = new ArrayList<>(); + final List pendingPhotoResults = new ArrayList<>(); + + this.pendingPromise = promise; + + if (this.isClientDisconnected()) return; + + Places.GeoDataApi.getPlacePhotos(mGoogleApiClient, placeID) + .then(new ResultTransform() { + @Override + public PendingResult onSuccess(PlacePhotoMetadataResult placePhotosResult) { + PlacePhotoMetadataBuffer placePhotos = placePhotosResult.getPhotoMetadata(); + final Batch.Builder batchBuilder = new Batch.Builder(mGoogleApiClient); + + for (PlacePhotoMetadata photoMeta : placePhotos) { + PendingResult photoResult = photoMeta.getPhoto(mGoogleApiClient); + + photoMetaList.add(propertiesMapForPhotoMetadata(placeID, photoMeta)); + pendingPhotoResults.add(photoResult); + batchBuilder.add(photoResult); + } + + placePhotos.release(); + + return batchBuilder.build(); + } + }) + .andFinally(new ResultCallbacks() { + @Override + public void onSuccess(BatchResult photoResults) { + WritableArray nativeResults = new WritableNativeArray(); + int idx = 0; + + for (PendingResult pendingResult : pendingPhotoResults) { + WritableMap photoMeta = photoMetaList.get(idx); + PlacePhotoResult photoResult = pendingResult.await(0, TimeUnit.SECONDS); + Bitmap photoData = photoResult.getBitmap(); + String uri; + + try { + uri = getUriForBitmap(photoData).toString(); + } catch (Exception e) { + promise.reject("E_PHOTO_PERSIST_ERROR", e); + return; + } + + photoMeta.putString("uri", uri); + nativeResults.pushMap(photoMeta); + + ++idx; + } + + promise.resolve(nativeResults); + } + + @Override + public void onFailure(Status status) { + promise.reject( + "E_PLACE_PHOTOS_ERROR", + new Error("Error retrieving place photos: " + status.toString())); + } + }); + } + @ReactMethod public void getPlacePhotosMetadata(final String placeID, final Promise promise) { this.pendingPromise = promise; diff --git a/index.js b/index.js index c02e92c..2e1941e 100644 --- a/index.js +++ b/index.js @@ -50,6 +50,11 @@ class RNGooglePlaces { return RNGooglePlacesNative.getCurrentPlace() } + async getPlacePhotos(placeID) { + const photos = await RNGooglePlacesNative.getPlacePhotos(placeID) + return photos.map((photo, idx) => ({photoID: idx, ...photo})) + } + async getPlacePhotosMetadata(placeID) { const photos = await RNGooglePlacesNative.getPlacePhotosMetadata(placeID) return photos.map((photo, idx) => ({photoID: idx, ...photo}))