Skip to main content

inset_services/
process_text.rs

1//! Flutter counterpart: `services/process_text.dart`.
2
3use inset_foundation::App;
4
5/// A data structure describing text processing actions.
6#[derive(Clone, Debug, PartialEq, Eq, Hash)]
7pub struct ProcessTextAction {
8    /// The action unique id.
9    pub id: String,
10    /// The action localized label.
11    pub label: String,
12}
13
14impl ProcessTextAction {
15    /// Creates text processing actions based on those returned by the engine.
16    pub fn new(id: impl Into<String>, label: impl Into<String>) -> ProcessTextAction {
17        ProcessTextAction {
18            id: id.into(),
19            label: label.into(),
20        }
21    }
22}
23
24/// Determines how to interact with the text processing feature.
25pub trait ProcessTextService {
26    /// Returns a [`Vec`] of [`ProcessTextAction`]s containing all text processing
27    /// actions available.
28    ///
29    /// If there are no actions available, an empty list will be returned.
30    fn query_text_actions(&self, app: &App) -> Vec<ProcessTextAction>;
31
32    /// Returns a [`String`] when the text action returns a transformed text or
33    /// [`None`] when the text action did not return a transformed text.
34    ///
35    /// The `id` parameter is the text action unique identifier returned by
36    /// [`query_text_actions`](Self::query_text_actions).
37    ///
38    /// The `text` parameter is the text to be processed.
39    ///
40    /// The `read_only` parameter indicates that the transformed text, if it exists,
41    /// will be used as read-only.
42    fn process_text_action(
43        &self,
44        app: &App,
45        id: &str,
46        text: &str,
47        read_only: bool,
48    ) -> Option<String>;
49}
50
51/// The service used by default for the text processing feature.
52///
53/// Any widget may use this service to get a list of text processing actions
54/// and send requests to activate these text actions.
55///
56/// This is currently only supported on Android and it requires adding the
57/// following '<queries>' element to the Android manifest file:
58///
59/// ```text
60/// <manifest ...>
61///     <application ...>
62///       ...
63///     </application>
64///     <!-- Required to query activities that can process text, see:
65///          https://developer.android.com/training/package-visibility and
66///          https://developer.android.com/reference/android/content/Intent#ACTION_PROCESS_TEXT.
67///
68///          In particular, this is used by the Flutter engine in io.flutter.plugin.text.ProcessTextPlugin. -->
69///     <queries>
70///         <intent>
71///             <action android:name="android.intent.action.PROCESS_TEXT"/>
72///             <data android:mimeType="text/plain"/>
73///         </intent>
74///     </queries>
75/// </manifest>
76/// ```
77///
78/// The '<queries>' element is part of the Android manifest file generated when
79/// running the 'flutter create' command.
80///
81/// If the '<queries>' element is not found, [`query_text_actions`](ProcessTextService::query_text_actions) will return an
82/// empty list of [`ProcessTextAction`].
83///
84/// See also:
85///
86///  * [`ProcessTextService`], the service that this implements.
87#[derive(Clone, Debug, Default)]
88pub struct DefaultProcessTextService;
89
90impl DefaultProcessTextService {
91    /// Creates the default service to interact with the platform text processing
92    /// feature.
93    pub fn new() -> DefaultProcessTextService {
94        DefaultProcessTextService
95    }
96}
97
98impl ProcessTextService for DefaultProcessTextService {
99    fn query_text_actions(&self, app: &App) -> Vec<ProcessTextAction> {
100        let _ = app;
101        Vec::new()
102    }
103
104    /// On Android, the readOnly parameter might be used by the targeted activity, see:
105    /// https://developer.android.com/reference/android/content/Intent#EXTRA_PROCESS_TEXT_READONLY.
106    fn process_text_action(
107        &self,
108        app: &App,
109        id: &str,
110        text: &str,
111        read_only: bool,
112    ) -> Option<String> {
113        let _ = (app, id, text, read_only);
114        None
115    }
116}
117
118#[cfg(test)]
119mod tests {
120    use inset_foundation::AppCell;
121
122    use super::*;
123
124    #[test]
125    fn action_equality() {
126        let action = ProcessTextAction::new("id", "Translate");
127        assert_eq!(action, ProcessTextAction::new("id", "Translate"));
128        assert_ne!(action, ProcessTextAction::new("id", "Share"));
129    }
130
131    #[test]
132    fn default_service_is_empty() {
133        let cell = AppCell::new();
134        let app = cell.borrow_mut();
135        let service = DefaultProcessTextService::new();
136        assert!(service.query_text_actions(&app).is_empty());
137        assert!(
138            service
139                .process_text_action(&app, "id", "hello", false)
140                .is_none()
141        );
142    }
143}