manager.dart 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  1. // The plugin manager is a singleton class that manages the plugins.
  2. // 1. It merge metadata and the desc of plugins.
  3. import 'dart:convert';
  4. import 'dart:collection';
  5. import 'package:flutter/material.dart';
  6. const String kValueTrue = '1';
  7. const String kValueFalse = '0';
  8. class ConfigItem {
  9. String key;
  10. String description;
  11. String defaultValue;
  12. ConfigItem(this.key, this.defaultValue, this.description);
  13. ConfigItem.fromJson(Map<String, dynamic> json)
  14. : key = json['key'] ?? '',
  15. description = json['description'] ?? '',
  16. defaultValue = json['default'] ?? '';
  17. static String get trueValue => kValueTrue;
  18. static String get falseValue => kValueFalse;
  19. static bool isTrue(String value) => value == kValueTrue;
  20. static bool isFalse(String value) => value == kValueFalse;
  21. }
  22. class UiType {
  23. String key;
  24. String text;
  25. String tooltip;
  26. String action;
  27. UiType(this.key, this.text, this.tooltip, this.action);
  28. UiType.fromJson(Map<String, dynamic> json)
  29. : key = json['key'] ?? '',
  30. text = json['text'] ?? '',
  31. tooltip = json['tooltip'] ?? '',
  32. action = json['action'] ?? '';
  33. static UiType? create(Map<String, dynamic> json) {
  34. if (json['t'] == 'Button') {
  35. return UiButton.fromJson(json['c']);
  36. } else if (json['t'] == 'Checkbox') {
  37. return UiCheckbox.fromJson(json['c']);
  38. } else {
  39. return null;
  40. }
  41. }
  42. }
  43. class UiButton extends UiType {
  44. String icon;
  45. UiButton(
  46. {required String key,
  47. required String text,
  48. required this.icon,
  49. required String tooltip,
  50. required String action})
  51. : super(key, text, tooltip, action);
  52. UiButton.fromJson(Map<String, dynamic> json)
  53. : icon = json['icon'] ?? '',
  54. super.fromJson(json);
  55. }
  56. class UiCheckbox extends UiType {
  57. UiCheckbox(
  58. {required String key,
  59. required String text,
  60. required String tooltip,
  61. required String action})
  62. : super(key, text, tooltip, action);
  63. UiCheckbox.fromJson(Map<String, dynamic> json) : super.fromJson(json);
  64. }
  65. class Location {
  66. // location key:
  67. // host|main|settings|plugin
  68. // client|remote|toolbar|display
  69. HashMap<String, UiType> ui;
  70. Location(this.ui);
  71. Location.fromJson(Map<String, dynamic> json) : ui = HashMap() {
  72. (json['ui'] as Map<String, dynamic>).forEach((key, value) {
  73. var ui = UiType.create(value);
  74. if (ui != null) {
  75. this.ui[ui.key] = ui;
  76. }
  77. });
  78. }
  79. }
  80. class PublishInfo {
  81. PublishInfo({
  82. required this.lastReleased,
  83. required this.published,
  84. });
  85. final DateTime lastReleased;
  86. final DateTime published;
  87. }
  88. class Meta {
  89. Meta({
  90. required this.id,
  91. required this.name,
  92. required this.version,
  93. required this.description,
  94. required this.author,
  95. required this.home,
  96. required this.license,
  97. required this.publishInfo,
  98. required this.source,
  99. });
  100. final String id;
  101. final String name;
  102. final String version;
  103. final String description;
  104. final String author;
  105. final String home;
  106. final String license;
  107. final PublishInfo publishInfo;
  108. final String source;
  109. }
  110. class SourceInfo {
  111. String name; // 1. RustDesk github 2. Local
  112. String url;
  113. String description;
  114. SourceInfo({
  115. required this.name,
  116. required this.url,
  117. required this.description,
  118. });
  119. }
  120. class PluginInfo with ChangeNotifier {
  121. SourceInfo sourceInfo;
  122. Meta meta;
  123. String installedVersion; // It is empty if not installed.
  124. String failedMsg;
  125. String invalidReason; // It is empty if valid.
  126. PluginInfo({
  127. required this.sourceInfo,
  128. required this.meta,
  129. required this.installedVersion,
  130. required this.invalidReason,
  131. this.failedMsg = '',
  132. });
  133. bool get installed => installedVersion.isNotEmpty;
  134. bool get needUpdate => installed && installedVersion != meta.version;
  135. void setInstall(String msg) {
  136. if (msg == "finished") {
  137. msg = '';
  138. }
  139. failedMsg = msg;
  140. if (msg.isEmpty) {
  141. installedVersion = meta.version;
  142. }
  143. notifyListeners();
  144. }
  145. void setUninstall(String msg) {
  146. failedMsg = msg;
  147. if (msg.isEmpty) {
  148. installedVersion = '';
  149. }
  150. notifyListeners();
  151. }
  152. }
  153. class PluginManager with ChangeNotifier {
  154. String failedReason = ''; // The reason of failed to load plugins.
  155. final List<PluginInfo> _plugins = [];
  156. PluginManager._();
  157. static final PluginManager _instance = PluginManager._();
  158. static PluginManager get instance => _instance;
  159. List<PluginInfo> get plugins => _plugins;
  160. PluginInfo? getPlugin(String id) {
  161. for (var p in _plugins) {
  162. if (p.meta.id == id) {
  163. return p;
  164. }
  165. }
  166. return null;
  167. }
  168. void handleEvent(Map<String, dynamic> evt) {
  169. if (evt['plugin_list'] != null) {
  170. _handlePluginList(evt['plugin_list']);
  171. } else if (evt['plugin_install'] != null && evt['id'] != null) {
  172. _handlePluginInstall(evt['id'], evt['plugin_install']);
  173. } else if (evt['plugin_uninstall'] != null && evt['id'] != null) {
  174. _handlePluginUninstall(evt['id'], evt['plugin_uninstall']);
  175. } else {
  176. debugPrint('Failed to handle manager event: $evt');
  177. }
  178. }
  179. void _sortPlugins() {
  180. plugins.sort((a, b) {
  181. if (a.installed) {
  182. return -1;
  183. } else if (b.installed) {
  184. return 1;
  185. } else {
  186. return 0;
  187. }
  188. });
  189. }
  190. void _handlePluginList(String pluginList) {
  191. _plugins.clear();
  192. try {
  193. for (var p in json.decode(pluginList) as List<dynamic>) {
  194. final plugin = _getPluginFromEvent(p);
  195. if (plugin == null) {
  196. continue;
  197. }
  198. _plugins.add(plugin);
  199. }
  200. } catch (e) {
  201. debugPrint('Failed to decode $e, plugin list \'$pluginList\'');
  202. }
  203. _sortPlugins();
  204. notifyListeners();
  205. }
  206. void _handlePluginInstall(String id, String msg) {
  207. debugPrint('Plugin \'$id\' install msg $msg');
  208. for (var i = 0; i < _plugins.length; i++) {
  209. if (_plugins[i].meta.id == id) {
  210. _plugins[i].setInstall(msg);
  211. _sortPlugins();
  212. notifyListeners();
  213. return;
  214. }
  215. }
  216. }
  217. void _handlePluginUninstall(String id, String msg) {
  218. debugPrint('Plugin \'$id\' uninstall msg $msg');
  219. for (var i = 0; i < _plugins.length; i++) {
  220. if (_plugins[i].meta.id == id) {
  221. _plugins[i].setUninstall(msg);
  222. _sortPlugins();
  223. notifyListeners();
  224. return;
  225. }
  226. }
  227. }
  228. PluginInfo? _getPluginFromEvent(Map<String, dynamic> evt) {
  229. final s = evt['source'];
  230. assert(s != null, 'Source is null');
  231. if (s == null) {
  232. return null;
  233. }
  234. final source = SourceInfo(
  235. name: s['name'],
  236. url: s['url'] ?? '',
  237. description: s['description'] ?? '',
  238. );
  239. final m = evt['meta'];
  240. assert(m != null, 'Meta is null');
  241. if (m == null) {
  242. return null;
  243. }
  244. late DateTime lastReleased;
  245. late DateTime published;
  246. try {
  247. lastReleased = DateTime.parse(
  248. m['publish_info']?['last_released'] ?? '1970-01-01T00+00:00');
  249. } catch (e) {
  250. lastReleased = DateTime.utc(1970);
  251. }
  252. try {
  253. published = DateTime.parse(
  254. m['publish_info']?['published'] ?? '1970-01-01T00+00:00');
  255. } catch (e) {
  256. published = DateTime.utc(1970);
  257. }
  258. final meta = Meta(
  259. id: m['id'],
  260. name: m['name'],
  261. version: m['version'],
  262. description: m['description'] ?? '',
  263. author: m['author'],
  264. home: m['home'] ?? '',
  265. license: m['license'] ?? '',
  266. source: m['source'] ?? '',
  267. publishInfo:
  268. PublishInfo(lastReleased: lastReleased, published: published),
  269. );
  270. return PluginInfo(
  271. sourceInfo: source,
  272. meta: meta,
  273. installedVersion: evt['installed_version'],
  274. invalidReason: evt['invalid_reason'] ?? '',
  275. );
  276. }
  277. }
  278. PluginManager get pluginManager => PluginManager.instance;