diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2816b58 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +.gradle +/local.properties +/.idea +*.iml +.DS_Store +/build diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..6467668 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,8 @@ +Change Log +========== + +## Version 1.0.0 + +_2015-03-25_ + + * Initial release. \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..57f3d49 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,44 @@ +# Contributing to Network Connection Class +We want to make contributing to this project as easy and transparent as +possible. + +## Our Development Process +We work directly in the github project and provide versioned releases +appropriate for major milestones and minor bug fixes or improvements. GitHub +is used directly for issues and pull requests and the developers actively +respond to requests. + +## Pull Requests +We actively welcome your pull requests. +1. Fork the repo and create your branch from `master`. +2. If you've added code that should be tested, add tests +3. If you've changed APIs, update the documentation. +4. Ensure the test suite passes. +5. Make sure your code lints. +6. If you haven't already, complete the Contributor License Agreement ("CLA"). + +## Contributor License Agreement ("CLA") +In order to accept your pull request, we need you to submit a CLA. You only need +to do this once to work on any of Facebook's open source projects. + +Complete your CLA here: + +## Issues +We use GitHub issues to track public bugs. Please ensure your description is +clear and has sufficient instructions to be able to reproduce the issue. + +Facebook has a [bounty program](https://www.facebook.com/whitehat/) for the safe +disclosure of security bugs. In those cases, please go through the process +outlined on that page and do not file a public issue. + +## Coding Style +* 2 spaces for indentation rather than tabs +* Line wrapping indents 4 spaces +* 100 character line length +* One parameter per line when line wrapping is required +* Use the `m` member variable prefix for private fields +* Opening braces to appear on the same line as code + +## License +By contributing to Network Connection Class, you agree that your contributions will be licensed +under its BSD license. \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..6bd6bca --- /dev/null +++ b/LICENSE @@ -0,0 +1,30 @@ +BSD License + +For Network Connection Class software + +Copyright (c) 2015, Facebook, Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + * Neither the name Facebook nor the names of its contributors may be used to + endorse or promote products derived from this software without specific + prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/PATENTS b/PATENTS new file mode 100644 index 0000000..030c5f8 --- /dev/null +++ b/PATENTS @@ -0,0 +1,24 @@ +Additional Grant of Patent Rights + +"Software" means the Network Connection Class software distributed by Facebook, +Inc. + +Facebook hereby grants you a perpetual, worldwide, royalty-free, non-exclusive, +irrevocable (subject to the termination provision below) license under any +rights in any patent claims owned by Facebook, to make, have made, use, sell, +offer to sell, import, and otherwise transfer the Software. For avoidance of +doubt, no license is granted under Facebook’s rights in any patent claims that +are infringed by (i) modifications to the Software made by you or a third party, +or (ii) the Software in combination with any software or other technology +provided by you or a third party. + +The license granted hereunder will terminate, automatically and without notice, +for anyone that makes any claim (including by filing any lawsuit, assertion or +other action) alleging (a) direct, indirect, or contributory infringement or +inducement to infringe any patent: (i) by Facebook or any of its subsidiaries or +affiliates, whether or not such claim is related to the Software, (ii) by any +party if such claim arises in whole or in part from any software, product or +service of Facebook or any of its subsidiaries or affiliates, whether or not +such claim is related to the Software, or (iii) by any party relating to the +Software; or (b) that any right in any patent claim of Facebook is invalid or +unenforceable. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..36b85f9 --- /dev/null +++ b/README.md @@ -0,0 +1,76 @@ +# Network Connection Class + +Network Connection Class is an Android library that allows you to figure out +the quality of the current user's internet connection. The connection gets +classified into several "Connection Classes" that make it easy to develop +against. The library does this by listening to the existing internet traffic +done by your app and notifying you when the user's connection quality changes. +Developers can then use this Connection Class information and adjust the application's +behaviour (request lower quality images or video, throttle type-ahead, etc). + +## Integration + +### Download +Download [the latest JARs](https://github.com/facebook/network-connection-class/releases/latest) or grab via Gradle: +```groovy +compile 'com.facebook.network.connectionclass:connectionclass:1.0.0' +``` +or Maven: +```xml + + com.facebook.network.connectionclass + connectionclass + 1.0.0 + +``` + +### Calculate Connection Class +Connection Class provides an interface for classes to add themselves as +listeners for when the network's connection quality changes. In the subscriber +class, implement `ConnectionClassStateChangeListener`: + +```java +public interface ConnectionClassStateChangeListener { + public void onBandwidthStateChange(ConnectionQuality bandwidthState); +} +``` + +and subscribe with the listener: + +```java +ConnectionClassManager.getInstance().register(mListener); +``` + +Alternatively, you can manually query for the connection quality bucket with +`getCurrentBandwidthQuality()`. + +```java +ConnectionQuality cq = ConnectionClassManager.getInstance().getCurrentBandwidthQuality(); +``` + +The main way to provide the ConnectionClassManager data is to use the DownloadBandwidthSampler. +The DownloadBandwidthSampler samples the device's underlying network stats when you tell it +you're performing some sort of network activity (downloading photos, playing a video, etc). + +```java +// Override ConnectionClassStateChangeListener +ConnectionClassManager.getInstance().register(mListener); +DownloadBandwidthSampler.getInstance().startSampling(); +// Do some downloading tasks +DownloadBandwidthSampler.getInstance().stopSampling(); +``` + +If the application is aware of the bandwidth downloaded in a certain time frame, +data can be added to the moving average using: + +```java +ConnectionClassManager.addBandwidth(bandwidth, time); +``` + +See the `connectionclass-sample` project for more details. + +## Improve Connection Class! +See the CONTRIBUTING.md file for how to help out. + +## License +Connection Class is BSD-licensed. We also provide an additional patent grant. diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..1d37845 --- /dev/null +++ b/build.gradle @@ -0,0 +1,14 @@ +buildscript { + repositories { + jcenter() + } + dependencies { + classpath 'com.android.tools.build:gradle:1.0.0' + classpath 'com.github.jcandksolutions.gradle:android-unit-test:2.1.1' + } +} +allprojects { + repositories { + jcenter() + } +} diff --git a/connectionclass-sample/.gitignore b/connectionclass-sample/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/connectionclass-sample/.gitignore @@ -0,0 +1 @@ +/build diff --git a/connectionclass-sample/build.gradle b/connectionclass-sample/build.gradle new file mode 100644 index 0000000..d274c23 --- /dev/null +++ b/connectionclass-sample/build.gradle @@ -0,0 +1,22 @@ +apply plugin: 'com.android.application' + +repositories { + jcenter() +} + +android { + compileSdkVersion 21 + buildToolsVersion "21.1.2" + + defaultConfig { + applicationId "com.facebook.network.connectionclass.sample" + minSdkVersion 9 + targetSdkVersion 21 + versionCode 1 + versionName "1.0" + } +} + +dependencies { + compile project (":connectionclass") +} diff --git a/connectionclass-sample/src/main/AndroidManifest.xml b/connectionclass-sample/src/main/AndroidManifest.xml new file mode 100644 index 0000000..21648b7 --- /dev/null +++ b/connectionclass-sample/src/main/AndroidManifest.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + diff --git a/connectionclass-sample/src/main/java/com/facebook/network/connectionclass/sample/MainActivity.java b/connectionclass-sample/src/main/java/com/facebook/network/connectionclass/sample/MainActivity.java new file mode 100644 index 0000000..caa09f5 --- /dev/null +++ b/connectionclass-sample/src/main/java/com/facebook/network/connectionclass/sample/MainActivity.java @@ -0,0 +1,137 @@ +/* + * This file provided by Facebook is for non-commercial testing and evaluation + * purposes only. Facebook reserves all rights not expressly granted. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ + +package com.facebook.network.connectionclass.sample; + +import android.app.Activity; +import android.os.AsyncTask; +import android.os.Bundle; + +import android.util.Log; +import android.view.View; +import android.widget.TextView; +import com.facebook.network.connectionclass.*; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; + + +public class MainActivity extends Activity { + + private static final String TAG = "ConnectionClass-Sample"; + + private ConnectionClassManager mConnectionClassManager; + private DeviceBandwidthSampler mDeviceBandwidthSampler; + private ConnectionChangedListener mListener; + private TextView mTextView; + private View mRunningBar; + + private String mURL = "http://connectionclass.parseapp.com/m100_hubble_4060.jpg"; + private int mTries = 0; + private ConnectionQuality mConnectionClass = ConnectionQuality.UNKNOWN; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + mConnectionClassManager = ConnectionClassManager.getInstance(); + mDeviceBandwidthSampler = DeviceBandwidthSampler.getInstance(); + findViewById(R.id.test_btn).setOnClickListener(testButtonClicked); + mTextView = (TextView)findViewById(R.id.connection_class); + mTextView.setText(mConnectionClassManager.getCurrentBandwidthQuality().toString()); + mRunningBar = findViewById(R.id.runningBar); + mRunningBar.setVisibility(View.GONE); + mListener = new ConnectionChangedListener(); + } + + @Override + protected void onPause() { + super.onPause(); + mConnectionClassManager.remove(mListener); + } + + @Override + protected void onResume() { + super.onResume(); + mConnectionClassManager.register(mListener); + } + + /** + * Listener to update the UI upon connectionclass change. + */ + private class ConnectionChangedListener + implements ConnectionClassManager.ConnectionClassStateChangeListener { + + @Override + public void onBandwidthStateChange(ConnectionQuality bandwidthState) { + mConnectionClass = bandwidthState; + runOnUiThread(new Runnable() { + @Override + public void run() { + mTextView.setText(mConnectionClass.toString()); + } + }); + } + } + + private final View.OnClickListener testButtonClicked = new View.OnClickListener() { + @Override + public void onClick(View v) { + new DownloadImage().execute(mURL); + } + }; + + /** + * AsyncTask for handling downloading and making calls to the timer. + */ + private class DownloadImage extends AsyncTask { + + @Override + protected void onPreExecute() { + mDeviceBandwidthSampler.startSampling(); + mRunningBar.setVisibility(View.VISIBLE); + } + + @Override + protected Void doInBackground(String... url) { + String imageURL = url[0]; + try { + // Open a stream to download the image from our URL. + InputStream input = new URL(imageURL).openStream(); + try { + byte[] buffer = new byte[1024]; + + // Do some busy waiting while the stream is open. + while (input.read(buffer) != -1) { + } + } finally { + input.close(); + } + } catch (IOException e) { + Log.e(TAG, "Error while downloading image."); + } + return null; + } + + @Override + protected void onPostExecute(Void v) { + mDeviceBandwidthSampler.stopSampling(); + // Retry for up to 10 times until we find a ConnectionClass. + if (mConnectionClass == ConnectionQuality.UNKNOWN && mTries < 10) { + mTries++; + new DownloadImage().execute(mURL); + } + if (!mDeviceBandwidthSampler.isSampling()) { + mRunningBar.setVisibility(View.GONE); + } + } + } +} diff --git a/connectionclass-sample/src/main/res/drawable-hdpi/ic_launcher.png b/connectionclass-sample/src/main/res/drawable-hdpi/ic_launcher.png new file mode 100755 index 0000000..eba8496 Binary files /dev/null and b/connectionclass-sample/src/main/res/drawable-hdpi/ic_launcher.png differ diff --git a/connectionclass-sample/src/main/res/drawable-mdpi/ic_launcher.png b/connectionclass-sample/src/main/res/drawable-mdpi/ic_launcher.png new file mode 100755 index 0000000..3f0c6ff Binary files /dev/null and b/connectionclass-sample/src/main/res/drawable-mdpi/ic_launcher.png differ diff --git a/connectionclass-sample/src/main/res/drawable-xhdpi/ic_launcher.png b/connectionclass-sample/src/main/res/drawable-xhdpi/ic_launcher.png new file mode 100755 index 0000000..c49fc7b Binary files /dev/null and b/connectionclass-sample/src/main/res/drawable-xhdpi/ic_launcher.png differ diff --git a/connectionclass-sample/src/main/res/drawable-xxhdpi/ic_launcher.png b/connectionclass-sample/src/main/res/drawable-xxhdpi/ic_launcher.png new file mode 100755 index 0000000..3fb1b64 Binary files /dev/null and b/connectionclass-sample/src/main/res/drawable-xxhdpi/ic_launcher.png differ diff --git a/connectionclass-sample/src/main/res/layout/activity_main.xml b/connectionclass-sample/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..d94f91f --- /dev/null +++ b/connectionclass-sample/src/main/res/layout/activity_main.xml @@ -0,0 +1,36 @@ + + +