diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index a26ccc5a..8fefa56f 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -23,6 +23,9 @@
+
+
+
diff --git a/android/app/src/main/kotlin/com/edde746/plezy/MainActivity.kt b/android/app/src/main/kotlin/com/edde746/plezy/MainActivity.kt
index 1ba23782..d2a92aad 100644
--- a/android/app/src/main/kotlin/com/edde746/plezy/MainActivity.kt
+++ b/android/app/src/main/kotlin/com/edde746/plezy/MainActivity.kt
@@ -253,6 +253,9 @@ class MainActivity : FlutterActivity() {
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, THEME_CHANNEL).setMethodCallHandler { call, result ->
when (call.method) {
"getRenderer" -> result.success(if (usingSkia) "Skia" else "Impeller")
+ "hasCamera" -> result.success(
+ packageManager.hasSystemFeature(android.content.pm.PackageManager.FEATURE_CAMERA_ANY)
+ )
"setSplashTheme" -> {
val mode = call.argument("mode")
diff --git a/lib/screens/companion_remote/pairing_screen.dart b/lib/screens/companion_remote/pairing_screen.dart
index 2110980b..23855234 100644
--- a/lib/screens/companion_remote/pairing_screen.dart
+++ b/lib/screens/companion_remote/pairing_screen.dart
@@ -28,12 +28,34 @@ class _PairingScreenState extends State {
// QR scanner state
MobileScannerController? _scannerController;
String? _lastScannedCode;
+ bool _hasCamera = false;
- bool get _isMobile => Platform.isAndroid || Platform.isIOS;
+ bool get _canScan => (Platform.isAndroid || Platform.isIOS) && _hasCamera;
- // Tab indices: mobile gets Scan (0) + Manual (1), desktop gets Manual (0)
- int get _scanTabIndex => _isMobile ? 0 : -1;
- int get _manualTabIndex => _isMobile ? 1 : 0;
+ // Tab indices: scannable gets Scan (0) + Manual (1), otherwise Manual (0)
+ int get _scanTabIndex => _canScan ? 0 : -1;
+ int get _manualTabIndex => _canScan ? 1 : 0;
+
+ @override
+ void initState() {
+ super.initState();
+ _checkCamera();
+ }
+
+ Future _checkCamera() async {
+ if (Platform.isIOS) {
+ setState(() => _hasCamera = true);
+ return;
+ }
+ if (!Platform.isAndroid) return;
+ try {
+ const channel = MethodChannel('com.plezy/theme');
+ final result = await channel.invokeMethod('hasCamera');
+ if (mounted) setState(() => _hasCamera = result ?? false);
+ } catch (e) {
+ debugPrint('Camera check failed: $e');
+ }
+ }
@override
void dispose() {
@@ -158,7 +180,7 @@ class _PairingScreenState extends State {
),
body: Column(
children: [
- if (_isMobile)
+ if (_canScan)
SegmentedButton(
segments: [
ButtonSegment(
@@ -186,7 +208,7 @@ class _PairingScreenState extends State {
}
Widget _buildTabContent() {
- if (_selectedTab == _scanTabIndex && _isMobile) return _buildScanTab();
+ if (_selectedTab == _scanTabIndex && _canScan) return _buildScanTab();
return _buildManualEntryTab();
}
@@ -416,7 +438,7 @@ class _PairingScreenState extends State {
Text(t.companionRemote.pairing.tips, style: Theme.of(context).textTheme.titleMedium),
const SizedBox(height: 8),
_buildTipCard(context, Icons.computer, t.companionRemote.pairing.tipDesktop),
- if (_isMobile) ...[
+ if (_canScan) ...[
const SizedBox(height: 8),
_buildTipCard(context, Icons.qr_code, t.companionRemote.pairing.tipScan),
],