Skip to content
This repository has been archived by the owner on Jan 27, 2023. It is now read-only.

Commit

Permalink
Full GIF Insertion
Browse files Browse the repository at this point in the history
  • Loading branch information
zlshames committed May 14, 2022
1 parent d1b9a69 commit 7985974
Show file tree
Hide file tree
Showing 6 changed files with 189 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.json.JSONArray;
Expand Down Expand Up @@ -315,6 +316,14 @@ public void previous(int inputClientId) {
"TextInputClient.performAction", Arrays.asList(inputClientId, "TextInputAction.previous"));
}

/** Instructs flutter to commit content back to the text channel */
public void commitContent(int inputClientId, Map<String, Object> content) {
Log.v(TAG, "Sending 'commitContent' message.");
channel.invokeMethod(
"TextInputClient.performAction",
Arrays.asList(inputClientId, "TextInputAction.commitContent", content));
}

/** Instructs Flutter to execute an "unspecified" action. */
public void unspecifiedAction(int inputClientId) {
Log.v(TAG, "Sending 'unspecified' message.");
Expand Down Expand Up @@ -450,6 +459,19 @@ public static Configuration fromJson(@NonNull JSONObject json)
}
}
final Integer inputAction = inputActionFromTextInputAction(inputActionName);

// Build list of content commit mime types from the passed in JSON list
List<String> contentList = new ArrayList<String>();
JSONArray contentCommitMimeTypes =
json.isNull("contentCommitMimeTypes")
? null
: json.getJSONArray("contentCommitMimeTypes");
if (contentCommitMimeTypes != null) {
for (int i = 0; i < contentCommitMimeTypes.length(); i++) {
contentList.add(contentCommitMimeTypes.optString(i));
}
}

return new Configuration(
json.optBoolean("obscureText"),
json.optBoolean("autocorrect", true),
Expand All @@ -461,6 +483,7 @@ public static Configuration fromJson(@NonNull JSONObject json)
inputAction,
json.isNull("actionLabel") ? null : json.getString("actionLabel"),
json.isNull("autofill") ? null : Autofill.fromJson(json.getJSONObject("autofill")),
contentList.toArray(new String[contentList.size()]),
fields);
}

Expand Down Expand Up @@ -618,6 +641,7 @@ public Autofill(
@Nullable public final Integer inputAction;
@Nullable public final String actionLabel;
@Nullable public final Autofill autofill;
@Nullable public final String[] contentCommitMimeTypes;
@Nullable public final Configuration[] fields;

public Configuration(
Expand All @@ -631,6 +655,7 @@ public Configuration(
@Nullable Integer inputAction,
@Nullable String actionLabel,
@Nullable Autofill autofill,
@Nullable String[] contentCommitMimeTypes,
@Nullable Configuration[] fields) {
this.obscureText = obscureText;
this.autocorrect = autocorrect;
Expand All @@ -642,6 +667,7 @@ public Configuration(
this.inputAction = inputAction;
this.actionLabel = actionLabel;
this.autofill = autofill;
this.contentCommitMimeTypes = contentCommitMimeTypes;
this.fields = fields;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@

package io.flutter.plugin.editing;

import android.annotation.TargetApi;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.text.DynamicLayout;
Expand All @@ -22,11 +24,19 @@
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.ExtractedText;
import android.view.inputmethod.ExtractedTextRequest;
import android.view.inputmethod.InputContentInfo;
import android.view.inputmethod.InputMethodManager;
import androidx.annotation.RequiresApi;
import androidx.core.view.inputmethod.InputConnectionCompat;
import io.flutter.Log;
import io.flutter.embedding.android.KeyboardManager;
import io.flutter.embedding.engine.FlutterJNI;
import io.flutter.embedding.engine.systemchannels.TextInputChannel;
import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;

class InputConnectionAdaptor extends BaseInputConnection
implements ListenableEditingState.EditingStateWatcher {
Expand Down Expand Up @@ -473,6 +483,69 @@ public boolean performEditorAction(int actionCode) {
return true;
}

@Override
@TargetApi(25)
@RequiresApi(25)
public boolean commitContent(InputContentInfo inputContentInfo, int flags, Bundle opts) {
// Ensure permission is granted.
if (Build.VERSION.SDK_INT >= 25
&& (flags & InputConnectionCompat.INPUT_CONTENT_GRANT_READ_URI_PERMISSION) != 0) {
try {
inputContentInfo.requestPermission();
} catch (Exception e) {
return false;
}
} else {
return false;
}

if (inputContentInfo.getDescription().getMimeTypeCount() > 0) {
inputContentInfo.requestPermission();

final Uri uri = inputContentInfo.getContentUri();
final String mimeType = inputContentInfo.getDescription().getMimeType(0);
Context context = mFlutterView.getContext();
Boolean retval = false;

try {
final InputStream is = context.getContentResolver().openInputStream(uri);
final byte[] data = this.readStreamFully(is, 64 * 1024);

final Map<String, Object> obj = new HashMap<>();
obj.put("mimeType", mimeType);
obj.put("data", data);
obj.put("uri", uri != null ? uri.toString() : null);

// Commit the content to the text input channel
textInputChannel.commitContent(mClient, obj);
retval = true;
} catch (FileNotFoundException ex) {
} catch (Exception ex) {
} finally {
inputContentInfo.releasePermission();
}
return retval;
}
return false;
}

public byte[] readStreamFully(InputStream is, int blocksize) {
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();

byte[] buffer = new byte[blocksize];
while (true) {
int len = is.read(buffer);
if (len == -1) break;
baos.write(buffer, 0, len);
}
return baos.toByteArray();
} catch (Exception e) {
}

return new byte[0];
}

// -------- Start: ListenableEditingState watcher implementation -------
@Override
public void didChangeEditingState(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.core.view.inputmethod.EditorInfoCompat;
import io.flutter.Log;
import io.flutter.embedding.android.KeyboardManager;
import io.flutter.embedding.engine.systemchannels.TextInputChannel;
Expand Down Expand Up @@ -295,6 +296,11 @@ public InputConnection createInputConnection(
}
outAttrs.imeOptions |= enterAction;

if (configuration.contentCommitMimeTypes != null) {
String[] imgTypeString = configuration.contentCommitMimeTypes;
EditorInfoCompat.setContentMimeTypes(outAttrs, imgTypeString);
}

InputConnectionAdaptor connection =
new InputConnectionAdaptor(
view, inputTarget.id, textInputChannel, keyboardManager, mEditable, outAttrs);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,11 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import android.content.ClipDescription;
import android.content.ClipboardManager;
import android.content.Context;
import android.content.res.AssetManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.text.InputType;
Expand All @@ -31,6 +33,7 @@
import android.view.inputmethod.ExtractedText;
import android.view.inputmethod.ExtractedTextRequest;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputContentInfo;
import android.view.inputmethod.InputMethodManager;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.ibm.icu.lang.UCharacter;
Expand Down Expand Up @@ -173,6 +176,44 @@ public void testPerformContextMenuAction_paste() {
assertTrue(editable.toString().startsWith(textToBePasted));
}

@Test
public void testCommitContent() throws JSONException {
View testView = new View(RuntimeEnvironment.application);
int client = 0;
FlutterJNI mockFlutterJNI = mock(FlutterJNI.class);
DartExecutor dartExecutor = spy(new DartExecutor(mockFlutterJNI, mock(AssetManager.class)));
TextInputChannel textInputChannel = new TextInputChannel(dartExecutor);
ListenableEditingState editable = sampleEditable(0, 0);
InputConnectionAdaptor adaptor =
new InputConnectionAdaptor(
testView,
client,
textInputChannel,
mockKeyboardManager,
editable,
null,
mockFlutterJNI);
adaptor.commitContent(
new InputContentInfo(
Uri.parse("content://mock/uri/test/commitContent"),
new ClipDescription("commitContent test", new String[] {"image/png"})),
0,
null);

ArgumentCaptor<String> channelCaptor = ArgumentCaptor.forClass(String.class);
ArgumentCaptor<ByteBuffer> bufferCaptor = ArgumentCaptor.forClass(ByteBuffer.class);
verify(dartExecutor, times(1)).send(channelCaptor.capture(), bufferCaptor.capture(), isNull());
assertEquals("flutter/textinput", channelCaptor.getValue());
verifyMethodCall(
bufferCaptor.getValue(),
"TextInputClient.performAction",
new String[] {
"0",
"TextInputAction.commitContent",
"{\"data\":[],\"mimeType\":\"image\\/png\",\"uri\":\"content:\\/\\/mock\\/uri\\/test\\/commitContent\"}"
});
}

@Test
public void testPerformPrivateCommand_dataIsNull() throws JSONException {
View testView = new View(RuntimeEnvironment.application);
Expand Down
Loading

0 comments on commit 7985974

Please sign in to comment.