Upload Firestore Cache Write Data Upon Getting Internet - android

My app allows the user make firestore writes when the user is offline. I have noticed that cache data is only written to firestone when the user makes another write (so it takes all the initial writes when user was offline, and then adds the new write data and send to firestore).
I am looking for a way to send the cached data to firestore without the user having to make another write entry. Please how can I achieve this functionality? I'd greatly appreciate any help you can provide

You can get around this by detecting when the user gets an internet connection again, I suggest prompting the user before doing so. But when this event is done. I suggest writing to a dedicated location with a "last updated' field such as a dedicated collection for all users where you can track the last update timestamp, it'll incur a write but this way you have control over it.
It will require permission to check from the device such as a simple query to a URL, or ideally with the getActiveNetworkInfo() method from the ConnectivityManager returns a NetworkInfo instance representing the first connected network interface it can find or null if none of the interfaces are connected.
private boolean isNetworkAvailable() {
ConnectivityManager connectivityManager
= (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo();
return activeNetworkInfo != null && activeNetworkInfo.isConnected();
}
You will also need:
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
in your android manifest.
Note that having an active network interface doesn't guarantee that a particular networked service is available.

Related

Android Crashlytics - restrict network access

I've been working for a while around a lack of ability to restrict Crashlytics network usage under certain conditions. For example - on roaming, on metered networks and so on..
According to SDK documentation, only two options I found addressing somehow this:
"Opt Out" on runtime by simply not initialize Crashlytics
built-in User consent dialog before sending a crash report
This API's are very limited, because:
Not initialize Crashlytics not only prevents network access but also prevents any chance Crashlytics will save locally the crash report so that eventually the event will be sent. Not to mention there is no good way to opt out in runtime, besides overriding brutally the Thread.setUncaughtExceptionHandler
consent dialog not making any sense to the user if a crash happens in the background.
My question basically:
Am I missing something?
Is there any way to restrict Crashlytics network access?
My motivation comes from a need to prevent situation my app uses network bandwidth potentially can cost money to the user under certain conditions, although "cellular network" or "use data over roaming" device settings are enabled.
There is two step process which we are using in our app, this is not using Mobile Network and also not related to roaming as well.
Saving crash logs to file in app data partition i.e. on device:
Refer to this link
Upload crash data to server when WiFi network is connected:
public class ConnectivityStatusReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
final ConnectivityManager connMgr = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetworkInfo = connMgr.getActiveNetworkInfo();
if (activeNetworkInfo != null && activeNetworkInfo.getTypeName() == "WIFI") {
// post your crash logs to server
}
}
}
There is not a way to restrict the internet usage for Crashlytics in an application. But how I would fix it is to either give the user information that Crashlytics is using roaming or just save the crash report locally and send them once the user in connected with a wifi network. Also you could give the user the choice if he prefers to save the crash reports locally or send them right away over roaming.
Save the ErrorLog locally on the device
Upload the ErrorLog once a connection with a wifi is established
You should be able to use the ConnectivityManager to get the state of the Wi-Fi adapter. From there you can check if it is connected or even available.
ConnectivityManager connManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo mWifi = connManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
if (mWifi.isConnected()) {
// post error logs
}
I'm the former maintainer of the Crashlytics SDK for iOS/macOS. I'm relatively unfamiliar with the Android version of the SDK, and definitely unfamiliar with Android in general. But, I'll give this a shot.
What you want to do is something that has been requested on the iOS side a few times. I would have loved to do it actually, because it seems pretty terrible to force end-users to incur these costs. However, the iOS SDK's networking and start up routine are both very complex and very delicate. It is highly challenging to guarantee that crashes are delivered and that there are zero possibilities for inconsistent states. I believe that Android is simpler here, but I cannot say this with authority.
The iOS SDK, however, does have some hooks for additional client-level functionality. Check out the warning around one of those APIs:
* #warning Just implementing this delegate method will disable all forms of synchronous report submission. This can
* impact the reliability of reporting crashes very early in application launch.
Basically, in order to satisfy the contract of this particular API, some techniques to improve reporting reliability have to be disabled. The thing is, sometimes it's worth it. Lots of apps decide to make this tradeoff. Many apps also delay initializing Crashlytics to eek out extra performance. This has a huge impact on reporting reliability, but that's another tradeoff app developers have to make.
I think you should seriously consider just not enabling Crashlytics in these situations, if you can easily detect them. Maybe Android even allows end-users to do this on a per-app basis? In that case, you'd never get any reports anyways. I would imagine that your user base is diverse enough that missing some reports in these situations wouldn't be that terrible. Or, perhaps you'd like to surface it as a user-facing option.
You could even do something totally crazy, like override Thread.setUncaughtExceptionHandler yourself, and buffer up exceptions during this situation to disk. And then, replay them to Crashlytics when things are better. Turn it into an open source lib. I bet people will love it! Possibly not the Crashlytics' Android team though ;) (Hi!)
This is also basically the same recommendation Gastón offered above, with just some extra context around what I've seen on the iOS side. Also shoot the Crashlytics people an email asking for this. I think it's a great idea.
I was reading the docs at fabric and I just found something interesting:
Crashlytics processes exceptions on a dedicated background thread, so
the performance impact to your app is minimal. To reduce your users’
network traffic, Crashlytics batches logged exceptions together and
sends them the next time the app launches.
So I was thinking about a workaround since the crashes without network are being sent when the app is initialized, you could prompt any dialog to the user at startup telling if they want to connect to the internet to send crash reports to solve current problems in the app. ( so you are using their network data with the user consent)
The thing here is we don't know how to stop crashlytics from sending this reports, they will store it at the device if the device is offline and send it back after the device is just with connection again as it states here
Another way out could be just log important fatal issues with the custom login they offer and just send them, you can find more about it here
To make sure that sending crash reports has the smallest impact on
your user’s devices, Crashlytics logs have a maximum size of 64 KB.
When a log exceeds 64 KB, the earliest logged values will be dropped
in order to maintain this threshold.
In conclusion, after reading the docs, there is no way to disable Crashlytics to constantly send reports, you can only manage the network connection of the user when you want them to send or not reports. It's like connectivity is the switch on and off of Crashlytics at the moment.
It just talks about "Reducing network traffic" but not about disabling Crashlytics network at all.
Another way that comes to my head is to make a flag for starting crashlytics, and then use inside a condition Crashlytics.start()
When you want to disable it just do the following
CrashlyticsCore core = new CrashlyticsCore.Builder().disabled(BuildConfig.DEBUG).build();
Fabric.with(this, new Crashlytics.Builder().core(core).build());
Playing with these two things is the only way I think it's possible to reduce network usage of Crashlytics at the moment.
You can restrict Crashlytics network usage by a static field.
Define a static global variable, according to its value write logic for your Crashlytics.
private static boolean INROAMING = false;
Now you can use below logic for your purpose. Like don't provide co
if(isInternetIsConnected(this).equals("MOBILE")){
if(INROAMING){
//write your logic for context here, when phone is in roaming
//restrict logic for crashlytics
}else{
//write your logic for context herem, when phone is not in roaming
//un-restrict logic for crashlytics
}
}
public boolean checkForRoaming() {
final TelephonyManager telephonyManager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
PhoneStateListener phoneStateListener = new PhoneStateListener() {
#Override
public void onServiceStateChanged(ServiceState serviceState) {
super.onServiceStateChanged(serviceState);
if (telephonyManager.isNetworkRoaming()) {
// In Roaming
INROAMING = true;
} else {
// Not in Roaming
INROAMING = false;
}
// You can also check roaming state using this
if (serviceState.getRoaming()) {
// In Roaming
INROAMING = true;
} else {
// Not in Roaming
INROAMING = false;
}
}
};
}
public String isInternetIsConnected(Context context) {
try {
ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
assert cm != null;
#SuppressLint("MissingPermission") NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
if (activeNetwork != null) { // connected to the internet
if (activeNetwork.getType() == ConnectivityManager.TYPE_WIFI) {
// connected to wifi
return "WIFI";
} else if (activeNetwork.getType() == ConnectivityManager.TYPE_MOBILE) {
// connected to the mobile provider's data plan
checkForRoaming();
return "MOBILE";
}
} else {
// not connected to the internet
return "NO CONNECTION";
}
} catch (Exception e) {
e.printStackTrace();
}
return "NO CONNECTION";
}
}
There is not a way to restrict the internet usage for Crashlytics in an application. You can give choice to user, if he prefers to save the crash reports locally or send them right away over roaming.
Save the ErrorLog locally on the device
Upload the ErrorLog once a connection with a wifi is established.
You can use ConnectivityManager to get the state of the network. You can check if it is connected or even available.
ConnectivityManager connManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo mWifi = connManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
if (mWifi.isConnected()) {
// send error logs
}
Above code you can add in broadcastreceiver which will notify connection
Example:
public class ConnectivityStatusReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
ConnectivityManager connManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo mWifi = connManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
if (mWifi.isConnected()) {
// send error logs
}
}
}

Preventing user from manipulating the time that will be stored in the local database of Android?

I'm trying to make an employee attendance app where the users (employees) could punch in when they come to the office. The details they punch-in will be sent to the server. The major focus in on the time.
I am trying to implement offline functionality as well. (If the app doesn't detect internet connectivity, the punched-in record will be stored on the local db (SQLite) , and once it does, push the record from the db (and clear it) onto the server.
I tried capturing the time using the GregorianCalendar class but the time values seem to be vulnerable to user manipulation. (Especially Scenario A)
Scenario A
A user could turn internet connectivity off, turn Automatic Data & Time off, manually set the time and then open the app to punch in.
Example: Mr. X comes to office at 8.45AM, turns airplane mode on, manually sets the device time (say 8.30AM) and then punches-in the record. The time value that he sets gets entered in the db instead of the actual time he came in.
How do I prevent this from happening?
Scenario B
A user could just edit the local db values manually (rooted phones). [I know this is inevitable but any suggestions to make his harder?]
Scenario A:
Solution 1: only accept values when there is an internet connection:
private boolean haveInternet()
{
ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo();
return activeNetworkInfo != null && activeNetworkInfo.isConnected();
}
Solution 2: ping your server for the timeDate.
Solution 3: register a broadcast receiver for the timechange event and disable the app until you can ping your server (taken from here: https://stackoverflow.com/a/20766107/2540578)
Scenario B: encrypt the data. There are multiple solutions to do this, but it depends on what you are using (sqlite, realm, greendao etc)

Which API decides if the call is going through VoLTE or VoWiFi in Android?

I want to write an xposed module where I can redirect VoLTE calls through VoWiFi. I want to know which method decides if the call is going through VoLTE or VoWifi and I would hook that method and get the work done.
Basically using WiFi I want to give the network an illusion that the device is using mobile data and send calls through wifi
Note : I am new to android programming. Excuse me if my question looks vague.
So you want to give illusion that you are connected to WIFI even though you are on mobile data.
the way we check that is this:
ConnectivityManager cm =
(ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
boolean isWiFi = activeNetwork.getType() == ConnectivityManager.TYPE_WIFI;
So what you can do is, hook getType method of ConnectivityManager and in afterhook method always return ConnectivityManager.TYPE_WIFI
the way to do that is by calling
param.setResult(ConnectivityManager.TYPE_WIFI);//may be you need casting here
in afterHook.
read more about connectivity here:
https://developer.android.com/training/monitoring-device-state/connectivity-monitoring.html
Edit: I hope you are familiar with afterHook and beforeHook methods and how xposed works.

Android is device connected to the internet method

I want to create a method which tells if the device is online. As far as I understand ConnectivityManager tells only if the device is connected to a network. This doesn't mean that the device is connected to the internet. To ensure that the device is online I'm using InetAddress.getByName("google.com").isReachable(3); but I can't use it on the main thread. I can create a separate thread to check the connectivity and then use a callback function but is there another way? I don't want my app to do anything before it is connected. Do you have any solutions? Thank you!
With any networking, there isn't a guaranteed way to check whether or not you are connected to an endpoint without actually sending data. Even if the device is connected to a network, has an ip address, recently received data, e.t.c, it doesn't mean that you still have a connection.
I would suggest allowing the user to progress into your application as much as possible, queuing up the requests to your server in the background whilst a connection is established. Use the same framework to resend data if the connection is lost whilst the user is using the app. Make the connection to the server as transparent to the user as possible, unless it fails to connect after ~1 minute
try this:
public static boolean isOnline(Context context) {
ConnectivityManager cm =
(ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo netInfo = cm.getActiveNetworkInfo();
if (netInfo != null && netInfo.isConnected()) {
return true;
}
return false;
}

How can I check if Internet connection is turned on every n seconds?

Hi guys I'm new on android and I wish to check every "some" seconds if the internet connection has been turned on from the user.
This is my situation: I've a map on a fragment and I download from my app an xml file with the position of markers, titles, addresses, and many other things. Now if the person that download my app don't have the internet turned on, I can't place any markers on the map. So a popup come out that request to activate an internet connection. But if the person activate it without refresh the main activity nothing happens.
So how can I do it? Which methods I have to use?
There's no need to schedule an update based on an Internet resource if you aren't connected to the Internet. The following snippet shows how to use the ConnectivityManager to query the active network and determine if it has Internet connectivity.
ConnectivityManager cm =
(ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
boolean isConnected = activeNetwork != null &&
activeNetwork.isConnectedOrConnecting();
For Determining and Monitoring the Connectivity Status Help Link

Categories

Resources