import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'dart:ui'; import 'dart:io' show Platform; import '../controllers/permissions_controller.dart'; import '../models/permission_group.dart'; class PermissionsView extends GetView { const PermissionsView({Key? key}) : super(key: key); @override Widget build(BuildContext context) { final isDarkMode = Theme.of(context).brightness == Brightness.dark; return Scaffold( backgroundColor: isDarkMode ? Colors.black : Colors.grey[50], appBar: AppBar( backgroundColor: isDarkMode ? Colors.black : Colors.white, elevation: 0.5, leading: IconButton( icon: Icon( Icons.arrow_back_ios, size: 20.w, color: isDarkMode ? Colors.white : Colors.black87, ), onPressed: () => Get.back(), ), title: Text( 'permissionManagement'.tr, style: TextStyle( fontSize: 17.sp, fontWeight: FontWeight.w600, color: isDarkMode ? Colors.white : Colors.black, ), ), centerTitle: true, ), body: SafeArea( top: false, child: Obx(() { if (controller.permissionStatuses.isEmpty) { return const Center(child: CircularProgressIndicator()); } return RefreshIndicator( onRefresh: controller.checkAllPermissions, child: SingleChildScrollView( padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 16.h), child: _buildPermissionGroups(isDarkMode), ), ); }), ), ); } Widget _buildPermissionGroups(bool isDarkMode) { return FutureBuilder>( future: PermissionGroup.getPlatformGroups(), builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { return const Center(child: CircularProgressIndicator()); } if (snapshot.hasError) { return Center(child: Text('加载权限组失败: ${snapshot.error}')); } final groups = snapshot.data ?? []; final List widgets = []; // 添加后台权限条目到最前端(仅在满足条件时) final backgroundPermissionWidget = _buildBackgroundPermissionSection(isDarkMode); if (backgroundPermissionWidget is! SizedBox) { widgets.add(backgroundPermissionWidget); if (groups.isNotEmpty) { widgets.add(SizedBox(height: 20.h)); } } for (int i = 0; i < groups.length; i++) { final group = groups[i]; // 过滤平台特定权限 if (_shouldShowGroup(group)) { widgets.add(_buildPermissionSection( group.titleKey, group.icon, group.permissions, controller.permissionStatuses, isDarkMode, )); if (i < groups.length - 1) { widgets.add(SizedBox(height: 20.h)); } } } return Column(children: widgets); }, ); } bool _shouldShowGroup(PermissionGroupModel group) { // Android特定权限组 if (group.titleKey == 'communicationPermissions' || group.titleKey == 'alarmPermissions') { return Platform.isAndroid; } // 蓝牙权限组:Android 12+才显示,iOS不显示 if (group.titleKey == 'bluetoothPermissions') { if (Platform.isAndroid) { return controller.shouldShowBluetoothPermissions(); } return false; } // 存储权限组:Android 10及以下版本或iOS才显示 if (group.titleKey == 'storagePermissions') { return controller.shouldShowStoragePermissions(); } // 通知权限和麦克风权限在所有平台显示 if (group.titleKey == 'notificationPermissions' || group.titleKey == 'microphonePermissions') { return true; } return true; } Widget _buildPermissionSection( String titleKey, IconData icon, List permissions, Map statuses, bool isDarkMode, ) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // 组标题 Padding( padding: EdgeInsets.only(left: 4.w, bottom: 8.h), child: Text( titleKey.tr, style: TextStyle( fontSize: 12.sp, fontWeight: FontWeight.w500, color: isDarkMode ? Colors.blue[400] : Colors.blue[600], letterSpacing: 1, ), ), ), // 权限项容器 Container( decoration: BoxDecoration( color: isDarkMode ? Colors.white.withOpacity(0.05) : Colors.white, borderRadius: BorderRadius.circular(16.r), boxShadow: [ BoxShadow( color: isDarkMode ? Colors.black.withOpacity(0.2) : Colors.black.withOpacity(0.05), blurRadius: 10, offset: const Offset(0, 2), ), ], ), child: ClipRRect( borderRadius: BorderRadius.circular(16.r), child: BackdropFilter( filter: isDarkMode ? ImageFilter.blur(sigmaX: 5, sigmaY: 5) : ImageFilter.blur(sigmaX: 0, sigmaY: 0), child: Column( children: _buildPermissionItems(permissions, statuses, isDarkMode), ), ), ), ), ], ); } List _buildPermissionItems( List permissions, Map statuses, bool isDarkMode, ) { final List items = []; for (int i = 0; i < permissions.length; i++) { final permission = permissions[i]; final status = statuses[permission] ?? PermissionStatus.denied; items.add( _buildPermissionItem( permission: permission, status: status, isDarkMode: isDarkMode, ), ); // 添加分隔线,最后一个不加 if (i < permissions.length - 1) { items.add(Divider( height: 1, color: isDarkMode ? Colors.white.withOpacity(0.1) : Colors.grey[200], )); } } return items; } Widget _buildPermissionItem({ required Permission permission, required PermissionStatus status, required bool isDarkMode, }) { return Padding( padding: EdgeInsets.all(16.w), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( controller.getPermissionName(permission), style: TextStyle( fontSize: 16.sp, fontWeight: FontWeight.w500, color: isDarkMode ? Colors.white : Colors.black87, ), ), SizedBox(height: 4.h), Text( controller.getPermissionDescription(permission), style: TextStyle( fontSize: 12.sp, color: isDarkMode ? Colors.white70 : Colors.grey[600], ), ), SizedBox(height: 4.h), _buildStatusText(status, isDarkMode), ], ), ), Obx(() => Switch( value: status.isGranted, onChanged: controller.isRequestingPermission.value ? null : (value) => _handlePermissionToggle(permission, status, value), activeColor: Colors.blue[600], )), ], ), ); } Widget _buildStatusText(PermissionStatus status, bool isDarkMode) { String text; Color color; if (status.isGranted) { text = 'authorized'.tr; color = Colors.green; } else if (status.isDenied) { text = 'unauthorized'.tr; color = Colors.red; } else if (status.isPermanentlyDenied) { text = 'needOpenInSystemSettings'.tr; color = Colors.orange; } else if (status.isLimited) { text = 'partialAuthorization'.tr; color = Colors.orange; } else { text = 'unknownStatus'.tr; color = Colors.grey; } return Text( text, style: TextStyle( color: color, fontSize: 12.sp, fontWeight: FontWeight.w500, ), ); } /// 构建后台权限条目 Widget _buildBackgroundPermissionSection(bool isDarkMode) { // 检查是否为Android系统且语言为中文 if (!_shouldShowBackgroundPermissionTutorial()) { return const SizedBox.shrink(); // 不满足条件时完全隐藏 } return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // 组标题 Padding( padding: EdgeInsets.only(left: 4.w, bottom: 8.h), child: Text( '后台权限'.tr, // 后台权限 style: TextStyle( fontSize: 12.sp, fontWeight: FontWeight.w500, color: isDarkMode ? Colors.blue[400] : Colors.blue[600], letterSpacing: 1, ), ), ), // 权限项容器 Container( decoration: BoxDecoration( color: isDarkMode ? Colors.white.withOpacity(0.05) : Colors.white, borderRadius: BorderRadius.circular(16.r), boxShadow: [ BoxShadow( color: isDarkMode ? Colors.black.withOpacity(0.2) : Colors.black.withOpacity(0.05), blurRadius: 10, offset: const Offset(0, 2), ), ], ), child: ClipRRect( borderRadius: BorderRadius.circular(16.r), child: BackdropFilter( filter: isDarkMode ? ImageFilter.blur(sigmaX: 5, sigmaY: 5) : ImageFilter.blur(sigmaX: 0, sigmaY: 0), child: _buildBackgroundPermissionItem(isDarkMode), ), ), ), ], ); } /// 检查是否应该显示后台权限教程入口 /// 仅在Android系统且中文环境下显示 bool _shouldShowBackgroundPermissionTutorial() { // 1. 检查是否为Android系统 if (!Platform.isAndroid) { return false; } // 2. 检查设备语言区域是否为中文 final locale = Get.locale ?? Get.deviceLocale; if (locale == null) { return false; } // 检查语言代码是否为中文(zh) return locale.languageCode == 'zh'; } /// 构建后台权限条目内容 Widget _buildBackgroundPermissionItem(bool isDarkMode) { return InkWell( onTap: () { _showBackgroundPermissionGuide(); }, child: Padding( padding: EdgeInsets.all(16.w), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( '后台运行'.tr, // 确保随时可以唤醒AI助手 style: TextStyle( fontSize: 16.sp, fontWeight: FontWeight.w500, color: isDarkMode ? Colors.white : Colors.black87, ), ), SizedBox(height: 4.h), Text( '设置后台运行权限,确保AI助手随时响应您的需求'.tr, // 设置后台运行权限,确保AI助手随时响应您的需求 style: TextStyle( fontSize: 12.sp, color: isDarkMode ? Colors.white70 : Colors.grey[600], ), ), ], ), ), Icon( Icons.arrow_forward_ios, size: 16.sp, color: isDarkMode ? Colors.white70 : Colors.grey[600], ), ], ), ), ); } // 显示后台权限设置指南 void _showBackgroundPermissionGuide() { final isDarkMode = Theme.of(Get.context!).brightness == Brightness.dark; Get.bottomSheet( Container( height: Get.height * 0.8, decoration: BoxDecoration( color: isDarkMode ? Colors.grey[900] : Colors.white, borderRadius: BorderRadius.vertical(top: Radius.circular(20.r)), ), child: Column( children: [ // 顶部标题栏 Container( padding: EdgeInsets.all(16.w), decoration: BoxDecoration( border: Border( bottom: BorderSide( color: isDarkMode ? Colors.grey[700]! : Colors.grey.shade300)), ), child: Row( children: [ Text( '后台运行权限设置'.tr, style: TextStyle( fontSize: 18.sp, fontWeight: FontWeight.bold, color: isDarkMode ? Colors.white : Colors.black, ), ), Spacer(), IconButton( onPressed: () => Get.back(), icon: Icon( Icons.close, color: isDarkMode ? Colors.white : Colors.black, ), ), ], ), ), // 内容区域 Expanded( child: SingleChildScrollView( padding: EdgeInsets.all(16.w), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( '为确保AI助手能够在后台正常运行,请根据您的手机品牌进行以下设置:'.tr, style: TextStyle( fontSize: 14.sp, color: isDarkMode ? Colors.grey[300] : Colors.grey[600], ), ), SizedBox(height: 20.h), _buildBrandGuide( '华为/荣耀', [ '设置 → 应用 → 应用启动管理', '找到Voitrans应用', '关闭"自动管理"', '开启"允许自启动"、"允许关联启动"、"允许后台活动"' ], isDarkMode), _buildBrandGuide( 'OPPO', ['设置 → 应用 → 自启动', '找到Voitrans应用', '打开"后台自启动"'], isDarkMode), _buildBrandGuide( 'vivo', ['i管家 → 应用管理 → 自启动 → 关联启动', '找到Voitrans应用', '允许"关联启动"'], isDarkMode), _buildBrandGuide( '小米', ['设置 → 应用设置 → 应用管理', '找到Voitrans应用', '打开"自启动"'], isDarkMode), _buildBrandGuide( '三星', ['长按Voitrans图标', '应用程序信息 → 电池', '打开"允许后台活动"'], isDarkMode), ], ), ), ), ], ), ), isScrollControlled: true, ); } // 构建品牌设置指南 Widget _buildBrandGuide(String brand, List steps, bool isDarkMode) { return Container( margin: EdgeInsets.only(bottom: 20.h), padding: EdgeInsets.all(16.w), decoration: BoxDecoration( color: isDarkMode ? Colors.white.withOpacity(0.05) : Colors.grey[50], borderRadius: BorderRadius.circular(12.r), border: Border.all( color: isDarkMode ? Colors.grey[700]! : Colors.grey.shade300), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( brand, style: TextStyle( fontSize: 16.sp, fontWeight: FontWeight.bold, color: isDarkMode ? Colors.blue[400] : Colors.blue[600], ), ), SizedBox(height: 8.h), ...steps.asMap().entries.map((entry) { int index = entry.key; String step = entry.value; return Padding( padding: EdgeInsets.only(bottom: 4.h), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Container( width: 20.w, height: 20.w, decoration: BoxDecoration( color: isDarkMode ? Colors.blue[400] : Colors.blue[600], shape: BoxShape.circle, ), child: Center( child: Text( '${index + 1}', style: TextStyle( color: Colors.white, fontSize: 12.sp, fontWeight: FontWeight.bold, ), ), ), ), SizedBox(width: 8.w), Expanded( child: Text( step, style: TextStyle( fontSize: 14.sp, color: isDarkMode ? Colors.white : Colors.black87, ), ), ), ], ), ); }).toList(), ], ), ); } void _handlePermissionToggle( Permission permission, PermissionStatus status, bool value, ) { if (value) { // 请求权限 controller.requestPermission(permission); } else if (status.isGranted) { // 已授权但用户想关闭,引导到系统设置 controller.openSettings(); } } }