2025-11-21 20:59:49 +05:30
|
|
|
import 'dart:io';
|
2025-11-21 21:05:59 +05:30
|
|
|
import 'package:flutter/material.dart';
|
2025-11-21 19:50:39 +05:30
|
|
|
import 'package:flutter/services.dart';
|
|
|
|
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
2025-11-21 20:59:49 +05:30
|
|
|
import 'package:path/path.dart' as path;
|
|
|
|
|
import '../../features/chat/providers/chat_providers.dart';
|
|
|
|
|
import '../../features/chat/services/file_attachment_service.dart';
|
2025-11-21 21:05:59 +05:30
|
|
|
import '../../features/chat/views/voice_call_page.dart';
|
2025-11-21 20:59:49 +05:30
|
|
|
import '../services/navigation_service.dart';
|
|
|
|
|
import '../../shared/services/tasks/task_queue.dart';
|
|
|
|
|
import '../providers/app_providers.dart';
|
|
|
|
|
import '../../features/auth/providers/unified_auth_providers.dart';
|
|
|
|
|
import 'debug_logger.dart';
|
2025-11-21 19:50:39 +05:30
|
|
|
|
|
|
|
|
final androidAssistantProvider = Provider(
|
|
|
|
|
(ref) => AndroidAssistantHandler(ref),
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
final screenContextProvider = NotifierProvider<ScreenContextNotifier, String?>(
|
|
|
|
|
ScreenContextNotifier.new,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
class ScreenContextNotifier extends Notifier<String?> {
|
|
|
|
|
@override
|
|
|
|
|
String? build() => null;
|
|
|
|
|
|
|
|
|
|
void setContext(String? context) {
|
|
|
|
|
state = context;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class AndroidAssistantHandler {
|
|
|
|
|
static const platform = MethodChannel('app.cogwheel.conduit/assistant');
|
|
|
|
|
final Ref _ref;
|
|
|
|
|
|
|
|
|
|
AndroidAssistantHandler(this._ref) {
|
|
|
|
|
platform.setMethodCallHandler(_handleMethodCall);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Future<void> _handleMethodCall(MethodCall call) async {
|
|
|
|
|
if (call.method == 'analyzeScreen') {
|
|
|
|
|
final String context = call.arguments as String;
|
|
|
|
|
_ref.read(screenContextProvider.notifier).setContext(context);
|
2025-11-21 20:59:49 +05:30
|
|
|
} else if (call.method == 'analyzeScreenshot') {
|
|
|
|
|
final String screenshotPath = call.arguments as String;
|
|
|
|
|
await _processScreenshot(screenshotPath);
|
2025-11-21 21:05:59 +05:30
|
|
|
} else if (call.method == 'startVoiceCall') {
|
|
|
|
|
await _startVoiceCall();
|
2025-11-21 20:59:49 +05:30
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Future<void> _processScreenshot(String screenshotPath) async {
|
|
|
|
|
try {
|
|
|
|
|
DebugLogger.log('Processing screenshot: $screenshotPath', scope: 'assistant');
|
|
|
|
|
|
|
|
|
|
// Wait for app to be ready (authenticated and model available)
|
|
|
|
|
final navState = _ref.read(authNavigationStateProvider);
|
|
|
|
|
final model = _ref.read(selectedModelProvider);
|
|
|
|
|
|
|
|
|
|
if (navState != AuthNavigationState.authenticated || model == null) {
|
|
|
|
|
DebugLogger.log('App not ready for screenshot processing', scope: 'assistant');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Navigate to chat if not already there
|
|
|
|
|
final isOnChatRoute = NavigationService.currentRoute == Routes.chat;
|
|
|
|
|
if (!isOnChatRoute) {
|
|
|
|
|
// Navigation will happen via auth state
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Start a fresh chat context
|
|
|
|
|
startNewChat(_ref);
|
|
|
|
|
|
|
|
|
|
// Add screenshot as attachment
|
|
|
|
|
final file = File(screenshotPath);
|
|
|
|
|
if (!await file.exists()) {
|
|
|
|
|
DebugLogger.log('Screenshot file not found: $screenshotPath', scope: 'assistant');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
final svc = _ref.read(fileAttachmentServiceProvider);
|
|
|
|
|
if (svc != null) {
|
|
|
|
|
final attachment = LocalAttachment(
|
|
|
|
|
file: file,
|
|
|
|
|
displayName: path.basename(screenshotPath),
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
_ref.read(attachedFilesProvider.notifier).addFiles([attachment]);
|
|
|
|
|
|
|
|
|
|
// Enqueue upload via task queue
|
|
|
|
|
final activeConv = _ref.read(activeConversationProvider);
|
|
|
|
|
try {
|
|
|
|
|
await _ref.read(taskQueueProvider.notifier).enqueueUploadMedia(
|
|
|
|
|
conversationId: activeConv?.id,
|
|
|
|
|
filePath: attachment.file.path,
|
|
|
|
|
fileName: attachment.displayName,
|
|
|
|
|
fileSize: await attachment.file.length(),
|
|
|
|
|
);
|
|
|
|
|
DebugLogger.log('Screenshot uploaded successfully', scope: 'assistant');
|
|
|
|
|
} catch (e) {
|
|
|
|
|
DebugLogger.log('Failed to upload screenshot: $e', scope: 'assistant');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} catch (e) {
|
|
|
|
|
DebugLogger.log('Failed to process screenshot: $e', scope: 'assistant');
|
2025-11-21 19:50:39 +05:30
|
|
|
}
|
|
|
|
|
}
|
2025-11-21 21:05:59 +05:30
|
|
|
|
|
|
|
|
Future<void> _startVoiceCall() async {
|
|
|
|
|
try {
|
|
|
|
|
DebugLogger.log('Starting voice call from assistant', scope: 'assistant');
|
|
|
|
|
|
|
|
|
|
// Wait for app to be ready (authenticated and model available)
|
|
|
|
|
final navState = _ref.read(authNavigationStateProvider);
|
|
|
|
|
final model = _ref.read(selectedModelProvider);
|
|
|
|
|
|
|
|
|
|
if (navState != AuthNavigationState.authenticated || model == null) {
|
|
|
|
|
DebugLogger.log('App not ready for voice call', scope: 'assistant');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Navigate to chat if not already there
|
|
|
|
|
final isOnChatRoute = NavigationService.currentRoute == Routes.chat;
|
|
|
|
|
if (!isOnChatRoute) {
|
|
|
|
|
// Navigation will happen via auth state
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Get the current BuildContext from the navigation service
|
|
|
|
|
final context = NavigationService.navigatorKey.currentContext;
|
|
|
|
|
if (context == null) {
|
|
|
|
|
DebugLogger.log('No context available for voice call navigation', scope: 'assistant');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Navigate to voice call page
|
|
|
|
|
await Navigator.of(context).push(
|
|
|
|
|
MaterialPageRoute(
|
|
|
|
|
builder: (context) => const VoiceCallPage(),
|
|
|
|
|
fullscreenDialog: true,
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
DebugLogger.log('Voice call page launched', scope: 'assistant');
|
|
|
|
|
} catch (e) {
|
|
|
|
|
DebugLogger.log('Failed to start voice call: $e', scope: 'assistant');
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-11-21 19:50:39 +05:30
|
|
|
}
|