using XiaoZhiSharp_MauiApp.Services; using System.Collections.Specialized; namespace XiaoZhiSharp_MauiApp { public partial class MainPage : ContentPage { private readonly XiaoZhi_AgentService _agentService; private readonly ICameraService? _cameraService; private System.Timers.Timer _updateTimer; private bool _isUpdatingUI = false; public MainPage(XiaoZhi_AgentService agentService, ICameraService? cameraService = null) { _cameraService = cameraService; InitializeComponent(); _agentService = agentService; BindingContext = _agentService; // 监听聊天历史变化 _agentService.ChatHistory.CollectionChanged += OnChatHistoryChanged; // 启动定时器更新UI(主要用于更新音频强度等实时数据) _updateTimer = new System.Timers.Timer(100); _updateTimer.Elapsed += OnTimerElapsed; _updateTimer.Start(); } private void OnChatHistoryChanged(object sender, NotifyCollectionChangedEventArgs e) { MainThread.BeginInvokeOnMainThread(() => { UpdateChatMessages(); }); } private void UpdateChatMessages() { // 如果没有聊天记录,显示欢迎消息 WelcomeFrame.IsVisible = _agentService.ChatHistory.Count == 0; // 清除现有的消息(除了欢迎消息) var messagesToRemove = ChatMessagesLayout.Children .Where(c => c != WelcomeFrame) .ToList(); foreach (var message in messagesToRemove) { ChatMessagesLayout.Children.Remove(message); } // 添加所有聊天消息 foreach (var message in _agentService.ChatHistory) { AddMessageToUI(message); } // 滚动到底部 _ = ScrollToBottom(); } private void AddMessageToUI(ChatMessage message) { // 时间标签 var timeLabel = new Label { Text = message.Timestamp.ToString("HH:mm"), FontSize = 12, TextColor = Colors.Gray, HorizontalOptions = LayoutOptions.Center, Margin = new Thickness(0, 5) }; ChatMessagesLayout.Children.Add(timeLabel); // 消息框架 var messageFrame = new Frame { Padding = new Thickness(10, 8), CornerRadius = 10, HasShadow = false, Margin = new Thickness(0, 2) }; // 创建消息内容容器 var messageContainer = new VerticalStackLayout { Spacing = 8 }; // 如果有图片,先添加图片 if (!string.IsNullOrEmpty(message.ImagePath) && File.Exists(message.ImagePath)) { var imageView = new Image { Source = ImageSource.FromFile(message.ImagePath), Aspect = Aspect.AspectFit, MaximumHeightRequest = 200, MaximumWidthRequest = 250, BackgroundColor = Colors.LightGray }; // 添加图片点击事件,可以查看大图 var tapGesture = new TapGestureRecognizer(); tapGesture.Tapped += async (s, e) => await ShowImageFullscreen(message.ImagePath); imageView.GestureRecognizers.Add(tapGesture); messageContainer.Children.Add(imageView); } // 添加文本消息 var messageLabel = new Label { Text = message.Content, FontSize = 14, LineBreakMode = LineBreakMode.WordWrap }; messageContainer.Children.Add(messageLabel); if (message.IsUser) { messageFrame.BackgroundColor = Color.FromHex("#dcf8c6"); messageFrame.HorizontalOptions = LayoutOptions.End; messageFrame.Margin = new Thickness(50, 2, 0, 2); messageLabel.TextColor = Colors.Black; } else { messageFrame.BackgroundColor = Colors.White; messageFrame.HorizontalOptions = LayoutOptions.Start; messageFrame.Margin = new Thickness(0, 2, 50, 2); messageLabel.TextColor = Colors.Black; } messageFrame.Content = messageContainer; ChatMessagesLayout.Children.Add(messageFrame); } private async Task ScrollToBottom() { await Task.Delay(100); // 等待UI更新 await ChatScrollView.ScrollToAsync(0, ChatMessagesLayout.Height, false); } private async void OnTimerElapsed(object sender, System.Timers.ElapsedEventArgs e) { if (_isUpdatingUI) return; _isUpdatingUI = true; await MainThread.InvokeOnMainThreadAsync(() => { // 由于XiaoZhi_AgentService已经实现了INotifyPropertyChanged, // 它的属性变化会自动通知UI更新,所以这里什么都不需要做 // 定时器主要是为了确保UI能及时响应 }); _isUpdatingUI = false; } private async void OnRecordingButtonClicked(object sender, EventArgs e) { try { if (_agentService.IsRecording) { await _agentService.Agent.StopRecording(); } else { await _agentService.Agent.StartRecording("auto"); } } catch (Exception ex) { await DisplayAlert("错误", $"录音操作失败: {ex.Message}", "确定"); } } private async void OnStopChatClicked(object sender, EventArgs e) { try { await _agentService.Agent.ChatAbort(); } catch (Exception ex) { await DisplayAlert("错误", $"停止对话失败: {ex.Message}", "确定"); } } private async void OnCameraButtonClicked(object sender, EventArgs e) { try { // 先检查是否有摄像头服务 if (_cameraService == null) { await DisplayAlert("提示", "摄像头功能不可用", "确定"); return; } // 检查设备支持 if (!_cameraService.IsSupported) { await DisplayAlert("提示", "设备不支持摄像头功能", "确定"); return; } // 检查权限 if (!_cameraService.HasPermission) { var granted = await _cameraService.RequestPermissionAsync(); if (!granted) { await DisplayAlert("提示", "需要摄像头权限才能使用此功能", "确定"); return; } } try { // 第一步:直接打开相机拍照 await DisplayAlert("拍照提示", "即将打开相机,请拍摄您要识别的内容", "确定"); var imageData = await _cameraService.CapturePhotoAsync(); if (imageData == null || imageData.Length == 0) { await DisplayAlert("提示", "拍照失败或已取消", "确定"); return; } //XiaoZhiSharp.Services.ImageStorageService imageStorageService = new XiaoZhiSharp.Services.ImageStorageService(); //imageStorageService.PostImage("https://coze.nbee.net/image/v1/stream/1", "", "deviceId", "clientId", imageData); // 第二步:保存照片并在聊天记录中显示 string imagePath = await SaveImageToLocal(imageData); // 第三步:拍照成功后询问问题(可选) var question = await DisplayPromptAsync("拍照成功", "拍照成功!请输入您要询问关于这张图片的问题:\n(直接点击确定将使用默认问题)", "确定", "取消", "请描述这张图片的内容", maxLength: 200); if (question == null) // 用户点击取消 { return; } if (string.IsNullOrWhiteSpace(question)) { question = "请描述这张图片的内容"; // 默认问题 } // 第四步:在聊天记录中显示用户的问题和图片 var userMessage = $"📷 {question}"; var imageMessage = new ChatMessage(userMessage, true) { ImagePath = imagePath }; _agentService.ChatHistory.Add(imageMessage); await ScrollToBottom(); // 第五步:显示AI识别进度 var progressMessage = $"正在进行AI识别...\n问题: {question}"; var loadingTask = DisplayAlert("AI识别中", progressMessage, "请稍候"); try { // 第六步:调用AI识别服务(直接对已拍摄的图片进行识别) var result = await _cameraService.ExplainImageAsync(imageData, question); // 第七步:解析并显示结果 if (!string.IsNullOrEmpty(result)) { // AI的回答 var aiResponse = $"根据您拍摄的图片,{ExtractAIResponse(result)}"; // 添加到聊天记录 await Task.Delay(300); _agentService.ChatHistory.Add(new ChatMessage(aiResponse, false)); await ScrollToBottom(); } else { await DisplayAlert("识别失败", "AI识别失败,请重试", "确定"); } } catch (Exception ex) { await DisplayAlert("错误", $"AI识别失败: {ex.Message}", "确定"); } } catch (Exception ex) { await DisplayAlert("错误", $"拍照过程出错: {ex.Message}", "确定"); } } catch (Exception ex) { await DisplayAlert("错误", $"摄像头功能出错: {ex.Message}", "确定"); } } private async Task SaveImageToLocal(byte[] imageData) { try { // 创建应用文件夹 var appDataPath = FileSystem.CacheDirectory; var imagesPath = Path.Combine(appDataPath, "CapturedImages"); if (!Directory.Exists(imagesPath)) { Directory.CreateDirectory(imagesPath); } // 生成文件名 var fileName = $"camera_{DateTime.Now:yyyyMMdd_HHmmss}.jpg"; var filePath = Path.Combine(imagesPath, fileName); // 保存文件 await File.WriteAllBytesAsync(filePath, imageData); return filePath; } catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"保存图片失败: {ex.Message}"); return string.Empty; } } private string ExtractAIResponse(string jsonResult) { try { // 简单的JSON解析,提取AI回答 if (jsonResult.Contains("\"success\": true")) { // 如果有结果字段,提取结果 var start = jsonResult.IndexOf("\"result\":"); if (start >= 0) { start = jsonResult.IndexOf("\"", start + 9) + 1; var end = jsonResult.IndexOf("\"", start); if (end > start) { return jsonResult.Substring(start, end - start); } } // 如果有message字段,提取消息 start = jsonResult.IndexOf("\"message\":"); if (start >= 0) { start = jsonResult.IndexOf("\"", start + 10) + 1; var end = jsonResult.IndexOf("\"", start); if (end > start) { return jsonResult.Substring(start, end - start); } } return "AI识别成功,但未获取到具体描述。"; } else { // 提取错误消息 var start = jsonResult.IndexOf("\"message\":"); if (start >= 0) { start = jsonResult.IndexOf("\"", start + 10) + 1; var end = jsonResult.IndexOf("\"", start); if (end > start) { return $"识别失败: {jsonResult.Substring(start, end - start)}"; } } return "识别失败,请重试。"; } } catch { return jsonResult; // 如果解析失败,返回原始结果 } } private async void OnMessageSendClicked(object sender, EventArgs e) { var message = MessageEntry.Text?.Trim(); if (!string.IsNullOrWhiteSpace(message)) { MessageEntry.Text = string.Empty; await _agentService.Agent.ChatMessage(message); await ScrollToBottom(); } } private void OnClearChatClicked(object sender, EventArgs e) { _agentService.ClearChatHistory(); UpdateChatMessages(); } private void OnChatPageClicked(object sender, TappedEventArgs e) { // 当前已在聊天页面 } private async void OnSettingsPageClicked(object sender, TappedEventArgs e) { await Navigation.PushAsync(new SettingsPage(_agentService)); } private async Task ShowImageFullscreen(string imagePath) { try { if (string.IsNullOrEmpty(imagePath) || !File.Exists(imagePath)) { await DisplayAlert("提示", "图片文件不存在", "确定"); return; } // 创建全屏图片页面 var fullscreenPage = new ContentPage { Title = "查看图片", BackgroundColor = Colors.Black }; var imageView = new Image { Source = ImageSource.FromFile(imagePath), Aspect = Aspect.AspectFit, HorizontalOptions = LayoutOptions.FillAndExpand, VerticalOptions = LayoutOptions.FillAndExpand }; // 添加点击关闭功能 var tapGesture = new TapGestureRecognizer(); tapGesture.Tapped += async (s, e) => await Navigation.PopModalAsync(); imageView.GestureRecognizers.Add(tapGesture); var closeButton = new Button { Text = "关闭", BackgroundColor = Colors.Gray, TextColor = Colors.White, HorizontalOptions = LayoutOptions.Center, VerticalOptions = LayoutOptions.End, Margin = new Thickness(0, 0, 0, 50) }; closeButton.Clicked += async (s, e) => await Navigation.PopModalAsync(); var grid = new Grid { RowDefinitions = new RowDefinitionCollection { new RowDefinition(GridLength.Star), new RowDefinition(GridLength.Auto) } }; grid.Children.Add(imageView); Grid.SetRow(imageView, 0); grid.Children.Add(closeButton); Grid.SetRow(closeButton, 1); fullscreenPage.Content = grid; await Navigation.PushModalAsync(fullscreenPage); } catch (Exception ex) { await DisplayAlert("错误", $"显示图片失败: {ex.Message}", "确定"); } } protected override void OnDisappearing() { base.OnDisappearing(); _updateTimer?.Stop(); _updateTimer?.Dispose(); } } }