Hide QR code scanner on devices without cameras (#101)

This commit is contained in:
John Maguire 2022-11-22 16:50:11 -05:00 committed by GitHub
parent 6b1bbf7352
commit a5139c4335
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 114 additions and 28 deletions

View File

@ -7,6 +7,7 @@ import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.ServiceConnection
import android.content.pm.PackageManager
import android.net.VpnService
import android.os.*
import android.util.Log
@ -62,6 +63,7 @@ class MainActivity: FlutterActivity() {
ui!!.setMethodCallHandler { call, result ->
when(call.method) {
"android.registerActiveSite" -> registerActiveSite(result)
"android.deviceHasCamera" -> deviceHasCamera(result)
"nebula.parseCerts" -> nebulaParseCerts(call, result)
"nebula.generateKeyPair" -> nebulaGenerateKeyPair(result)
@ -120,6 +122,10 @@ class MainActivity: FlutterActivity() {
result.success(null)
}
private fun deviceHasCamera(result: MethodChannel.Result) {
result.success(context.packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY))
}
private fun nebulaParseCerts(call: MethodCall, result: MethodChannel.Result) {
val certs = call.argument<String>("certs")
if (certs == "") {

View File

@ -21,6 +21,7 @@ class HostInfoScreen extends StatefulWidget {
required this.pending,
this.onChanged,
required this.site,
required this.supportsQRScanning,
}) : super(key: key);
final bool isLighthouse;
@ -29,6 +30,8 @@ class HostInfoScreen extends StatefulWidget {
final Function? onChanged;
final Site site;
final bool supportsQRScanning;
@override
_HostInfoScreenState createState() => _HostInfoScreenState();
}
@ -72,7 +75,10 @@ class _HostInfoScreenState extends State<HostInfoScreen> {
labelWidth: 150,
content: Text(hostInfo.cert!.details.name),
onPressed: () => Utils.openPage(
context, (context) => CertificateDetailsScreen(certInfo: CertificateInfo(cert: hostInfo.cert!))))
context, (context) => CertificateDetailsScreen(
certInfo: CertificateInfo(cert: hostInfo.cert!),
supportsQRScanning: widget.supportsQRScanning,
)))
: Container(),
]);
}

View File

@ -74,6 +74,8 @@ class _MainScreenState extends State<MainScreen> {
// A set of widgets to display in a column that represents an error blocking us from moving forward entirely
List<Widget>? error;
bool supportsQRScanning = false;
static const platform = MethodChannel('net.defined.mobileNebula/NebulaVpnService');
RefreshController refreshController = RefreshController();
ScrollController scrollController = ScrollController();
@ -123,6 +125,16 @@ class _MainScreenState extends State<MainScreen> {
);
}
// Determine whether the device supports QR scanning. For example, some
// Chromebooks do not have camera support.
if (Platform.isAndroid) {
platform.invokeMethod("android.deviceHasCamera").then(
(hasCamera) => setState(() => supportsQRScanning = hasCamera)
);
} else {
supportsQRScanning = true;
}
return SimplePage(
title: Text('Nebula'),
scrollable: SimpleScrollable.vertical,
@ -133,12 +145,11 @@ class _MainScreenState extends State<MainScreen> {
onPressed: () => Utils.openPage(context, (context) {
return SiteConfigScreen(onSave: (_) {
_loadSites();
});
}, supportsQRScanning: supportsQRScanning);
}),
),
refreshController: refreshController,
onRefresh: () {
print("onRefresh");
_loadSites();
refreshController.refreshCompleted();
},
@ -198,7 +209,11 @@ class _MainScreenState extends State<MainScreen> {
site: site,
onPressed: () {
Utils.openPage(context, (context) {
return SiteDetailScreen(site: site, onChanged: () => _loadSites());
return SiteDetailScreen(
site: site,
onChanged: () => _loadSites(),
supportsQRScanning: supportsQRScanning,
);
});
}));
});

View File

@ -21,10 +21,16 @@ import 'package:pull_to_refresh/pull_to_refresh.dart';
//TODO: ios is now the problem with connecting screwing our ability to query the hostmap (its a race)
class SiteDetailScreen extends StatefulWidget {
const SiteDetailScreen({Key? key, required this.site, this.onChanged}) : super(key: key);
const SiteDetailScreen({
Key? key,
required this.site,
this.onChanged,
required this.supportsQRScanning,
}) : super(key: key);
final Site site;
final Function? onChanged;
final bool supportsQRScanning;
@override
_SiteDetailScreenState createState() => _SiteDetailScreenState();
@ -193,7 +199,9 @@ class _SiteDetailScreenState extends State<SiteDetailScreen> {
setState(() {
activeHosts = hosts;
});
}));
},
supportsQRScanning: widget.supportsQRScanning,
));
},
label: Text("Active"),
content: Container(alignment: Alignment.centerRight, child: active)),
@ -211,7 +219,9 @@ class _SiteDetailScreenState extends State<SiteDetailScreen> {
setState(() {
pendingHosts = hosts;
});
}));
},
supportsQRScanning: widget.supportsQRScanning,
));
},
label: Text("Pending"),
content: Container(alignment: Alignment.centerRight, child: pending))
@ -231,7 +241,9 @@ class _SiteDetailScreenState extends State<SiteDetailScreen> {
onSave: (site) async {
changed = true;
setState(() {});
});
},
supportsQRScanning: widget.supportsQRScanning,
);
});
},
),

View File

@ -10,8 +10,14 @@ import 'package:mobile_nebula/services/utils.dart';
import 'package:pull_to_refresh/pull_to_refresh.dart';
class SiteTunnelsScreen extends StatefulWidget {
const SiteTunnelsScreen(
{Key? key, required this.site, required this.tunnels, required this.pending, required this.onChanged})
const SiteTunnelsScreen({
Key? key,
required this.site,
required this.tunnels,
required this.pending,
required this.onChanged,
required this.supportsQRScanning,
})
: super(key: key);
final Site site;
@ -19,6 +25,8 @@ class SiteTunnelsScreen extends StatefulWidget {
final bool pending;
final Function(List<HostInfo>)? onChanged;
final bool supportsQRScanning;
@override
_SiteTunnelsScreenState createState() => _SiteTunnelsScreenState();
}
@ -67,7 +75,10 @@ class _SiteTunnelsScreenState extends State<SiteTunnelsScreen> {
site: widget.site,
onChanged: () {
_listHostmap();
})),
},
supportsQRScanning: widget.supportsQRScanning,
),
),
label: Row(children: <Widget>[Padding(child: icon, padding: EdgeInsets.only(right: 10)), Text(hostInfo.vpnIp)]),
labelWidth: ipWidth,
content: Container(alignment: Alignment.centerRight, child: Text(hostInfo.cert?.details.name ?? "")),

View File

@ -30,6 +30,7 @@ class AddCertificateScreen extends StatefulWidget {
this.onReplace,
required this.pubKey,
required this.privKey,
required this.supportsQRScanning,
}) : super(key: key);
// onSave will pop a new CertificateDetailsScreen.
@ -42,6 +43,8 @@ class AddCertificateScreen extends StatefulWidget {
final String pubKey;
final String privKey;
final bool supportsQRScanning;
@override
_AddCertificateScreenState createState() => _AddCertificateScreenState();
}
@ -100,6 +103,16 @@ class _AddCertificateScreenState extends State<AddCertificateScreen> {
}
List<Widget> _buildLoadCert() {
Map<String, Widget> children = {
'paste': Text('Copy/Paste'),
'file': Text('File'),
};
// not all devices have a camera for QR codes
if (widget.supportsQRScanning) {
children['qr'] = Text('QR Code');
}
List<Widget> items = [
Padding(
padding: EdgeInsets.fromLTRB(10, 25, 10, 0),
@ -112,11 +125,7 @@ class _AddCertificateScreenState extends State<AddCertificateScreen> {
});
}
},
children: {
'paste': Text('Copy/Paste'),
'file': Text('File'),
'qr': Text('QR Code'),
},
children: children,
))
];
@ -124,7 +133,7 @@ class _AddCertificateScreenState extends State<AddCertificateScreen> {
items.addAll(_addPaste());
} else if (inputType == 'file') {
items.addAll(_addFile());
} else {
} else if (inputType == 'qr') {
items.addAll(_addQr());
}
@ -257,7 +266,9 @@ class _AddCertificateScreenState extends State<AddCertificateScreen> {
onSave: () {
Navigator.pop(context);
widget.onSave!(CertificateResult(certInfo: tryCertInfo, key: keyController.text));
});
},
supportsQRScanning: widget.supportsQRScanning,
);
});
}
}

View File

@ -21,11 +21,14 @@ class CAListScreen extends StatefulWidget {
Key? key,
required this.cas,
this.onSave,
required this.supportsQRScanning,
}) : super(key: key);
final List<CertificateInfo> cas;
final ValueChanged<List<CertificateInfo>>? onSave;
final bool supportsQRScanning;
@override
_CAListScreenState createState() => _CAListScreenState();
}
@ -88,7 +91,9 @@ class _CAListScreenState extends State<CAListScreen> {
changed = true;
cas.remove(key);
});
});
},
supportsQRScanning: widget.supportsQRScanning,
);
});
},
));
@ -129,6 +134,16 @@ class _CAListScreenState extends State<CAListScreen> {
}
List<Widget> _addCA() {
Map<String, Widget> children = {
'paste': Text('Copy/Paste'),
'file': Text('File'),
};
// not all devices have a camera for QR codes
if (widget.supportsQRScanning) {
children['qr'] = Text('QR Code');
}
List<Widget> items = [
Padding(
padding: EdgeInsets.fromLTRB(10, 25, 10, 0),
@ -141,11 +156,7 @@ class _CAListScreenState extends State<CAListScreen> {
});
}
},
children: {
'paste': Text('Copy/Paste'),
'file': Text('File'),
'qr': Text('QR Code'),
},
children: children,
))
];

View File

@ -18,6 +18,7 @@ class CertificateDetailsScreen extends StatefulWidget {
this.onReplace,
this.pubKey,
this.privKey,
required this.supportsQRScanning,
}) : super(key: key);
final CertificateInfo certInfo;
@ -35,6 +36,8 @@ class CertificateDetailsScreen extends StatefulWidget {
final String? pubKey;
final String? privKey;
final bool supportsQRScanning;
@override
_CertificateDetailsScreenState createState() => _CertificateDetailsScreenState();
}
@ -178,7 +181,9 @@ class _CertificateDetailsScreenState extends State<CertificateDetailsScreen> {
duration: const Duration(milliseconds: 10), curve: Curves.linearToEaseOut);
},
pubKey: widget.pubKey!,
privKey: widget.privKey!);
privKey: widget.privKey!,
supportsQRScanning: widget.supportsQRScanning,
);
});
})));
}

View File

@ -27,6 +27,7 @@ class SiteConfigScreen extends StatefulWidget {
Key? key,
this.site,
required this.onSave,
required this.supportsQRScanning,
}) : super(key: key);
final Site? site;
@ -34,6 +35,8 @@ class SiteConfigScreen extends StatefulWidget {
// This is called after the target OS has saved the configuration
final ValueChanged<Site> onSave;
final bool supportsQRScanning;
@override
_SiteConfigScreenState createState() => _SiteConfigScreenState();
}
@ -186,7 +189,9 @@ class _SiteConfigScreenState extends State<SiteConfigScreen> {
site.certInfo = result.certInfo;
site.key = result.key;
});
});
},
supportsQRScanning: widget.supportsQRScanning,
);
}
return AddCertificateScreen(
@ -198,7 +203,9 @@ class _SiteConfigScreenState extends State<SiteConfigScreen> {
site.certInfo = result.certInfo;
site.key = result.key;
});
});
},
supportsQRScanning: widget.supportsQRScanning,
);
});
},
),
@ -222,7 +229,9 @@ class _SiteConfigScreenState extends State<SiteConfigScreen> {
changed = true;
site.ca = ca;
});
});
},
supportsQRScanning: widget.supportsQRScanning,
);
});
})
],