Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
e28c475
Enable SDK logging
hiroshihorie Aug 17, 2025
b7d9c9b
Bump macOS deployment target
hiroshihorie Aug 17, 2025
b841ce3
Pre-connect audio buffer
hiroshihorie Aug 17, 2025
05c4d4e
Merge branch 'main' into hiroshi/pre-connect-audio
hiroshihorie Aug 17, 2025
3b09957
Use local packages temporarily
hiroshihorie Sep 2, 2025
ba1d8e8
Merge branch 'main' into hiroshi/pre-connect-audio
hiroshihorie Sep 5, 2025
eeeb143
Logging
hiroshihorie Sep 7, 2025
182ccf8
Agent listening indicator
hiroshihorie Sep 8, 2025
187e3f2
Fix event
hiroshihorie Sep 8, 2025
a0c5ce7
Update android settings
hiroshihorie Sep 8, 2025
2f08d38
Merge branch 'main' into hiroshi/pre-connect-audio
hiroshihorie Sep 9, 2025
b8f367b
Update pubspec.yaml
hiroshihorie Sep 9, 2025
1d3de0d
Update pubspec.yaml
hiroshihorie Sep 9, 2025
5477483
Merge branch 'hiroshi/pre-connect-audio' of https://github.com/liveki…
hiroshihorie Sep 9, 2025
ad24c6a
Merge branch 'main' into hiroshi/pre-connect-audio
hiroshihorie Sep 9, 2025
377228c
Adjust progress indicator
hiroshihorie Sep 9, 2025
e6d8b9d
Update Podfile.lock
hiroshihorie Sep 9, 2025
b8f1554
Merge branch 'main' into hiroshi/pre-connect-audio
hiroshihorie Sep 9, 2025
6a6284d
Merge branch 'hiroshi/pre-connect-audio' of https://github.com/liveki…
hiroshihorie Sep 21, 2025
253a350
Merge branch 'main' into hiroshi/pre-connect-audio
hiroshihorie Oct 1, 2025
a17465e
swift format
hiroshihorie Nov 13, 2025
8fdeb86
Update GeneratedPluginRegistrant.swift
hiroshihorie Nov 13, 2025
f316694
update
hiroshihorie Dec 1, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 10 additions & 10 deletions ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,19 @@ PODS:
- device_info_plus (0.0.1):
- Flutter
- Flutter (1.0.0)
- flutter_webrtc (0.14.0):
- flutter_webrtc (1.2.0):
- Flutter
- WebRTC-SDK (= 125.6422.07)
- livekit_client (2.4.9):
- WebRTC-SDK (= 137.7151.04)
- livekit_client (2.5.4):
- Flutter
- flutter_webrtc
- WebRTC-SDK (= 125.6422.07)
- WebRTC-SDK (= 137.7151.04)
- path_provider_foundation (0.0.1):
- Flutter
- FlutterMacOS
- url_launcher_ios (0.0.1):
- Flutter
- WebRTC-SDK (125.6422.07)
- WebRTC-SDK (137.7151.04)

DEPENDENCIES:
- connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`)
Expand Down Expand Up @@ -51,11 +51,11 @@ SPEC CHECKSUMS:
connectivity_plus: cb623214f4e1f6ef8fe7403d580fdad517d2f7dd
device_info_plus: 21fcca2080fbcd348be798aa36c3e5ed849eefbe
Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467
flutter_webrtc: fd0d3bdef8766a0736dbbe2e5b7e85f1f3c52117
livekit_client: 3f79d79233a5bd13d5b541732624ef959d7c538e
path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564
url_launcher_ios: 694010445543906933d732453a59da0a173ae33d
WebRTC-SDK: dff00a3892bc570b6014e046297782084071657e
flutter_webrtc: c3e21fc0dcd9d8eb246ae4d5256fcbeb2f5ecd22
livekit_client: 53ca658779b78710fb458cccee28b53a13356c15
path_provider_foundation: bb55f6dbba17d0dccd6737fe6f7f34fbd0376880
url_launcher_ios: 7a95fa5b60cc718a708b8f2966718e93db0cef1b
WebRTC-SDK: 40d4f5ba05cadff14e4db5614aec402a633f007e

PODFILE CHECKSUM: a57f30d18f102dd3ce366b1d62a55ecbef2158e5

Expand Down
14 changes: 7 additions & 7 deletions ios/Runner/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ import UIKit

@main
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}
10 changes: 4 additions & 6 deletions ios/RunnerTests/RunnerTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,8 @@ import UIKit
import XCTest

class RunnerTests: XCTestCase {

func testExample() {
// If you add code to the Runner application, consider adding tests here.
// See https://developer.apple.com/documentation/xctest for more information about using XCTest.
}

func testExample() {
// If you add code to the Runner application, consider adding tests here.
// See https://developer.apple.com/documentation/xctest for more information about using XCTest.
}
}
69 changes: 51 additions & 18 deletions lib/controllers/app_ctrl.dart
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ class AppCtrl extends ChangeNotifier {
AppScreenState appScreenState = AppScreenState.welcome;
ConnectionState connectionState = ConnectionState.disconnected;
AgentScreenState agentScreenState = AgentScreenState.visualizer;
bool isAgentListening = false;

//Test
bool isUserCameEnabled = false;
Expand All @@ -42,6 +43,10 @@ class AppCtrl extends ChangeNotifier {
// Timer for checking agent connection
Timer? _agentConnectionTimer;

// Event listeners cleanup functions
sdk.CancelListenFunc? _preConnectAudioStartedListener;
sdk.CancelListenFunc? _preConnectAudioStoppedListener;

AppCtrl() {
final format = DateFormat('HH:mm:ss');
// configure logs for debugging
Expand All @@ -57,12 +62,16 @@ class AppCtrl extends ChangeNotifier {
notifyListeners();
}
});

// Listen for pre-connect audio buffer events
_setupPreConnectAudioListeners();
}

@override
void dispose() {
messageCtrl.dispose();
_cancelAgentTimer();
_cleanupPreConnectAudioListeners();
super.dispose();
}

Expand Down Expand Up @@ -101,36 +110,58 @@ class AppCtrl extends ChangeNotifier {
notifyListeners();
}

void _setupPreConnectAudioListeners() {
_preConnectAudioStartedListener = room.events.on<sdk.PreConnectAudioBufferStartedEvent>((event) {
_logger.info('Pre-connect audio buffer started: ${event.sampleRate}Hz, timeout: ${event.timeout}');
isAgentListening = true;
notifyListeners();
});

_preConnectAudioStoppedListener = room.events.on<sdk.PreConnectAudioBufferStoppedEvent>((event) {
_logger.info('Pre-connect audio buffer stopped: ${event.bufferedSize} bytes, sent: ${event.isBufferSent}');
isAgentListening = false;
notifyListeners();
});
}

void _cleanupPreConnectAudioListeners() {
_preConnectAudioStartedListener?.call();
_preConnectAudioStartedListener = null;
_preConnectAudioStoppedListener?.call();
_preConnectAudioStoppedListener = null;
}

void connect() async {
_logger.info("Connect....");
connectionState = ConnectionState.connecting;
notifyListeners();

try {
// Generate random room and participant names
// In a real app, you'd likely use meaningful names
final roomName = 'room-${(1000 + DateTime.now().millisecondsSinceEpoch % 9000)}';
final participantName = 'user-${(1000 + DateTime.now().millisecondsSinceEpoch % 9000)}';

// Get connection details from token service
final connectionDetails = await tokenService.fetchConnectionDetails(
roomName: roomName,
participantName: participantName,
);
_logger.info("Starting pre-connect audio...");

_logger.info("Fetched Connection Details: $connectionDetails, connecting to room...");
await room.withPreConnectAudio(() async {
_logger.info("Pre-connect audio started...");

await room.connect(
connectionDetails.serverUrl,
connectionDetails.participantToken,
);
// Generate random room and participant names
// In a real app, you'd likely use meaningful names
final roomName = 'room-${(1000 + DateTime.now().millisecondsSinceEpoch % 9000)}';
final participantName = 'user-${(1000 + DateTime.now().millisecondsSinceEpoch % 9000)}';

_logger.info("Connected to room");
// Get connection details from token service
final connectionDetails = await tokenService.fetchConnectionDetails(
roomName: roomName,
participantName: participantName,
);

await room.localParticipant?.setMicrophoneEnabled(true);
_logger.info("Fetched Connection Details: $connectionDetails, connecting to room...");

_logger.info("Microphone enabled");
await room.connect(
connectionDetails.serverUrl,
connectionDetails.participantToken,
);

_logger.info("Connected to room");
});
connectionState = ConnectionState.connected;
appScreenState = AppScreenState.agent;

Expand All @@ -143,6 +174,7 @@ class AppCtrl extends ChangeNotifier {

connectionState = ConnectionState.disconnected;
appScreenState = AppScreenState.welcome;
isAgentListening = false;
notifyListeners();
}
}
Expand All @@ -155,6 +187,7 @@ class AppCtrl extends ChangeNotifier {
connectionState = ConnectionState.disconnected;
appScreenState = AppScreenState.welcome;
agentScreenState = AgentScreenState.visualizer;
isAgentListening = false;

notifyListeners();
}
Expand Down
28 changes: 28 additions & 0 deletions lib/screens/welcome_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,34 @@ class WelcomeScreen extends StatelessWidget {
],
),
),
// Agent listening indicator
Consumer<ctrl.AppCtrl>(
builder: (ctx, appCtrl, child) => AnimatedOpacity(
opacity: appCtrl.isAgentListening ? 1.0 : 0.0,
duration: const Duration(milliseconds: 300),
child: Container(
padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 16),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
Icons.mic,
color: Colors.green,
size: 18,
),
const SizedBox(width: 8),
const Text(
'Agent is listening',
style: TextStyle(
color: Colors.green,
fontWeight: FontWeight.w500,
),
),
],
),
),
),
),
Builder(
builder: (ctx) {
final isProgressing = [
Expand Down
20 changes: 10 additions & 10 deletions macos/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,20 @@ PODS:
- FlutterMacOS
- device_info_plus (0.0.1):
- FlutterMacOS
- flutter_webrtc (0.14.0):
- flutter_webrtc (1.2.0):
- FlutterMacOS
- WebRTC-SDK (= 125.6422.07)
- WebRTC-SDK (= 137.7151.04)
- FlutterMacOS (1.0.0)
- livekit_client (2.4.9):
- livekit_client (2.5.4):
- flutter_webrtc
- FlutterMacOS
- WebRTC-SDK (= 125.6422.07)
- WebRTC-SDK (= 137.7151.04)
- path_provider_foundation (0.0.1):
- Flutter
- FlutterMacOS
- url_launcher_macos (0.0.1):
- FlutterMacOS
- WebRTC-SDK (125.6422.07)
- WebRTC-SDK (137.7151.04)

DEPENDENCIES:
- connectivity_plus (from `Flutter/ephemeral/.symlinks/plugins/connectivity_plus/macos`)
Expand Down Expand Up @@ -50,12 +50,12 @@ EXTERNAL SOURCES:
SPEC CHECKSUMS:
connectivity_plus: 4adf20a405e25b42b9c9f87feff8f4b6fde18a4e
device_info_plus: 4fb280989f669696856f8b129e4a5e3cd6c48f76
flutter_webrtc: a7eeb54859e672228c28f4b48b1fb61561976ea3
flutter_webrtc: 718eae22a371cd94e5d56aa4f301443ebc5bb737
FlutterMacOS: d0db08ddef1a9af05a5ec4b724367152bb0500b1
livekit_client: c9d9f41996f5cf22b9ba0e8483e6af4ca5094059
path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564
url_launcher_macos: 0fba8ddabfc33ce0a9afe7c5fef5aab3d8d2d673
WebRTC-SDK: dff00a3892bc570b6014e046297782084071657e
livekit_client: 3df5a1787d64010ca56c4002959d9e47c03ba3fb
path_provider_foundation: bb55f6dbba17d0dccd6737fe6f7f34fbd0376880
url_launcher_macos: f87a979182d112f911de6820aefddaf56ee9fbfd
WebRTC-SDK: 40d4f5ba05cadff14e4db5614aec402a633f007e

PODFILE CHECKSUM: 9ebaf0ce3d369aaa26a9ea0e159195ed94724cf3

Expand Down
10 changes: 6 additions & 4 deletions macos/Runner.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@
331C80D7294CF71000263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = "<group>"; };
333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = "<group>"; };
335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = "<group>"; };
33CC10ED2044A3C60003C045 /* voice_assistant.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = voice_assistant.app; sourceTree = BUILT_PRODUCTS_DIR; };
33CC10ED2044A3C60003C045 /* Voice Assistant.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Voice Assistant.app"; sourceTree = BUILT_PRODUCTS_DIR; };
33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = "<group>"; };
33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = "<group>"; };
Expand Down Expand Up @@ -144,7 +144,7 @@
33CC10EE2044A3C60003C045 /* Products */ = {
isa = PBXGroup;
children = (
33CC10ED2044A3C60003C045 /* voice_assistant.app */,
33CC10ED2044A3C60003C045 /* Voice Assistant.app */,
331C80D5294CF71000263BE5 /* RunnerTests.xctest */,
);
name = Products;
Expand Down Expand Up @@ -195,7 +195,6 @@
A456EC03A0B3E32F7C86CADF /* Pods-RunnerTests.release.xcconfig */,
93FFB8C7B6A7F2D36EE98E96 /* Pods-RunnerTests.profile.xcconfig */,
);
name = Pods;
path = Pods;
sourceTree = "<group>";
};
Expand Down Expand Up @@ -249,7 +248,7 @@
);
name = Runner;
productName = Runner;
productReference = 33CC10ED2044A3C60003C045 /* voice_assistant.app */;
productReference = 33CC10ED2044A3C60003C045 /* Voice Assistant.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
Expand Down Expand Up @@ -579,6 +578,7 @@
"$(inherited)",
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 10.15;
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_VERSION = 5.0;
};
Expand Down Expand Up @@ -711,6 +711,7 @@
"$(inherited)",
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 10.15;
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
Expand All @@ -731,6 +732,7 @@
"$(inherited)",
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 10.15;
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_VERSION = 5.0;
};
Expand Down
12 changes: 6 additions & 6 deletions macos/Runner/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ import FlutterMacOS

@main
class AppDelegate: FlutterAppDelegate {
override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
return true
}
override func applicationShouldTerminateAfterLastWindowClosed(_: NSApplication) -> Bool {
return true
}

override func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool {
return true
}
override func applicationSupportsSecureRestorableState(_: NSApplication) -> Bool {
return true
}
}
16 changes: 8 additions & 8 deletions macos/Runner/MainFlutterWindow.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@ import Cocoa
import FlutterMacOS

class MainFlutterWindow: NSWindow {
override func awakeFromNib() {
let flutterViewController = FlutterViewController()
let windowFrame = self.frame
self.contentViewController = flutterViewController
self.setFrame(windowFrame, display: true)
override func awakeFromNib() {
let flutterViewController = FlutterViewController()
let windowFrame = frame
contentViewController = flutterViewController
setFrame(windowFrame, display: true)

RegisterGeneratedPlugins(registry: flutterViewController)
RegisterGeneratedPlugins(registry: flutterViewController)

super.awakeFromNib()
}
super.awakeFromNib()
}
}
10 changes: 4 additions & 6 deletions macos/RunnerTests/RunnerTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,8 @@ import FlutterMacOS
import XCTest

class RunnerTests: XCTestCase {

func testExample() {
// If you add code to the Runner application, consider adding tests here.
// See https://developer.apple.com/documentation/xctest for more information about using XCTest.
}

func testExample() {
// If you add code to the Runner application, consider adding tests here.
// See https://developer.apple.com/documentation/xctest for more information about using XCTest.
}
}
Loading
Loading