Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 12122ea

Browse files
authored
Reland - [Android] Add support for text processing actions (#46817)
## Description This is a reland of #44579 which was reverted in #46788. This reland adds a check into `onActivityResult` in order to return early if the result is related to an unknown request code (aka the result is related to a request sent by another plugin). It also adds one test that simulates receiving such an unknown request code. ## Related Issue Android engine side for flutter/flutter#107603 ## Tests Adds 4 tests.
1 parent 0a0b4d4 commit 12122ea

File tree

7 files changed

+626
-0
lines changed

7 files changed

+626
-0
lines changed

ci/licenses_golden/licenses_flutter

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3094,6 +3094,7 @@ ORIGIN: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/syst
30943094
ORIGIN: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/NavigationChannel.java + ../../../flutter/LICENSE
30953095
ORIGIN: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/PlatformChannel.java + ../../../flutter/LICENSE
30963096
ORIGIN: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/PlatformViewsChannel.java + ../../../flutter/LICENSE
3097+
ORIGIN: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/ProcessTextChannel.java + ../../../flutter/LICENSE
30973098
ORIGIN: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/RestorationChannel.java + ../../../flutter/LICENSE
30983099
ORIGIN: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/SpellCheckChannel.java + ../../../flutter/LICENSE
30993100
ORIGIN: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/SystemChannel.java + ../../../flutter/LICENSE
@@ -3136,6 +3137,7 @@ ORIGIN: ../../../flutter/shell/platform/android/io/flutter/plugin/platform/Platf
31363137
ORIGIN: ../../../flutter/shell/platform/android/io/flutter/plugin/platform/PlatformViewsController.java + ../../../flutter/LICENSE
31373138
ORIGIN: ../../../flutter/shell/platform/android/io/flutter/plugin/platform/SingleViewPresentation.java + ../../../flutter/LICENSE
31383139
ORIGIN: ../../../flutter/shell/platform/android/io/flutter/plugin/platform/VirtualDisplayController.java + ../../../flutter/LICENSE
3140+
ORIGIN: ../../../flutter/shell/platform/android/io/flutter/plugin/text/ProcessTextPlugin.java + ../../../flutter/LICENSE
31393141
ORIGIN: ../../../flutter/shell/platform/android/io/flutter/util/HandlerCompat.java + ../../../flutter/LICENSE
31403142
ORIGIN: ../../../flutter/shell/platform/android/io/flutter/util/PathUtils.java + ../../../flutter/LICENSE
31413143
ORIGIN: ../../../flutter/shell/platform/android/io/flutter/util/Preconditions.java + ../../../flutter/LICENSE
@@ -5868,6 +5870,7 @@ FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/system
58685870
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/NavigationChannel.java
58695871
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/PlatformChannel.java
58705872
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/PlatformViewsChannel.java
5873+
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/ProcessTextChannel.java
58715874
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/RestorationChannel.java
58725875
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/SettingsChannel.java
58735876
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/SpellCheckChannel.java
@@ -5915,6 +5918,7 @@ FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/platform/Platfor
59155918
FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/platform/SingleViewPresentation.java
59165919
FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/platform/SurfaceTexturePlatformViewRenderTarget.java
59175920
FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/platform/VirtualDisplayController.java
5921+
FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/text/ProcessTextPlugin.java
59185922
FILE: ../../../flutter/shell/platform/android/io/flutter/util/HandlerCompat.java
59195923
FILE: ../../../flutter/shell/platform/android/io/flutter/util/PathUtils.java
59205924
FILE: ../../../flutter/shell/platform/android/io/flutter/util/Preconditions.java

shell/platform/android/AndroidManifest.xml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,12 @@
2424
</intent-filter>
2525
</activity>
2626
</application>
27+
28+
<!-- Required for io.flutter.plugin.text.ProcessTextPlugin to query activities that can process text. -->
29+
<queries>
30+
<intent>
31+
<action android:name="android.intent.action.PROCESS_TEXT" />
32+
<data android:mimeType="text/plain" />
33+
</intent>
34+
</queries>
2735
</manifest>

shell/platform/android/BUILD.gn

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,7 @@ android_java_sources = [
275275
"io/flutter/embedding/engine/systemchannels/NavigationChannel.java",
276276
"io/flutter/embedding/engine/systemchannels/PlatformChannel.java",
277277
"io/flutter/embedding/engine/systemchannels/PlatformViewsChannel.java",
278+
"io/flutter/embedding/engine/systemchannels/ProcessTextChannel.java",
278279
"io/flutter/embedding/engine/systemchannels/RestorationChannel.java",
279280
"io/flutter/embedding/engine/systemchannels/SettingsChannel.java",
280281
"io/flutter/embedding/engine/systemchannels/SpellCheckChannel.java",
@@ -322,6 +323,7 @@ android_java_sources = [
322323
"io/flutter/plugin/platform/SingleViewPresentation.java",
323324
"io/flutter/plugin/platform/SurfaceTexturePlatformViewRenderTarget.java",
324325
"io/flutter/plugin/platform/VirtualDisplayController.java",
326+
"io/flutter/plugin/text/ProcessTextPlugin.java",
325327
"io/flutter/util/HandlerCompat.java",
326328
"io/flutter/util/PathUtils.java",
327329
"io/flutter/util/Preconditions.java",

shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,15 @@
3131
import io.flutter.embedding.engine.systemchannels.MouseCursorChannel;
3232
import io.flutter.embedding.engine.systemchannels.NavigationChannel;
3333
import io.flutter.embedding.engine.systemchannels.PlatformChannel;
34+
import io.flutter.embedding.engine.systemchannels.ProcessTextChannel;
3435
import io.flutter.embedding.engine.systemchannels.RestorationChannel;
3536
import io.flutter.embedding.engine.systemchannels.SettingsChannel;
3637
import io.flutter.embedding.engine.systemchannels.SpellCheckChannel;
3738
import io.flutter.embedding.engine.systemchannels.SystemChannel;
3839
import io.flutter.embedding.engine.systemchannels.TextInputChannel;
3940
import io.flutter.plugin.localization.LocalizationPlugin;
4041
import io.flutter.plugin.platform.PlatformViewsController;
42+
import io.flutter.plugin.text.ProcessTextPlugin;
4143
import io.flutter.util.ViewUtils;
4244
import java.util.HashSet;
4345
import java.util.List;
@@ -95,6 +97,7 @@ public class FlutterEngine implements ViewUtils.DisplayUpdater {
9597
@NonNull private final NavigationChannel navigationChannel;
9698
@NonNull private final RestorationChannel restorationChannel;
9799
@NonNull private final PlatformChannel platformChannel;
100+
@NonNull private final ProcessTextChannel processTextChannel;
98101
@NonNull private final SettingsChannel settingsChannel;
99102
@NonNull private final SpellCheckChannel spellCheckChannel;
100103
@NonNull private final SystemChannel systemChannel;
@@ -329,6 +332,7 @@ public FlutterEngine(
329332
mouseCursorChannel = new MouseCursorChannel(dartExecutor);
330333
navigationChannel = new NavigationChannel(dartExecutor);
331334
platformChannel = new PlatformChannel(dartExecutor);
335+
processTextChannel = new ProcessTextChannel(dartExecutor, context.getPackageManager());
332336
restorationChannel = new RestorationChannel(dartExecutor, waitForRestorationData);
333337
settingsChannel = new SettingsChannel(dartExecutor);
334338
spellCheckChannel = new SpellCheckChannel(dartExecutor);
@@ -384,6 +388,9 @@ public FlutterEngine(
384388
}
385389

386390
ViewUtils.calculateMaximumDisplayMetrics(context, this);
391+
392+
ProcessTextPlugin processTextPlugin = new ProcessTextPlugin(this.getProcessTextChannel());
393+
this.pluginRegistry.add(processTextPlugin);
387394
}
388395

389396
private void attachToJni() {
@@ -545,6 +552,12 @@ public PlatformChannel getPlatformChannel() {
545552
return platformChannel;
546553
}
547554

555+
/** System channel that sends text processing requests from Flutter to Android. */
556+
@NonNull
557+
public ProcessTextChannel getProcessTextChannel() {
558+
return processTextChannel;
559+
}
560+
548561
/**
549562
* System channel to exchange restoration data between framework and engine.
550563
*
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
package io.flutter.embedding.engine.systemchannels;
6+
7+
import android.content.pm.PackageManager;
8+
import androidx.annotation.NonNull;
9+
import androidx.annotation.Nullable;
10+
import io.flutter.embedding.engine.dart.DartExecutor;
11+
import io.flutter.plugin.common.MethodCall;
12+
import io.flutter.plugin.common.MethodChannel;
13+
import io.flutter.plugin.common.StandardMethodCodec;
14+
import java.util.ArrayList;
15+
import java.util.Map;
16+
17+
/**
18+
* {@link ProcessTextChannel} is a platform channel that is used by the framework to initiate text
19+
* processing feature in the embedding and for the embedding to send back the results.
20+
*
21+
* <p>When the framework needs to query the list of text processing actions (for instance to expose
22+
* them in the selected text context menu), it will send to the embedding the message {@code
23+
* ProcessText.queryTextActions}. In response, the {@link io.flutter.plugin.text.ProcessTextPlugin}
24+
* will return a map of all activities that can process text. The map keys are generated IDs and the
25+
* values are the activities labels. On the first request, the {@link
26+
* io.flutter.plugin.text.ProcessTextPlugin} will make a call to Android's package manager to query
27+
* all activities that can be performed for the {@code Intent.ACTION_PROCESS_TEXT} intent.
28+
*
29+
* <p>When a text processing action has to be executed, the framework will send to the embedding the
30+
* message {@code ProcessText.processTextAction} with the {@code int id} of the choosen text action
31+
* and the {@code String} of text to process as arguments. In response, the {@link
32+
* io.flutter.plugin.text.ProcessTextPlugin} will make a call to the Android application activity to
33+
* start the activity exposing the text action. The {@link io.flutter.plugin.text.ProcessTextPlugin}
34+
* will return the processed text if there is one, or null if the activity did not return a
35+
* transformed text.
36+
*
37+
* <p>{@link io.flutter.plugin.text.ProcessTextPlugin} implements {@link ProcessTextMethodHandler}
38+
* that parses incoming messages from Flutter.
39+
*/
40+
public class ProcessTextChannel {
41+
private static final String TAG = "ProcessTextChannel";
42+
private static final String CHANNEL_NAME = "flutter/processtext";
43+
private static final String METHOD_QUERY_TEXT_ACTIONS = "ProcessText.queryTextActions";
44+
private static final String METHOD_PROCESS_TEXT_ACTION = "ProcessText.processTextAction";
45+
46+
public final MethodChannel channel;
47+
public final PackageManager packageManager;
48+
private ProcessTextMethodHandler processTextMethodHandler;
49+
50+
@NonNull
51+
public final MethodChannel.MethodCallHandler parsingMethodHandler =
52+
new MethodChannel.MethodCallHandler() {
53+
@Override
54+
public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result result) {
55+
if (processTextMethodHandler == null) {
56+
return;
57+
}
58+
String method = call.method;
59+
Object args = call.arguments;
60+
switch (method) {
61+
case METHOD_QUERY_TEXT_ACTIONS:
62+
try {
63+
Map<String, String> actions = processTextMethodHandler.queryTextActions();
64+
result.success(actions);
65+
} catch (IllegalStateException exception) {
66+
result.error("error", exception.getMessage(), null);
67+
}
68+
break;
69+
case METHOD_PROCESS_TEXT_ACTION:
70+
try {
71+
final ArrayList<Object> argumentList = (ArrayList<Object>) args;
72+
String id = (String) (argumentList.get(0));
73+
String text = (String) (argumentList.get(1));
74+
boolean readOnly = (boolean) (argumentList.get(2));
75+
processTextMethodHandler.processTextAction(id, text, readOnly, result);
76+
} catch (IllegalStateException exception) {
77+
result.error("error", exception.getMessage(), null);
78+
}
79+
break;
80+
default:
81+
result.notImplemented();
82+
break;
83+
}
84+
}
85+
};
86+
87+
public ProcessTextChannel(
88+
@NonNull DartExecutor dartExecutor, @NonNull PackageManager packageManager) {
89+
this.packageManager = packageManager;
90+
channel = new MethodChannel(dartExecutor, CHANNEL_NAME, StandardMethodCodec.INSTANCE);
91+
channel.setMethodCallHandler(parsingMethodHandler);
92+
}
93+
94+
/**
95+
* Sets the {@link ProcessTextMethodHandler} which receives all requests to the text processing
96+
* feature sent through this channel.
97+
*/
98+
public void setMethodHandler(@Nullable ProcessTextMethodHandler processTextMethodHandler) {
99+
this.processTextMethodHandler = processTextMethodHandler;
100+
}
101+
102+
public interface ProcessTextMethodHandler {
103+
/** Requests the map of text actions. Each text action has a unique id and a localized label. */
104+
Map<String, String> queryTextActions();
105+
106+
/**
107+
* Requests to run a text action on a given input text.
108+
*
109+
* @param id The ID of the text action returned by {@code ProcessText.queryTextActions}.
110+
* @param input The text to be processed.
111+
* @param readOnly Indicates to the activity if the processed text will be used as read-only.
112+
* see
113+
* https://developer.android.com/reference/android/content/Intent#EXTRA_PROCESS_TEXT_READONLY
114+
* @param result The method channel result instance used to reply.
115+
*/
116+
void processTextAction(
117+
@NonNull String id,
118+
@NonNull String input,
119+
@NonNull boolean readOnly,
120+
@NonNull MethodChannel.Result result);
121+
}
122+
}

0 commit comments

Comments
 (0)