34
34
import android .view .inputmethod .EditorInfo ;
35
35
import android .view .inputmethod .InputConnection ;
36
36
import android .view .inputmethod .InputMethodManager ;
37
+ import android .util .Base64 ;
38
+ import android .util .Base64OutputStream ;
37
39
import androidx .annotation .Nullable ;
38
40
import androidx .appcompat .widget .AppCompatEditText ;
41
+ import androidx .core .os .BuildCompat ;
39
42
import androidx .core .view .AccessibilityDelegateCompat ;
40
43
import androidx .core .view .ViewCompat ;
41
44
import androidx .core .view .inputmethod .EditorInfoCompat ;
52
55
import com .facebook .react .views .text .TextAttributes ;
53
56
import com .facebook .react .views .text .TextInlineImageSpan ;
54
57
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 ;
55
63
import java .util .ArrayList ;
56
64
57
65
/**
@@ -95,6 +103,7 @@ public class ReactEditText extends AppCompatEditText {
95
103
private @ Nullable SelectionWatcher mSelectionWatcher ;
96
104
private @ Nullable ContentSizeWatcher mContentSizeWatcher ;
97
105
private @ Nullable ScrollWatcher mScrollWatcher ;
106
+ private @ Nullable ImageInputWatcher mImageInputWatcher ;
98
107
private final InternalKeyListener mKeyListener ;
99
108
private boolean mDetectScrollMovement = false ;
100
109
private boolean mOnKeyPress = false ;
@@ -136,6 +145,7 @@ public ReactEditText(Context context) {
136
145
mStagedInputType = getInputType ();
137
146
mKeyListener = new InternalKeyListener ();
138
147
mScrollWatcher = null ;
148
+ mImageInputWatcher = null ;
139
149
mTextAttributes = new TextAttributes ();
140
150
141
151
applyTextAttributes ();
@@ -227,10 +237,14 @@ protected void onScrollChanged(int horiz, int vert, int oldHoriz, int oldVert) {
227
237
@ Override
228
238
public InputConnection onCreateInputConnection (EditorInfo outAttrs ) {
229
239
230
- ReactContext reactContext = getReactContext (this );
240
+ final ReactContext reactContext = getReactContext (this );
231
241
InputConnection ic = super .onCreateInputConnection (outAttrs );
232
242
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" });
234
248
235
249
final InputConnectionCompat .OnCommitContentListener callback =
236
250
new InputConnectionCompat .OnCommitContentListener () {
@@ -247,14 +261,28 @@ public boolean onCommitContent(InputContentInfoCompat inputContentInfo, int flag
247
261
}
248
262
}
249
263
264
+ // Avoid loading the image into memory if its not going to be used
265
+ if (mImageInputWatcher == null ) {
266
+ return false ;
267
+ }
268
+
250
269
String uri = null ;
251
270
String linkUri = null ;
252
271
String mime = null ;
272
+ String data = null ;
253
273
254
- // Get the uri to the local content
255
274
Uri contentUri = inputContentInfo .getContentUri ();
256
275
if (contentUri != null ) {
257
276
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
+ }
258
286
}
259
287
260
288
// Get the optional uri to web link
@@ -263,26 +291,24 @@ public boolean onCommitContent(InputContentInfoCompat inputContentInfo, int flag
263
291
linkUri = link .toString ();
264
292
}
265
293
266
- // Get the mime type of the image
267
294
ClipDescription description = inputContentInfo .getDescription ();
268
295
if (description != null && description .getMimeTypeCount () > 0 ) {
269
296
mime = description .getMimeType (0 );
270
297
}
271
298
272
299
if (mImageInputWatcher != null ) {
273
- mImageInputWatcher .onImageInput (uri , linkUri , mime );
300
+ mImageInputWatcher .onImageInput (uri , linkUri , data , mime );
274
301
}
275
302
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 ();
278
305
279
306
return true ;
280
307
}
281
308
};
282
309
283
310
InputConnection inputConnection = InputConnectionCompat .createWrapper (ic , outAttrs , callback );
284
311
285
- >>>>>>> Add support for image keyboards to TextInput on Android
286
312
if (inputConnection != null && mOnKeyPress ) {
287
313
inputConnection = new ReactEditTextInputConnectionWrapper (inputConnection , reactContext , this );
288
314
}
@@ -355,6 +381,10 @@ public void setScrollWatcher(ScrollWatcher scrollWatcher) {
355
381
mScrollWatcher = scrollWatcher ;
356
382
}
357
383
384
+ public void setImageInputWatcher (ImageInputWatcher imageInputWatcher ) {
385
+ mImageInputWatcher = imageInputWatcher ;
386
+ }
387
+
358
388
@ Override
359
389
public void setSelection (int start , int end ) {
360
390
// Skip setting the selection if the text wasn't set because of an out of date value.
@@ -864,6 +894,24 @@ protected void applyTextAttributes() {
864
894
}
865
895
}
866
896
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
+
867
915
/**
868
916
* This class will redirect *TextChanged calls to the listeners only in the case where the text is
869
917
* changed by the user, and not explicitly set by JS.
0 commit comments