020be77faf2ff9847dda4de76ce94aafd8b5a867
4
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
7c0e5caa92 |
fix(ui,ios): first-tap on TextField now opens the keyboard
User report: 'every first time to tap to input box nothing
happened'. Symptom is iPhone-specific: the very first tap on any
of the Host / Nickname / Password text boxes in the connect form
fails to focus + open the keyboard. The second tap on the same
field works.
Two distinct causes, both addressed:
apps/chanora_flutter/lib/main.dart
The connect form lives inside a SingleChildScrollView. Flutter
on iOS has a long-standing issue (flutter#19027) where the
enclosing Scrollable's gesture-arena participant absorbs the
first tap as a possible scroll-intent, leaving the TextField
unfocused; the second tap reaches the field because the
scrollable has already declined to handle a drag.
Fix: _ConnectForm converted from StatelessWidget to
StatefulWidget so it can own FocusNodes for the three text
fields. Each TextField gains:
* focusNode: <its own FocusNode>
* onTap: () => focusNode.requestFocus()
— forces focus on tap-down regardless of arena outcome
* textInputAction: TextInputAction.next (host, nick) /
TextInputAction.done (password) for return-key flow
* autocorrect: false, enableSuggestions: false
— these are server-host / nickname / password fields, the
iOS auto-correct + suggestion bar is wrong for all three.
Also set keyboardDismissBehavior: ScrollViewKeyboardDismissBehavior
.onDrag on the SingleChildScrollView so the keyboard hides
when the user starts scrolling the bookmark list below.
apps/chanora_flutter/ios/Runner/AppDelegate.swift
AVAudioSession.sharedInstance().requestRecordPermission was
fired synchronously from didFinishLaunchingWithOptions. The
permission alert can race with iOS's text-input subsystem
initialisation: if the alert appears before the keyboard
layer finishes wiring up, subsequent text-field focus
requests are dropped silently.
Fix: DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) to
defer the permission request until ~1 s after the app shell
is on screen. Long enough for iOS's text-input layer to fully
initialise; short enough that the user reads the prompt
before tapping a field.
flutter analyze: clean (6 pre-existing Radio.groupValue infos).
flutter build ios --release --no-codesign: 26.8 s clean
(Runner.app 29.8 MB).
|
||
|
|
d4c04b6a72 |
fix(ui,audio,ios,macos): eight P0 mobile fixes
User report from sideloaded iPhone build, in order of priority: #4 'Could not join channel: audio: audio backend: build_output_stream: The requested stream configuration is not supported by the device.' Cause: we forced cpal::BufferSize::Fixed(2048) on the output and input streams unconditionally on non-Linux. iOS CoreAudio RemoteIO units reject arbitrary buffer-size requests with that exact error. Windows WASAPI needs the pinning for shared-mode jitter, but macOS / iOS do not. Fix: cfg-gate Fixed(2048) to target_os = 'windows'; everywhere else use BufferSize::Default and let the platform HAL pick. crates/chanora_audio/src/engine.rs. #5 'Could not join channel: invariant violated: voice_in already taken' Cause: start_audio tore down the old engine BEFORE attempting to construct the new one, and consumed voice_in (an mpsc Receiver that can only be taken once) early. When the new engine failed mid-construction (e.g. because of #4 above) the session was left with: no audio engine, voice_in consumed, no way to retry without reconnect. The second voice_join attempt surfaced the invariant message. Fix: build the new engine BEFORE tearing down the old. Only swap state.audio if construction succeeded. crates/chanora_ core/src/lib.rs::ChanoraSession::start_audio. Additionally added a put_voice_in helper to the protocol adapter ( crates/chanora_protocol/src/adapter.rs) for a future broadcast-channel migration; the helper is unused on the immediate fix path but documents the intent. #3 'permission request would better on first open' Cause: AVAudioSession only triggers the mic-permission prompt the first time it tries to record. We never recorded until voice_join, so the prompt fired then. Fix iOS: AVAudioSession.sharedInstance().requestRecordPermission in AppDelegate.swift::application(_:didFinishLaunchingWithOptions:). Fix macOS: AVCaptureDevice.requestAccess(for: .audio) in macos/Runner/AppDelegate.swift::applicationDidFinishLaunching. Both run non-blocking; user can deny without crashing app launch, and voice_join then surfaces a clearer downstream error when the engine fails to open the input device. #1 + #2 'one-column upper takes too much space; Push to Talk button at bottom would be better' Layout rework for narrow-mode (single column, mobile shape): - Flipped the stacking order in main.dart so Voice Bar moves to the BOTTOM of the body and the channel tree (Expanded) fills above. Wide-mode (Row, >= 840 dp) layout unchanged. - Inside the Voice Bar on touch-only hosts, moved the on-screen Push to Talk button to be the LAST element of the Voice Bar (was Row 3). Order now: pill + mutes, mode badge + settings, level meter, stats line, release-tail caption, PTT button. The button is closest to the user's thumb when the Voice Bar is pinned to the bottom of a narrow-layout screen. #6 'remove right top debug badge' debugShowCheckedModeBanner: false on the MaterialApp. Release builds never showed it anyway; this only affects local dev / debug builds. #7 'what does the refresh button use for? nothing happened' Removed. The snapshot updates via BridgeEvent::SnapshotChanged are pushed from the bridge — a manual rust.snapshot() call was redundant. Now only the Diagnostics + Disconnect actions remain in the AppBar trailing row when connected. #8 'Bind Key related function should not be added to a mobile platform' widgets/voice_settings.dart: bind-key OutlinedButton is now #cfg'd out when Platform.isIOS || Platform.isAndroid. The release-tail slider stays because it still applies to the on-screen PTT button. Capability badge in voice_bar.dart also hidden on mobile (it would always show L0Focused which is redundant with the visible on-screen button). Tests + analyze: chanora_audio 34/0/0 on macOS, workspace 78/0/1 on Linux; flutter analyze clean (6 pre-existing Radio.groupValue infos). flutter build ios --release --no-codesign: 28.8 s clean (Runner.app 29.9 MB). |
||
|
|
f3320715ea |
feat(audio,ios): AVAudioSession PlayAndRecord+voiceChat in AppDelegate
iOS AVAudioSession must be configured BEFORE Flutter starts its
audio pipeline; the canonical place is application(_:didFinishLaunching\
WithOptions:) in AppDelegate.swift. This commit:
apps/chanora_flutter/ios/Runner/AppDelegate.swift:
* import AVFoundation
* In application(_:didFinishLaunchingWithOptions:), call
AVAudioSession.sharedInstance().setCategory(.playAndRecord,
mode: .voiceChat,
options: [.defaultToSpeaker, .allowBluetooth, .allowBluetoothA2DP])
followed by setActive(true). Failures are NSLogged but do not
block app launch — cpal's CoreAudio backend will still come up
against the default iOS routing.
This shape:
* routes the receiver/speaker like a phone call (.playAndRecord +
.voiceChat),
* engages on-device AEC / NS where supported,
* defaults to speaker so users don't have to hold the phone to
their ear,
* permits Bluetooth headsets (AirPods et al. just work).
crates/chanora_audio/src/engine.rs:
* Replace the iOS engine-start placeholder log line ('binding
pending — Chanora iOS audio is documented-only for Beta') with
an honest acknowledgment that the AVAudioSession configuration
lives Swift-side. The Rust engine acknowledges the request, then
cpal opens its CoreAudio streams against the session.
iOS-only Rust code is #[cfg(target_os = "ios")]-gated so this commit
is no-op on every other platform.
SRS-197: iOS/macOS audio routing contract. DEC-025: iOS officially
in scope for P0 (Focused PTT only — Apple's sandbox model has no
global PTT analogue).
|
||
|
|
4d57d189c9 |
feat(flutter,macos,ios): scaffold platform Xcode projects via flutter create
Ran `flutter create --platforms=macos,ios --project-name=chanora_flutter
--org=app.chanora .` on the M1 Mac to generate the standard Flutter
platform-specific scaffolding (Runner.xcodeproj, Podfile, AppDelegate,
entitlements, etc.) for both macOS and iOS.
The cross-platform Dart source (lib/) and Rust workspace (crates/,
core/) carry the actual application logic; these scaffolds are
required only so flutter build macos / ios can resolve their Xcode
projects. No application code added.
macOS smoke-launch from the M1 Mac verified the bridge dylib load
path: after manually wrapping libchanora_bridge.dylib into a proper
chanora_bridge.framework bundle (FRB on macOS expects a framework,
not a plain dylib) and codesigning ad-hoc, the runner starts cleanly
through 'bridge initialised', 'identity store initialised', 'bookmark
store initialised' just like the Linux runner.
Following commits will:
* Wire the framework-bundling step into build glue (currently manual
install_name_tool + codesign).
* Replace the macOS PTT backend stub (crates/chanora_audio/src/
ptt_backends/macos.rs) with live IOHIDCheckAccess +
CGEventTapCreate so the descriptor advertises real L2/L3 capability
on a permission-granted box (SDD-085).
* Add the iOS AVAudioSession PlayAndRecord+voiceChat wiring.
* Add docs/verification/macos-p0-acceptance.md and ios-p0-acceptance.md.
|