1+ import 'dart:async' ;
2+ import 'dart:convert' ;
13import 'dart:io' ;
24import 'package:commet/client/components/push_notification/notification_content.dart' ;
35import 'package:commet/client/components/push_notification/notification_manager.dart' ;
@@ -6,13 +8,16 @@ import 'package:commet/config/build_config.dart';
68import 'package:commet/config/platform_utils.dart' ;
79import 'package:commet/diagnostic/diagnostics.dart' ;
810import 'package:commet/main.dart' ;
11+ import 'package:commet/ui/atoms/code_block.dart' ;
12+ import 'package:commet/ui/navigation/adaptive_dialog.dart' ;
913import 'package:commet/ui/navigation/navigation_utils.dart' ;
1014import 'package:commet/ui/pages/developer/benchmarks/timeline_viewer_benchmark.dart' ;
1115import 'package:commet/ui/pages/settings/categories/app/boolean_toggle.dart' ;
1216import 'package:commet/ui/pages/settings/categories/app/double_preference_slider.dart' ;
1317import 'package:commet/ui/pages/settings/categories/developer/cumulative_diagnostics_widget.dart' ;
1418import 'package:commet/utils/background_tasks/background_task_manager.dart' ;
1519import 'package:commet/utils/background_tasks/mock_tasks.dart' ;
20+ import 'package:commet/utils/system_processes_utils.dart' ;
1621import 'package:file_picker/file_picker.dart' ;
1722import 'package:flutter/material.dart' ;
1823import 'package:flutter/rendering.dart' ;
@@ -42,6 +47,7 @@ class _DeveloperSettingsPageState extends State<DeveloperSettingsPage> {
4247 if (PlatformUtils .isAndroid) shortcuts (),
4348 backgroundTasks (),
4449 dumpDatabases (),
50+ executeShellCommand (),
4551 tiamat.Panel (
4652 header: "Other Settings" ,
4753 mode: TileType .surfaceContainerLow,
@@ -153,6 +159,10 @@ class _DeveloperSettingsPageState extends State<DeveloperSettingsPage> {
153159 spacing: 8 ,
154160 runSpacing: 8 ,
155161 children: [
162+ tiamat.Button (
163+ text: "Maximize" ,
164+ onTap: () => windowManager.maximize (),
165+ ),
156166 tiamat.Button (
157167 text: "1280x720" ,
158168 onTap: () => windowManager.setSize (const Size (1280 , 720 )),
@@ -328,6 +338,51 @@ class _DeveloperSettingsPageState extends State<DeveloperSettingsPage> {
328338 );
329339 }
330340
341+ Widget executeShellCommand () {
342+ return ExpansionTile (
343+ title: const tiamat.Text .labelEmphasised ("Dangerous" ),
344+ backgroundColor: Theme .of (context).colorScheme.surfaceContainerLow,
345+ collapsedBackgroundColor:
346+ Theme .of (context).colorScheme.surfaceContainerLow,
347+ children: [
348+ Wrap (spacing: 8 , runSpacing: 8 , children: [
349+ tiamat.Button (
350+ text: "Get Process List" ,
351+ onTap: () async {
352+ var list = await SystemProcessesUtils .getProcessList ();
353+ AdaptiveDialog .show (
354+ context,
355+ builder: (context) {
356+ return Codeblock (
357+ text: list
358+ .map ((i) =>
359+ "[${i .processId }] = ${i .command } ${i .args }" )
360+ .join ("\n " ));
361+ },
362+ );
363+ },
364+ ),
365+ tiamat.Button (
366+ text: "Execute Shell Command" ,
367+ onTap: () async {
368+ var text = await AdaptiveDialog .textPrompt (context,
369+ title: "Execute Shell Command" );
370+ if (text != null ) {
371+ var command = text.split (" " );
372+ var exe = command.first;
373+ var args = command.sublist (1 );
374+
375+ var process = await Process .start (exe, args);
376+
377+ AdaptiveDialog .show (context,
378+ builder: (context) => ProcessOutputViewer (process));
379+ }
380+ }),
381+ ])
382+ ],
383+ );
384+ }
385+
331386 Widget dumpDatabases () {
332387 return ExpansionTile (
333388 title: const tiamat.Text .labelEmphasised ("Dump Databases" ),
@@ -365,3 +420,64 @@ class _DeveloperSettingsPageState extends State<DeveloperSettingsPage> {
365420 );
366421 }
367422}
423+
424+ class ProcessOutputViewer extends StatefulWidget {
425+ const ProcessOutputViewer (this .process, {super .key});
426+ final Process process;
427+
428+ @override
429+ State <ProcessOutputViewer > createState () => _ProcessOutputViewerState ();
430+ }
431+
432+ class _ProcessOutputViewerState extends State <ProcessOutputViewer > {
433+ String stdOut = "" ;
434+ String stdError = "" ;
435+
436+ late List <StreamSubscription > subs;
437+
438+ @override
439+ void initState () {
440+ super .initState ();
441+
442+ subs = [
443+ widget.process.stdout.transform (utf8.decoder).listen (onStdout),
444+ widget.process.stderr.transform (utf8.decoder).listen (onStderr),
445+ ];
446+ }
447+
448+ @override
449+ void dispose () {
450+ for (var sub in subs) {
451+ sub.cancel ();
452+ }
453+
454+ widget.process.kill ();
455+ super .dispose ();
456+ }
457+
458+ @override
459+ Widget build (BuildContext context) {
460+ return SizedBox (
461+ height: 1000 ,
462+ child: SingleChildScrollView (
463+ child: Codeblock (
464+ text: stdOut,
465+ clipboardText: stdOut,
466+ language: "stdout" ,
467+ ),
468+ ),
469+ );
470+ }
471+
472+ void onStderr (String event) {
473+ setState (() {
474+ stdError += event;
475+ });
476+ }
477+
478+ void onStdout (String event) {
479+ setState (() {
480+ stdOut += event;
481+ });
482+ }
483+ }
0 commit comments