Using Nearby Connections API of Google Play Services in Android

    One of the many APIs available in Google Play Services is the Nearby Connections API. Introduced in early 2015, this framework lets you set one device running your application as a host and have multiple other devices connect to it in order to communicate over a Local Area Network (LAN).
    In my opinion, this is very interesting and highly applicable API. Let's imagine, some Android devices which connected to the same wifi network or  Ethernet data connection can send messages to together. In this post, you will learn how to set up applications for connecting multiple devices together over a network and how to send data over that connection.

Setting up Nearby API to Android Studio Project

    Once you have your initial program created in Android Studio, you will need to import the Play Services library into your app. To do this, place the following line of code under the dependency node of the build.gradle file. At the time of writing, Nearby API 9.4.0 is the most recent release for development:
compile 'com.google.android.gms:play-services-nearby:9.4.0'
    Adding checking network state permission to your AndroidManifest.xml because this feature uses a LAN to communicate:
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    Next, you will need to add a piece of meta-data in the application tag that defines a service identifier that will be used by your app so that it can discover hosts advertising with that same identifier. In this example, our service identifier is defined in strings.xml as service_id:
<meta-data
            android:name="com.google.android.gms.nearby.connection.SERVICE_ID"
            android:value="@string/service_id" />
    Now, go to your main activity Java code. This is the class where we will implement both advertising and discovery. In MainActivity, you will also control sending messages between different devices.
    Firstly, in order to start using the Nearby Connections API, you must set up and connect to the Google API Client. Implementing ConnectionCallbacks and OnConnectionFailedListener at the top of your class. While we're adding our interfaces, let's also include the three that are needed by the API and an View.OnClickListener to handle Buttons event:
public class MainActivity extends AppCompatActivity implements GoogleApiClient.OnConnectionFailedListener,
        Connections.ConnectionRequestListener, Connections.MessageListener,
        GoogleApiClient.ConnectionCallbacks,
        Connections.EndpointDiscoveryListener, View.OnClickListener {

        ... 
    Like another Google Play Services API, in Android, you work with them through a GoogleApiClient object. Initializing it in onCreate() method of MainActivity like this:
googleApiClient = new GoogleApiClient.Builder(this)
                .addConnectionCallbacks(this)
                .addOnConnectionFailedListener(this)
                .addApi(Nearby.CONNECTIONS_API)
                .build();
    Start/Stop connecting in onStart()/onStop():
    @Override
    protected void onStart() {
        super.onStart();
        googleApiClient.connect();
    }

    @Override
    protected void onStop() {
        super.onStop();
        if (googleApiClient != null && googleApiClient.isConnected()) {
            Nearby.Connections.stopAdvertising(googleApiClient);
            googleApiClient.disconnect();
        }
    }
    Let's now create the member variables that we'll need in the MainActivity class. The most important here are two boolean flags for designating whether or not the device is connected and if it is the connection host, the GoogleApiClient that is necessary for using the Nearby Connections API:
    private String mRemoteHostEndpoint;
    private GoogleApiClient googleApiClient;
    private ListView listView;
    private View btnConnect;
    private View btnSend;
    private ArrayAdapter<String> adapter;
    private List<String> remotePeerEndpoints;
    private TextInputLayout textInputLayout;
    private View inputLayout;
    private TextView status;
    private RadioGroup radioGroup;
    private long CONNECTION_TIME_OUT = 15000;
    private boolean isConnected;
    private boolean isHost = false;

Advertising connections

    Up to now, you've just completed connecting to the Google API Client, you now can start working with nearby connections.
    With your device, there are two connecting options: advertising and discovering. We'll go over the first option(advertising), which allows a device to assume the role of host and manage connections between various peers for communication. After checking your network, start advertising by call Nearby.Connections.startAdvertising() method. Adding some proper parameters and setResultCallback() like this:
public boolean isConnectedToNetwork() {
        ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo wifiNetwork = cm.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
        if (wifiNetwork != null && wifiNetwork.isConnected()) {
            return true;
        }

        NetworkInfo mobileNetwork = cm.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
        if (mobileNetwork != null && mobileNetwork.isConnected()) {
            return true;
        }

        NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
        if (activeNetwork != null && activeNetwork.isConnected()) {
            return true;
        }

        return false;
    }

    private void advertise() {
        if (!isConnectedToNetwork()) return;

        String name = "Nearby Advertising";
        Nearby.Connections.startAdvertising(googleApiClient, name, null, CONNECTION_TIME_OUT, this)
                .setResultCallback(new ResultCallback<Connections.StartAdvertisingResult>() {
                    @Override
                    public void onResult(Connections.StartAdvertisingResult result) {
                        if (result.getStatus().isSuccess()) {
                            status.setText("Advertising");
                        }
                    }
                });
    }
    Once your "host device" is advertising, it will be able to receive connection requests from peers. When a device attempts to connect, onConnectionRequest() will be called. To accept the request, you call Nearby.Connections.acceptConnectionRequest with a ResultCallback. In this example, we'll add the remote endpoint to a list to keep track of it and broadcast to any connected peers that this new device has connected. Moreover, you determine that the device should not connect to your application, you can reject it by calling Nearby.Connections.rejectConnectionRequest:
    @Override
    public void onConnectionRequest(final String remoteEndpointId, final String remoteDeviceId,
                                    final String remoteEndpointName, byte[] payload) {
        if (isHost) {
            Nearby.Connections.acceptConnectionRequest(googleApiClient, remoteEndpointId, payload, this)
                    .setResultCallback(new ResultCallback() {
                        @Override
                        public void onResult(Status status) {
                            if (status.isSuccess()) {
                                if (!remotePeerEndpoints.contains(remoteEndpointId)) {
                                    remotePeerEndpoints.add(remoteEndpointId);
                                }

                                getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
                                adapter.notifyDataSetChanged();
                                sendMessage(remoteDeviceId + " connected!");
                                inputLayout.setVisibility(View.VISIBLE);
                            }
                        }
                    });
        } else {
            Nearby.Connections.rejectConnectionRequest(googleApiClient, remoteEndpointId);
        }
    }

Discovering connections

    Like the option above, you can start discovery by passing the application's service identifier into the Nearby.Connections.startDiscovery() method, which sets your user's device into discovery mode.
    When a host device is detected, onEndpointFound() will be called. Request to connect with host by calling Nearby.Connections.sendConnectionRequest(). If the connection is accepted or rejected by the host, the sendConnectionRequest() result callback will be called. If the connection is accepted, we now prepare for sending messages across the connection channel:
private void discover() {
        if (!isConnectedToNetwork())
            return;

        String serviceId = getString(R.string.service_id);
        Nearby.Connections.startDiscovery(googleApiClient, serviceId, CONNECTION_TIME_OUT, this)
                .setResultCallback(new ResultCallback<Status>() {
                    @Override
                    public void onResult(Status status) {
                        if (status.isSuccess()) {
                            MainActivity.this.status.setText("Discovering");
                        }
                    }
                });
    }

    @Override
    public void onEndpointFound(String endpointId, String deviceId, final String serviceId, String endpointName) {
        byte[] payload = null;

        Nearby.Connections.sendConnectionRequest(googleApiClient, deviceId, endpointId, payload,
                new Connections.ConnectionResponseCallback() {

                    @Override
                    public void onConnectionResponse(String s, Status status, byte[] bytes) {
                        if (status.isSuccess()) {
                            MainActivity.this.status.setText("Connected to: " + s);
                            Nearby.Connections.stopDiscovery(googleApiClient, serviceId);
                            mRemoteHostEndpoint = s;
                            getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
                            inputLayout.setVisibility(View.VISIBLE);

                            if (!isHost) {
                                isConnected = true;
                            }
                        } else {
                            MainActivity.this.status.setText("Connection to " + s + " failed");
                            if (!isHost) {
                                isConnected = false;
                            }
                        }
                    }
                }, this);
    }

Sending messages via the connection

    When a message is received over the API, onMessageReceived() will be called. In this application, you will use this to display the payload as a string in a ListView and, if the device is the host, rebroadcast it out to every connected device:
    @Override
    public void onMessageReceived(String s, byte[] bytes, boolean b) {
        adapter.add(new String(bytes));
        adapter.notifyDataSetChanged();

        if (isHost) {
            sendMessage(new String(bytes));
        }
    }
    And this is method that sending a message (user input) via the connection, I divided into 2 cases: host device message and client device message:
private void sendMessage(String message) {
        if (isHost) {
            Nearby.Connections.sendReliableMessage(googleApiClient, remotePeerEndpoints, message.getBytes());
            adapter.add(message);
            adapter.notifyDataSetChanged();
        } else {
            Nearby.Connections.sendReliableMessage(googleApiClient, mRemoteHostEndpoint,
                    (Nearby.Connections.getLocalDeviceId(googleApiClient) + " says: " + message).getBytes());
        }
    }

Disconnecting from the connection

    If you attempt to disconnect on a client that hasn't connected to a host yet, you simply need to stop discovery. If you've already connected to a host, you call disconnectFromEndpoint() and the API will handle severing the connection:
private void disconnect() {
        if (!isConnectedToNetwork())
            return;

        if (isHost) {
            sendMessage("Shutting down host");
            Nearby.Connections.stopAdvertising(googleApiClient);
            Nearby.Connections.stopAllEndpoints(googleApiClient);
            isHost = false;
            status.setText("Not connected");
            remotePeerEndpoints.clear();
        } else {
            if (!isConnected || TextUtils.isEmpty(mRemoteHostEndpoint)) {
                Nearby.Connections.stopDiscovery(googleApiClient, getString(R.string.service_id));
                return;
            }

            sendMessage("Disconnecting");
            Nearby.Connections.disconnectFromEndpoint(googleApiClient, mRemoteHostEndpoint);
            mRemoteHostEndpoint = null;
            status.setText("Disconnected");
        }

        isConnected = false;
    }

Running application

    I run this sample project in my 2 physically devices. This is the host device screen, when I select "Host" RadioButton and click the button to start connecting, status become "advertising":

    In the second device, choose "Client" and start connecting, the status change to "Discovering":

When the client device connected:

    Sending some messages:

Conclusions

    Through this post, you have learned how to implement communication between various Android devices over a LAN using the Nearby Connections API - a part of Google Play Services. You should now be able to enhance your own apps by connecting devices together and keeping them in sync through various updates to each other. Moreover, you can custom this project to make it become a "LAN message" application, this is not too hard!
    References:

Share


Previous post
« Prev Post
Next post
Next Post »