email_util.dart 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. import 'dart:io';
  2. import 'package:archive/archive_io.dart';
  3. import 'package:email_validator/email_validator.dart';
  4. import 'package:flutter/material.dart';
  5. import 'package:flutter/services.dart';
  6. import 'package:flutter/widgets.dart';
  7. import 'package:flutter_email_sender/flutter_email_sender.dart';
  8. import 'package:logging/logging.dart';
  9. import 'package:path_provider/path_provider.dart';
  10. import 'package:photos/core/error-reporting/super_logging.dart';
  11. import 'package:photos/ente_theme_data.dart';
  12. import 'package:photos/ui/common/dialogs.dart';
  13. import 'package:photos/ui/log_file_viewer.dart';
  14. import 'package:photos/utils/dialog_util.dart';
  15. import 'package:share_plus/share_plus.dart';
  16. final Logger _logger = Logger('email_util');
  17. bool isValidEmail(String email) {
  18. return EmailValidator.validate(email);
  19. }
  20. Future<void> sendLogs(
  21. BuildContext context,
  22. String title,
  23. String toEmail, {
  24. Function postShare,
  25. String subject,
  26. String body,
  27. }) async {
  28. final List<Widget> actions = [
  29. TextButton(
  30. child: Row(
  31. mainAxisAlignment: MainAxisAlignment.start,
  32. children: [
  33. Icon(
  34. Icons.feed_outlined,
  35. color: Theme.of(context).iconTheme.color.withOpacity(0.85),
  36. ),
  37. Padding(padding: EdgeInsets.all(4)),
  38. Text(
  39. "View logs",
  40. style: TextStyle(
  41. fontFamily: "Inter",
  42. color: Theme.of(context)
  43. .colorScheme
  44. .defaultTextColor
  45. .withOpacity(0.85)),
  46. ),
  47. ],
  48. ),
  49. onPressed: () async {
  50. showDialog(
  51. context: context,
  52. builder: (BuildContext context) {
  53. return LogFileViewer(SuperLogging.logFile);
  54. },
  55. barrierColor: Colors.black87,
  56. barrierDismissible: false,
  57. );
  58. },
  59. ),
  60. TextButton(
  61. child: Text(
  62. title,
  63. style: TextStyle(
  64. color: Theme.of(context).buttonColor,
  65. ),
  66. ),
  67. onPressed: () async {
  68. Navigator.of(context, rootNavigator: true).pop('dialog');
  69. await _sendLogs(context, toEmail, subject, body);
  70. if (postShare != null) {
  71. postShare();
  72. }
  73. },
  74. ),
  75. ];
  76. final List<Widget> content = [];
  77. content.addAll(
  78. [
  79. Text(
  80. "This will send across logs and metrics that will help us debug your issue better",
  81. style: TextStyle(
  82. height: 1.5,
  83. fontFamily: 'Ubuntu',
  84. fontSize: 16,
  85. ),
  86. ),
  87. Padding(padding: EdgeInsets.all(12)),
  88. Row(
  89. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  90. children: actions,
  91. ),
  92. ],
  93. );
  94. final confirmation = AlertDialog(
  95. title: Text(
  96. title,
  97. style: TextStyle(
  98. fontSize: 18,
  99. ),
  100. ),
  101. content: SingleChildScrollView(
  102. child: ListBody(
  103. children: content,
  104. ),
  105. ),
  106. );
  107. showDialog(
  108. context: context,
  109. builder: (_) {
  110. return confirmation;
  111. },
  112. );
  113. }
  114. Future<void> _sendLogs(
  115. BuildContext context, String toEmail, String subject, String body) async {
  116. String zipFilePath = await getZippedLogsFile(context);
  117. final Email email = Email(
  118. recipients: [toEmail],
  119. subject: subject,
  120. body: body,
  121. attachmentPaths: [zipFilePath],
  122. isHTML: false,
  123. );
  124. try {
  125. await FlutterEmailSender.send(email);
  126. } catch (e, s) {
  127. _logger.severe('email sender failed', e, s);
  128. await shareLogs(context, toEmail, zipFilePath);
  129. }
  130. }
  131. Future<String> getZippedLogsFile(BuildContext context) async {
  132. final dialog = createProgressDialog(context, "Preparing logs...");
  133. await dialog.show();
  134. final tempPath = (await getTemporaryDirectory()).path;
  135. final zipFilePath = tempPath + "/logs.zip";
  136. final logsDirectory = Directory(tempPath + "/logs");
  137. var encoder = ZipFileEncoder();
  138. encoder.create(zipFilePath);
  139. encoder.addDirectory(logsDirectory);
  140. encoder.close();
  141. await dialog.hide();
  142. return zipFilePath;
  143. }
  144. Future<void> shareLogs(
  145. BuildContext context, String toEmail, String zipFilePath) async {
  146. final result = await showChoiceDialog(
  147. context, "Email logs", "Please send the logs to $toEmail",
  148. firstAction: "Copy email", secondAction: "Send");
  149. if (result != null && result == DialogUserChoice.firstChoice) {
  150. await Clipboard.setData(ClipboardData(text: toEmail));
  151. }
  152. final Size size = MediaQuery.of(context).size;
  153. await Share.shareFiles([zipFilePath],
  154. sharePositionOrigin: Rect.fromLTWH(0, 0, size.width, size.height / 2));
  155. }