fix(android): polish app chrome and join guard
This commit is contained in:
+8
@@ -1,7 +1,10 @@
|
||||
package app.chanora.chanora_flutter
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Color
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import app.chanora.chanora_flutter.AndroidPermissionRequester
|
||||
import app.chanora.chanora_flutter.BackIntentBridge
|
||||
import app.chanora.chanora_flutter.MethodChannels
|
||||
@@ -76,6 +79,11 @@ class MainActivity : FlutterActivity() {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
window.navigationBarColor = Color.rgb(255, 251, 254)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
window.decorView.systemUiVisibility =
|
||||
window.decorView.systemUiVisibility or View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR
|
||||
}
|
||||
// SDD-105: pass the Application context to the Rust side so the
|
||||
// audio engine can open device handles. Must run on the main thread
|
||||
// before any `chanora_audio` call from Dart. The native library is
|
||||
|
||||
@@ -31,6 +31,8 @@ import 'widgets/voice_settings.dart';
|
||||
|
||||
bool get _isMacOS => !kIsWeb && Platform.isMacOS;
|
||||
|
||||
const Color _appSurfaceColor = Color(0xFFFFFBFE);
|
||||
|
||||
/// Top padding for macOS to clear traffic-light buttons.
|
||||
const double _macOSTrafficLightPad = 56.0;
|
||||
|
||||
@@ -205,19 +207,29 @@ class ChanoraApp extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MaterialApp(
|
||||
onGenerateTitle: (ctx) => AppL10n.of(ctx).appTitle,
|
||||
// No "DEBUG" banner in the top-right corner. This is purely
|
||||
// cosmetic for the developer-build experience; release builds
|
||||
// never render it regardless of this flag.
|
||||
debugShowCheckedModeBanner: false,
|
||||
theme: ThemeData(
|
||||
useMaterial3: true,
|
||||
colorSchemeSeed: const Color(0xFF3F51B5),
|
||||
return AnnotatedRegion<SystemUiOverlayStyle>(
|
||||
value: const SystemUiOverlayStyle(
|
||||
statusBarColor: Colors.transparent,
|
||||
statusBarIconBrightness: Brightness.dark,
|
||||
systemNavigationBarColor: _appSurfaceColor,
|
||||
systemNavigationBarDividerColor: _appSurfaceColor,
|
||||
systemNavigationBarIconBrightness: Brightness.dark,
|
||||
),
|
||||
child: MaterialApp(
|
||||
onGenerateTitle: (ctx) => AppL10n.of(ctx).appTitle,
|
||||
// No "DEBUG" banner in the top-right corner. This is purely
|
||||
// cosmetic for the developer-build experience; release builds
|
||||
// never render it regardless of this flag.
|
||||
debugShowCheckedModeBanner: false,
|
||||
theme: ThemeData(
|
||||
useMaterial3: true,
|
||||
colorSchemeSeed: const Color(0xFF3F51B5),
|
||||
scaffoldBackgroundColor: _appSurfaceColor,
|
||||
),
|
||||
localizationsDelegates: AppL10n.localizationsDelegates,
|
||||
supportedLocales: AppL10n.supportedLocales,
|
||||
home: const _BetaHome(),
|
||||
),
|
||||
localizationsDelegates: AppL10n.localizationsDelegates,
|
||||
supportedLocales: AppL10n.supportedLocales,
|
||||
home: const _BetaHome(),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -703,6 +715,7 @@ class _BetaHomeState extends State<_BetaHome> {
|
||||
password = await _askChannelPassword(l10n);
|
||||
if (password == null) return; // cancelled
|
||||
}
|
||||
setState(() => _pendingVoiceChannelId = ch.id);
|
||||
try {
|
||||
// SDD-106 §1, §6 + SRS-209: Android runtime permission gate.
|
||||
// Request RECORD_AUDIO at or before voice_join. On denial or
|
||||
@@ -756,6 +769,7 @@ class _BetaHomeState extends State<_BetaHome> {
|
||||
// is selected by TS3 error code per the canonical
|
||||
// catalogue at https://github.com/ReSpeak/tsdeclarations.
|
||||
final message = _channelJoinErrorMessage(l10n, e);
|
||||
setState(() => _pendingVoiceChannelId = null);
|
||||
messenger.showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(message),
|
||||
@@ -1274,7 +1288,7 @@ class _BetaHomeState extends State<_BetaHome> {
|
||||
],
|
||||
];
|
||||
|
||||
const headerTitle = _AppBarTitle();
|
||||
const headerTitle = SizedBox.shrink();
|
||||
|
||||
final bodyContent = LayoutBuilder(
|
||||
builder: (ctx, bodyConstraints) {
|
||||
@@ -1491,19 +1505,6 @@ class _BetaHomeState extends State<_BetaHome> {
|
||||
}
|
||||
}
|
||||
|
||||
/// App header title. The current channel is highlighted only in the
|
||||
/// channel tree and Voice Bar to avoid repeating the same badge in
|
||||
/// multiple places.
|
||||
class _AppBarTitle extends StatelessWidget {
|
||||
const _AppBarTitle();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final l10n = AppL10n.of(context);
|
||||
return Text(l10n.appTitle);
|
||||
}
|
||||
}
|
||||
|
||||
/// 'Save bookmark' name-entry dialog. Owns its own
|
||||
/// TextEditingController via a StatefulWidget lifecycle so the
|
||||
/// dispose() runs cleanly at unmount time (after the route pop
|
||||
|
||||
Reference in New Issue
Block a user