enable rust default option
Signed-off-by: fufesou <shuanglongchen@yeah.net>
This commit is contained in:
parent
7bb39f5607
commit
75d8168070
@ -246,13 +246,12 @@ class _ConnectionTabPageState extends State<ConnectionTabPage> {
|
||||
dismissOnClicked: true,
|
||||
),
|
||||
],
|
||||
curOptionGetter: () async {
|
||||
return await bind.sessionGetOption(id: key, arg: 'view-style') ??
|
||||
'adaptive';
|
||||
},
|
||||
curOptionGetter: () async =>
|
||||
// null means peer id is not found, which there's no need to care about
|
||||
await bind.sessionGetViewStyle(id: key) ?? '',
|
||||
optionSetter: (String oldValue, String newValue) async {
|
||||
await bind.sessionPeerOption(
|
||||
id: key, name: "view-style", value: newValue);
|
||||
await bind.sessionSetViewStyle(
|
||||
id: key, value: newValue);
|
||||
ffi.canvasModel.updateViewStyle();
|
||||
cancelFunc();
|
||||
},
|
||||
|
@ -22,7 +22,7 @@ import './popup_menu.dart';
|
||||
import './material_mod_popup_menu.dart' as mod_menu;
|
||||
|
||||
class MenubarState {
|
||||
final kStoreKey = "remoteMenubarState";
|
||||
final kStoreKey = 'remoteMenubarState';
|
||||
late RxBool show;
|
||||
late RxBool _pin;
|
||||
|
||||
@ -195,7 +195,7 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
|
||||
}
|
||||
|
||||
_updateScreen() async {
|
||||
final v = await DesktopMultiWindow.invokeMethod(0, "get_window_info", "");
|
||||
final v = await DesktopMultiWindow.invokeMethod(0, 'get_window_info', '');
|
||||
final String valueStr = v;
|
||||
if (valueStr.isEmpty) {
|
||||
_screen = null;
|
||||
@ -322,7 +322,7 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
|
||||
child: Obx(() {
|
||||
RxInt display = CurrentDisplayState.find(widget.id);
|
||||
return Text(
|
||||
"${display.value + 1}/${pi.displays.length}",
|
||||
'${display.value + 1}/${pi.displays.length}',
|
||||
style: const TextStyle(
|
||||
color: _MenubarTheme.commonColor, fontSize: 8),
|
||||
);
|
||||
@ -595,10 +595,10 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
|
||||
));
|
||||
}
|
||||
}
|
||||
if (perms["restart"] != false &&
|
||||
(pi.platform == "Linux" ||
|
||||
pi.platform == "Windows" ||
|
||||
pi.platform == "Mac OS")) {
|
||||
if (perms['restart'] != false &&
|
||||
(pi.platform == 'Linux' ||
|
||||
pi.platform == 'Windows' ||
|
||||
pi.platform == 'Mac OS')) {
|
||||
displayMenu.add(MenuEntryButton<String>(
|
||||
childBuilder: (TextStyle? style) => Text(
|
||||
translate('Restart Remote Device'),
|
||||
@ -629,14 +629,14 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
|
||||
displayMenu.add(MenuEntryButton<String>(
|
||||
childBuilder: (TextStyle? style) => Obx(() => Text(
|
||||
translate(
|
||||
'${BlockInputState.find(widget.id).value ? "Unb" : "B"}lock user input'),
|
||||
'${BlockInputState.find(widget.id).value ? 'Unb' : 'B'}lock user input'),
|
||||
style: style,
|
||||
)),
|
||||
proc: () {
|
||||
RxBool blockInput = BlockInputState.find(widget.id);
|
||||
bind.sessionToggleOption(
|
||||
id: widget.id,
|
||||
value: '${blockInput.value ? "un" : ""}block-input');
|
||||
value: '${blockInput.value ? 'un' : ''}block-input');
|
||||
blockInput.value = !blockInput.value;
|
||||
},
|
||||
padding: padding,
|
||||
@ -671,7 +671,7 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
|
||||
// ClipboardData? data =
|
||||
// await Clipboard.getData(Clipboard.kTextPlain);
|
||||
// if (data != null && data.text != null) {
|
||||
// bind.sessionInputString(id: widget.id, value: data.text ?? "");
|
||||
// bind.sessionInputString(id: widget.id, value: data.text ?? '');
|
||||
// }
|
||||
// }();
|
||||
// },
|
||||
@ -679,18 +679,6 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
|
||||
// dismissOnClicked: true,
|
||||
// ));
|
||||
// }
|
||||
|
||||
displayMenu.add(MenuEntryButton<String>(
|
||||
childBuilder: (TextStyle? style) => Text(
|
||||
translate('Reset canvas'),
|
||||
style: style,
|
||||
),
|
||||
proc: () {
|
||||
widget.ffi.cursorModel.reset();
|
||||
},
|
||||
padding: padding,
|
||||
dismissOnClicked: true,
|
||||
));
|
||||
}
|
||||
|
||||
return displayMenu;
|
||||
@ -740,14 +728,11 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
|
||||
dismissOnClicked: true,
|
||||
),
|
||||
],
|
||||
curOptionGetter: () async {
|
||||
return await bind.sessionGetOption(
|
||||
id: widget.id, arg: 'view-style') ??
|
||||
'adaptive';
|
||||
},
|
||||
curOptionGetter: () async =>
|
||||
// null means peer id is not found, which there's no need to care about
|
||||
await bind.sessionGetViewStyle(id: widget.id) ?? '',
|
||||
optionSetter: (String oldValue, String newValue) async {
|
||||
await bind.sessionPeerOption(
|
||||
id: widget.id, name: "view-style", value: newValue);
|
||||
await bind.sessionSetViewStyle(id: widget.id, value: newValue);
|
||||
widget.ffi.canvasModel.updateViewStyle();
|
||||
},
|
||||
padding: padding,
|
||||
@ -768,14 +753,11 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
|
||||
dismissOnClicked: true,
|
||||
),
|
||||
],
|
||||
curOptionGetter: () async {
|
||||
return await bind.sessionGetOption(
|
||||
id: widget.id, arg: 'scroll-style') ??
|
||||
'';
|
||||
},
|
||||
curOptionGetter: () async =>
|
||||
// null means peer id is not found, which there's no need to care about
|
||||
await bind.sessionGetScrollStyle(id: widget.id) ?? '',
|
||||
optionSetter: (String oldValue, String newValue) async {
|
||||
await bind.sessionPeerOption(
|
||||
id: widget.id, name: "scroll-style", value: newValue);
|
||||
await bind.sessionSetScrollStyle(id: widget.id, value: newValue);
|
||||
widget.ffi.canvasModel.updateScrollStyle();
|
||||
},
|
||||
padding: padding,
|
||||
@ -805,12 +787,9 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
|
||||
value: 'custom',
|
||||
dismissOnClicked: true),
|
||||
],
|
||||
curOptionGetter: () async {
|
||||
String quality =
|
||||
await bind.sessionGetImageQuality(id: widget.id) ?? 'balanced';
|
||||
if (quality == '') quality = 'balanced';
|
||||
return quality;
|
||||
},
|
||||
curOptionGetter: () async =>
|
||||
// null means peer id is not found, which there's no need to care about
|
||||
await bind.sessionGetImageQuality(id: widget.id) ?? '',
|
||||
optionSetter: (String oldValue, String newValue) async {
|
||||
if (oldValue != newValue) {
|
||||
await bind.sessionSetImageQuality(id: widget.id, value: newValue);
|
||||
@ -1075,14 +1054,14 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
|
||||
}
|
||||
return list;
|
||||
},
|
||||
curOptionGetter: () async {
|
||||
return await bind.sessionGetOption(
|
||||
id: widget.id, arg: 'codec-preference') ??
|
||||
'auto';
|
||||
},
|
||||
curOptionGetter: () async =>
|
||||
// null means peer id is not found, which there's no need to care about
|
||||
await bind.sessionGetOption(
|
||||
id: widget.id, arg: 'codec-preference') ??
|
||||
'',
|
||||
optionSetter: (String oldValue, String newValue) async {
|
||||
await bind.sessionPeerOption(
|
||||
id: widget.id, name: "codec-preference", value: newValue);
|
||||
id: widget.id, name: 'codec-preference', value: newValue);
|
||||
bind.sessionChangePreferCodec(id: widget.id);
|
||||
},
|
||||
padding: padding,
|
||||
@ -1195,9 +1174,8 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
|
||||
MenuEntryRadioOption(text: translate('Legacy mode'), value: 'legacy'),
|
||||
MenuEntryRadioOption(text: translate('Map mode'), value: 'map'),
|
||||
],
|
||||
curOptionGetter: () async {
|
||||
return await bind.sessionGetKeyboardName(id: widget.id);
|
||||
},
|
||||
curOptionGetter: () async =>
|
||||
await bind.sessionGetKeyboardName(id: widget.id),
|
||||
optionSetter: (String oldValue, String newValue) async {
|
||||
await bind.sessionSetKeyboardMode(
|
||||
id: widget.id, keyboardMode: newValue);
|
||||
@ -1229,16 +1207,16 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
|
||||
void showSetOSPassword(
|
||||
String id, bool login, OverlayDialogManager dialogManager) async {
|
||||
final controller = TextEditingController();
|
||||
var password = await bind.sessionGetOption(id: id, arg: "os-password") ?? "";
|
||||
var autoLogin = await bind.sessionGetOption(id: id, arg: "auto-login") != "";
|
||||
var password = await bind.sessionGetOption(id: id, arg: 'os-password') ?? '';
|
||||
var autoLogin = await bind.sessionGetOption(id: id, arg: 'auto-login') != '';
|
||||
controller.text = password;
|
||||
dialogManager.show((setState, close) {
|
||||
submit() {
|
||||
var text = controller.text.trim();
|
||||
bind.sessionPeerOption(id: id, name: "os-password", value: text);
|
||||
bind.sessionPeerOption(id: id, name: 'os-password', value: text);
|
||||
bind.sessionPeerOption(
|
||||
id: id, name: "auto-login", value: autoLogin ? 'Y' : '');
|
||||
if (text != "" && login) {
|
||||
id: id, name: 'auto-login', value: autoLogin ? 'Y' : '');
|
||||
if (text != '' && login) {
|
||||
bind.sessionInputOsPassword(id: id, value: text);
|
||||
}
|
||||
close();
|
||||
@ -1285,7 +1263,7 @@ void showAuditDialog(String id, dialogManager) async {
|
||||
dialogManager.show((setState, close) {
|
||||
submit() {
|
||||
var text = controller.text.trim();
|
||||
if (text != "") {
|
||||
if (text != '') {
|
||||
bind.sessionSendNote(id: id, note: text);
|
||||
}
|
||||
close();
|
||||
@ -1324,11 +1302,11 @@ void showAuditDialog(String id, dialogManager) async {
|
||||
keyboardType: TextInputType.multiline,
|
||||
textInputAction: TextInputAction.newline,
|
||||
decoration: const InputDecoration.collapsed(
|
||||
hintText: "input note here",
|
||||
hintText: 'input note here',
|
||||
),
|
||||
// inputFormatters: [
|
||||
// LengthLimitingTextInputFormatter(16),
|
||||
// // FilteringTextInputFormatter(RegExp(r"[a-zA-z][a-zA-z0-9\_]*"), allow: true)
|
||||
// // FilteringTextInputFormatter(RegExp(r'[a-zA-z][a-zA-z0-9\_]*'), allow: true)
|
||||
// ],
|
||||
maxLines: null,
|
||||
maxLength: 256,
|
||||
|
@ -474,7 +474,7 @@ class _RemotePageState extends State<RemotePage> {
|
||||
},
|
||||
onTwoFingerScaleEnd: (d) {
|
||||
_scale = 1;
|
||||
bind.sessionPeerOption(id: widget.id, name: "view-style", value: "");
|
||||
bind.sessionSetViewStyle(id: widget.id, value: "");
|
||||
},
|
||||
onThreeFingerVerticalDragUpdate: gFFI.ffiModel.isPeerAndroid
|
||||
? null
|
||||
@ -1001,7 +1001,7 @@ void showOptions(
|
||||
setState(() {
|
||||
viewStyle = value;
|
||||
bind
|
||||
.sessionPeerOption(id: id, name: "view-style", value: value)
|
||||
.sessionSetViewStyle(id: id, value: value)
|
||||
.then((_) => gFFI.canvasModel.updateViewStyle());
|
||||
});
|
||||
}
|
||||
|
@ -413,7 +413,7 @@ class ImageModel with ChangeNotifier {
|
||||
await initializeCursorAndCanvas(parent.target!);
|
||||
}
|
||||
if (parent.target?.ffiModel.isPeerAndroid ?? false) {
|
||||
bind.sessionPeerOption(id: id, name: 'view-style', value: 'adaptive');
|
||||
bind.sessionSetViewStyle(id: id, value: 'adaptive');
|
||||
parent.target?.canvasModel.updateViewStyle();
|
||||
}
|
||||
}
|
||||
@ -535,7 +535,7 @@ class CanvasModel with ChangeNotifier {
|
||||
double get scrollY => _scrollY;
|
||||
|
||||
updateViewStyle() async {
|
||||
final style = await bind.sessionGetOption(id: id, arg: 'view-style');
|
||||
final style = await bind.sessionGetViewStyle(id: id);
|
||||
if (style == null) {
|
||||
return;
|
||||
}
|
||||
@ -561,7 +561,7 @@ class CanvasModel with ChangeNotifier {
|
||||
}
|
||||
|
||||
updateScrollStyle() async {
|
||||
final style = await bind.sessionGetOption(id: id, arg: 'scroll-style');
|
||||
final style = await bind.sessionGetScrollStyle(id: id);
|
||||
if (style == 'scrollbar') {
|
||||
_scrollStyle = ScrollStyle.scrollbar;
|
||||
_scrollX = 0.0;
|
||||
|
@ -9,6 +9,7 @@ use std::{
|
||||
|
||||
use anyhow::Result;
|
||||
use rand::Rng;
|
||||
use serde as de;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use sodiumoxide::crypto::sign;
|
||||
|
||||
@ -79,6 +80,26 @@ pub const RS_PUB_KEY: &'static str = "OeVuKk5nlHiXp+APNn0Y3pC1Iwpwn44JGqrQCsWqmB
|
||||
pub const RENDEZVOUS_PORT: i32 = 21116;
|
||||
pub const RELAY_PORT: i32 = 21117;
|
||||
|
||||
macro_rules! serde_field_string {
|
||||
($default_func:ident, $de_func:ident, $default_expr:expr) => {
|
||||
fn $default_func() -> String {
|
||||
$default_expr
|
||||
}
|
||||
|
||||
fn $de_func<'de, D>(deserializer: D) -> Result<String, D::Error>
|
||||
where
|
||||
D: de::Deserializer<'de>,
|
||||
{
|
||||
let s: &str = de::Deserialize::deserialize(deserializer)?;
|
||||
Ok(if s.is_empty() {
|
||||
Self::$default_func()
|
||||
} else {
|
||||
s.to_owned()
|
||||
})
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||
pub enum NetworkType {
|
||||
Direct,
|
||||
@ -141,9 +162,20 @@ pub struct PeerConfig {
|
||||
pub size_ft: Size,
|
||||
#[serde(default)]
|
||||
pub size_pf: Size,
|
||||
#[serde(default)]
|
||||
pub view_style: String, // original (default), scale
|
||||
#[serde(default)]
|
||||
#[serde(
|
||||
default = "PeerConfig::default_view_style",
|
||||
deserialize_with = "PeerConfig::deserialize_view_style"
|
||||
)]
|
||||
pub view_style: String,
|
||||
#[serde(
|
||||
default = "PeerConfig::default_scroll_style",
|
||||
deserialize_with = "PeerConfig::deserialize_scroll_style"
|
||||
)]
|
||||
pub scroll_style: String,
|
||||
#[serde(
|
||||
default = "PeerConfig::default_image_quality",
|
||||
deserialize_with = "PeerConfig::deserialize_image_quality"
|
||||
)]
|
||||
pub image_quality: String,
|
||||
#[serde(default)]
|
||||
pub custom_image_quality: Vec<i32>,
|
||||
@ -167,7 +199,10 @@ pub struct PeerConfig {
|
||||
pub show_quality_monitor: bool,
|
||||
|
||||
// The other scalar value must before this
|
||||
#[serde(default)]
|
||||
#[serde(
|
||||
default,
|
||||
deserialize_with = "PeerConfig::deserialize_options"
|
||||
)]
|
||||
pub options: HashMap<String, String>,
|
||||
// Various data for flutter ui
|
||||
#[serde(default)]
|
||||
@ -400,7 +435,9 @@ impl Config {
|
||||
#[cfg(target_os = "macos")]
|
||||
let org = ORG.read().unwrap().clone();
|
||||
// /var/root for root
|
||||
if let Some(project) = directories_next::ProjectDirs::from("", &org, &*APP_NAME.read().unwrap()) {
|
||||
if let Some(project) =
|
||||
directories_next::ProjectDirs::from("", &org, &*APP_NAME.read().unwrap())
|
||||
{
|
||||
let mut path = patch(project.config_dir().to_path_buf());
|
||||
path.push(p);
|
||||
return path;
|
||||
@ -896,6 +933,21 @@ impl PeerConfig {
|
||||
}
|
||||
Default::default()
|
||||
}
|
||||
|
||||
serde_field_string!(default_view_style, deserialize_view_style, "original".to_owned());
|
||||
serde_field_string!(default_scroll_style, deserialize_scroll_style, "scrollauto".to_owned());
|
||||
serde_field_string!(default_image_quality, deserialize_image_quality, "balanced".to_owned());
|
||||
|
||||
fn deserialize_options<'de, D>(deserializer: D) -> Result<HashMap<String, String>, D::Error>
|
||||
where
|
||||
D: de::Deserializer<'de>,
|
||||
{
|
||||
let mut mp: HashMap<String, String> = de::Deserialize::deserialize(deserializer)?;
|
||||
if !mp.contains_key("codec-preference") {
|
||||
mp.insert("codec-preference".to_owned(), "auto".to_owned());
|
||||
}
|
||||
Ok(mp)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Serialize, Deserialize, Clone)]
|
||||
|
@ -974,6 +974,8 @@ impl LoginConfigHandler {
|
||||
self.save_config(config);
|
||||
}
|
||||
|
||||
//to-do: too many dup code below.
|
||||
|
||||
/// Save view style to the current config.
|
||||
///
|
||||
/// # Arguments
|
||||
@ -985,13 +987,24 @@ impl LoginConfigHandler {
|
||||
self.save_config(config);
|
||||
}
|
||||
|
||||
/// Save scroll style to the current config.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `value` - The view style to be saved.
|
||||
pub fn save_scroll_style(&mut self, value: String) {
|
||||
let mut config = self.load_config();
|
||||
config.scroll_style = value;
|
||||
self.save_config(config);
|
||||
}
|
||||
|
||||
/// Set a ui config of flutter for handler's [`PeerConfig`].
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `k` - key of option
|
||||
/// * `v` - value of option
|
||||
pub fn set_ui_flutter(&mut self, k: String, v: String) {
|
||||
pub fn save_ui_flutter(&mut self, k: String, v: String) {
|
||||
let mut config = self.load_config();
|
||||
config.ui_flutter.insert(k, v);
|
||||
self.save_config(config);
|
||||
|
@ -170,7 +170,7 @@ pub fn session_get_flutter_config(id: String, k: String) -> Option<String> {
|
||||
|
||||
pub fn session_set_flutter_config(id: String, k: String, v: String) {
|
||||
if let Some(session) = SESSIONS.write().unwrap().get_mut(&id) {
|
||||
session.set_flutter_config(k, v);
|
||||
session.save_flutter_config(k, v);
|
||||
}
|
||||
}
|
||||
|
||||
@ -182,6 +182,34 @@ pub fn set_local_flutter_config(k: String, v: String) {
|
||||
ui_interface::set_local_flutter_config(k, v);
|
||||
}
|
||||
|
||||
pub fn session_get_view_style(id: String) -> Option<String> {
|
||||
if let Some(session) = SESSIONS.read().unwrap().get(&id) {
|
||||
Some(session.get_view_style())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn session_set_view_style(id: String, value: String) {
|
||||
if let Some(session) = SESSIONS.write().unwrap().get_mut(&id) {
|
||||
session.save_view_style(value);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn session_get_scroll_style(id: String) -> Option<String> {
|
||||
if let Some(session) = SESSIONS.read().unwrap().get(&id) {
|
||||
Some(session.get_scroll_style())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn session_set_scroll_style(id: String, value: String) {
|
||||
if let Some(session) = SESSIONS.write().unwrap().get_mut(&id) {
|
||||
session.save_scroll_style(value);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn session_get_image_quality(id: String) -> Option<String> {
|
||||
if let Some(session) = SESSIONS.read().unwrap().get(&id) {
|
||||
Some(session.get_image_quality())
|
||||
|
@ -25,12 +25,16 @@ use hbb_common::tokio::sync::{
|
||||
mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender},
|
||||
Mutex as TokioMutex,
|
||||
};
|
||||
#[cfg(not(windows))]
|
||||
use scrap::Capturer;
|
||||
use scrap::{
|
||||
codec::{Encoder, EncoderCfg, HwEncoderConfig},
|
||||
record::{Recorder, RecorderContext},
|
||||
vpxcodec::{VpxEncoderConfig, VpxVideoCodecId},
|
||||
Capturer, Display, TraitCapturer,
|
||||
Display, TraitCapturer,
|
||||
};
|
||||
#[cfg(windows)]
|
||||
use std::sync::Once;
|
||||
use std::{
|
||||
collections::HashSet,
|
||||
io::ErrorKind::WouldBlock,
|
||||
@ -38,8 +42,6 @@ use std::{
|
||||
time::{self, Duration, Instant},
|
||||
};
|
||||
#[cfg(windows)]
|
||||
use std::sync::Once;
|
||||
#[cfg(windows)]
|
||||
use virtual_display;
|
||||
|
||||
pub const SCRAP_UBUNTU_HIGHER_REQUIRED: &str = "Wayland requires Ubuntu 21.04 or higher version.";
|
||||
|
@ -79,6 +79,10 @@ impl<T: InvokeUiSession> Session<T> {
|
||||
self.lc.read().unwrap().view_style.clone()
|
||||
}
|
||||
|
||||
pub fn get_scroll_style(&self) -> String {
|
||||
self.lc.read().unwrap().scroll_style.clone()
|
||||
}
|
||||
|
||||
pub fn get_image_quality(&self) -> String {
|
||||
self.lc.read().unwrap().image_quality.clone()
|
||||
}
|
||||
@ -99,8 +103,12 @@ impl<T: InvokeUiSession> Session<T> {
|
||||
self.lc.write().unwrap().save_view_style(value);
|
||||
}
|
||||
|
||||
pub fn set_flutter_config(&mut self, k: String, v: String) {
|
||||
self.lc.write().unwrap().set_ui_flutter(k, v);
|
||||
pub fn save_scroll_style(&mut self, value: String) {
|
||||
self.lc.write().unwrap().save_scroll_style(value);
|
||||
}
|
||||
|
||||
pub fn save_flutter_config(&mut self, k: String, v: String) {
|
||||
self.lc.write().unwrap().save_ui_flutter(k, v);
|
||||
}
|
||||
|
||||
pub fn get_flutter_config(&self, k: String) -> String {
|
||||
|
Loading…
Reference in New Issue
Block a user