@@ -2,6 +2,7 @@ import 'package:app_settings/app_settings.dart';
2
2
import 'package:file_picker/file_picker.dart' ;
3
3
import 'package:flutter/material.dart' ;
4
4
import 'package:flutter/services.dart' ;
5
+ import 'package:image_picker/image_picker.dart' ;
5
6
import 'dialog.dart' ;
6
7
7
8
import '../api/route/messages.dart' ;
@@ -376,6 +377,51 @@ class _AttachMediaButton extends _AttachUploadsButton {
376
377
}
377
378
}
378
379
380
+ class _AttachFromCameraButton extends _AttachUploadsButton {
381
+ const _AttachFromCameraButton ({required super .contentController, required super .contentFocusNode});
382
+
383
+ @override
384
+ IconData get icon => Icons .camera_alt;
385
+
386
+ @override
387
+ Future <Iterable <_File >?> getFiles (BuildContext context) async {
388
+ final picker = ImagePicker ();
389
+ final XFile ? result;
390
+ try {
391
+ // Ideally we'd open a platform interface that lets you choose between
392
+ // taking a photo and a video. `image_picker` doesn't yet have that
393
+ // option: https://github.com/flutter/flutter/issues/89159
394
+ // so just stick with images for now. We could add another button for
395
+ // videos, but we don't want too many buttons.
396
+ result = await picker.pickImage (source: ImageSource .camera, requestFullMetadata: false );
397
+ } catch (e) {
398
+ if (e is PlatformException && e.code == 'camera_access_denied' ) {
399
+ // iOS has a quirk where it will only request the native
400
+ // permission-request alert once, the first time the app wants to
401
+ // use a protected resource. After that, the only way the user can
402
+ // grant it is in Settings.
403
+ showSuggestedActionDialog (context: context, // TODO(i18n)
404
+ title: 'Permissions needed' ,
405
+ message: 'To upload an image, please grant Zulip additional permissions in Settings.' ,
406
+ actionButtonText: 'Open settings' ,
407
+ onActionButtonPress: () {
408
+ AppSettings .openAppSettings ();
409
+ });
410
+ } else {
411
+ // TODO(i18n)
412
+ showErrorDialog (context: context, title: 'Error' , message: e.toString ());
413
+ }
414
+ return null ;
415
+ }
416
+ if (result == null ) {
417
+ return null ; // User cancelled; do nothing
418
+ }
419
+ final length = await result.length ();
420
+
421
+ return [_File (content: result.openRead (), length: length, filename: result.name)];
422
+ }
423
+ }
424
+
379
425
/// The send button for StreamComposeBox.
380
426
class _StreamSendButton extends StatefulWidget {
381
427
const _StreamSendButton ({required this .topicController, required this .contentController});
@@ -575,6 +621,7 @@ class _StreamComposeBoxState extends State<StreamComposeBox> {
575
621
children: [
576
622
_AttachFileButton (contentController: _contentController, contentFocusNode: _contentFocusNode),
577
623
_AttachMediaButton (contentController: _contentController, contentFocusNode: _contentFocusNode),
624
+ _AttachFromCameraButton (contentController: _contentController, contentFocusNode: _contentFocusNode),
578
625
])),
579
626
]))));
580
627
}
0 commit comments