Giter Site home page Giter Site logo

pwittchen / reactivebeacons Goto Github PK

View Code? Open in Web Editor NEW
167.0 10.0 38.0 552 KB

Android library scanning BLE beacons nearby with RxJava

License: Apache License 2.0

Java 89.31% Kotlin 10.69%
android beacon ble bluetooth bluetooth-low-energy rxjava estimote kontaktio ibeacon eddystone

reactivebeacons's Introduction

ReactiveBeacons

Android Arsenal

Current Branch Branch Artifact Id Build Status Maven Central
RxJava1.x reactivebeacons Build Status for RxJava1.x Maven Central
โ˜‘๏ธ RxJava2.x reactivebeacons-rx2 Build Status for RxJava2.x Maven Central

This is RxJava2.x branch. To see documentation for RxJava1.x, switch to RxJava1.x branch.

Android library scanning BLE (Bluetooth Low Energy) beacons nearby with RxJava

Library was tested with Estimote and Kontakt beacons.

This library has limited functionality, but its API is simple:

ReactiveBeacons(context)
boolean isBleSupported()
boolean isBluetoothEnabled()
boolean isLocationEnabled(context)
boolean isAtLeastAndroidLollipop()
void requestBluetoothAccess(activity)
void requestLocationAccess(activity)
Observable<Beacon> observe()
Observable<Beacon> observe(ScanStrategy scanStrategy)

JavaDoc is available at: http://pwittchen.github.io/ReactiveBeacons/RxJava2.x

min SDK = 9, but if you are using API level lower than 18, don't forget to check BLE support on the device.

Contents

Usage

Step 1

Initialize ReactiveBeacons object:

private ReactiveBeacons reactiveBeacons;

@Override protected void onCreate(Bundle savedInstanceState) {
  reactiveBeacons = new ReactiveBeacons(this);
}

Step 2

Create subscribtion:

private Disposable subscription;

@Override protected void onResume() {
  super.onResume();
  
  if (!reactiveBeacons.isBleSupported()) { // optional, but recommended step
    // show message for the user that BLE is not supported on the device
    return;
  }
  
  // we should check Bluetooth and Location access here
  // if they're disabled, we can request access
  // if you want to know how to do it, check next sections 
  // of this documentation and sample app

  subscription = reactiveBeacons.observe()
    .subscribeOn(Schedulers.computation())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(new Consumer<Beacon>() {
      @Override public void call(Beacon beacon) {
        // do something with beacon
      }
    });
}    

Step 3

Unsubscribe subscription in onPause() method to stop BLE scan.

@Override protected void onPause() {
  super.onPause();
  if (subscription != null && !subscription.isDisposed()) {
    subscription.dispose()
  }
}

Please note: Library may emit information about the same beacon multiple times. New emission is created everytime when RSSI changes. We can distinguish several beacons by their MAC addresses with beacon.device.getAddress() method.

Good practices

Updating Manifest

Add <uses-feature .../> tag inside <manifest ...> tag in AndroidManifest.xml file in your application if you support Android devices with API level 18 or higher. You can skip this, if you are supporting lower API levels.

<uses-feature
    android:name="android.hardware.bluetooth_le"
    android:required="true" />

Checking BLE support

Check BLE support if you are supporting devices with API level lower than 18.

if (!reactiveBeacons.isBleSupported()) {
  // show message for the user that BLE is not supported on the device
}

If BLE is not supported, Observable emitting Beacons will be always empty.

Requesting Bluetooth access

Use requestBluetoothAccess(activity) method to ensure that Bluetooth is enabled. If you are supporting devices with API level lower than 18, you don't have to request Bluetooth access every time.

if (!reactiveBeacons.isBluetoothEnabled()) {
  reactiveBeacons.requestBluetoothAccess(activity);
}

Requesting Location access

Since API 23 (Android 6 - Marshmallow), Bluetooth Low Energy scan, requires ACCESS_COARSE_LOCATION or ACCESS_FINE_LOCATION permissions. Moreover, we need to enable Location services in order to scan BLE beacons. You don't have to worry about that if your apps are targeted to lower APIs than 23. Nevertheless, you have to be aware of that, if you want to detect beacons on the newest versions of Android. Read more at: https://code.google.com/p/android/issues/detail?id=190372. Use requestLocationAccess(activity) method to ensure that Location services are enabled. If you are supporting devices with API level lower than 18, you don't have to request Location access every time.

if (!reactiveBeacons.isLocationEnabled(activity)) {
  reactiveBeacons.requestLocationAccess(activity);
}

Requesting Runtime Permissions

Since Android M (API 23), we need to request Runtime Permissions. If we want to scan for BLE beacons, we need to request for ACCESS_COARSE_LOCATION or ACCESS_FINE_LOCATION permission. For more details, check sample app.

Exemplary code snippet

With API methods, we can create the following code snippet:

private boolean canObserveBeacons() {
  if (!reactiveBeacons.isBleSupported()) {
    Toast.makeText(this, "BLE is not supported on this device", Toast.LENGTH_SHORT).show();
    return false;
  }

  if (!reactiveBeacons.isBluetoothEnabled()) {
    reactiveBeacons.requestBluetoothAccess(this);
    return false;
  } else if (!reactiveBeacons.isLocationEnabled(this)) {
    reactiveBeacons.requestLocationAccess(this);
    return false;
  } else if (!isFineOrCoarseLocationPermissionGranted()
             && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
    requestCoarseLocationPermission();
    return false;
   }

  return true;
}

You can adjust this snippet to your needs or handle this logic in your own way.

After that, we can perform the following operation:

if(canObserveBeacons()) {
  // observe beacons here
}

Examples

Exemplary application is located in app directory of this repository.

If you want to know, how to use this library with Kotlin, check app-kotlin sample.

Compatibility with different Android versions

BLE scanning is available from Android 4.3 JELLY_BEAN_MR2 (API 18). You can use this library on lower versions of Android, but you won't be able to scan BLE devices, you should handle that situation in your app and notify user about that. See Good practices section. Since Android 5.0 LOLLIPOP (API 21), we have different API for BLE scanning. That's why this library has two different BLE scanning strategies:

  • PreLollipopScanStrategy used for pre-Lollipop devices (from API 18 to 20)
  • LollipopScanStrategy used for Lollipop devices (API 21 or higher)

Library automatically chooses proper strategy with isAtLeastAndroidLollipop() method, which checks version of the system installed on a device and uses selected strategy in Observable<Beacon> observe() method from the library. Moreover, you can force using one of the existing strategies or your own custom scanning strategy with the following method available in the library:

Observable<Beacon> observe(ScanStrategy scanStrategy)

ScanStrategy is an interface with the following method:

Observable<Beacon> observe();

Beacon class

Beacon class represents BLE beacon and has the following attributes:

BluetoothDevice device;
int rssi;
byte[] scanRecord;
int txPower;

All of the elements are assigned dynamically, but txPower has default value equal to -59. It works quite fine for different types of beacons.

Beacon class has also getDistance() method, which returns distance from mobile device to beacon in meters and getProximity() method, which returns Proximity value.

Proximity can be as follows:

  • IMMEDIATE - from 0m to 1m
  • NEAR - from 1m to 3m
  • FAR - more than 3m

Beacon class has also static create(...) method responsible for creating Beacon objects.

Filter class

Filter class provides static filtering methods, which can be used with RxJava filter(...) method inside specific subscription.

Currently the following filters are available:

  • proximityIsEqualTo(Proximity)
  • proximityIsNotEqualTo(Proximity)
  • distanceIsEqualTo(double)
  • distanceIsGreaterThan(double)
  • distanceIsLowerThan(double)
  • hasName(String)
  • hasMacAddress(String)

Of course, we can create our own custom filters, which are not listed above if we need to.

Exemplary usage

In the example below, we are filtering all Beacons with Proximity equal to NEAR value.

reactiveBeacons.observe()
    .filter(Filter.proximityIsEqualTo(Proximity.NEAR))
    .subscribe(new Consumer<Beacon>() {
      @Override public void call(Beacon beacon) {
        beacons.put(beacon.device.getAddress(), beacon);
        refreshBeaconList();
      }
    });

Download

You can depend on the library through Maven:

<dependency>
    <groupId>com.github.pwittchen</groupId>
    <artifactId>reactivebeacons-rx2</artifactId>
    <version>0.6.0</version>
</dependency>

or through Gradle:

dependencies {
  compile 'com.github.pwittchen:reactivebeacons-rx2:0.6.0'
}

Tests

Tests are available in library/src/test/java/ directory and can be executed without emulator or Android device from CLI with the following command:

./gradlew test

Code style

Code style used in the project is called SquareAndroid from Java Code Styles repository by Square available at: https://github.com/square/java-code-styles. Currently, library doesn't have checkstyle verification attached. It can be done in the future.

Static code analysis

Static code analysis runs Checkstyle, FindBugs, PMD and Lint. It can be executed with command:

./gradlew check

Reports from analysis are generated in library/build/reports/ directory.

References

Useful resources

Producers of BLE beacons

Other APIs and libraries

License

Copyright 2015 Piotr Wittchen

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

reactivebeacons's People

Contributors

benburkhart1 avatar dependabot-preview[bot] avatar juliocbcotta avatar pwittchen avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

reactivebeacons's Issues

Release 0.3.2

Initial release notes:

  • bug fix: wrapped BluetoothManager inside isBleSupported() to avoid NoClassDefFound error occurring while instantiating ReactiveBeacons object on devices running API < 18 - fixed in PR #30.

Things to do:

  • bump library version ๐Ÿ‘‰ PR #32
  • upload archives to Maven Central
  • close and release artifact on Maven Central
  • update CHANGELOG.md after Maven Sync
  • bump library version in README.md
  • create new GitHub release

HashCode and Equals?!

Wouldn't it be better just to use MacAddress for equals and hashcode? So you could compare them while observe and just list the current values of a specifiy beacon.

@Override public boolean equals(Object o) {
    if (this == o) {
      return true;
    }

    if (o == null || getClass() != o.getClass()) {
      return false;
    }

    Beacon beacon = (Beacon) o;

    if (rssi != beacon.rssi) {
      return false;
    }

    if (!device.equals(beacon.device)) {
      return false;
    }

    return Arrays.equals(scanRecord, beacon.scanRecord);
  }

  @Override public int hashCode() {
    int result = device.hashCode();
    result = 31 * result + rssi;
    result = 31 * result + (scanRecord != null ? Arrays.hashCode(scanRecord) : 0);
    return result;
  }

distinct operator should be distinctUntilChanged?

In ReactiveBeacons.java the distinct() operator is used:

/**
   * Creates an observable stream of BLE beacons, which can be subscribed with RxJava
   *
   * @return Observable stream of beacons
   */
  @SuppressWarnings("deprecation") public Observable<Beacon> observe() {
    leScanCallbackAdapter = new LeScanCallbackAdapter();
    bluetoothAdapter.startLeScan(leScanCallbackAdapter);
    return leScanCallbackAdapter.toObservable().repeat().distinct().doOnUnsubscribe(new Action0() {
      @Override public void call() {
        bluetoothAdapter.stopLeScan(leScanCallbackAdapter);
      }
    });

But because Beacon.java equals():

...
    if (rssi != beacon.rssi) return false;
...

This causes any beacon throughout scanning history with identical Rssi levels to not be emitted.

I guess what's really important for proximity detection is to have the latest beacon Rssi value - even if it existed in history already. This could be solved (and at the same time avoiding immediate duplicates) using distinctUntilChanged.

Should I make a PR?

Release 0.4.0

Initial release notes:

  • added MacAddress class with MAC address validation
  • added macAddress field to Beacon class
  • added exceptName(final String... names) method to Filter class
  • added exceptMacAddress(final String... macs) method to Filter class
  • added hasMacAddress(final MacAddress... macs) method to Filter class
  • added exceptMacAddress(final MacAddress... macs) method to Filter class

Things to do:

  • bump library version
  • upload archives to Maven Central
  • close and release artifact on Maven Central
  • update JavaDoc on gh-pages
  • update CHANGELOG.md after Maven Sync
  • bump library version in README.md
  • create new GitHub release

Organize Git branches & documentation

  • add documentation regarding RxJava1.x and RxJava2.x to README.md for both branches
  • create RxJava1.x branch out of master branch
  • add Travis badge for RxJava1.x and RxJava2.x branch
  • add protection for RxJava1.x branch
  • update documentation in README.md for RxJava2.x branch
  • remove master branch

NullPointerException in LollipopScanStrategy

Fatal Exception: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.bluetooth.le.BluetoothLeScanner.startScan(android.bluetooth.le.ScanCallback)' on a null object reference at com.github.pwittchen.reactivebeacons.library.scan.strategy.lollipop.LollipopScanStrategy.observe(LollipopScanStrategy.java:36) at com.github.pwittchen.reactivebeacons.library.ReactiveBeacons.observe(ReactiveBeacons.java:143) at com.github.pwittchen.reactivebeacons.library.ReactiveBeacons.observe(ReactiveBeacons.java:120) at com.sminq.user.services.BleBeaconService$1.run(BleBeaconService.java:180) at android.os.Handler.handleCallback(Handler.java:815) at android.os.Handler.dispatchMessage(Handler.java:104) at android.os.Looper.loop(Looper.java:207) at android.app.ActivityThread.main(ActivityThread.java:5769) at java.lang.reflect.Method.invoke(Method.java) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:789) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:679)

Release 0.6.0

Initial release notes:

  • migrated library to RxJava2.x on RxJava2.x branch and released it as reactivebeacons-rx2 artifact
  • kept library compatible with RxJava1.x on a RxJava1.x branch and released it as reactivebeacons artifact
  • removed master branch
  • bumped library dependencies
  • added permission annotations
  • organized gradle configuration
  • transformed instrumentation unit tests to pure java unit tests
  • started executing unit tests on Travis CI server
  • created separate JavaDoc for RxJava1.x and RxJava2.x

Things to do:

  • CI issues
    • wait until latest CI builds will be successfully completed (Travis has problems now)
  • Testing
    • perform short manual smoke tests before the release on both branches
  • RxJava1.x branch:
    • bump library version
    • upload archives to Maven Central
    • close and release artifact on Maven Central
    • update CHANGELOG.md after Maven Sync
    • bump library version in README.md
  • RxJava2.x branch:
    • bump library version
    • upload archives to Maven Central
    • close and release artifact on Maven Central
    • update CHANGELOG.md after Maven Sync
    • update download section in README.md
  • General:
    • update gh-pages
    • create new GitHub release

Support (handle gracefully) older Android APIs

Instead of forcing a minSdk=18 on the library consumers, it would be nice to handle older API versions gracefully. Ie. introduce some checks against the Build API version and report an error to the library consumer during initialization. Currently, an app using minSdk < 18 cannot integrate the library without force overriding the minSdk of the library.

Not all apps have the luxury of supporting min 4.3+ Android versions and although the constraint is valid for usage of BLE, providing a graceful error to the app would allow it to provide a fallback option or error message when running on older devices.

Release 0.2.0

Initial release notes:

  • decreased min SDK version to 9
  • added isBleSupported() method to the public API
  • if BLE is not supported by the device, library emits an empty Observable
  • updated exemplary app
  • updated documentation in README.md file

Things to be done:

  • bump library version
  • upload archives to Maven Central
  • close & release repository on Nexus
  • update CHANGELOG.md file
  • update version in README.md file
  • create new GitHub release

Incorrect distance

I am using Kontakt beacons for testing and have changed the txPower to -69 as that is the default values for Kontakt.
But for beacons that are placed at 5m the my values range from 11-45m with a median of 18m.
Interestingly the closer the beacon is from the device the more accurate my values are for the distance.

Is there a way to get correct distances for beacons placed at about 5-10m?

Investigate possibility to connect to GATT Server on a Bluetooth device

Update package for library on RxJava2.x branch

Update package for the library on RxJava2.x branch to avoid confusion of the users while migrating to the RxJava2.x library version. After that, users will need to re-import packages.

I think library.rx2.* instead of library.* will be enough.

Release 0.5.0

Initial release notes:

  • added support for BLE scanning API available on Android API 21 (Lollipop) mentioned in issue #43
  • created two scanning strategies for BLE scanning on pre-Lollipop devices and Lollipop devices
  • updated body of Observable<Beacon> observe() method
  • added PreLollipopScanStrategy class, LollipopScanStrategy class and ScanStrategy interface
  • added ScanCallbackAdapter class used in LollipopScanStrategy
  • added Observable<Beacon> observe(ScanStrategy scanStrategy) method to library API, which allows to use one of the existing scan strategies or custom scan strategy
  • added note about Compatibility with different Android versions in REAMDE.md
  • bumped RxJava version to 1.1.2
  • bumped Kotlin version in sample app to 1.0.1-1
  • changed Schedulers.io() in subscribeOn(...) to Schedulers.computation() in sample apps

Things to do:

  • bump library version
  • upload archives to Maven Central
  • close and release artifact on Maven Central
  • update JavaDoc on gh-pages
  • update CHANGELOG.md after Maven Sync
  • bump library version in README.md
  • create new GitHub release

Use Lollipop enhancements in Lollipop and above?

I was dropping through the code hoping for a less aggressive scanning and noticed that this library is using the depreciated versions of the API. Any chances you've looked into updating that? Or is that accomplished using the depreciated methods for now as Google appears to be implanting the depreciated usages using the new BluetoothLeScanner

Thoughts?

Release 0.4.1

Initial release notes:

  • bumped RxJava dependency to v. 1.1.0
  • bumped Google Truth test dependency to v. 0.27
  • bumped RxAndroid dependency in code samples to v. 1.1.0
  • bumped Gradle Build Tools version to v. 1.3.1

Things to do:

  • bump library version
  • upload archives to Maven Central
  • close and release artifact on Maven Central
  • update CHANGELOG.md after Maven Sync
  • bump library version in README.md
  • create new GitHub release

IllegalStateException in LollipopScanStrategy

java.lang.IllegalStateException: BT Adapter is not turned ON at android.bluetooth.le.BluetoothLeUtils.checkAdapterStateOn(BluetoothLeUtils.java:136) at android.bluetooth.le.BluetoothLeScanner.stopScan(BluetoothLeScanner.java:206) at com.github.pwittchen.reactivebeacons.library.scan.strategy.lollipop.LollipopScanStrategy$1.call(LollipopScanStrategy.java:43) at rx.subscriptions.BooleanSubscription.unsubscribe(BooleanSubscription.java:71) at rx.internal.util.SubscriptionList.unsubscribeFromAll(SubscriptionList.java:136) at rx.internal.util.SubscriptionList.unsubscribe(SubscriptionList.java:125) at rx.Subscriber.unsubscribe(Subscriber.java:98) at rx.internal.util.SubscriptionList.unsubscribeFromAll(SubscriptionList.java:136) at rx.internal.util.SubscriptionList.unsubscribe(SubscriptionList.java:125) at rx.Subscriber.unsubscribe(Subscriber.java:98) at com.sminq.user.services.BleBeaconService.onDestroy(BleBeaconService.java:219) at android.app.ActivityThread.handleStopService(ActivityThread.java:3327) at android.app.ActivityThread.-wrap27(ActivityThread.java) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1570) at android.os.Handler.dispatchMessage(Handler.java:102) at android.os.Looper.loop(Looper.java:154) at android.app.ActivityThread.main(ActivityThread.java:6077) at java.lang.reflect.Method.invoke(Method.java) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:865) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:755)

Release 0.3.0

Initial release notes:

  • replaced distinct() operator with distinctUntilChanged() operator in Observable<Beacon> observe() method in ReactiveBeacons class.
  • added permissions ACCESS_FINE_LOCATION and ACCESS_COARSE_LOCATION to satisfy requirements of Android 6
  • renamed void requestBluetoothAccessIfDisabled(activity) method to void requestBluetoothAccess(activity)
  • added boolean isBluetoothEnabled() method
  • added boolean isLocationEnabled(context) method
  • added void requestLocationAccess(activity) method
  • modified sample app in order to make it work on Android 6 Marshmallow
  • reduced target API from 23 to 22 in library due to problems with additional permissions and new permission model (it can be subject of improvements in the next releases)
  • added package private AccessRequester class

Things to do:

  • bump library version
  • upload archives to Maven Central
  • release library on Maven Central
  • update version in README.md after Maven Sync
  • update CHANGELOG.md
  • update JavaDoc on gh-pages
  • create new GitHub release

Release 0.1.0

Initial release notes:

  • added Filter class providing methods, which can be used with filter(...) method from RxJava inside specific subscription. These methods can be used for filtering stream of Beacons by Proximity, distance, device names and MAC addresses.
  • added missing reactivebeacons package to library module

Things to do:

  • write unit tests for Filter class
  • perform manual tests of Filter class
  • add short documentation about Filter class in README.md
  • bump version of library to 0.1.0
  • upload archives to Maven Central
  • update JavaDoc on gh-pages
  • update CHANGELOG.md
  • bump library version to 0.1.0 in README.md after Maven Sync
  • create new GitHub release

Release 0.3.1

Note: Consider resolving open issues before releasing new version.

Initial release notes:

  • improved filters in PR #24
  • fixed RxJava usage in sample app
  • fixed RxJava usage in code snippets in README.md
  • added static code analysis
  • added sample app in Kotlin
  • added sample app with Android Service

Things to do:

  • bump library version
  • upload archives to Maven Central
  • close and release artifact on Maven Central
  • update CHANGELOG.md after Maven Sync
  • bump library version in README.md
  • create new GitHub release

Release 0.5.1

Initial release notes:

  • bumped RxJava to v. 1.1.8
  • bumped Truth to v. 0.28
  • bumped compile SDK version to 23
  • updated dependencies in sample apps
  • updated documentation

Things to do:

  • bump library version
  • upload archives to Maven Central
  • close and release artifact on Maven Central
  • update CHANGELOG.md after Maven Sync
  • bump library version in README.md
  • create new GitHub release

Scan speed

I tried both (Pre Lollipop and Lollipop) strategy and I found that the Pre Lollipop version receives much faster updates from the same device.
Is there any reason for that?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.