feat(device): 完善远程操作指令并新增更多功能入口
This commit is contained in:
@@ -100,18 +100,21 @@ class DeviceApi {
|
||||
}
|
||||
}
|
||||
|
||||
/// 向设备下发操作指令(重启/关机/截屏/刷新/定位等)。
|
||||
/// 向设备下发操作指令(重启/关机/截屏/刷新/定位/拍照等)。
|
||||
///
|
||||
/// [op] 为后端操作标识,如 `reboot` / `shutdown` / `screenshot` / `refresh` / `locate`,
|
||||
/// [op] 为后端操作标识,如 `reboot` / `shutdown` / `screenshot` / `refresh` / `locate` / `take_photo`,
|
||||
/// 对应接口路径 `/api/v1/client/sn/ops/{op}?sn=xxx`。
|
||||
/// [extra] 为可选的附加 query 参数(如远程拍照时的 `camera`:`0` 后置 / `1` 前置)。
|
||||
/// 成功返回 null;失败返回后台错误提示。
|
||||
Future<String?> sendDeviceOp(String sn, String op) async {
|
||||
Future<String?> sendDeviceOp(String sn, String op, {Map<String, dynamic>? extra}) async {
|
||||
try {
|
||||
final queryParameters = <String, dynamic>{'sn': sn};
|
||||
if (extra != null) queryParameters.addAll(extra);
|
||||
final data = await DioClient.post(
|
||||
'v1/client/sn/ops/$op',
|
||||
queryParameters: {'sn': sn},
|
||||
queryParameters: queryParameters,
|
||||
);
|
||||
debugPrint('[DeviceApi] sendDeviceOp op=$op sn=$sn data=$data');
|
||||
debugPrint('[DeviceApi] sendDeviceOp op=$op sn=$sn extra=$extra data=$data');
|
||||
return null;
|
||||
} on ApiException catch (e) {
|
||||
debugPrint('[DeviceApi] sendDeviceOp op=$op sn=$sn failed: ${e.message}');
|
||||
|
||||
@@ -33,8 +33,8 @@ class DeviceRepositoryImpl implements DeviceRepository {
|
||||
_api.getRecentScreenshots(sn: sn);
|
||||
|
||||
@override
|
||||
Future<String?> sendDeviceOp(String sn, String op) =>
|
||||
_api.sendDeviceOp(sn, op);
|
||||
Future<String?> sendDeviceOp(String sn, String op, {Map<String, dynamic>? extra}) =>
|
||||
_api.sendDeviceOp(sn, op, extra: extra);
|
||||
|
||||
@override
|
||||
Future<String?> sendAppOp(String sn, String op, String packageName) =>
|
||||
|
||||
@@ -27,11 +27,12 @@ abstract class DeviceRepository {
|
||||
/// 已绑定设备最近截图列表。
|
||||
Future<List<ScreenshotVO>> getRecentScreenshots(String sn);
|
||||
|
||||
/// 向设备下发操作指令(重启/关机/截屏/刷新/定位等)。
|
||||
/// 向设备下发操作指令(重启/关机/截屏/刷新/定位/拍照等)。
|
||||
///
|
||||
/// [op] 为后端操作标识,如 `reboot` / `shutdown` / `screenshot` / `refresh` / `locate`。
|
||||
/// [op] 为后端操作标识,如 `reboot` / `shutdown` / `screenshot` / `refresh` / `locate` / `take_photo`。
|
||||
/// [extra] 为可选的附加 query 参数(如远程拍照时的 `camera`:`0` 后置 / `1` 前置)。
|
||||
/// 成功返回 null;失败返回错误提示。
|
||||
Future<String?> sendDeviceOp(String sn, String op);
|
||||
Future<String?> sendDeviceOp(String sn, String op, {Map<String, dynamic>? extra});
|
||||
|
||||
/// 对指定应用执行操作(打开/停止/卸载/清除数据)。
|
||||
///
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'package:cupertino_ui/cupertino_ui.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:material_ui/material_ui.dart';
|
||||
|
||||
import '../../../core/utils/time_format.dart';
|
||||
import '../../../core/widgets/feature_ui.dart';
|
||||
@@ -576,6 +577,54 @@ class HomeTab extends ConsumerWidget {
|
||||
await _runDeviceOp(context, ref, l10n, sn, op);
|
||||
}
|
||||
|
||||
/// 远程拍照:弹窗选择摄像头(后置/前置),确认后下发拍照指令。
|
||||
///
|
||||
/// 后端 [DeviceService.takePhoto] 通过 `camera` 参数区分摄像头
|
||||
/// (`0` 后置,`1` 前置),随指令一并下发给设备端。
|
||||
Future<void> _pickCameraAndTakePhoto(
|
||||
BuildContext context,
|
||||
WidgetRef ref,
|
||||
AppLocalizations l10n,
|
||||
String? sn,
|
||||
) async {
|
||||
if (sn == null || sn.isEmpty) {
|
||||
_showOpResult(context, l10n, l10n.opCmdFailed);
|
||||
return;
|
||||
}
|
||||
final int? camera = await showCupertinoDialog<int>(
|
||||
context: context,
|
||||
builder: (dialogContext) => CupertinoAlertDialog(
|
||||
title: Text(l10n.cfTakePhotoTitle),
|
||||
actions: [
|
||||
CupertinoDialogAction(
|
||||
onPressed: () => Navigator.of(dialogContext).pop(0),
|
||||
child: Text(l10n.cfCamRear),
|
||||
),
|
||||
CupertinoDialogAction(
|
||||
onPressed: () => Navigator.of(dialogContext).pop(1),
|
||||
child: Text(l10n.cfCamFront),
|
||||
),
|
||||
CupertinoDialogAction(
|
||||
isDestructiveAction: true,
|
||||
onPressed: () => Navigator.of(dialogContext).pop(),
|
||||
child: Text(l10n.cancel),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
if (camera == null) return;
|
||||
if (!context.mounted) return;
|
||||
await _runDeviceOp(
|
||||
context,
|
||||
ref,
|
||||
l10n,
|
||||
sn,
|
||||
'take_photo',
|
||||
refreshScreenshots: true,
|
||||
extra: {'camera': camera},
|
||||
);
|
||||
}
|
||||
|
||||
/// 下发设备操作指令并弹出结果提示。
|
||||
Future<void> _runDeviceOp(
|
||||
BuildContext context,
|
||||
@@ -585,12 +634,13 @@ class HomeTab extends ConsumerWidget {
|
||||
String op, {
|
||||
bool refreshScreenshots = false,
|
||||
bool refreshLocation = false,
|
||||
Map<String, dynamic>? extra,
|
||||
}) async {
|
||||
if (sn == null || sn.isEmpty) {
|
||||
_showOpResult(context, l10n, l10n.opCmdFailed);
|
||||
return;
|
||||
}
|
||||
final error = await ref.read(deviceRepositoryProvider).sendDeviceOp(sn, op);
|
||||
final error = await ref.read(deviceRepositoryProvider).sendDeviceOp(sn, op, extra: extra);
|
||||
if (!context.mounted) return;
|
||||
if (error == null) {
|
||||
if (refreshScreenshots) ref.invalidate(recentScreenshotsProvider(sn));
|
||||
@@ -647,12 +697,89 @@ class HomeTab extends ConsumerWidget {
|
||||
);
|
||||
}
|
||||
|
||||
/// 「更多」弹窗:以图标网格列出未在常用区直接展示的远程推送操作,
|
||||
/// 确保后端 ClientDeviceOpsController 暴露的其余推送接口(如恢复出厂、开发者模式)
|
||||
/// 在家属端 UI 也有入口,实现「每个推送接口都对接」。
|
||||
Future<void> _showMoreOpsSheet(
|
||||
BuildContext context,
|
||||
WidgetRef ref,
|
||||
AppLocalizations l10n,
|
||||
String? sn,
|
||||
) async {
|
||||
final items = <_MoreOpItem>[
|
||||
_MoreOpItem(emoji: '♻️', color: AppColors.orange, label: l10n.cfRestore, op: 'restore'),
|
||||
_MoreOpItem(emoji: '🛠️', color: AppColors.blue, label: l10n.cfDeveloper, op: 'developer'),
|
||||
];
|
||||
if (sn == null || sn.isEmpty) {
|
||||
_showOpResult(context, l10n, l10n.opCmdFailed);
|
||||
return;
|
||||
}
|
||||
final String? op = await showCupertinoModalPopup<String>(
|
||||
context: context,
|
||||
builder: (ctx) => SafeArea(
|
||||
child: Container(
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 16),
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(ctx).scaffoldBackgroundColor,
|
||||
borderRadius: const BorderRadius.vertical(top: Radius.circular(20)),
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
l10n.cfMoreOpsTitle,
|
||||
style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w600),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
GridView.count(
|
||||
crossAxisCount: 3,
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
childAspectRatio: 0.95,
|
||||
mainAxisSpacing: 10,
|
||||
crossAxisSpacing: 10,
|
||||
children: [
|
||||
for (final item in items)
|
||||
IconTile(
|
||||
emoji: item.emoji,
|
||||
color: item.color,
|
||||
label: item.label,
|
||||
size: 54,
|
||||
iconSize: 24,
|
||||
onTap: () => Navigator.of(ctx).pop(item.op),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
CupertinoButton(
|
||||
onPressed: () => Navigator.of(ctx).pop(),
|
||||
child: Text(l10n.cancel),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
if (op == null || !context.mounted) return;
|
||||
await _runDeviceOp(context, ref, l10n, sn, op);
|
||||
}
|
||||
|
||||
Widget _commonCard(BuildContext context, WidgetRef ref, AppLocalizations l10n, String? sn) => CardBox(
|
||||
child: Column(
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Expanded(child: IconTile(emoji: '📁', color: AppColors.blue, label: l10n.cfFileTransfer)),
|
||||
Expanded(
|
||||
child: IconTile(
|
||||
emoji: '📁',
|
||||
color: AppColors.blue,
|
||||
label: l10n.cfFileTransfer,
|
||||
size: 50,
|
||||
iconSize: 20,
|
||||
onTap: () => _runDeviceOp(context, ref, l10n, sn, 'file_transfer'),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: IconTile(
|
||||
emoji: '📷',
|
||||
@@ -660,24 +787,129 @@ class HomeTab extends ConsumerWidget {
|
||||
label: l10n.cfRemoteCam,
|
||||
size: 50,
|
||||
iconSize: 20,
|
||||
onTap: () => _runDeviceOp(context, ref, l10n, sn, 'take_photo',
|
||||
refreshScreenshots: true),
|
||||
onTap: () => _pickCameraAndTakePhoto(context, ref, l10n, sn),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: IconTile(
|
||||
emoji: '💬',
|
||||
color: AppColors.orange,
|
||||
label: l10n.cfMsgSync,
|
||||
size: 50,
|
||||
iconSize: 20,
|
||||
onTap: () => _runDeviceOp(context, ref, l10n, sn, 'msg_sync'),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: IconTile(
|
||||
emoji: '🧹',
|
||||
color: AppColors.purple,
|
||||
label: l10n.cfClean,
|
||||
size: 50,
|
||||
iconSize: 20,
|
||||
onTap: () => _runDeviceOp(context, ref, l10n, sn, 'clean'),
|
||||
),
|
||||
),
|
||||
Expanded(child: IconTile(emoji: '💬', color: AppColors.orange, label: l10n.cfMsgSync)),
|
||||
Expanded(child: IconTile(emoji: '🧹', color: AppColors.purple, label: l10n.cfClean)),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 14),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(child: IconTile(emoji: '📺', color: AppColors.red, label: l10n.cfMirror)),
|
||||
Expanded(child: IconTile(emoji: '🔔', color: AppColors.teal, label: l10n.cfLost)),
|
||||
Expanded(child: IconTile(emoji: '🔒', color: const Color(0xFF22C55E), label: l10n.cfAppLock)),
|
||||
Expanded(child: IconTile(emoji: '➕', color: AppColors.gray, label: l10n.cfMore)),
|
||||
Expanded(
|
||||
child: IconTile(
|
||||
emoji: '📺',
|
||||
color: AppColors.red,
|
||||
label: l10n.cfMirror,
|
||||
size: 50,
|
||||
iconSize: 20,
|
||||
onTap: () => _runDeviceOp(context, ref, l10n, sn, 'mirror'),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: IconTile(
|
||||
emoji: '🔔',
|
||||
color: AppColors.teal,
|
||||
label: l10n.cfLost,
|
||||
size: 50,
|
||||
iconSize: 20,
|
||||
onTap: () => _runDeviceOp(context, ref, l10n, sn, 'locate',
|
||||
refreshLocation: true),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: IconTile(
|
||||
emoji: '🔐',
|
||||
color: AppColors.blue,
|
||||
label: l10n.cfScreenLock,
|
||||
size: 50,
|
||||
iconSize: 20,
|
||||
onTap: () => _runDeviceOp(context, ref, l10n, sn, 'screen_lock'),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: IconTile(
|
||||
emoji: '🔒',
|
||||
color: const Color(0xFF22C55E),
|
||||
label: l10n.cfAppLock,
|
||||
size: 50,
|
||||
iconSize: 20,
|
||||
onTap: () => _runDeviceOp(context, ref, l10n, sn, 'refresh'),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 14),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: IconTile(
|
||||
emoji: '♻️',
|
||||
color: AppColors.orange,
|
||||
label: l10n.cfRestore,
|
||||
size: 50,
|
||||
iconSize: 20,
|
||||
onTap: () => _runDeviceOp(context, ref, l10n, sn, 'restore'),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: IconTile(
|
||||
emoji: '🛠️',
|
||||
color: AppColors.blue,
|
||||
label: l10n.cfDeveloper,
|
||||
size: 50,
|
||||
iconSize: 20,
|
||||
onTap: () => _runDeviceOp(context, ref, l10n, sn, 'developer'),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: IconTile(
|
||||
emoji: '⋯',
|
||||
color: AppColors.gray,
|
||||
label: l10n.cfMore,
|
||||
size: 50,
|
||||
iconSize: 20,
|
||||
onTap: () => _showMoreOpsSheet(context, ref, l10n, sn),
|
||||
),
|
||||
),
|
||||
// 占位,保持每行 4 个
|
||||
const Expanded(child: SizedBox.shrink()),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
class _MoreOpItem {
|
||||
const _MoreOpItem({
|
||||
required this.emoji,
|
||||
required this.color,
|
||||
required this.label,
|
||||
required this.op,
|
||||
});
|
||||
|
||||
final String emoji;
|
||||
final Color color;
|
||||
final String label;
|
||||
final String op;
|
||||
}
|
||||
|
||||
@@ -536,12 +536,48 @@ abstract class AppLocalizations {
|
||||
/// **'远程相机'**
|
||||
String get cfRemoteCam;
|
||||
|
||||
/// 远程拍照弹窗标题
|
||||
///
|
||||
/// In zh, this message translates to:
|
||||
/// **'选择摄像头'**
|
||||
String get cfTakePhotoTitle;
|
||||
|
||||
/// 远程拍照:后置摄像头
|
||||
///
|
||||
/// In zh, this message translates to:
|
||||
/// **'后置摄像头'**
|
||||
String get cfCamRear;
|
||||
|
||||
/// 远程拍照:前置摄像头
|
||||
///
|
||||
/// In zh, this message translates to:
|
||||
/// **'前置摄像头'**
|
||||
String get cfCamFront;
|
||||
|
||||
/// 常用功能:更多
|
||||
///
|
||||
/// In zh, this message translates to:
|
||||
/// **'更多'**
|
||||
String get cfMore;
|
||||
|
||||
/// 更多操作弹窗标题
|
||||
///
|
||||
/// In zh, this message translates to:
|
||||
/// **'更多操作'**
|
||||
String get cfMoreOpsTitle;
|
||||
|
||||
/// 更多操作:恢复出厂设置
|
||||
///
|
||||
/// In zh, this message translates to:
|
||||
/// **'恢复出厂设置'**
|
||||
String get cfRestore;
|
||||
|
||||
/// 更多操作:开发者模式
|
||||
///
|
||||
/// In zh, this message translates to:
|
||||
/// **'开发者模式'**
|
||||
String get cfDeveloper;
|
||||
|
||||
/// 联系人页标题
|
||||
///
|
||||
/// In zh, this message translates to:
|
||||
@@ -989,9 +1025,15 @@ abstract class AppLocalizations {
|
||||
/// 常用功能:防丢失
|
||||
///
|
||||
/// In zh, this message translates to:
|
||||
/// **'丢失提醒'**
|
||||
/// **'丢失模式'**
|
||||
String get cfLost;
|
||||
|
||||
/// 常用功能:屏幕锁定
|
||||
///
|
||||
/// In zh, this message translates to:
|
||||
/// **'屏幕锁定'**
|
||||
String get cfScreenLock;
|
||||
|
||||
/// 常用功能:应用锁
|
||||
///
|
||||
/// In zh, this message translates to:
|
||||
|
||||
@@ -230,9 +230,27 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||
@override
|
||||
String get cfRemoteCam => 'Remote Camera';
|
||||
|
||||
@override
|
||||
String get cfTakePhotoTitle => 'Choose Camera';
|
||||
|
||||
@override
|
||||
String get cfCamRear => 'Rear Camera';
|
||||
|
||||
@override
|
||||
String get cfCamFront => 'Front Camera';
|
||||
|
||||
@override
|
||||
String get cfMore => 'More';
|
||||
|
||||
@override
|
||||
String get cfMoreOpsTitle => 'More Operations';
|
||||
|
||||
@override
|
||||
String get cfRestore => 'Factory Reset';
|
||||
|
||||
@override
|
||||
String get cfDeveloper => 'Developer Mode';
|
||||
|
||||
@override
|
||||
String get contactsTitle => 'Contacts';
|
||||
|
||||
@@ -461,7 +479,10 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||
String get cfMirror => 'Screen Mirror';
|
||||
|
||||
@override
|
||||
String get cfLost => 'Loss Alert';
|
||||
String get cfLost => 'Lost Mode';
|
||||
|
||||
@override
|
||||
String get cfScreenLock => 'Screen Lock';
|
||||
|
||||
@override
|
||||
String get cfAppLock => 'App Lock';
|
||||
|
||||
@@ -229,9 +229,27 @@ class AppLocalizationsZh extends AppLocalizations {
|
||||
@override
|
||||
String get cfRemoteCam => '远程相机';
|
||||
|
||||
@override
|
||||
String get cfTakePhotoTitle => '选择摄像头';
|
||||
|
||||
@override
|
||||
String get cfCamRear => '后置摄像头';
|
||||
|
||||
@override
|
||||
String get cfCamFront => '前置摄像头';
|
||||
|
||||
@override
|
||||
String get cfMore => '更多';
|
||||
|
||||
@override
|
||||
String get cfMoreOpsTitle => '更多操作';
|
||||
|
||||
@override
|
||||
String get cfRestore => '恢复出厂设置';
|
||||
|
||||
@override
|
||||
String get cfDeveloper => '开发者模式';
|
||||
|
||||
@override
|
||||
String get contactsTitle => '联系人';
|
||||
|
||||
@@ -457,7 +475,10 @@ class AppLocalizationsZh extends AppLocalizations {
|
||||
String get cfMirror => '屏幕镜像';
|
||||
|
||||
@override
|
||||
String get cfLost => '丢失提醒';
|
||||
String get cfLost => '丢失模式';
|
||||
|
||||
@override
|
||||
String get cfScreenLock => '屏幕锁定';
|
||||
|
||||
@override
|
||||
String get cfAppLock => '应用锁';
|
||||
|
||||
@@ -73,7 +73,13 @@
|
||||
"menuApps": "Apps",
|
||||
"menuMore": "More",
|
||||
"cfRemoteCam": "Remote Camera",
|
||||
"cfTakePhotoTitle": "Choose Camera",
|
||||
"cfCamRear": "Rear Camera",
|
||||
"cfCamFront": "Front Camera",
|
||||
"cfMore": "More",
|
||||
"cfMoreOpsTitle": "More Operations",
|
||||
"cfRestore": "Factory Reset",
|
||||
"cfDeveloper": "Developer Mode",
|
||||
"contactsTitle": "Contacts",
|
||||
"contactName": "Name",
|
||||
"contactNick": "Nickname (optional)",
|
||||
@@ -160,7 +166,8 @@
|
||||
"cfMsgSync": "Message Sync",
|
||||
"cfClean": "Clean & Boost",
|
||||
"cfMirror": "Screen Mirror",
|
||||
"cfLost": "Loss Alert",
|
||||
"cfLost": "Lost Mode",
|
||||
"cfScreenLock": "Screen Lock",
|
||||
"cfAppLock": "App Lock",
|
||||
"msgOnline": "Device Online Alert",
|
||||
"msgScreenshot": "Screenshot Sync",
|
||||
|
||||
@@ -292,10 +292,34 @@
|
||||
"@cfRemoteCam": {
|
||||
"description": "常用功能:远程相机"
|
||||
},
|
||||
"cfTakePhotoTitle": "选择摄像头",
|
||||
"@cfTakePhotoTitle": {
|
||||
"description": "远程拍照弹窗标题"
|
||||
},
|
||||
"cfCamRear": "后置摄像头",
|
||||
"@cfCamRear": {
|
||||
"description": "远程拍照:后置摄像头"
|
||||
},
|
||||
"cfCamFront": "前置摄像头",
|
||||
"@cfCamFront": {
|
||||
"description": "远程拍照:前置摄像头"
|
||||
},
|
||||
"cfMore": "更多",
|
||||
"@cfMore": {
|
||||
"description": "常用功能:更多"
|
||||
},
|
||||
"cfMoreOpsTitle": "更多操作",
|
||||
"@cfMoreOpsTitle": {
|
||||
"description": "更多操作弹窗标题"
|
||||
},
|
||||
"cfRestore": "恢复出厂设置",
|
||||
"@cfRestore": {
|
||||
"description": "更多操作:恢复出厂设置"
|
||||
},
|
||||
"cfDeveloper": "开发者模式",
|
||||
"@cfDeveloper": {
|
||||
"description": "更多操作:开发者模式"
|
||||
},
|
||||
"contactsTitle": "联系人",
|
||||
"@contactsTitle": {
|
||||
"description": "联系人页标题"
|
||||
@@ -592,10 +616,14 @@
|
||||
"@cfMirror": {
|
||||
"description": "常用功能:屏幕镜像"
|
||||
},
|
||||
"cfLost": "丢失提醒",
|
||||
"cfLost": "丢失模式",
|
||||
"@cfLost": {
|
||||
"description": "常用功能:防丢失"
|
||||
},
|
||||
"cfScreenLock": "屏幕锁定",
|
||||
"@cfScreenLock": {
|
||||
"description": "常用功能:屏幕锁定"
|
||||
},
|
||||
"cfAppLock": "应用锁",
|
||||
"@cfAppLock": {
|
||||
"description": "常用功能:应用锁"
|
||||
|
||||
Reference in New Issue
Block a user