feat: multi-platform bug fixes, Android audio path, and build tooling

Flutter UI fixes:
- Fix stale channel badge/speaker when moved by others (derive current
  channel from ownClientId instead of optimistic local state)
- Fix Linux PTT via focused fallback key handler
- Distinguish ServerQuery clients with terminal icon in client list
- Reduce duplicate current-channel badge display
- Prevent PTT key-bind save from permanently closing voice settings
- Fix Linux GTK reopen-after-close (quit app on window destroy)
- Fix focused PTT: consume key events, release held keys on
  disconnect/leave-channel/mode/backend changes, suppress stale errors

Flutter Rust bridge:
- Thread is_server_query flag through protocol→bridge→Dart
- Add own_client_id to BridgeSnapshot DTO
- Add log_file_path_str() for platform log path queries

Rust protocol:
- Add ServerQuery test coverage (query_client_type_maps_to_server_query_flag)
- Split reqwest TLS: native-tls for desktop/iOS, rustls for Android

Rust audio:
- Upgrade cpal 0.16→0.17.3 with API adjustments (SampleRate, description())
- Suppress Android-only dead-code warnings (open_log_file, keyring_account)

Android build tooling:
- tools/build-opus-android.sh: NDK auto-discovery, correct CMake
  Android variables (ANDROID_ABI, ANDROID_PLATFORM), portable baseline
- tools/build-android-rust.sh: build+copy Rust cdylib for arm64-v8a,
  armeabi-v7a, x86_64 into android/app/src/main/jniLibs/
- Add jniLibs/ to .gitignore

Rust bridge:
- Guard open_log_file() on non-Android (Android uses logcat)
This commit is contained in:
Edison Jwa
2026-05-18 00:13:21 +09:00
parent 00692b76a2
commit dc9c5c0a4e
17 changed files with 760 additions and 208 deletions
+8 -2
View File
@@ -338,14 +338,19 @@ class BridgeClient {
/// Nickname.
final String name;
/// True for TeamSpeak ServerQuery clients.
final bool isServerQuery;
const BridgeClient({
required this.id,
required this.channel,
required this.name,
required this.isServerQuery,
});
@override
int get hashCode => id.hashCode ^ channel.hashCode ^ name.hashCode;
int get hashCode =>
id.hashCode ^ channel.hashCode ^ name.hashCode ^ isServerQuery.hashCode;
@override
bool operator ==(Object other) =>
@@ -354,7 +359,8 @@ class BridgeClient {
runtimeType == other.runtimeType &&
id == other.id &&
channel == other.channel &&
name == other.name;
name == other.name &&
isServerQuery == other.isServerQuery;
}
@freezed
@@ -1157,12 +1157,13 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
BridgeClient dco_decode_bridge_client(dynamic raw) {
// Codec=Dco (DartCObject based), see doc to use other codecs
final arr = raw as List<dynamic>;
if (arr.length != 3)
throw Exception('unexpected arr length: expect 3 but see ${arr.length}');
if (arr.length != 4)
throw Exception('unexpected arr length: expect 4 but see ${arr.length}');
return BridgeClient(
id: dco_decode_u_64(arr[0]),
channel: dco_decode_u_64(arr[1]),
name: dco_decode_String(arr[2]),
isServerQuery: dco_decode_bool(arr[3]),
);
}
@@ -1454,7 +1455,13 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
var var_id = sse_decode_u_64(deserializer);
var var_channel = sse_decode_u_64(deserializer);
var var_name = sse_decode_String(deserializer);
return BridgeClient(id: var_id, channel: var_channel, name: var_name);
var var_isServerQuery = sse_decode_bool(deserializer);
return BridgeClient(
id: var_id,
channel: var_channel,
name: var_name,
isServerQuery: var_isServerQuery,
);
}
@protected
@@ -1799,6 +1806,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
sse_encode_u_64(self.id, serializer);
sse_encode_u_64(self.channel, serializer);
sse_encode_String(self.name, serializer);
sse_encode_bool(self.isServerQuery, serializer);
}
@protected