@@ -4,6 +4,7 @@ import 'package:app_settings/app_settings.dart';
44import 'package:flutter/material.dart' ;
55import 'package:flutter/services.dart' ;
66import 'package:mime/mime.dart' ;
7+ import 'package:path/path.dart' as path;
78
89import '../api/exception.dart' ;
910import '../api/model/model.dart' ;
@@ -465,6 +466,39 @@ class _ContentInputState extends State<_ContentInput> with WidgetsBindingObserve
465466 }
466467 }
467468
469+ void _handleContentInserted (KeyboardInsertedContent content) async {
470+ if (content.data == null || content.data! .isEmpty) {
471+ // As of writing, the engine implementation never leaves `content.data` as
472+ // `null`, but ideally it should be when the data cannot be read for
473+ // errors.
474+ //
475+ // When `content.data` is empty, the data is not literally empty — this
476+ // can also happen when the data can't be read from the input stream
477+ // provided by the Android SDK because of an IO exception.
478+ //
479+ // See Flutter engine implementation that prepares this data:
480+ // https://github.com/flutter/flutter/blob/0ffc4ce00/engine/src/flutter/shell/platform/android/io/flutter/plugin/editing/InputConnectionAdaptor.java#L497-L548
481+ // TODO(upstream): improve the API for this
482+ final zulipLocalizations = ZulipLocalizations .of (context);
483+ showErrorDialog (context: context,
484+ title: zulipLocalizations.errorContentNotInsertedTitle,
485+ message: zulipLocalizations.errorContentToInsertIsEmpty);
486+ return ;
487+ }
488+
489+ final file = _File (
490+ content: Stream .fromIterable ([content.data! ]),
491+ length: content.data! .length,
492+ filename: path.basename (content.uri),
493+ mimeType: content.mimeType);
494+
495+ await _uploadFiles (
496+ context: context,
497+ contentController: widget.controller.content,
498+ contentFocusNode: widget.controller.contentFocusNode,
499+ files: [file]);
500+ }
501+
468502 static double maxHeight (BuildContext context) {
469503 final clampingTextScaler = MediaQuery .textScalerOf (context)
470504 .clamp (maxScaleFactor: 1.5 );
@@ -508,6 +542,8 @@ class _ContentInputState extends State<_ContentInput> with WidgetsBindingObserve
508542 child: TextField (
509543 controller: widget.controller.content,
510544 focusNode: widget.controller.contentFocusNode,
545+ contentInsertionConfiguration: ContentInsertionConfiguration (
546+ onContentInserted: _handleContentInserted),
511547 // Let the content show through the `contentPadding` so that
512548 // our [InsetShadowBox] can fade it smoothly there.
513549 clipBehavior: Clip .none,
0 commit comments