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