mirror of
https://gitlab.com/nkming2/nc-photos.git
synced 2025-01-22 16:56:19 +01:00
Capture logs from settings for bug report
This commit is contained in:
parent
858455206c
commit
620c840ccc
11 changed files with 210 additions and 15 deletions
|
@ -48,6 +48,13 @@ class NotificationChannelHandler(activity: Activity)
|
|||
} catch (e: Throwable) {
|
||||
result.error("systemException", e.toString(), null)
|
||||
}
|
||||
} else if (call.method == "notifyLogSaveSuccessful") {
|
||||
try {
|
||||
notifyLogSaveSuccessful(call.argument<String>("fileUri")!!,
|
||||
result)
|
||||
} catch (e: Throwable) {
|
||||
result.error("systemException", e.toString(), null)
|
||||
}
|
||||
} else {
|
||||
result.notImplemented()
|
||||
}
|
||||
|
@ -130,6 +137,44 @@ class NotificationChannelHandler(activity: Activity)
|
|||
result.success(null)
|
||||
}
|
||||
|
||||
private fun notifyLogSaveSuccessful(fileUri: String,
|
||||
result: MethodChannel.Result) {
|
||||
val uri = Uri.parse(fileUri)
|
||||
val mimeType = "text/plain"
|
||||
val builder = NotificationCompat.Builder(_context, DOWNLOAD_CHANNEL_ID)
|
||||
.setSmallIcon(R.drawable.baseline_download_white_18)
|
||||
.setWhen(System.currentTimeMillis())
|
||||
.setPriority(NotificationCompat.PRIORITY_HIGH)
|
||||
.setSound(RingtoneManager.getDefaultUri(
|
||||
RingtoneManager.TYPE_NOTIFICATION))
|
||||
.setAutoCancel(true)
|
||||
.setLocalOnly(true)
|
||||
.setTicker(_context.getString(
|
||||
R.string.log_save_successful_notification_title))
|
||||
.setContentTitle(_context.getString(
|
||||
R.string.log_save_successful_notification_title))
|
||||
.setContentText(_context.getString(
|
||||
R.string.log_save_successful_notification_text))
|
||||
|
||||
val openIntent = Intent().apply {
|
||||
action = Intent.ACTION_VIEW
|
||||
setDataAndType(uri, mimeType)
|
||||
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
|
||||
}
|
||||
val openPendingIntent = PendingIntent.getActivity(_context, 0,
|
||||
openIntent, PendingIntent.FLAG_UPDATE_CURRENT)
|
||||
builder.setContentIntent(openPendingIntent)
|
||||
|
||||
// can't add the share action here because android will share the URI as
|
||||
// plain text instead of treating it as a text file...
|
||||
|
||||
with(NotificationManagerCompat.from(_context)) {
|
||||
notify(DOWNLOAD_NOTIFICATION_ID, builder.build())
|
||||
}
|
||||
result.success(null)
|
||||
}
|
||||
|
||||
private fun loadNotificationImage(fileUri: Uri): Bitmap? {
|
||||
try {
|
||||
val resolver = _context.applicationContext.contentResolver
|
||||
|
|
|
@ -9,4 +9,6 @@
|
|||
<string name="download_successful_notification_action_share">SHARE</string>
|
||||
<string name="download_successful_notification_action_share_chooser">Share with:</string>
|
||||
<string name="download_multiple_successful_notification_title">Downloaded %1$s items successfully</string>
|
||||
<string name="log_save_successful_notification_title">Logs saved successfully</string>
|
||||
<string name="log_save_successful_notification_text">Tap to view your saved logs</string>
|
||||
</resources>
|
||||
|
|
|
@ -71,21 +71,6 @@ class ListPersonBlocFailure extends ListPersonBlocState {
|
|||
class ListPersonBloc extends Bloc<ListPersonBlocEvent, ListPersonBlocState> {
|
||||
ListPersonBloc() : super(ListPersonBlocInit());
|
||||
|
||||
static ListPersonBloc of(Account account) {
|
||||
final id = "${account.scheme}://${account.username}@${account.address}";
|
||||
try {
|
||||
_log.fine("[of] Resolving bloc for '$id'");
|
||||
return KiwiContainer().resolve<ListPersonBloc>("ListPersonBloc($id)");
|
||||
} catch (_) {
|
||||
// no created instance for this account, make a new one
|
||||
_log.info("[of] New bloc instance for account: $account");
|
||||
final bloc = ListPersonBloc();
|
||||
KiwiContainer()
|
||||
.registerInstance<ListPersonBloc>(bloc, name: "ListPersonBloc($id)");
|
||||
return bloc;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
mapEventToState(ListPersonBlocEvent event) async* {
|
||||
_log.info("[mapEventToState] $event");
|
||||
|
|
|
@ -1,3 +1,45 @@
|
|||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:nc_photos/mobile/platform.dart'
|
||||
if (dart.library.html) 'package:nc_photos/web/platform.dart' as platform;
|
||||
|
||||
class LogCapturer {
|
||||
factory LogCapturer() {
|
||||
if (_inst == null) {
|
||||
_inst = LogCapturer._();
|
||||
}
|
||||
return _inst!;
|
||||
}
|
||||
|
||||
LogCapturer._();
|
||||
|
||||
/// Start capturing logs
|
||||
void start() {
|
||||
_isEnable = true;
|
||||
}
|
||||
|
||||
/// Stop capturing and save the captured logs
|
||||
Future<dynamic> stop() {
|
||||
_isEnable = false;
|
||||
final saver = platform.FileSaver();
|
||||
final content = Utf8Encoder().convert(_logs.join("\n"));
|
||||
_logs.clear();
|
||||
return saver.saveFile("nc-photos.log", content);
|
||||
}
|
||||
|
||||
void onLog(String log) {
|
||||
if (_isEnable) {
|
||||
_logs.add(log);
|
||||
}
|
||||
}
|
||||
|
||||
bool get isEnable => _isEnable;
|
||||
|
||||
final _logs = <String>[];
|
||||
bool _isEnable = false;
|
||||
|
||||
static LogCapturer? _inst;
|
||||
}
|
||||
|
||||
const bool shouldLogFileName = kDebugMode;
|
||||
|
|
|
@ -361,6 +361,11 @@
|
|||
"@settingsBugReportTitle": {
|
||||
"description": "Report issue"
|
||||
},
|
||||
"settingsCaptureLogsTitle": "Capture logs",
|
||||
"@settingsCaptureLogsTitle": {
|
||||
"description": "Capture app logs for bug report"
|
||||
},
|
||||
"settingsCaptureLogsDescription": "Help developer to diagnose bugs",
|
||||
"settingsTranslatorTitle": "Translator",
|
||||
"@settingsTranslatorTitle": {
|
||||
"description": "Title of the translator item"
|
||||
|
@ -381,6 +386,14 @@
|
|||
"@exifSupportConfirmationDialogTitle": {
|
||||
"description": "Title of the dialog to confirm enabling exif support"
|
||||
},
|
||||
"captureLogDetails": "To take logs for a bug report:\n\n1. Enable this setting\n2. Reproduce the issue\n3. Disable this setting\n4. Look for nc-photos.log in the download folder\n\n*If the issue causes the app to crash, no logs could be captured. In such case, please contact the developer for further instructions",
|
||||
"@captureLogDetails": {
|
||||
"description": "Detailed description on capturing logs"
|
||||
},
|
||||
"captureLogSuccessNotification": "Logs saved successfully",
|
||||
"@captureLogSuccessNotification": {
|
||||
"description": "Captured logs are successfully saved to the download directory"
|
||||
},
|
||||
"doneButtonLabel": "DONE",
|
||||
"@doneButtonLabel": {
|
||||
"description": "Label of the done button"
|
||||
|
|
|
@ -15,6 +15,10 @@
|
|||
"settingsUseBlackInDarkThemeTitle",
|
||||
"settingsUseBlackInDarkThemeTrueDescription",
|
||||
"settingsUseBlackInDarkThemeFalseDescription",
|
||||
"settingsCaptureLogsTitle",
|
||||
"settingsCaptureLogsDescription",
|
||||
"captureLogDetails",
|
||||
"captureLogSuccessNotification",
|
||||
"sortOptionAlbumNameLabel",
|
||||
"sortOptionAlbumNameDescendingLabel",
|
||||
"listEmptyText",
|
||||
|
@ -51,6 +55,13 @@
|
|||
"unmuteTooltip"
|
||||
],
|
||||
|
||||
"es": [
|
||||
"settingsCaptureLogsTitle",
|
||||
"settingsCaptureLogsDescription",
|
||||
"captureLogDetails",
|
||||
"captureLogSuccessNotification"
|
||||
],
|
||||
|
||||
"fr": [
|
||||
"collectionsTooltip",
|
||||
"settingsViewerTitle",
|
||||
|
@ -67,6 +78,10 @@
|
|||
"settingsUseBlackInDarkThemeTitle",
|
||||
"settingsUseBlackInDarkThemeTrueDescription",
|
||||
"settingsUseBlackInDarkThemeFalseDescription",
|
||||
"settingsCaptureLogsTitle",
|
||||
"settingsCaptureLogsDescription",
|
||||
"captureLogDetails",
|
||||
"captureLogSuccessNotification",
|
||||
"sortOptionAlbumNameLabel",
|
||||
"sortOptionAlbumNameDescendingLabel",
|
||||
"helpTooltip",
|
||||
|
@ -84,6 +99,10 @@
|
|||
],
|
||||
|
||||
"ru": [
|
||||
"settingsCaptureLogsTitle",
|
||||
"settingsCaptureLogsDescription",
|
||||
"captureLogDetails",
|
||||
"captureLogSuccessNotification",
|
||||
"sortOptionAlbumNameLabel",
|
||||
"sortOptionAlbumNameDescendingLabel"
|
||||
]
|
||||
|
|
|
@ -6,6 +6,7 @@ import 'package:flutter/material.dart';
|
|||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:kiwi/kiwi.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:nc_photos/debug_util.dart';
|
||||
import 'package:nc_photos/k.dart' as k;
|
||||
import 'package:nc_photos/mobile/android/android_info.dart';
|
||||
import 'package:nc_photos/mobile/self_signed_cert_manager.dart';
|
||||
|
@ -69,6 +70,7 @@ void _initLog() {
|
|||
msg = "\x1B[${color}m$msg\x1B[0m";
|
||||
}
|
||||
debugPrint(msg);
|
||||
LogCapturer().onLog(msg);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -8,6 +8,11 @@ class Notification {
|
|||
"mimeTypes": mimeTypes,
|
||||
});
|
||||
|
||||
static Future<void> notifyLogSaveSuccessful(String fileUri) =>
|
||||
_channel.invokeMethod("notifyLogSaveSuccessful", <String, dynamic>{
|
||||
"fileUri": fileUri,
|
||||
});
|
||||
|
||||
static const _channel =
|
||||
const MethodChannel("com.nkming.nc_photos/notification");
|
||||
}
|
||||
|
|
|
@ -13,3 +13,15 @@ class AndroidItemDownloadSuccessfulNotification
|
|||
final List<String> fileUris;
|
||||
final List<String?> mimeTypes;
|
||||
}
|
||||
|
||||
class AndroidLogSaveSuccessfulNotification
|
||||
extends itf.LogSaveSuccessfulNotification {
|
||||
AndroidLogSaveSuccessfulNotification(this.fileUri);
|
||||
|
||||
@override
|
||||
Future<void> notify() {
|
||||
return Notification.notifyLogSaveSuccessful(fileUri);
|
||||
}
|
||||
|
||||
final String fileUri;
|
||||
}
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
abstract class ItemDownloadSuccessfulNotification {
|
||||
Future<void> notify();
|
||||
}
|
||||
|
||||
abstract class LogSaveSuccessfulNotification {
|
||||
Future<void> notify();
|
||||
}
|
||||
|
|
|
@ -4,11 +4,13 @@ import 'package:kiwi/kiwi.dart';
|
|||
import 'package:logging/logging.dart';
|
||||
import 'package:nc_photos/account.dart';
|
||||
import 'package:nc_photos/app_localizations.dart';
|
||||
import 'package:nc_photos/debug_util.dart';
|
||||
import 'package:nc_photos/event/event.dart';
|
||||
import 'package:nc_photos/k.dart' as k;
|
||||
import 'package:nc_photos/language_util.dart' as language_util;
|
||||
import 'package:nc_photos/metadata_task_manager.dart';
|
||||
import 'package:nc_photos/mobile/android/android_info.dart';
|
||||
import 'package:nc_photos/mobile/notification.dart';
|
||||
import 'package:nc_photos/platform/k.dart' as platform_k;
|
||||
import 'package:nc_photos/pref.dart';
|
||||
import 'package:nc_photos/snack_bar_manager.dart';
|
||||
|
@ -122,6 +124,12 @@ class _SettingsState extends State<Settings> {
|
|||
launch(_bugReportUrl);
|
||||
},
|
||||
),
|
||||
SwitchListTile(
|
||||
title: Text(L10n.global().settingsCaptureLogsTitle),
|
||||
subtitle: Text(L10n.global().settingsCaptureLogsDescription),
|
||||
value: LogCapturer().isEnable,
|
||||
onChanged: (value) => _onCaptureLogChanged(context, value),
|
||||
),
|
||||
if (translator.isNotEmpty)
|
||||
ListTile(
|
||||
title: Text(L10n.global().settingsTranslatorTitle),
|
||||
|
@ -251,6 +259,64 @@ class _SettingsState extends State<Settings> {
|
|||
}
|
||||
}
|
||||
|
||||
void _onCaptureLogChanged(BuildContext context, bool value) async {
|
||||
if (value) {
|
||||
final result = await showDialog<bool>(
|
||||
context: context,
|
||||
builder: (context) => AppTheme(
|
||||
child: AlertDialog(
|
||||
content: Text(L10n.global().captureLogDetails),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop(true);
|
||||
},
|
||||
child: Text(L10n.global().enableButtonLabel),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
if (result == true) {
|
||||
setState(() {
|
||||
LogCapturer().start();
|
||||
});
|
||||
}
|
||||
} else {
|
||||
if (LogCapturer().isEnable) {
|
||||
setState(() {
|
||||
LogCapturer().stop().then((result) {
|
||||
_onLogSaveSuccessful(result);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _onLogSaveSuccessful(dynamic result) {
|
||||
var notif;
|
||||
if (platform_k.isAndroid) {
|
||||
notif = AndroidLogSaveSuccessfulNotification(result);
|
||||
}
|
||||
if (notif != null) {
|
||||
try {
|
||||
notif.notify();
|
||||
return;
|
||||
} catch (e, stacktrace) {
|
||||
_log.shout(
|
||||
"[_onLogSaveSuccessful] Failed showing platform notification",
|
||||
e,
|
||||
stacktrace);
|
||||
}
|
||||
}
|
||||
|
||||
// fallback
|
||||
SnackBarManager().showSnackBar(SnackBar(
|
||||
content: Text(L10n.global().downloadSuccessNotification),
|
||||
duration: k.snackBarDurationShort,
|
||||
));
|
||||
}
|
||||
|
||||
Future<void> _setExifSupport(bool value) async {
|
||||
final oldValue = _isEnableExif;
|
||||
setState(() {
|
||||
|
|
Loading…
Reference in a new issue