Added support for taking snapshots on Android#625
Added support for taking snapshots on Android#625spikebrehm merged 6 commits intoreact-native-maps:masterfrom
Conversation
# Conflicts: # android/src/main/java/com/airbnb/android/react/maps/AirMapModule.java
|
Additionally, I'd like to make a proposal for a new and improved const promise = map.takeSnapshot({
width: 100, // optional, when omitted the view width is used
height: 100, // optional, when omitted the view height is used
result: 'file', // values: 'file', 'base64' (default: 'file')
format: 'png', // file-format: 'png', 'jpg' (default: 'png')
quality: 0.8, // compression-quality (only relevant for jpg) (default: 1.0)
});The result of the promise is a file-uri when And for backwards compatibility the function can be used the old way: map.takeSnapshot(300, 200, region, (err, result) => {
...
});I'd like to hear your thoughts on this. cheers, Hein |
|
This is awesome, great! I'm not super excited about the forking between iOS & Android, but it's opaque to the end developer so that's fine. I like your new new API. We can introduce that with deprecation warning and then cut over to the new API in the subsequent breaking release. |
| // Save the snapshot to disk | ||
| OutputStream outputStream = null; | ||
| try { | ||
| if ("file".equals(result)) { |
There was a problem hiding this comment.
Perhaps we can pull these strings out to CONSTANTs?
components/MapView.js
Outdated
| if (typeof width === 'object') { | ||
| options = width; | ||
| } else { | ||
| options = { |
There was a problem hiding this comment.
Can you add a deprecation warning for this case? That way we can remove the old API in the next breaking release.
There was a problem hiding this comment.
Done. I've updated the function so that it uses the old API when 4 arguments are specified (and only when on iOS). In all other cases the new API is used.
The deprecation message is generated using console.warn.
|
@spikebrehm Good calls! I'm gonna make those changes and let you know. What exactly did you mean by "forking between iOS & Android" ? |
# Conflicts: # components/MapView.js # ios/AirMaps/AIRMapManager.m
|
Alright, I've implemented the new takeSnapshot API. I've updated the iOS code to support optional The API now looks like this: |
|
Looks great! Should we try and get this merged in for the next release ? |
|
Any chance we can get this in for the next release? |
|
/cc @felipecsl You good with this? |
felipecsl
left a comment
There was a problem hiding this comment.
@christopherdro thanks for the heads up. I left some comments but overall looks good. Can approve once they are addressed!
| try { | ||
| outputStream.close(); | ||
| } catch (IOException e) { | ||
| e.printStackTrace(); |
There was a problem hiding this comment.
remove this line, just ignore the exception
There was a problem hiding this comment.
you could use closeQuietly here too
There was a problem hiding this comment.
Thanks so much for all the feedback @felipecsl . I will update the code accordingly and re-test and the update the PR. Hopefully this week, but probably next week.
Cheers, Hein
| outputStream = null; | ||
| byte[] bytes = ((ByteArrayOutputStream) outputStream).toByteArray(); | ||
| String data = Base64.encodeToString(bytes, Base64.NO_WRAP); | ||
| promise.resolve(data); |
There was a problem hiding this comment.
it would be nice if you could break down this logic into smaller methods. maybe takeBase64Snapshot() and takeFileSnapshot()?
| return; | ||
| } | ||
| view.map.snapshot(new GoogleMap.SnapshotReadyCallback() { | ||
| public void onSnapshotReady(Bitmap snapshot) { |
| if (snapshot != null && width != 0 && height != 0 && (width != snapshot.getWidth() || height != snapshot.getHeight())) { | ||
| snapshot = Bitmap.createScaledBitmap(snapshot, width, height, true); | ||
| } | ||
| if (snapshot == null) { |
There was a problem hiding this comment.
invert the order of these 2 ifs
| File tempFile = File.createTempFile("AirMapSnapshot", "." + format, context.getCacheDir()); | ||
| outputStream = new FileOutputStream(tempFile); | ||
| snapshot.compress(compressFormat, (int)(100.0 * quality), outputStream); | ||
| outputStream.close(); |
There was a problem hiding this comment.
close() can throw IOException. A common pattern is to create a method closeQuietly() that looks like this:
public static void closeQuietly(Closeable closeable) {
if (closeable == null) return;
try {
closeable.close();
} catch (IOException ignored) {
}
}| promise.resolve(data); | ||
| } | ||
| } | ||
| catch (Exception e) { |
There was a problem hiding this comment.
what exceptions are you catching exactly? can you be more specific or narrow down its scope?
|
Hi, it look a little longer but I've updated the code and addressed all the issues you pointed out. |
|
Thanks, will take a look soon. |
|
Awesome, thanks @spikebrehm |
|
@spikebrehm Any idea when the next release will be? |
spikebrehm
left a comment
There was a problem hiding this comment.
I added some non-blocking comments. I'll merge now and feel free to follow up.
Sorry for the delay!!! We've been very busy with the release of the new Airbnb Trips product.
| this._runCommand('takeSnapshot', [ | ||
| width || 0, | ||
| height || 0, | ||
| region || {}, |
There was a problem hiding this comment.
The region logic used to be:
const finalRegion = region || this.props.region || this.props.initialRegion;Shouldn't we preserve that here?
| * @param [config.region] Region to render (Only supported on iOS). | ||
| * @param [config.format] Encoding format ('png', 'jpg') (default: 'png'). | ||
| * @param [config.quality] Compression quality (only used for jpg) (default: 1.0). | ||
| * @param [config.result] Result format ('file', 'base64') (default: 'file'). |
| quality: args.quality || 1.0, | ||
| result: args.result || 'file', | ||
| }; | ||
| if ((config.format !== 'png') && |
There was a problem hiding this comment.
Consider pulling these out to constants at the top of the file, like:
const VALID_FORMATS = ['png', 'jpg'];
const VALID_RESULTS = ['file', 'base64'];
// ...
if (!VALID_FORMATS.include(config.format)) {
throw new Error('Invalid format specified');
}Also it may be nice to use the invariant package like:
invariant(VALID_FORMATS.include(config.format), 'Invalid format specified');|
Published as v0.12.2 |
|
Awesome, thanks so much @spikebrehm ! |
…esnapshot-android Added support for taking snapshots on Android
This pull request adds support for
takeSnapshoton Android.The takeSnapshot function was purposefully implemented on the AirMapModule class and not as a command on AirMapManager. The reason was that with using commands it is not possible to call the callback that is provided. The callback cannot be extracted from
ReadableMap. This made it unsuitable for this specific function. I've taking inspiration fromreact-native-view-shotand was able to implement the functionality on the AirMapModule instead.The region argument is not used on Android and the width and height are interpreted differently. This is because the
GoogleMaps.snapshotdoesn't take the size or region as input. This creates a feature difference with the iOS version.Additionally, I've made
takeSnapshotpromise aware. This means it now returns a promise with the resulting snapshot value (the uri) (or rejects the promise in case of an error).Android screenshot:
