Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 44 additions & 5 deletions Examples/UIExplorer/SnapshotExample.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ var {
} = React;

var ScreenshotExample = React.createClass({
_view: null,

getInitialState() {
return {
uri: undefined,
Expand All @@ -33,29 +35,66 @@ var ScreenshotExample = React.createClass({

render() {
return (
<View>
<View style={style.root} ref={ref => { this._view = ref; }}>
<Image
source={{uri: 'http://facebook.github.io/react/img/logo_og.png'}}
style={style.image}
/>
<Text onPress={this.takeScreenshot} style={style.button}>
Click to take a screenshot
Screenshot the App (JPG 50%)
</Text>
<Text onPress={this.takeScreenshot2} style={style.button}>
Screenshot this View (PNG)
</Text>
<Image style={style.image} source={{uri: this.state.uri}}/>
<Text onPress={this.takeScreenshot3} style={style.button}>
Screenshot the App & resize to 32x48
</Text>
<Image style={style.result} source={{uri: this.state.uri}}/>
</View>
);
},

takeScreenshot() {
UIManager
.takeSnapshot('window', {format: 'jpeg', quality: 0.8}) // See UIManager.js for options
.takeSnapshot('window', {format: 'jpeg', quality: 0.5 }) // See UIManager.js for options
.then((uri) => this.setState({uri}))
.catch((error) => alert(error));
},

takeScreenshot2() {
UIManager
.takeSnapshot(this._view)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

property _view Property not found in React component

.then((uri) => this.setState({uri}))
.catch((error) => alert(error));
},

takeScreenshot3() {
UIManager
.takeSnapshot('window', { width: 32, height: 48 })
.then((uri) => this.setState({uri}))
.catch((error) => alert(error));
}
});

var style = StyleSheet.create({
root: {
backgroundColor: '#fff',
position: 'relative',
},
button: {
marginBottom: 10,
margin: 5,
fontWeight: '500',
backgroundColor: 'transparent',
textDecorationLine: 'underline'
},
image: {
width: 40,
height: 40,
position: 'absolute',
top: 0,
right: 0,
},
result: {
flex: 1,
height: 300,
resizeMode: 'contain',
Expand Down
4 changes: 4 additions & 0 deletions Examples/UIExplorer/UIExplorerList.android.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ var ComponentExamples: Array<UIExplorerExample> = [
key: 'ScrollViewSimpleExample',
module: require('./ScrollViewSimpleExample'),
},
{
key: 'SnapshotExample',
module: require('./SnapshotExample'),
},
{
key: 'StatusBarExample',
module: require('./StatusBarExample'),
Expand Down
9 changes: 5 additions & 4 deletions Libraries/Utilities/UIManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,15 +49,15 @@ const _takeSnapshot = UIManager.takeSnapshot;
* Capture an image of the screen, window or an individual view. The image
* will be stored in a temporary file that will only exist for as long as the
* app is running.
*
*
* The `view` argument can be the literal string `window` if you want to
* capture the entire window, or it can be a reference to a specific
* React Native component.
*
* The `options` argument may include:
* - width/height (number) - the width and height of the image to capture.
* - format (string) - either 'png' or 'jpeg'. Defaults to 'png'.
* - quality (number) - the quality when using jpeg. 0.0 - 1.0 (default).
* - format (string) - either 'png' or 'jpg'/'jpeg' or 'webm' (Android). Defaults to 'png'.
* - quality (number) - the quality. 0.0 - 1.0 (default). (only available on lossy formats like jpeg)
*
* Returns a Promise.
* @platform ios
Expand All @@ -78,7 +78,8 @@ UIManager.takeSnapshot = async function(
if (typeof view !== 'number' && view !== 'window') {
view = findNodeHandle(view) || 'window';
}
return _takeSnapshot(view, options);
const isTag = typeof view === 'number';
return _takeSnapshot(isTag ? null : view, isTag ? view : 0, options || {});
};

module.exports = UIManager;
11 changes: 6 additions & 5 deletions React/Modules/RCTUIManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -1226,7 +1226,8 @@ static void RCTMeasureLayout(RCTShadowView *view,
callback(@[results]);
}

RCT_EXPORT_METHOD(takeSnapshot:(id /* NSString or NSNumber */)target
RCT_EXPORT_METHOD(takeSnapshot:(NSString *)target
withTag:(nonnull NSNumber *)tag
withOptions:(NSDictionary *)options
resolve:(RCTPromiseResolveBlock)resolve
reject:(RCTPromiseRejectBlock)reject)
Expand All @@ -1235,10 +1236,10 @@ static void RCTMeasureLayout(RCTShadowView *view,

// Get view
UIView *view;
if (target == nil || [target isEqual:@"window"]) {
if (target && [target isEqual:@"window"]) {
view = RCTKeyWindow();
} else if ([target isKindOfClass:[NSNumber class]]) {
view = viewRegistry[target];
} else {
view = viewRegistry[tag];
if (!view) {
RCTLogError(@"No view found with reactTag: %@", target);
return;
Expand Down Expand Up @@ -1269,7 +1270,7 @@ static void RCTMeasureLayout(RCTShadowView *view,
NSData *data;
if ([format isEqualToString:@"png"]) {
data = UIImagePNGRepresentation(image);
} else if ([format isEqualToString:@"jpeg"]) {
} else if ([format isEqualToString:@"jpeg"]||[format isEqualToString:@"jpg"]) {
CGFloat quality = [RCTConvert CGFloat:options[@"quality"] ?: @1];
data = UIImageJPEGRepresentation(image, quality);
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@
import com.facebook.systrace.Systrace;
import com.facebook.systrace.SystraceMessage;

import java.io.FileOutputStream;
import java.io.IOException;

/**
* Delegate of {@link UIManagerModule} that owns the native view hierarchy and mapping between
* native view names used in JS and corresponding instances of {@link ViewManager}. The
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package com.facebook.react.uimanager;

import javax.annotation.Nullable;
import android.graphics.Bitmap;
import android.view.View;
import java.io.FileOutputStream;

/**
* Snapshot utility class allow to screenshot a view.
*/
public class Snapshot {

static final String ERROR_UNABLE_TO_SNAPSHOT = "E_UNABLE_TO_SNAPSHOT";

Bitmap.CompressFormat format;
double quality;
Integer width;
Integer height;

public Snapshot(Bitmap.CompressFormat format, double quality, @Nullable Integer width, @Nullable Integer height) {
this.format = format;
this.quality = quality;
this.width = width;
this.height = height;
}

/**
* Write the captured image to a file output stream.
* @param view the view to screenshot
* @param out the file output stream to write
*/
public void captureViewToFileOutputStream (View view, FileOutputStream out) {
Bitmap bitmap = captureView(view);
if (bitmap == null) {
throw new RuntimeException("Impossible to snapshot the view");
}
bitmap.compress(format, (int)(100.0 * quality), out);
}

/**
* Screenshot a view and return the captured bitmap.
* @param view the view to capture
* @return the screenshot or null if it failed.
*/
public Bitmap captureView (View view) {
int w = view.getWidth();
int h = view.getHeight();
if (w <= 0 || h <= 0) return null;
Bitmap bitmap = view.getDrawingCache();
if (bitmap == null)
view.setDrawingCacheEnabled(true);
bitmap = view.getDrawingCache();
if (width != null && height != null && (width != w || height != h)) {
bitmap = Bitmap.createScaledBitmap(bitmap, width, height, true);
}
return bitmap;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

import javax.annotation.Nullable;

import java.io.File;
import java.util.Arrays;
import java.util.List;

Expand All @@ -18,6 +19,8 @@
import com.facebook.react.animation.Animation;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.Callback;
import com.facebook.react.bridge.JSApplicationIllegalArgumentException;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
Expand Down Expand Up @@ -729,4 +732,25 @@ protected void applyUpdatesRecursive(
}
cssNode.markUpdateSeen();
}

public void takeSnapshot(String target, int tag, Snapshot snapshot, File destFile, Promise promise) {
int t;
if (target == null) {
t = tag;
}
else {
if (target.equals("window")) {
t = mShadowNodeRegistry.getRootTag(0);
}
else {
throw new JSApplicationIllegalArgumentException("Invalid snapshot target: "+target);
}
}
mOperationsQueue.enqueueTakeSnapshot(
t,
snapshot,
destFile,
promise
);
}
}
Loading