feat(voice-call): Implement direct voice call launch from assistant
This commit is contained in:
@@ -34,11 +34,10 @@ class ConduitVoiceInteractionSession(context: Context) : VoiceInteractionSession
|
|||||||
launchApp()
|
launchApp()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Voice button
|
// Voice button - opens voice call directly
|
||||||
val voiceButton = view.findViewById<android.view.View>(app.cogwheel.conduit.R.id.btn_voice)
|
val voiceButton = view.findViewById<android.view.View>(app.cogwheel.conduit.R.id.btn_voice)
|
||||||
voiceButton?.setOnClickListener {
|
voiceButton?.setOnClickListener {
|
||||||
// TODO: Implement voice input functionality
|
launchAppForVoiceCall()
|
||||||
launchAppWithContext(includeScreenshot = false)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return view
|
return view
|
||||||
@@ -158,13 +157,32 @@ class ConduitVoiceInteractionSession(context: Context) : VoiceInteractionSession
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun launchAppForVoiceCall() {
|
||||||
|
try {
|
||||||
|
android.util.Log.d("ConduitVoiceSession", "Attempting to launch app for voice call")
|
||||||
|
val intent = Intent(context, MainActivity::class.java)
|
||||||
|
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||||
|
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP)
|
||||||
|
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
|
||||||
|
|
||||||
|
intent.putExtra("start_voice_call", true)
|
||||||
|
android.util.Log.d("ConduitVoiceSession", "Voice call flag attached")
|
||||||
|
|
||||||
|
context.startActivity(intent)
|
||||||
|
android.util.Log.d("ConduitVoiceSession", "App launch requested for voice call")
|
||||||
|
finish() // Close the overlay
|
||||||
|
} catch (e: Exception) {
|
||||||
|
android.util.Log.e("ConduitVoiceSession", "Failed to launch app for voice call", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun traverseNode(node: AssistStructure.ViewNode?, builder: StringBuilder) {
|
private fun traverseNode(node: AssistStructure.ViewNode?, builder: StringBuilder) {
|
||||||
if (node == null) return
|
if (node == null) return
|
||||||
|
|
||||||
if (node.text != null) {
|
if (node.text != null) {
|
||||||
builder.append(node.text).append("\n")
|
builder.append(node.text).append("\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Also check content description for accessibility text
|
// Also check content description for accessibility text
|
||||||
if (node.contentDescription != null) {
|
if (node.contentDescription != null) {
|
||||||
builder.append(node.contentDescription).append("\n")
|
builder.append(node.contentDescription).append("\n")
|
||||||
|
|||||||
@@ -50,12 +50,17 @@ class MainActivity : FlutterActivity() {
|
|||||||
|
|
||||||
val screenContext = intent.getStringExtra("screen_context")
|
val screenContext = intent.getStringExtra("screen_context")
|
||||||
val screenshotPath = intent.getStringExtra("screenshot_path")
|
val screenshotPath = intent.getStringExtra("screenshot_path")
|
||||||
|
val startVoiceCall = intent.getBooleanExtra("start_voice_call", false)
|
||||||
|
|
||||||
android.util.Log.d("MainActivity", "screenContext: $screenContext")
|
android.util.Log.d("MainActivity", "screenContext: $screenContext")
|
||||||
android.util.Log.d("MainActivity", "screenshotPath: $screenshotPath")
|
android.util.Log.d("MainActivity", "screenshotPath: $screenshotPath")
|
||||||
|
android.util.Log.d("MainActivity", "startVoiceCall: $startVoiceCall")
|
||||||
android.util.Log.d("MainActivity", "methodChannel: $methodChannel")
|
android.util.Log.d("MainActivity", "methodChannel: $methodChannel")
|
||||||
|
|
||||||
if (screenContext != null) {
|
if (startVoiceCall) {
|
||||||
|
android.util.Log.d("MainActivity", "Invoking startVoiceCall")
|
||||||
|
methodChannel?.invokeMethod("startVoiceCall", null)
|
||||||
|
} else if (screenContext != null) {
|
||||||
android.util.Log.d("MainActivity", "Invoking analyzeScreen")
|
android.util.Log.d("MainActivity", "Invoking analyzeScreen")
|
||||||
methodChannel?.invokeMethod("analyzeScreen", screenContext)
|
methodChannel?.invokeMethod("analyzeScreen", screenContext)
|
||||||
} else if (screenshotPath != null) {
|
} else if (screenshotPath != null) {
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:path/path.dart' as path;
|
import 'package:path/path.dart' as path;
|
||||||
import '../../features/chat/providers/chat_providers.dart';
|
import '../../features/chat/providers/chat_providers.dart';
|
||||||
import '../../features/chat/services/file_attachment_service.dart';
|
import '../../features/chat/services/file_attachment_service.dart';
|
||||||
|
import '../../features/chat/views/voice_call_page.dart';
|
||||||
import '../services/navigation_service.dart';
|
import '../services/navigation_service.dart';
|
||||||
import '../../shared/services/tasks/task_queue.dart';
|
import '../../shared/services/tasks/task_queue.dart';
|
||||||
import '../providers/app_providers.dart';
|
import '../providers/app_providers.dart';
|
||||||
@@ -42,6 +44,8 @@ class AndroidAssistantHandler {
|
|||||||
} else if (call.method == 'analyzeScreenshot') {
|
} else if (call.method == 'analyzeScreenshot') {
|
||||||
final String screenshotPath = call.arguments as String;
|
final String screenshotPath = call.arguments as String;
|
||||||
await _processScreenshot(screenshotPath);
|
await _processScreenshot(screenshotPath);
|
||||||
|
} else if (call.method == 'startVoiceCall') {
|
||||||
|
await _startVoiceCall();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,4 +106,45 @@ class AndroidAssistantHandler {
|
|||||||
DebugLogger.log('Failed to process screenshot: $e', scope: 'assistant');
|
DebugLogger.log('Failed to process screenshot: $e', scope: 'assistant');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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');
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user