This repo contains information that is common to all Novoda's Open Source projects.
novoda / merlin Goto Github PK
View Code? Open in Web Editor NEWObserves network connection status & gives callbacks
License: Other
Observes network connection status & gives callbacks
License: Other
Does isConnected()
check for WIFI AND internet and isConnectedToWifi()
only check for WIFI?
05-23 12:10:46.669: ERROR/AndroidRuntime(12764): *** FATAL EXCEPTION IN SYSTEM PROCESS: android.server.ServerThread
java.lang.RuntimeException: Error receiving broadcast Intent { act=android.intent.action.PACKAGE_CHANGED dat=package:com.example.foo flg=0x8000010 (has extras) } in com.android.server.NotificationManagerService$2@429774b0
at android.app.LoadedApk$ReceiverDispatcher$Args.run(LoadedApk.java:768)
at android.os.Handler.handleCallback(Handler.java:725)
at android.os.Handler.dispatchMessage(Handler.java:92)
at android.os.Looper.loop(Looper.java:137)
at com.android.server.ServerThread.run(SystemServer.java:1000)
Caused by: java.lang.IllegalArgumentException: Unknown package: com.example.foo
at com.android.server.pm.Settings.getApplicationEnabledSettingLPr(Settings.java:2440)
at com.android.server.pm.PackageManagerService.getApplicationEnabledSetting(PackageManagerService.java:9026)
at android.app.ApplicationPackageManager.getApplicationEnabledSetting(ApplicationPackageManager.java:1279)
at com.android.server.NotificationManagerService$2.onReceive(NotificationManagerService.java:539)
at android.app.LoadedApk$ReceiverDispatcher$Args.run(LoadedApk.java:758)
... 4 more
Seems to be an android bug -
http://code.google.com/p/android/issues/detail?id=55781&can=4&colspec=ID%20Type%20Status%20Owner%20Summary%20Stars
A String representing an Endpoint
is passed all the way down through Merlin
. The type String
makes it difficult to see all usages of what is meant to be an Endpoint
and does not enforce type safety. In future, if we wanted to add any validation around this String
we would need to extract an additional class or swap this out.
Replace the String
representation of an Endpoint
with a concrete implementation of an Endpoint
to enforce type safety.
We can accurately see all usages of Endpoint
. Easy to add validation to Endpoint
should we wish it in the future.
I only receive one onConnect event from Merlin with a Nexus 5 running Lollipop 5.0.1. All subsequent onConnect events never seem to be fired. Code working fine on a Nexus 5 running KitKat 4.4.4. Thanks!
I did some more testing and I can confirm this issue still exists. The first onConnect gets fired and every event from then on does not get fired. I am not getting any onBind, onConnect, or onDisconnect events. After calling the merlin.bind() and merlin.unbind() (my Activity went into the background and then the foreground), I started receiving events again. After I get one onConnect event though again they stop firing. I tested this code again on the 4.4.4 Nexus 5 and it was working expecting.
The default endpoint is http://connectivitycheck.android.com/generate_204
. This is fine for now, but as it isn't a widely-used URL, it may change in future and break connectivity.
We could change this to google.com
which is much less likely to break.
We are planning a major release of the Merlin
library which includes breaking changes.
Communicate these changes on the README
and update the Wiki to include a migration guide.
Without a migration guide, we may see an increase in GitHub issues / stackoverflow questions about how to incorporate this library.
Rather than using a setter.
Remove the Merlin Log class & add a dependency on NoTils
https://github.com/novoda/merlin/blob/master/core/src/main/java/com/novoda/merlin/Log.java
ConnectivityReceiver
exposes protected
methods to allow the class to be testable. Some methods are only partially testable because they are backed by android.
Have the ConnectivityReceiver
delegate work to a fully testable collaborator.
@VisibleForTesting
annotation on ConnectivityReceiver
.Merlin has a hard dependency on RxJava1 which forces clients to include it even if not required.
Modularise Merlin
to remove the hard dependency.
Merlin
library rather having hard dependencies.The base url (http://connectivitycheck.android.com/generate_204) is getting HTTP 405 response for the HEAD request. I don't think it's merlin's intention.
I think you should use GET instead of it.
Alternatives for HEAD:
http://clients3.google.com/generate_204
http://connectivitycheck.gstatic.com/generate_204
When pinging a remote host to determine whether we can access a publicly accessible host, we're not interested in the Response body, so it would be much more efficient to use a HEAD request instead of a GET.
We now have multiple modules in the project. They all use similar dependencies by duplicating them. We should also drop the targetSdkVersion
see here.
Just like some other Novoda projects we can have a shared gradle file for these.
Maintaining dependencies is potentially harder without this change.
We are currently using an outdated version of Mockito org.mockito:mockito-core:1.9.5
, current is v2.8.9
.
Update Mockito
to the latest stable release.
Benefit from an added annotation @Rule
which prevents us from having a setup
method just for initMocks(this)
.
04-20 16:11:52.038: E/AndroidRuntime(16724): java.lang.RuntimeException: An error occured while executing doInBackground()
04-20 16:11:52.038: E/AndroidRuntime(16724): at android.os.AsyncTask$3.done(AsyncTask.java:300)
04-20 16:11:52.038: E/AndroidRuntime(16724): at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:355)
04-20 16:11:52.038: E/AndroidRuntime(16724): at java.util.concurrent.FutureTask.setException(FutureTask.java:222)
04-20 16:11:52.038: E/AndroidRuntime(16724): at java.util.concurrent.FutureTask.run(FutureTask.java:242)
04-20 16:11:52.038: E/AndroidRuntime(16724): at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:231)
04-20 16:11:52.038: E/AndroidRuntime(16724): at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
04-20 16:11:52.038: E/AndroidRuntime(16724): at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
04-20 16:11:52.038: E/AndroidRuntime(16724): at java.lang.Thread.run(Thread.java:811)
04-20 16:11:52.038: E/AndroidRuntime(16724): Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/github/kevinsawicki/http/HttpRequest;
04-20 16:11:52.038: E/AndroidRuntime(16724): at com.novoda.merlin.service.HostPinger$ResponseCodeFetcher.from(HostPinger.java:34)
04-20 16:11:52.038: E/AndroidRuntime(16724): at com.novoda.merlin.service.HostPinger$PingTask.doInBackground(HostPinger.java:77)
04-20 16:11:52.038: E/AndroidRuntime(16724): at com.novoda.merlin.service.HostPinger$PingTask.doInBackground(HostPinger.java:59)
04-20 16:11:52.038: E/AndroidRuntime(16724): at android.os.AsyncTask$2.call(AsyncTask.java:288)
04-20 16:11:52.038: E/AndroidRuntime(16724): at java.util.concurrent.FutureTask.run(FutureTask.java:237)
04-20 16:11:52.038: E/AndroidRuntime(16724): ... 4 more
04-20 16:11:52.038: E/AndroidRuntime(16724): Caused by: java.lang.ClassNotFoundException: Didn't find class "com.github.kevinsawicki.http.HttpRequest" on path: DexPathList[[zip file "/data/app/com.depop-1.apk"],nativeLibraryDirectories=[/data/app-lib/com.depop-1, /vendor/lib, /system/lib]]
04-20 16:11:52.038: E/AndroidRuntime(16724): at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:56)
04-20 16:11:52.038: E/AndroidRuntime(16724): at java.lang.ClassLoader.loadClass(ClassLoader.java:511)
04-20 16:11:52.038: E/AndroidRuntime(16724): at java.lang.ClassLoader.loadClass(ClassLoader.java:469)
04-20 16:11:52.038: E/AndroidRuntime(16724): ... 9 more
04-20 16:11:52.038: E/AndroidRuntime(16724): Suppressed: java.lang.ClassNotFoundException: com.github.kevinsawicki.http.HttpRequest
04-20 16:11:52.038: E/AndroidRuntime(16724): at java.lang.Class.classForName(Native Method)
04-20 16:11:52.038: E/AndroidRuntime(16724): at java.lang.BootClassLoader.findClass(ClassLoader.java:781)
04-20 16:11:52.038: E/AndroidRuntime(16724): at java.lang.BootClassLoader.loadClass(ClassLoader.java:841)
04-20 16:11:52.038: E/AndroidRuntime(16724): at java.lang.ClassLoader.loadClass(ClassLoader.java:504)
04-20 16:11:52.038: E/AndroidRuntime(16724): ... 10 more
04-20 16:11:52.038: E/AndroidRuntime(16724): Caused by: java.lang.NoClassDefFoundError: Class "Lcom/github/kevinsawicki/http/HttpRequest;" not found
04-20 16:11:52.038: E/AndroidRuntime(16724): ... 14 more
04-20 16:11:53.678: E/GCM(1303): Failed to start GCM
04-20 16:11:53.678: E/GCM(1303): java.lang.NullPointerException: Attempt to invoke virtual method 'byte[] java.net.InetAddress.getAddress()' on a null object reference
04-20 16:11:53.678: E/GCM(1303): at efs.onReceive(SourceFile:1260)
04-20 16:11:53.678: E/GCM(1303): at com.google.android.gms.gcm.GcmService.onStartCommand(SourceFile:377)
04-20 16:11:53.678: E/GCM(1303): at android.app.ActivityThread.handleServiceArgs(ActivityThread.java:2702)
04-20 16:11:53.678: E/GCM(1303): at android.app.ActivityThread.access$2100(ActivityThread.java:135)
04-20 16:11:53.678: E/GCM(1303): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1293)
04-20 16:11:53.678: E/GCM(1303): at android.os.Handler.dispatchMessage(Handler.java:102)
04-20 16:11:53.678: E/GCM(1303): at android.os.Looper.loop(Looper.java:136)
04-20 16:11:53.678: E/GCM(1303): at android.app.ActivityThread.main(ActivityThread.java:5017)
04-20 16:11:53.678: E/GCM(1303): at java.lang.reflect.Method.invoke(Native Method)
04-20 16:11:53.678: E/GCM(1303): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:779)
04-20 16:11:53.678: E/GCM(1303): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:595)
Currently, merlin only triggers when the connection type changes (cell/wifi).
The following could be used to detect changes of the data connection for mobile connection type.
telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_DATA_CONNECTION_STATE);
at the moment it is only released to Novoda public maven https://github.com/novoda/public-mvn-repo/tree/master/releases/com/novoda/notils
MerlinLog
is tightly coupled to the android framework with no easy way to switch out implementations of logging as and when required.
Replace MerlinLog
with a simple Logger
to which the client can specify the type of logging they want to be performed.
When adding a custom endpoint for Merlin
to ping it is ignored. The custom endpoint seems to be ignored because the ping
is performed immediately when the service is created, not after the onBind
. Here are the logs demonstrating this:
05-18 09:41:05.850 15958-15958/com.novoda.merlin.demo D/DemoLogHandle: MerlinService.onCreate
05-18 09:41:05.850 15958-15958/com.novoda.merlin.demo D/DemoLogHandle: Host address not set, using Merlin default: http://connectivitycheck.android.com/generate_204
05-18 09:41:05.856 15958-16714/com.novoda.merlin.demo D/ConnectivityManager.CallbackHandler: CM callback handler got msg 524290
05-18 09:41:05.862 15958-16715/com.novoda.merlin.demo D/DemoLogHandle: Pinging: Endpoint{endpoint='http://connectivitycheck.android.com/generate_204'}
05-18 09:41:05.894 15958-15958/com.novoda.merlin.demo D/DemoLogHandle: onServiceConnected
05-18 09:41:05.895 15958-15958/com.novoda.merlin.demo D/DemoLogHandle: MerlinService.onServiceConnected
05-18 09:41:05.895 15958-15958/com.novoda.merlin.demo D/DemoLogHandle: onBind
05-18 09:41:05.919 15958-16009/com.novoda.merlin.demo D/OpenGLRenderer: endAllStagingAnimators on 0xee83cf00 (RippleDrawable) with handle 0xe0dfff30
05-18 09:41:06.182 15958-15958/com.novoda.merlin.demo D/DemoLogHandle: onConnect
05-18 09:41:56.143 15958-16714/com.novoda.merlin.demo D/ConnectivityManager.CallbackHandler: CM callback handler got msg 524296
Change the call order to ensure that the ping is performed after the MerlinService
is bound.
Clients will be able to specify a custom endpoint.
Mentioned in #79
This means Merlin will tell you that you have a proper Internet connection, even if you're connecting to a captive portal and haven't logged in. This is bad!
Hello! Last day I was hacking with Merlin and I came up with a small wrapper around it to make it a stream.
public class RxMerlin {
private RxMerlin() {
throw new AssertionError("No instances.");
}
public static Observable<Boolean> from(final Context context) {
return Observable.create(new Observable.OnSubscribe<Boolean>() {
@Override public void call(final Subscriber<? super Boolean> subscriber) {
final Merlin merlin = new Merlin.Builder()
.withConnectableCallbacks()
.withDisconnectableCallbacks()
.build(context);
MerlinsBeard beard = MerlinsBeard.from(context);
merlin.registerConnectable(new Connectable() {
@Override public void onConnect() {
subscriber.onNext(true);
}
});
merlin.registerDisconnectable(new Disconnectable() {
@Override public void onDisconnect() {
subscriber.onNext(false);
}
});
subscriber.add(Subscriptions.create(new Action0() {
@Override public void call() {
merlin.unbind();
}
}));
subscriber.onNext(beard.isConnected());
merlin.bind();
}
});
}
}
I thought it would make a good companion library, separate from the core, for whoever wants to use it in the same way. What do you guys think?
Shouldn't a 404, 503 blah blah (at the intended address) indicate that you are connected to the internet?
Providing a fallback to android.com
would be good, in the unlikely but possible case it's down?
We are currently explicitly registering a BroadcastReceiver
for all Android versions. For Android Lollipop and above we should be using ConnectivityManager.NetworkCallbacks
. See conversation in #93 for further details.
For Android Lollipop and above use ConnectivityManager.NetworkCallbacks
but we should build in a compatibility layer to ensure that MerlinService
is not aware of this change.
sorry if "notifications" isn't the right term - the only thing I know of merlin is that it's for checking for a real internet connection.
My use case is that I'd like to download data/sync only when connected to WiFi, even if my app isn't running.
Currently I believe merlin can be used to check for net connection, but not type.
The setup of the library (which works great btw, kudos) is a bit long, with the need to specify withBindableCallbacks
withConnectableCallbacks
and what not.
For my use case, I have to implement all of these, but in the end, I'm not interested in the callbacks themselves, I just want to know - at some given point - whether I have a connection... perhaps with merlin.isConnectedToNetwork()
or something?
peekService(context, new Intent(context, MerlinService.class)) can return null if the service is not running
05-15 10:52:09.047: ERROR/AndroidRuntime(1635): FATAL EXCEPTION: main
java.lang.RuntimeException: Unable to start receiver com.novoda.merlin.receiver.ConnectivityReceiver: java.lang.NullPointerException
at android.app.ActivityThread.handleReceiver(ActivityThread.java:2383)
at android.app.ActivityThread.access$1500(ActivityThread.java:141)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1310)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:5039)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:793)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:560)
at dalvik.system.NativeStart.main(Native Method)
Caused by: java.lang.NullPointerException
at com.novoda.merlin.receiver.ConnectivityReceiver.getMerlinService(ConnectivityReceiver.java:34)
at com.novoda.merlin.receiver.ConnectivityReceiver.notifyMerlinService(ConnectivityReceiver.java:29)
at com.novoda.merlin.receiver.ConnectivityReceiver.onReceive(ConnectivityReceiver.java:19)
at android.app.ActivityThread.handleReceiver(ActivityThread.java:2376)
... 10 more
Link MerlinActivity on code pages takes to 404. not found.
For example (but with more enums)
public class NetworkConnectionType {
static final String OFFLINE = "offline";
static final String WIFI = "wifi";
static final String UNKNOWN = "unknown";
private final NetworkInfo networkInfo;
NetworkConnectionType(NetworkInfo networkInfo) {
this.networkInfo = networkInfo;
}
public static NetworkConnectionType from(ConnectivityManager connectivityManager) {
return new NetworkConnectionType(connectivityManager.getActiveNetworkInfo());
}
@Override
public String toString() {
if (noConnection()) {
return OFFLINE;
}
if (connectedToWifi()) {
return WIFI;
}
if (connectedViaMobileNetwork()) {
return mobileNetworkType();
}
return UNKNOWN;
}
private String mobileNetworkType() {
return networkInfo.getSubtypeName();
}
private boolean connectedViaMobileNetwork() {
return networkInfo.getType() == ConnectivityManager.TYPE_MOBILE && networkInfo.getSubtype() != TelephonyManager.NETWORK_TYPE_UNKNOWN;
}
private boolean connectedToWifi() {
return networkInfo.getType() == ConnectivityManager.TYPE_WIFI;
}
private boolean noConnection() {
return networkInfo == null || !networkInfo.isConnected();
}
}
MerlinService
exposes protected
methods to allow the class to be testable. Some methods are only partially testable because they are backed by android.
Have the MerlinService
delegate work to a fully testable collaborator.
@VisibleForTesting
annotation on MerlinService
.We are currently using a JS-like "should" notation for most of our tests in the codebase.
ShouldExpectedBehaviorWhenStateUnderTest
This omits any pre-conditions that need to happen before the test is run to make it successful.
Remove JS-like "should" notation and replace it with the BDD format of:
givenPreconditions_whenStateUnderTest_thenExpectedBehavior
This will allow us to take into account any preconditions and make the tests much more explicit. When updating the library to use BDD we should also include a test naming
entry in the wiki to explain to external collaborators the different variations on naming we will accept.
Make tests easier to read and allow for preconditions.
Apps targeting Android 7.0 do not receive CONNECTIVITY_ACTION broadcasts, even if they have manifest entries to request notification of these events. Apps that are running can still listen for CONNECTIVITY_CHANGE on their main thread if they request notification with a BroadcastReceiver.
Currently, we implicitly register a BroadcastReceiver
for all API levels EXCEPT for Android N and above. This will potentially have a huge impact on battery life for devices using merlin in the background. See conversation in #93 for further details.
Explicitly register a BroadcastReceiver
for all Android versions.
Android uses connectivitycheck.android.com
to check if the device is connected to the Internet so I guess it'd be nice if Merlin used that as well.
Maybe related to #23
When connecting to public WiFi services such as The Cloud, many require login and will redirect any web requests to their own login page.
The current implementation will indicate that network connectivity is available when the hotspot redirects to its login page, but in many cases internet access will be blocked until login has been successfully completed.
HostPinger should, at the very least, verify that the host responding to the ping is actually the host that we think that we're pinging.
Hello fellow-developers,
Looks like the latest commit was in October 2015 and the latest gradle package repository was created in July 2015. Could you please check and update asap?
I'm looking through the code but am a bit of a noobie. What is the host that is pinged to determine that there is internet connectivity? Where is the part of the code to look?
With the build tools moving to gradle, everything should be .aar'd! also makes the set up easy since the manifest merging works so nicely
It would be nice if we could pull out what we need rather than include the full library.
The only usage of the library is for HttpRequest.get(hostname).getConnection().getResponseCode()
, used in the hostpinger.
How to use on fragments when you can't extend from MarlinActivity
the system shows "!" on the wifi when an active internet connection doesn't have network access, we should see if we can make use of this
The latest release of the library is from June 2016. Could an updated artifact be released with the latest fixes?
It will be important for the developers to know licence terms before using it in production.
Provide a simple way get the network state Merlin.isConnected()
This state should be the one provided from the callbacks (post pinged)
Merlin doesn't work in apps targeting API 24
From Android 7.0 Behavior Changes
Apps targeting Android 7.0 do not receive CONNECTIVITY_ACTION broadcasts, even if they have manifest entries to request notification of these events. Apps that are running can still listen for CONNECTIVITY_CHANGE on their main thread if they request notification with a BroadcastReceiver.
ConnectivityReceiver needs to be registered explicitly.
RxJava2
has been released and Merlin
does not support it ๐ฑ
Add RxJava2
module so that we support it.
Clients can opt-in to use RxJava2
in Merlin
.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.