Hide QR code scanner on devices without cameras (#101)
This commit is contained in:
parent
6b1bbf7352
commit
a5139c4335
|
@ -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 == "") {
|
||||
|
|
|
@ -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(),
|
||||
]);
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
);
|
||||
});
|
||||
}));
|
||||
});
|
||||
|
|
|
@ -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,
|
||||
);
|
||||
});
|
||||
},
|
||||
),
|
||||
|
|
|
@ -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 ?? "")),
|
||||
|
|
|
@ -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,
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
))
|
||||
];
|
||||
|
||||
|
|
|
@ -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,
|
||||
);
|
||||
});
|
||||
})));
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
);
|
||||
});
|
||||
})
|
||||
],
|
||||
|
|
Loading…
Reference in New Issue