Im using connectivity package to track users connection changes. The idea is to pop up a warning page for connection loss when the ConnectivityResult is none (aka wifi and mobile is disconnected). But instead i get these results :
If the wifi is connected and you disconnect it, 50% of the time the warning pops up.
If you are on mobile and turn it off, the connectivity returns that user is still on ConnectivityResult.mobile not ConnectivityResult.none.
Tried to make a doublecheck with pinging google, but even that doesnt work as smooth as i expected it to be.
My code :
I have created seperated file with functions :
void trackNetworkStatus(BuildContext ctx) {
//check in case state is just opened
pingGoogle(ctx);
//add network listener
Connectivity().onConnectivityChanged.listen((ConnectivityResult result) {
print("Network changed to $result");
//if user lost connection
if (result == ConnectivityResult.none) {
openNoInternetScreen(ctx);
}else{
//if user has connection, doublecheck
//mobile network is tricky on android
pingGoogle(ctx);
}
});
}
Future<void> pingGoogle(BuildContext ctx) async {
try {
//ping internet page
final result = await InternetAddress.lookup('google.com');
//if ping is successful
if (result.isNotEmpty && result[0].rawAddress.isNotEmpty) {
print('connected');
}else{
print('not connected');
openNoInternetScreen(ctx);
}
} on SocketException catch (_) {
print('not connected');
openNoInternetScreen(ctx);
}
}
void openNoInternetScreen(BuildContext ctx) {
Navigator.push(
ctx,
MaterialPageRoute(builder: (context) => noInternetPage()),
);
}
because i am calling them on every apps init like this :
#override
void initState() {
super.initState();
//listen to netwoek changes
trackNetworkStatus(context);
}
which leads to problem that sometimes warning page pops up twice because, as i believe, the previous listener has not been stopped, but i can figure out how to fix it. The question is why connectivity package returns false callback on mobile. Ive tested on virtual Android API 23 and Samsung S9+, both share same results.
I gave my app to couple early testers and everything turned out just fine for them, even if some of them own Samsung devices too. Looks like there is no problem at all with the connectivity package, some of Samsung phones just act weird as they shouldnt. Looks like there will always be a black sheep in the whole community, sadly thats me and im the developer with this buggy device.
Havent found the fix for my device, but looks like the package is safe to go for the most of the devices in market.
The phone model, that is acting strange : SM-G965F.
Related
I'm working on an app that includes taking pictures and sending them to the backend. Nothing crazy, and everything seems to be good. Since we need to send information back to the server, I need implemented something such that the app verifies the connection token's validity every time the App's state goes from Paused or Exited to Resumed.
That being said, a friend of mine was testing the app and apparently taking pictures from his Samsung S20 FE (android 12) can crash the app. It happens at seemingly random times, so I've plugged his phone into Android Studio and go this error message:
[ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: Error During Communication: Error occured while Communication with Server with StatusCode: 404
That's really weird because we don't try to send the pictures until a button is pressed and this happens right as the picture is taken. So I thought it might be my code that checks for lifecycle changes, since it does call the backend to check on the auth token, but it's surrounded with a .then and .onError which should catch the network error before it's propagated to the app.
It also bugs me that I've never been able to reproduce it with my OnePlus 6 (Android 11) or using a simulator. I'll be joining some code below to illustrate my functionalities. Any idea why I'm having this problem and how I can fix it?
Thanks in advance !
App lifecycle function
#override
Future<void> didChangeAppLifecycleState(AppLifecycleState state) async {
print("State changed to: $state");
switch(state){
case AppLifecycleState.resumed:
final LoginManager loginManager = locator<LoginManager>();
final userToken = await loginManager.getUserTokenFromStorage();
if (userToken != null) {
final Future<LoginResponse?> loginResponse = locator<LoginManager>().verifyToken(refreshToken);
loginResponse.then((value) {
if(value == null) {
_showTimeoutDialog(navigatorKey.currentContext!);
}
}).onError((error, stackTrace) {
_showTimeoutDialog(navigatorKey.currentContext!);
});
}
break;
case AppLifecycleState.inactive:
// TODO: Handle this case.
break;
case AppLifecycleState.paused:
// TODO: Handle this case.
break;
case AppLifecycleState.detached:
// TODO: Handle this case.
break;
}
}
Note that showTimeoutDialog shows a dialog and properly disconnects the user, no information is sent whatsoever.
Taking a picture function
Future<File?> _getPhotoFromCamera(bool isSelfie) async {
return ImagePicker()
.pickImage(
source: ImageSource.camera,
preferredCameraDevice:
isSelfie ? CameraDevice.front : CameraDevice.rear,
)
.then((value) => value != null ? File(value.path) : null);
}
Uses the flutter image_picker package
We have the problem that we want to protect the complete app with a fingerprint/TouchID/FaceID. I will only mention fingerprint here, which always means all biometric features of local_auth. Partly this works quite well with the function "didChangeAppLifecycleState", but only partly.
Since under iOS the app becomes inactive while the fingerprint is being queried, the query comes in a continuous loop when queried at "AppLifecycleState.resumed". Therefore we set a timestamp at "AppLifecycleState.pause" and query the fingerprint at resume only if x seconds have passed since the app was paused. We have now also set this to a slightly higher time, so that the lock does not go on immediately if you have to switch to the camera from the app. Of course there are more checks, but that's the basic idea right now.
Here is the problem that flutter takes some time to execute paused. Both iOS and Android. So if you now quickly minimize the app and open it again (because minimizing was perhaps an accident?), the fingerprint comes directly. Nothing serious, but unpleasant.
Our real problem, which I can't really understand, since I don't know how to debug it either. If the app hasn't been used for a long time, especially on a Monday after the weekend, then work colleagues have the problem that they can simply start the app without a fingerprint. So far only noticed under iOS, since the colleagues use all iOS, but does not exclude that it could happen under Android as well.
I just don't know how to fix it. Has anyone had any experience with this? Or is there a very simple way to use the fingerprint in flutter in a way that you can open the app only with fingerprint, maybe at a completely different place than "didChangeAppLifecycleState"?
Kind regards,
Jakob
As far as I understand you are using lifecycle hook to prompt for biometric verification and you are having issues. The following is the solution if I understand your query right.
First instead of using lifecycle to verify the biometric check. Can you not use initialization widget which gets called every time before opening up the app. Here is an example.
class Initialize extends StatefulWidget {
_InitializeState createState() => _InitializeState();
}
class _InitializeState extends State<Initialize> {
void initState() {
super.initState();
_initialize();
}
Future<void> _initialize() async {
final userDetails = await getUserSavedDetailsFromStroage();
await doAnyOtherTask();
final user = authService.checkIfPrevLoggedIn(authDetails);
if (user == null) {
_gotoLogin();
} else {
if (userDetails.biometricEnabled) {
final verified = await biometricService.onBiometricVerify();
if (!verified) {
_gotoLogin();
return;
}
}
authService.refreshUser();
_gotoHomePage();
}
}
#override
Widget build(BuildContext context) {
return SplashView();
}
}
And your main.dart file will pretty much look like the following, I am assuming you are using Provider package for state management.
#override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (_) => AuthService.instance,
child: MaterialApp(
home: Initialize(),
title: 'App Title',
),
);
}
}
you can set a flag that local auth request is pending.
If you got a response then set the flag back to false with a Futuredelay of 150ms.
Request the local auth in your AppLifecycleState.resumed only if the request is not pending.
I've got an app which connect itself programatically to a wifi connection. My problem is, I want to handle the case, that the password is wrong. I want to detect that the password is not correct in runtime. To be precise I've got a progressdialog running while the connection is established, so if the password is wrong the progressdialog is just shown all the time and can't be skipped. A further note: I handled a password which is less than 8 characters by using this code:
if(!m_wifiManager.enableNetwork(netId, true)) {
progressDialogConnecting.dismiss();
createInfoMessageDialog(CONST.WIFI_CON_FAILED_TITLE, CONST.WIFI_CON_FAILED_MSG_CONFAILURE);
m_wifiManager.reconnect();
return;
}
If the key for the wifi connection is less than 8 characters, this if-case gets triggered. But if it is longer than 8 characters and wrong I get an endless state of showing the progress dialog.
What I exactly want to ask: how do I handle 1. wrong password 2. connection states (just like Android system showing me the toasts "Connected to Wifi xyz") ? AND is it even possible to handel the first one (wrong password)?
Here is the code, that did not work for handling connection established event (this is just the wifirecevier, I also registered it in the activity):
public class WifiReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
if (action.equals(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION)) {
if (intent.getBooleanExtra(WifiManager.EXTRA_SUPPLICANT_CONNECTED, false)){
if(wrongNetworkConnected)
progressDialogConnecting.dismiss();
}
}
} else {
}
}
}
}
Edit: What I am currently doing, is that I have a Handler which tells me to whom I am connected. That's useful because I can say that after the reconnect() I am reconnected to the old network (current network) and not the new one - so apparently the password could be wrong (or something else), because I could not connect to the new network.
The problem about this method is that first of all it takes too much time and secondly it is not reliable. I can lie and say that if you will get reconnected to your current network it is the fault of a wrong password, but actually it is not 100% sure that you cannot reconnect because of this - it may also have other reasons. So I am still searching for a simple feedback/handle from the suplicant that the password is wrong, just like the android api does in the wifi settings of each android device...
My problem is, I want to handle the case, that the password is wrong.
After some research I found this post which is not marked as answered but it still worked for me very well.
Here is the if-case in which the program jumps (already tested several times by me) if there is an authentication error --> e.g. wrong password:
int supl_error=intent.getIntExtra(WifiManager.EXTRA_SUPPLICANT_ERROR, -1);
if(supl_error==WifiManager.ERROR_AUTHENTICATING){
// DO SOMETHING
}
NOTE: As seen in the linked post above this if-case should appear in a BroadcastReceiver adding the intent WifiManager.SUPPLICANT_STATE_CHANGED_ACTIONto the receiver-registration in your activity-class.
I'm using Azure Mobile Services in my android application to add authentication to the app, via Facebook and Google. However, every single time I attempt to login from the app, I receive the following error:
"com.microsoft.windowsazure.mobileservices.MobileServiceException: Logging >in with the selected authentication provider is not enabled".
No other errors occur. This is my code:
private void authenticate(boolean bRefreshCache)
throws ClientProtocolException, IOException {
bAuthenticating = true;
if (bRefreshCache || !loadUserTokenCache(mClient)) {
mClient.login(MobileServiceAuthenticationProvider.Facebook,
new UserAuthenticationCallback() {
#Override
public void onCompleted(MobileServiceUser user,
Exception exception,
ServiceFilterResponse response) {
synchronized (mAuthenticationLock) {
if (exception == null) {
cacheUserToken(mClient.getCurrentUser());
Log.i("MappingRoadConditions",
"authenticating");
createAndShowDialog(String.format(
"You are now logged in - %1$2s",
user.getUserId()), "Success");
} else {
createAndShowDialog(exception.getMessage(),
"Login Error");
}
bAuthenticating = false;
mAuthenticationLock.notifyAll();
}
}
});
} else {
// Other threads may be blocked waiting to be notified when
// authentication is complete.
synchronized (mAuthenticationLock) {
bAuthenticating = false;
mAuthenticationLock.notifyAll();
}
}
}
The function for logging in by Google is exactly the same, other than the name of the provider of course.
1) I have tried troubleshooting by logging in through the browser and I can login perfectly well using both Facebook and Google.
2) I have added the internet permission in the manifest file.
3) I have also tried testing the app by changing the internet connections, in case it's a network connection problem but to no avail. I am able to login perfectly well through the browser on the same internet connection.
Any ideas on what could be happening?
I struggled with this for a while when moving my working code over into a fresh app
It seems that after I eliminated the provider app connection as your problem (I used the javascript html client in parallel ) I needed to go back to basics because I found this similar question
Check your Manifest
I also had this issue just happen on a successful build - the ADB bridge had failed and the emulator could not connect to the internet (I had switched networks)
This error code is not descriptive, but Azure seems to assume if it can't connect to a provider then you didn't set it up!
All,
I am having a very strange issue.
I have the following code that enables and disables the PIN based on certain conditions
DevicePolicyManager mDPM;
ComponentName mDeviceAdminSample;
mDPM = (DevicePolicyManager)context.getSystemService(Context.DEVICE_POLICY_SERVICE);
mDeviceAdminSample = new ComponentName(context, DeviceAdminSampleReceiver.class);
public void disablePINLock(DBHelper myDBHelper, Context context) {
mDPM.setPasswordQuality(mDeviceAdminSample,
DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED);
mDPM.setPasswordMinimumLength(mDeviceAdminSample, 0);
boolean result = false;
result = mDPM.resetPassword("", DevicePolicyManager.RESET_PASSWORD_REQUIRE_ENTRY);
if (result) {
// Toast: "Successfully Disabled PIN lock"
}
else {
// Toast: "Could not disable PIN lock"
}
}
public void enablePINLock(DBHelper myDBHelper, Context context) {
mDPM.setPasswordQuality(mDeviceAdminSample,
DevicePolicyManager.PASSWORD_QUALITY_NUMERIC);
mDPM.setPasswordMinimumLength(mDeviceAdminSample, 4);
Cursor c = myDBHelper.getSetting("'random'");
if (c != null) {
boolean result = mDPM.resetPassword("1234",
DevicePolicyManager.RESET_PASSWORD_REQUIRE_ENTRY);
if (result) {
// Toast: "Successfully Enabled PIN lock"
}
else {
// Toast: "Could not enable PIN lock"
}
}
else {
// Toast: "Could not enable PIN lock"
c.close();
}
}
The code was working fine until a few days ago. I restarted the phone to finish updating and since then the disablePINLock function is not working correctly. When the function executes all I get is
"result" is false and toast message "Could not disable PIN". The enablePINLock method is working just fine and has always worked fine.
Since the restart result = mDPM.resetPassword("", DevicePolicyManager.RESET_PASSWORD_REQUIRE_ENTRY); has failed to execute. I tried to catch an exception but it didn't help.
At this point I am clueless as to why the method stopped working after the restart. I checked the device administrators, enabled/disabled the app few times without any success.
Also I am not sure if this is related, but let me say this. The device administrator screen has my app always checked, even after I uncheck the app and deactivate it, the app name has the box checked.
Thank you in advance for any response.
You say you've "checked the device administrators" but you don't say what you saw there. Are there any other active device administrators on the device? DevicePolicyManager.resetPassword() will return false if the password you provide is not compliant with the aggregate of password policies set by all device admins. It sounds like perhaps another device admin is still active.
Also, with regard to your last point:
The device administrator screen has my app always checked, even after I uncheck the app and deactivate it, the app name has the box checked.
That's a common bug with the Device Administrators screen: the checkbox often stays on even when you've successfully disabled an admin. Try backing out of the Device Administrators screen then going back in. It should be unchecked now.