Skip to content

Commit

Permalink
dev: add log for plugin channel (#1172)
Browse files Browse the repository at this point in the history
Signed-off-by: Caijinglong <[email protected]>
Co-authored-by: Alex Li <[email protected]>
  • Loading branch information
CaiJingLong and AlexV525 authored Aug 8, 2024
1 parent e89e22e commit 46c2aef
Show file tree
Hide file tree
Showing 7 changed files with 439 additions and 2 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ To know more about breaking changes, see the [Migration Guide][].

*None.*

### Developer

- Add verbose log for `MethodChannelPlugin`. (Use `PhotoManager.setLog` and pass `verboseFilePath` to enable it.)
- Add `getVerboseFilePath` for `PhotoManager`.

## 3.2.3

### Fixes
Expand Down
7 changes: 7 additions & 0 deletions example/lib/model/photo_provider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import '../util/common_util.dart';
import '../util/log.dart';

class PhotoProvider extends ChangeNotifier {
bool showVerboseLog = false;

List<AssetPathEntity> list = <AssetPathEntity>[];

RequestType type = RequestType.common;
Expand Down Expand Up @@ -281,6 +283,11 @@ class PhotoProvider extends ChangeNotifier {
);
notifyListeners();
}

void changeVerboseLog(bool v) {
showVerboseLog = v;
notifyListeners();
}
}

class AssetPathProvider extends ChangeNotifier {
Expand Down
29 changes: 29 additions & 0 deletions example/lib/page/developer/develop_index_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,18 @@ import 'package:oktoast/oktoast.dart';
import 'package:photo_manager/photo_manager.dart';
import 'package:photo_manager_example/page/developer/android/column_names_page.dart';
import 'package:photo_manager_example/page/developer/custom_filter_page.dart';
import 'package:provider/provider.dart';

import '../../model/photo_provider.dart';
import '../../util/log.dart';
import '../../util/log_export.dart';
import 'create_entity_by_id.dart';
import 'dev_title_page.dart';
import 'ios/create_folder_example.dart';
import 'ios/edit_asset.dart';
import 'issues_page/issue_index_page.dart';
import 'remove_all_android_not_exists_example.dart';
import 'verbose_log_page.dart';

class DeveloperIndexPage extends StatefulWidget {
const DeveloperIndexPage({super.key});
Expand All @@ -31,6 +35,26 @@ class _DeveloperIndexPageState extends State<DeveloperIndexPage> {
static const exampleHeicUrl =
'https://cdn.jsdelivr.net/gh/ExampleAssets/ExampleAsset@master/preview_0.heic';

Widget _buildVerboseLogSwitch(BuildContext conetxt) {
final PhotoProvider provider = context.watch<PhotoProvider>();
final bool verboseLog = provider.showVerboseLog;
return CheckboxListTile(
title: const Text('Verbose log'),
value: verboseLog,
onChanged: (value) async {
provider.changeVerboseLog(value!);
setState(() {});

if (verboseLog) {
final path = await PMVerboseLogUtil.shared.getLogFilePath();
PhotoManager.setLog(true, verboseFilePath: path);
} else {
PhotoManager.setLog(false);
}
},
);
}

@override
Widget build(BuildContext context) {
return Scaffold(
Expand All @@ -40,6 +64,11 @@ class _DeveloperIndexPageState extends State<DeveloperIndexPage> {
body: ListView(
padding: const EdgeInsets.all(8.0),
children: <Widget>[
_buildVerboseLogSwitch(context),
ElevatedButton(
onPressed: () => navToWidget(const VerboseLogPage()),
child: const Text('Show verbose log'),
),
ElevatedButton(
onPressed: () => navToWidget(const CustomFilterPage()),
child: const Text('Custom filter'),
Expand Down
240 changes: 240 additions & 0 deletions example/lib/page/developer/verbose_log_page.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,240 @@
import 'dart:io';

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

import '../../util/log_export.dart';

class VerboseLogPage extends StatefulWidget {
const VerboseLogPage({super.key});

@override
State<VerboseLogPage> createState() => _VerboseLogPageState();
}

class _LogUnit {
_LogUnit({required this.log});

final String log;

bool get isResultLog => log.contains('- result -');

bool get isInvokeLog => log.contains('- invoke -');

int? get swTime {
final reg = RegExp(r'Time: (\d+)ms');
final match = reg.firstMatch(log);
if (match != null) {
return int.parse(match.group(1)!);
}
return null;
}
}

class _VerboseLogPageState extends State<VerboseLogPage> {
final List<_LogUnit> logList = <_LogUnit>[];

final List<String> originList = <String>[];

@override
void initState() {
super.initState();
_loadLog();
}

List<_LogUnit> decodeUnit(String text) {
final List<_LogUnit> list = <_LogUnit>[];
final List<String> items = text.split('===');
for (final String item in items) {
list.add(_LogUnit(log: item.trim()));
}
return list;
}

Future<void> _loadLog() async {
final logFilePath = await PMVerboseLogUtil.shared.getLogFilePath();
final file = File(logFilePath);
if (!file.existsSync()) {
logList.clear();
setState(() {});
return;
}
final content = file.readAsStringSync();
logList.clear();
setState(() {
logList.addAll(decodeUnit(content));
});
}

Widget content(String text) {
final lines = text.split('\n');
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
for (final line in lines)
Text(
line,
maxLines: 1,
),
],
);
}

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('DevVerboseLogPage'),
actions: [
IconButton(
icon: const Icon(Icons.delete),
onPressed: () async {
final logFilePath =
await PMVerboseLogUtil.shared.getLogFilePath();
final file = File(logFilePath);
if (file.existsSync()) {
file.deleteSync();
}
_loadLog();
},
),
IconButton(
icon: const Icon(Icons.refresh),
onPressed: () {
_loadLog();
},
),
// 复制日志
IconButton(
icon: const Icon(Icons.copy),
onPressed: () {
final content = logList.map((e) => e.log).join('\n');
Clipboard.setData(ClipboardData(text: content));
},
),
],
),
body: Column(
children: [
_buildFunctions(),
_buildFilter(),
Expanded(
child: ListView.separated(
itemBuilder: (context, index) {
final item = logList[index];
return ListTile(
title: content(item.log),
tileColor: item.isResultLog
? Colors.green.withOpacity(0.1)
: item.isInvokeLog
? Colors.blue.withOpacity(0.1)
: null,
subtitle: item.swTime != null
? Text('Time: ${item.swTime}ms')
: null,
);
},
itemCount: logList.length,
separatorBuilder: (context, index) => const Divider(
height: 2,
),
),
),
],
),
);
}

Widget _buildFilter() {
return Container();
}

Widget functionItem(String text, VoidCallback onTap) {
return InkWell(
onTap: onTap,
child: Container(
padding: const EdgeInsets.all(8),
child: Text(text),
),
);
}

void showInfo(List<String> info) {
final text = info.join('\n');
showDialog(
context: context,
builder: (context) {
return AlertDialog(
content: Text(text),
actions: [
TextButton(
onPressed: () {
Navigator.pop(context);
},
child: const Text('OK'),
),
],
);
},
);
}

Widget _buildFunctions() {
return Wrap(
children: [
// analyze count
functionItem('Analyze Count', () {
final invokeCount = logList.where((e) => e.isInvokeLog).length;
final resultCount = logList.where((e) => e.isResultLog).length;

// time info
final timeList = logList
.where((e) => e.swTime != null)
.map((e) => e.swTime!)
.toList();

timeList.sort();

// 0~10ms
// 10~20ms
// 20~50ms
// 50~100ms
// 100ms+
final timeMap = <String, int>{
'0~10ms': 0,
'10~20ms': 0,
'20~50ms': 0,
'50~100ms': 0,
'100ms+': 0,
};

for (final time in timeList) {
if (time < 10) {
timeMap['0~10ms'] = timeMap['0~10ms']! + 1;
} else if (time < 20) {
timeMap['10~20ms'] = timeMap['10~20ms']! + 1;
} else if (time < 50) {
timeMap['20~50ms'] = timeMap['20~50ms']! + 1;
} else if (time < 100) {
timeMap['50~100ms'] = timeMap['50~100ms']! + 1;
} else {
timeMap['100ms+'] = timeMap['100ms+']! + 1;
}
}

final timeInfo = timeMap.entries
.where((e) => e.value > 0)
.map((e) => ' ${e.key}: ${e.value}')
.toList();

showInfo([
'Invoke Count: $invokeCount',
'Result Count: $resultCount',
'Time Info:',
...timeInfo,
]);
}),
],
);
}
}
32 changes: 32 additions & 0 deletions example/lib/util/log_export.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import 'package:path_provider/path_provider.dart';

/// Because the photo_manager plugin does not depend on the path_provider plugin,
/// we need to create a new class in the example to get the log file path.
class PMVerboseLogUtil {
PMVerboseLogUtil();

static final shared = PMVerboseLogUtil();

static String? _logDirPath;

String _logFilePath = '';

/// Get the log file path.
///
/// Use in the `PhotoManager.setLog` method.
Future<String> getLogFilePath() async {
if (_logFilePath.isNotEmpty) {
return _logFilePath;
}

if (_logDirPath == null) {
final cacheDir = await getApplicationCacheDirectory();
_logDirPath = cacheDir.path;
}

final timeStr = DateTime.now().toIso8601String();
_logFilePath = '$_logDirPath/pmlog-$timeStr.txt';

return _logFilePath;
}
}
Loading

0 comments on commit 46c2aef

Please sign in to comment.