clipshare_clipboard_listener 1.2.19 copy "clipshare_clipboard_listener: ^1.2.19" to clipboard
clipshare_clipboard_listener: ^1.2.19 copied to clipboard

Supports clipboard monitoring on Windows、Android、Linux、MacOS platforms, with support for text and images. For Android 10+ devices, background clipboard monitoring is supported (requires Shizuku).

example/lib/main.dart

import 'dart:io';
import 'dart:math';

import 'package:clipshare_clipboard_listener/clipboard_manager.dart';
import 'package:clipshare_clipboard_listener/enums.dart';
import 'package:clipshare_clipboard_listener/models/clipboard_source.dart';
import 'package:clipshare_clipboard_listener/models/notification_content_config.dart';
import 'package:desktop_multi_window/desktop_multi_window.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:hotkey_manager/hotkey_manager.dart';
import 'package:permission_handler/permission_handler.dart';

void main(List<String> args) {
  var isMultiWindow = args.firstOrNull == 'multi_window';
  if (isMultiWindow) {
    runApp(MultiWindow());
  } else {
    runApp(const MyApp());
  }
}

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

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> with ClipboardListener, WidgetsBindingObserver {
  String? type;
  String? content;
  ClipboardSource? source;
  EnvironmentType env = EnvironmentType.none;
  bool isGranted = false;
  bool excludeFormatEnabled = true;
  ClipboardListeningWay way = ClipboardListeningWay.logs;
  bool hasAlertWindowPermission = false;
  bool hasNotificationPermission = false;
  bool hasAccessibilityPermission = false;
  bool hasClipboardPermission = false;
  final controller = TextEditingController();

  @override
  void initState() {
    super.initState();
    clipboardManager.addListener(this);
    WidgetsBinding.instance.addObserver(this);
    initHotKey();
    initMultiWindowEvent();
    if (Platform.isAndroid) {
      clipboardManager.getCurrentEnvironment().then((env) {
        setState(() {
          this.env = env;
          this.isGranted = env != EnvironmentType.none;
        });
      });
      checkAndroidPermissions();
    }
    if (Platform.isWindows) {
      clipboardManager.isEnableExcludeFormat().then((enabled) {
        setState(() {
          excludeFormatEnabled = enabled;
        });
      });
    }
  }

  Future<void> checkAndroidPermissions() async {
    hasAlertWindowPermission = await Permission.systemAlertWindow.isGranted;
    hasNotificationPermission = await Permission.notification.isGranted;
    hasAccessibilityPermission = await clipboardManager.checkAccessibility();
    hasClipboardPermission = await clipboardManager.checkClipboardPermission();
    setState(() {});
  }

  @override
  void dispose() {
    super.dispose();
    clipboardManager.removeListener(this);
    WidgetsBinding.instance.removeObserver(this);
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Example'),
        ),
        body: SafeArea(
          child: Builder(builder: (context) {
            return SingleChildScrollView(
              scrollDirection: Axis.vertical,
              child: Column(
                children: [
                  const SizedBox(
                    height: 10,
                  ),
                  //region Android
                  Visibility(
                    visible: Platform.isAndroid,
                    child: Column(
                      crossAxisAlignment: CrossAxisAlignment.start,
                      children: [
                        Text('Current environment: ${env.name}, Status: $isGranted'),
                        Padding(
                          padding: const EdgeInsets.only(left: 10, right: 10),
                          child: Row(
                            children: [
                              GestureDetector(
                                onTap: () {
                                  clipboardManager.requestPermission(EnvironmentType.shizuku);
                                },
                                child: const Chip(label: Text("Request Shizuka")),
                              ),
                              const SizedBox(
                                width: 10,
                              ),
                              GestureDetector(
                                onTap: () {
                                  clipboardManager.requestPermission(EnvironmentType.root);
                                },
                                child: const Chip(label: Text("Request Root")),
                              ),
                              const SizedBox(
                                width: 10,
                              ),
                            ],
                          ),
                        ),
                        const Text('Permissions:'),
                        Padding(
                          padding: const EdgeInsets.only(left: 10, right: 10),
                          child: Wrap(
                            children: [
                              RawChip(
                                label: const Text("Alert Window"),
                                selected: hasAlertWindowPermission,
                                onSelected: (_) async {
                                  if (hasAlertWindowPermission) {
                                    return;
                                  }
                                  final result = await Permission.systemAlertWindow.request();
                                  print("result isDenied = ${result.isDenied}");
                                  if (result.isGranted) {
                                    setState(() {
                                      hasAlertWindowPermission = result.isGranted;
                                    });
                                    showSnackBarSuc(context, "SystemAlertWindow granted");
                                  } else {
                                    showSnackBarErr(context, "SystemAlertWindow denied");
                                  }
                                },
                              ),
                              const SizedBox(width: 10),
                              RawChip(
                                label: const Text("Notification"),
                                selected: hasNotificationPermission,
                                onSelected: (_) async {
                                  if (hasNotificationPermission) {
                                    return;
                                  }
                                  final result = await Permission.notification.request();
                                  if (result.isGranted) {
                                    setState(() {
                                      hasNotificationPermission = result.isGranted;
                                    });
                                    showSnackBarSuc(context, "Notification granted");
                                  } else {
                                    showSnackBarErr(context, "Notification denied");
                                  }
                                },
                              ),
                              const SizedBox(width: 10),
                              RawChip(
                                label: const Text("Accessibility"),
                                selected: hasAccessibilityPermission,
                                onSelected: (_) async {
                                  if (hasAccessibilityPermission) {
                                    return;
                                  }
                                  clipboardManager.requestAccessibility();
                                },
                              ),
                              const SizedBox(width: 10),
                              RawChip(
                                label: const Text("Clipboard"),
                                selected: hasClipboardPermission,
                                onSelected: (_) async {
                                  if (hasClipboardPermission) {
                                    return;
                                  }
                                  await clipboardManager.requestClipboardPermission();
                                  hasClipboardPermission = await clipboardManager.checkClipboardPermission();
                                  setState(() {});
                                },
                              ),
                            ],
                          ),
                        ),
                        Container(
                          margin: const EdgeInsets.symmetric(vertical: 5),
                          child: Text("Options:"),
                        ),
                        Padding(
                          padding: const EdgeInsets.only(left: 10, right: 10),
                          child: IntrinsicHeight(
                            child: Column(
                              crossAxisAlignment: CrossAxisAlignment.start,
                              children: [
                                GestureDetector(
                                  onTap: env == EnvironmentType.shizuku
                                      ? () {
                                          startListeningOnAndroid(
                                            context,
                                            env: EnvironmentType.shizuku,
                                            way: way,
                                          );
                                        }
                                      : null,
                                  child: Chip(
                                    label: Text(
                                      "Start listening by Shizuku",
                                      style: env == EnvironmentType.shizuku ? null : const TextStyle(color: Colors.grey),
                                    ),
                                  ),
                                ),
                                GestureDetector(
                                  onTap: env == EnvironmentType.root
                                      ? () {
                                          startListeningOnAndroid(
                                            context,
                                            env: EnvironmentType.root,
                                            way: way,
                                          );
                                        }
                                      : null,
                                  child: Chip(
                                    label: Text(
                                      "Start listening by Root",
                                      style: env == EnvironmentType.root ? null : const TextStyle(color: Colors.grey),
                                    ),
                                  ),
                                ),
                                GestureDetector(
                                  onTap: () {
                                    clipboardManager.stopListening();
                                    showSnackBarSuc(
                                      context,
                                      "Listening stopped",
                                    );
                                  },
                                  child: const Chip(label: Text("Stop listening")),
                                ),
                              ],
                            ),
                          ),
                        ),
                        Container(
                          margin: const EdgeInsets.symmetric(vertical: 5),
                          child: Text("listening way: ${way.name}"),
                        ),
                        Padding(
                          padding: const EdgeInsets.only(left: 10, right: 10),
                          child: Row(
                            children: [
                              RawChip(
                                label: const Text("Hidden API"),
                                selected: way == ClipboardListeningWay.hiddenApi,
                                onSelected: (_) async {
                                  setState(() {
                                    way = ClipboardListeningWay.hiddenApi;
                                  });
                                  await clipboardManager.stopListening();
                                  Future.delayed(const Duration(seconds: 1), () {
                                    startListeningOnAndroid(
                                      context,
                                      env: env,
                                      way: ClipboardListeningWay.hiddenApi,
                                    );
                                  });
                                },
                              ),
                              const SizedBox(width: 10),
                              RawChip(
                                label: const Text("System Logs"),
                                selected: way == ClipboardListeningWay.logs,
                                onSelected: (_) async {
                                  setState(() {
                                    way = ClipboardListeningWay.logs;
                                  });
                                  await clipboardManager.stopListening();
                                  Future.delayed(const Duration(seconds: 1), () {
                                    startListeningOnAndroid(
                                      context,
                                      env: env,
                                      way: ClipboardListeningWay.logs,
                                    );
                                  });
                                },
                              ),
                            ],
                          ),
                        ),
                      ],
                    ),
                  ),
                  //endregion

                  //region Linux
                  Visibility(
                    visible: !Platform.isAndroid,
                    child: Column(
                      children: [
                        GestureDetector(
                          onTap: () {
                            clipboardManager.startListening().then((res) {
                              if (res) {
                                showSnackBarSuc(
                                  context,
                                  "Listening started successfully",
                                );
                              } else {
                                showSnackBarErr(
                                  context,
                                  "Listening failed to start",
                                );
                              }
                            });
                          },
                          child: const Chip(label: Text("Start listening")),
                        ),
                        GestureDetector(
                          onTap: () {
                            clipboardManager.stopListening().then((res) {
                              showSnackBarSuc(
                                context,
                                "Listening stopped successfully",
                              );
                            }).catchError((err) {
                              showSnackBarErr(
                                context,
                                "Listening failed to stop",
                              );
                            });
                          },
                          child: const Chip(label: Text("Stop listening")),
                        ),
                      ],
                    ),
                  ),
                  //endregion
                  //region Windows
                  Column(
                    crossAxisAlignment: CrossAxisAlignment.center,
                    children: [
                      const Text("ExcludeClipboardContentFromMonitorProcessing:"),
                      Switch(
                          value: excludeFormatEnabled,
                          onChanged: (checked) async {
                            await clipboardManager.setExcludeFormatEnabled(checked);
                            setState(() {
                              excludeFormatEnabled = checked;
                            });
                          })
                    ],
                  ),
                  //endregion
                  Text('type: $type\n\ncontent:\n$content\n\nsource:${source?.name}\n\n'),
                  const SizedBox(height: 10),
                  if (source?.iconBytes != null)
                    Image.memory(
                      source!.iconBytes!,
                      height: 30,
                      width: 30,
                    ),
                  const SizedBox(height: 10),
                  TextField(controller: controller),
                  const SizedBox(
                    height: 10,
                  ),
                  GestureDetector(
                    onTap: () {
                      clipboardManager.copy(ClipboardContentType.text, controller.text);
                    },
                    child: const Chip(label: Text("Copy Input Data")),
                  ),
                  GestureDetector(
                    onTap: () {
                      clipboardManager.copy(ClipboardContentType.text, Random().nextInt(99999).toString());
                    },
                    child: const Chip(label: Text("Copy Random Data")),
                  ),
                  GestureDetector(
                    onTap: () {
                      const filePath = "/storage/emulated/0/DCIM/Camera/20231104_210215.jpg";
                      clipboardManager.copy(ClipboardContentType.image, filePath);
                    },
                    child: const Chip(label: Text("Copy Test Image(mannal set on code)")),
                  ),
                ],
              ),
            );
          }),
        ),
      ),
    );
  }

  @override
  void onClipboardChanged(ClipboardContentType type, String content, ClipboardSource? source) {
    print("type: ${type.name}, content: $content");
    setState(() {
      this.type = type.name;
      this.content = content;
    });
    setState(() {
      this.source = source;
    });
    var start = DateTime.now();
    clipboardManager.getLatestWriteClipboardSource().then((source) {
      if (source == null) {
        return;
      }
      final isTimeout = source.isTimeout(2000);
      print("source time: ${source.time?.toString()}, timeout: $isTimeout");
      var end = DateTime.now();
      print("source: ${source.name}, offset: ${end.difference(start).inMilliseconds}");
      if (isTimeout) {
        return;
      }
      setState(() {
        this.source = source;
      });
    }).catchError((err) {
      print("error: $err");
    });
  }

  @override
  void onPermissionStatusChanged(EnvironmentType environment, bool isGranted) {
    debugPrint("env: ${environment.name}, granted: $isGranted");
    setState(() {
      env = environment;
      this.isGranted = isGranted;
    });
  }

  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    switch (state) {
      case AppLifecycleState.resumed:
        if (Platform.isAndroid) {
          checkAndroidPermissions();
        }
        break;
      default:
    }
  }

  Future startListeningOnAndroid(
    BuildContext context, {
    NotificationContentConfig? notificationContentConfig,
    EnvironmentType? env,
    ClipboardListeningWay? way,
  }) {
    if (!Platform.isAndroid) return Future.value();
    //Android version>= 10
    if (!hasAlertWindowPermission) {
      showSnackBarErr(context, "No Alert Window permission");
      return Future.value();
    }
    //Android version>= 10
    if (!hasNotificationPermission) {
      showSnackBarErr(context, "No Notification permission");
      return Future.value();
    }
    return clipboardManager
        .startListening(
      env: env,
      way: way,
      notificationContentConfig: notificationContentConfig,
    )
        .then((res) {
      if (res) {
        showSnackBarSuc(
          context,
          "Listening started successfully",
        );
      } else {
        showSnackBarErr(
          context,
          "Listening failed to start",
        );
      }
    });
  }

  void showSnackBar(BuildContext context, String text, Color color) {
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(
        content: Text(text),
        backgroundColor: color,
      ),
    );
  }

  void showSnackBarSuc(BuildContext context, String text) {
    showSnackBar(context, text, Colors.lightBlue);
  }

  void showSnackBarErr(BuildContext context, String text) {
    showSnackBar(context, text, Colors.redAccent);
  }

  Future<void> initHotKey() async {
    await hotKeyManager.unregisterAll();
    final key = HotKey(
      key: PhysicalKeyboardKey.keyG,
      modifiers: [HotKeyModifier.alt],
      scope: HotKeyScope.system,
    );
    await hotKeyManager.register(
      key,
      keyDownHandler: (hotKey) async {
        await clipboardManager.storeCurrentWindowHwnd();
        //createWindow里面的参数必须传
        final window = await DesktopMultiWindow.createWindow('{}');
        window
          ..setFrame(const Offset(500, 500) & const Size(355.0, 630.0))
          ..setTitle('Window')
          ..show();
      },
    );
  }

  void initMultiWindowEvent() {
    DesktopMultiWindow.setMethodHandler((
      MethodCall call,
      int fromWindowId,
    ) {
      Clipboard.setData(ClipboardData(text: DateTime.now().toString()));
      clipboardManager.pasteToPreviousWindow();
      return Future.value();
    });
  }
}

class MultiWindow extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text("MultiWindow"),
        ),
        body: Column(
          children: [
            TextButton(
                onPressed: () {
                  DesktopMultiWindow.invokeMethod(
                    0,
                    "methodName",
                    "{}",
                  );
                },
                child: const Text("click me"))
          ],
        ),
      ),
    );
  }
}
2
likes
140
points
529
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

Supports clipboard monitoring on Windows、Android、Linux、MacOS platforms, with support for text and images. For Android 10+ devices, background clipboard monitoring is supported (requires Shizuku).

Homepage
Repository (GitHub)
View/report issues

License

MIT (license)

Dependencies

flutter, path, plugin_platform_interface

More

Packages that depend on clipshare_clipboard_listener

Packages that implement clipshare_clipboard_listener