email_util.dart 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  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_email_sender/flutter_email_sender.dart';
  7. import 'package:logging/logging.dart';
  8. import 'package:path_provider/path_provider.dart';
  9. import 'package:photos/core/configuration.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. color: Theme.of(context)
  42. .colorScheme
  43. .defaultTextColor
  44. .withOpacity(0.85),
  45. ),
  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. fontSize: 16,
  84. ),
  85. ),
  86. Padding(padding: EdgeInsets.all(12)),
  87. Row(
  88. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  89. children: actions,
  90. ),
  91. ],
  92. );
  93. final confirmation = AlertDialog(
  94. title: Text(
  95. title,
  96. style: TextStyle(
  97. fontSize: 18,
  98. ),
  99. ),
  100. content: SingleChildScrollView(
  101. child: ListBody(
  102. children: content,
  103. ),
  104. ),
  105. );
  106. showDialog(
  107. context: context,
  108. builder: (_) {
  109. return confirmation;
  110. },
  111. );
  112. }
  113. Future<void> _sendLogs(
  114. BuildContext context,
  115. String toEmail,
  116. String subject,
  117. String body,
  118. ) async {
  119. String zipFilePath = await getZippedLogsFile(context);
  120. final Email email = Email(
  121. recipients: [toEmail],
  122. subject: subject,
  123. body: body,
  124. attachmentPaths: [zipFilePath],
  125. isHTML: false,
  126. );
  127. try {
  128. await FlutterEmailSender.send(email);
  129. } catch (e, s) {
  130. _logger.severe('email sender failed', e, s);
  131. await shareLogs(context, toEmail, zipFilePath);
  132. }
  133. }
  134. Future<String> getZippedLogsFile(BuildContext context) async {
  135. final dialog = createProgressDialog(context, "Preparing logs...");
  136. await dialog.show();
  137. final logsPath = (await getApplicationSupportDirectory()).path;
  138. final logsDirectory = Directory(logsPath + "/logs");
  139. final tempPath = (await getTemporaryDirectory()).path;
  140. final zipFilePath =
  141. tempPath + "/logs-${Configuration.instance.getUserID() ?? 0}.zip";
  142. var encoder = ZipFileEncoder();
  143. encoder.create(zipFilePath);
  144. encoder.addDirectory(logsDirectory);
  145. encoder.close();
  146. await dialog.hide();
  147. return zipFilePath;
  148. }
  149. Future<void> shareLogs(
  150. BuildContext context,
  151. String toEmail,
  152. String zipFilePath,
  153. ) async {
  154. final result = await showChoiceDialog(
  155. context,
  156. "Email logs",
  157. "Please send the logs to $toEmail",
  158. firstAction: "Copy email",
  159. secondAction: "Send",
  160. );
  161. if (result != null && result == DialogUserChoice.firstChoice) {
  162. await Clipboard.setData(ClipboardData(text: toEmail));
  163. }
  164. final Size size = MediaQuery.of(context).size;
  165. await Share.shareFiles(
  166. [zipFilePath],
  167. sharePositionOrigin: Rect.fromLTWH(0, 0, size.width, size.height / 2),
  168. );
  169. }