Skip to content

Commit b15ad2c

Browse files
committed
Provide base64 encoded image in event
1 parent 355aa03 commit b15ad2c

File tree

5 files changed

+69
-19
lines changed

5 files changed

+69
-19
lines changed

Libraries/Components/TextInput/TextInput.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -120,9 +120,10 @@ export type EditingEvent = SyntheticEvent<
120120
>;
121121

122122
export type ImageInputEvent = SyntheticEvent<
123-
$Readonly<{|
123+
$ReadOnly<{|
124124
uri: string,
125125
linkUri: string,
126+
data: string,
126127
mime: string,
127128
|}>,
128129
>;
@@ -1034,8 +1035,8 @@ function InternalTextInput(props: Props): React.Node {
10341035
};
10351036

10361037
const _onImageInput = (event: ImageInputEvent) => {
1037-
this.props.onImageInput && this.props.onImageInput(event);
1038-
}
1038+
props.onImageInput && props.onImageInput(event);
1039+
};
10391040

10401041
let textInput = null;
10411042
let additionalTouchableProps: {|

ReactAndroid/src/main/java/com/facebook/react/views/textinput/ImageInputWatcher.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,5 @@
88
package com.facebook.react.views.textinput;
99

1010
public interface ImageInputWatcher {
11-
public void onImageInput(String uri, String linkUri, String mime);
11+
public void onImageInput(String uri, String linkUri, String data, String mime);
1212
}

ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditText.java

Lines changed: 56 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,11 @@
3434
import android.view.inputmethod.EditorInfo;
3535
import android.view.inputmethod.InputConnection;
3636
import android.view.inputmethod.InputMethodManager;
37+
import android.util.Base64;
38+
import android.util.Base64OutputStream;
3739
import androidx.annotation.Nullable;
3840
import androidx.appcompat.widget.AppCompatEditText;
41+
import androidx.core.os.BuildCompat;
3942
import androidx.core.view.AccessibilityDelegateCompat;
4043
import androidx.core.view.ViewCompat;
4144
import androidx.core.view.inputmethod.EditorInfoCompat;
@@ -52,6 +55,11 @@
5255
import com.facebook.react.views.text.TextAttributes;
5356
import com.facebook.react.views.text.TextInlineImageSpan;
5457
import com.facebook.react.views.view.ReactViewBackgroundManager;
58+
import java.io.ByteArrayOutputStream;
59+
import java.io.File;
60+
import java.io.FileInputStream;
61+
import java.io.InputStream;
62+
import java.io.IOException;
5563
import java.util.ArrayList;
5664

5765
/**
@@ -95,6 +103,7 @@ public class ReactEditText extends AppCompatEditText {
95103
private @Nullable SelectionWatcher mSelectionWatcher;
96104
private @Nullable ContentSizeWatcher mContentSizeWatcher;
97105
private @Nullable ScrollWatcher mScrollWatcher;
106+
private @Nullable ImageInputWatcher mImageInputWatcher;
98107
private final InternalKeyListener mKeyListener;
99108
private boolean mDetectScrollMovement = false;
100109
private boolean mOnKeyPress = false;
@@ -136,6 +145,7 @@ public ReactEditText(Context context) {
136145
mStagedInputType = getInputType();
137146
mKeyListener = new InternalKeyListener();
138147
mScrollWatcher = null;
148+
mImageInputWatcher = null;
139149
mTextAttributes = new TextAttributes();
140150

141151
applyTextAttributes();
@@ -227,10 +237,14 @@ protected void onScrollChanged(int horiz, int vert, int oldHoriz, int oldVert) {
227237
@Override
228238
public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
229239

230-
ReactContext reactContext = getReactContext(this);
240+
final ReactContext reactContext = getReactContext(this);
231241
InputConnection ic = super.onCreateInputConnection(outAttrs);
232242

233-
EditorInfoCompat.setContentMimeTypes(outAttrs, new String [] {"image/png", "image/gif", "image/jpg", "image/jpeg"});
243+
EditorInfoCompat.setContentMimeTypes(outAttrs, new String [] {"image/png",
244+
"image/gif",
245+
"image/jpg",
246+
"image/jpeg",
247+
"image/webp"});
234248

235249
final InputConnectionCompat.OnCommitContentListener callback =
236250
new InputConnectionCompat.OnCommitContentListener() {
@@ -247,14 +261,28 @@ public boolean onCommitContent(InputContentInfoCompat inputContentInfo, int flag
247261
}
248262
}
249263

264+
// Avoid loading the image into memory if its not going to be used
265+
if (mImageInputWatcher == null) {
266+
return false;
267+
}
268+
250269
String uri = null;
251270
String linkUri = null;
252271
String mime = null;
272+
String data = null;
253273

254-
// Get the uri to the local content
255274
Uri contentUri = inputContentInfo.getContentUri();
256275
if (contentUri != null) {
257276
uri = contentUri.toString();
277+
278+
// Load the data, we have to do this now otherwise we cannot release permissions
279+
try {
280+
data = loadFile(reactContext, contentUri);
281+
}
282+
catch(IOException e) {
283+
inputContentInfo.releasePermission();
284+
return false;
285+
}
258286
}
259287

260288
// Get the optional uri to web link
@@ -263,26 +291,24 @@ public boolean onCommitContent(InputContentInfoCompat inputContentInfo, int flag
263291
linkUri = link.toString();
264292
}
265293

266-
// Get the mime type of the image
267294
ClipDescription description = inputContentInfo.getDescription();
268295
if (description != null && description.getMimeTypeCount() > 0) {
269296
mime = description.getMimeType(0);
270297
}
271298

272299
if (mImageInputWatcher != null) {
273-
mImageInputWatcher.onImageInput(uri, linkUri, mime);
300+
mImageInputWatcher.onImageInput(uri, linkUri, data, mime);
274301
}
275302

276-
// TODO find better place to call this
277-
// inputContentInfo.releasePermission();
303+
// Releasing the permission means that the content at the uri is probably no longer accessible
304+
inputContentInfo.releasePermission();
278305

279306
return true;
280307
}
281308
};
282309

283310
InputConnection inputConnection = InputConnectionCompat.createWrapper(ic, outAttrs, callback);
284311

285-
>>>>>>> Add support for image keyboards to TextInput on Android
286312
if (inputConnection != null && mOnKeyPress) {
287313
inputConnection = new ReactEditTextInputConnectionWrapper(inputConnection, reactContext, this);
288314
}
@@ -355,6 +381,10 @@ public void setScrollWatcher(ScrollWatcher scrollWatcher) {
355381
mScrollWatcher = scrollWatcher;
356382
}
357383

384+
public void setImageInputWatcher(ImageInputWatcher imageInputWatcher) {
385+
mImageInputWatcher = imageInputWatcher;
386+
}
387+
358388
@Override
359389
public void setSelection(int start, int end) {
360390
// Skip setting the selection if the text wasn't set because of an out of date value.
@@ -864,6 +894,24 @@ protected void applyTextAttributes() {
864894
}
865895
}
866896

897+
private static String loadFile(Context context, Uri contentUri) throws IOException {
898+
InputStream inputStream = context.getContentResolver().openInputStream(contentUri);
899+
byte[] buffer = new byte[8192];
900+
int bytesRead;
901+
ByteArrayOutputStream output = new ByteArrayOutputStream();
902+
Base64OutputStream output64 = new Base64OutputStream(output, Base64.DEFAULT);
903+
try {
904+
while ((bytesRead = inputStream.read(buffer)) != -1) {
905+
output64.write(buffer, 0, bytesRead);
906+
}
907+
} catch (IOException e) {
908+
e.printStackTrace();
909+
}
910+
output64.close();
911+
912+
return output.toString();
913+
}
914+
867915
/**
868916
* This class will redirect *TextChanged calls to the listeners only in the case where the text is
869917
* changed by the user, and not explicitly set by JS.

ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputImageEvent.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,20 +18,23 @@
1818
*/
1919
public class ReactTextInputImageEvent extends Event<ReactTextInputImageEvent> {
2020

21-
public static final String EVENT_NAME = "topImage";
21+
public static final String EVENT_NAME = "topImageInput";
2222

2323
private String mUri;
2424
private String mLinkUri;
25+
private String mData;
2526
private String mMime;
2627

2728
public ReactTextInputImageEvent(
2829
int viewId,
2930
String uri,
3031
String linkUri,
32+
String data,
3133
String mime) {
3234
super(viewId);
3335
mUri = uri;
3436
mLinkUri = linkUri;
37+
mData = data;
3538
mMime = mime;
3639
}
3740

@@ -49,6 +52,7 @@ private WritableMap serializeEventData() {
4952
WritableMap eventData = Arguments.createMap();
5053
eventData.putString("uri", mUri);
5154
eventData.putString("linkUri", mLinkUri);
55+
eventData.putString("data", mData);
5256
eventData.putString("mime", mMime);
5357
eventData.putInt("target", getViewTag());
5458
return eventData;

ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputManager.java

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -166,11 +166,6 @@ public Map<String, Object> getExportedCustomBubblingEventTypeConstants() {
166166
MapBuilder.of(
167167
"phasedRegistrationNames",
168168
MapBuilder.of("bubbled", "onKeyPress", "captured", "onKeyPressCapture")))
169-
.put(
170-
"topImageInput",
171-
MapBuilder.of(
172-
"phasedRegistrationNames",
173-
MapBuilder.of("bubbled", "onImageInput", "captured", "onImageInputCapture")))
174169
.build();
175170
}
176171

@@ -181,6 +176,7 @@ public Map<String, Object> getExportedCustomDirectEventTypeConstants() {
181176
.put(
182177
ScrollEventType.getJSEventName(ScrollEventType.SCROLL),
183178
MapBuilder.of("registrationName", "onScroll"))
179+
.put("topImageInput", MapBuilder.of("registrationName", "onImageInput"))
184180
.build();
185181
}
186182

@@ -1194,12 +1190,13 @@ public ReactImageInputWatcher(ReactEditText editText) {
11941190
}
11951191

11961192
@Override
1197-
public void onImageInput(String uri, String linkUri, String mime) {
1193+
public void onImageInput(String uri, String linkUri, String data, String mime) {
11981194
if (uri != null) {
11991195
ReactTextInputImageEvent event = new ReactTextInputImageEvent(
12001196
mReactEditText.getId(),
12011197
uri,
12021198
linkUri,
1199+
data,
12031200
mime);
12041201

12051202
mEventDispatcher.dispatchEvent(event);

0 commit comments

Comments
 (0)