Google Nearby Connections API for Android

The Nearby Connections API enables your app to easily discover other devices on a local network, connect, and exchange messages in real-time. You can use the Nearby Connections API to give your apps the following capabilities:



  • Collaborative whiteboard: Jot ideas down with nearby participants on a shared virtual whiteboard.
  • Local multiplayer gaming: Set up a multiplayer game and invite other users on the local network to join it. Your app can also allow a player to start an in-game mission when enough nearby participants join.
  • Multi-screen gaming: Use a phone or tablet as a game controller to play games displayed on a nearby large-screen Android device, such as Android TV. Your app can also enable players to see a customized game screen on their personal devices while all nearby participants see a shared common view on a large-screen Android device.

The API is located in the com.google.android.gms.nearby.connection package, and lets you build apps that can join multiple nearby Android devices together on the same local network. One device advertises on the network as the host, while other devices act as clients and send connection requests to the host.

Before you begin

Before you start to code using the Nearby Connections API:


Once the system connects the GoogleApiClient, your game can use the Nearby Connections API.

Initializing your Google API client for nearby connections

To access the Nearby Connections API, you first need to initialize the Google Play services API client. Use the builder to add the Nearby Connections API.

The following example shows you how to initialize the Nearby Connections API in the onCreate() method of your activity:
import com.google.android.gms.nearby.Nearby;
import com.google.android.gms.nearby.connection.AppIdentifier;
import com.google.android.gms.nearby.connection.AppMetadata;
import com.google.android.gms.nearby.connection.Connections;
import com.google.android.gms.nearby.connection.ConnectionsStatusCodes;

public class MainActivity extends Activity implements
        GoogleApiClient.ConnectionCallbacks,
        GoogleApiClient.OnConnectionFailedListener,
        View.OnClickListener,
        Connections.ConnectionRequestListener,
        Connections.MessageListener,
        Connections.EndpointDiscoveryListener {

   // Identify if the device is the host
   private boolean mIsHost = false;

   

   @Override
    protected void onCreate(Bundle savedInstanceState) {
        

        mGoogleApiClient = new GoogleApiClient.Builder(this)
                .addConnectionCallbacks(this)
                .addOnConnectionFailedListener(this)
                .addApi(Nearby.CONNECTIONS_API)
                .build();
    }
}

Note:
If you are using Google+ sign-in with your project or any other API that requires authentication you may want to use a separate Google API Client for Nearby Connections. This API does not require the user to authenticate so it can be used even when the user does not want to sign in or sign-in has failed.

To use the Nearby Connections API, your client must first establish a connection to the Nearby Connections API service. You can disconnect the client once your game no longer needs access to this service. Connect the GoogleApiClient in your activity’s onStart() and disconnect it in onStop():
@Override
public void onStart() {
    super.onStart();
    mGoogleApiClient.connect();
}

@Override
public void onStop() {
    super.onStop();
    if (mGoogleApiClient != null && mGoogleApiClient.isConnected()) {
        mGoogleApiClient.disconnect();
    }
}

Validating network connectivity

Before using the Nearby Connections API, you should check to ensure the devices are connected to a network. If they are not connected, prompt players to connect to a network.

Note:
For devices to discover each other, they must be connected to the same network with multicast enabled. By default, most home routers have this setting enabled. If you experience connectivity issues while using this API, please consult your router device manufacturer documentation for more information on how to enable this setting.

To detect the network state, first add the following permission to your manifest:
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
The following example shows you how to determine whether a device is connected to a network:
private static int[] NETWORK_TYPES = {ConnectivityManager.TYPE_WIFI,
    ConnectivityManager.TYPE_ETHERNET};

private boolean isConnectedToNetwork() {
    ConnectivityManager connManager =
        (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
    for (int networkType : NETWORK_TYPES) {
        NetworkInfo info = connManager.getNetworkInfo(networkType);
        if (info != null && info.isConnectedOrConnecting()) {
            return true;
        }
    }
    return false;
}
Call this method before you start to advertise your device or set your device to discovery mode as described in the sections below.

Note:
If your Android device is acting as a WiFi hotspot for other devices, the hotspot device cannot advertise or discover Nearby Connections at the same time. However, other devices that are connected to this type of WiFi network can advertise and discover other devices on the network.

Advertising your device

To establish a connection between two or more devices, one device must advertise its service and one or more devices must request to connect to it. The advertising device is the 'host' in a multiplayer game, while the connecting device is the client.

To enable your app to advertise itself with the Nearby Connections API, add the following to your manifest:
<application>
  <!-- Required for Nearby Connections API -->
  <meta-data android:name="com.google.android.gms.nearby.connection.SERVICE_ID"
            android:value="@string/service_id" />
  <activity>
      ...
  </activity>
</application>
The service_id value allows client devices to discover the advertising device. The value must uniquely identify your app. As a best practice, use the package name of your app (for example, com.google.example.mygame.service).
The following example shows you how to declare the service_id value in the strings.xml file:
<?xml version="1.0" encoding="utf-8"?>
<resources>
    ...
    <!--
        This is the service identifier for Nearby Connections. Do NOT copy and paste this value
        into your own application.  Instead, choose a unique identifier that is appropriate for
        your application.
    -->
    <string name="service_id"><!-- your service ID goes here, e.g. com.google.example.mygame.service --></string>
    ...
</resources>
When a player starts the game and chooses to host, call Connections.startAdvertising() to advertise the device. When you pass in null for the name parameter, the API constructs a default name based on the device model (for example, “LGE Nexus 5”). The AppMetadata parameter allows Google apps to prompt a player to install your application if they don't currently have it installed.

The following example shows you how you can advertise the host player’s device:
private void startAdvertising() {
    if (!isConnectedToNetwork()) {
        // Implement logic when device is not connected to a network
    }

    // Identify that this device is the host
    mIsHost = true;

    // Advertising with an AppIdentifer lets other devices on the
    // network discover this application and prompt the user to
    // install the application.
    List<AppIdentifier> appIdentifierList = new ArrayList<>();
    appIdentifierList.add(new AppIdentifier(getPackageName()));
    AppMetadata appMetadata = new AppMetadata(appIdentifierList);

    // The advertising timeout is set to run indefinitely
    // Positive values represent timeout in milliseconds
    long NO_TIMEOUT = 0L;

    String name = null;
    Nearby.Connections.startAdvertising(mGoogleApiClient, name, appMetadata, NO_TIMEOUT,
            this).setResultCallback(new ResultCallback<Connections.StartAdvertisingResult>() {
        @Override
        public void onResult(Connections.StartAdvertisingResult result) {
            if (result.getStatus().isSuccess()) {
                // Device is advertising
            } else {
                int statusCode = result.getStatus().getStatusCode();
                // Advertising failed - see statusCode for more details
            }
        }
    });
  }

Discovering Nearby Devices

The discovery process allows a device, on the same Wi-Fi network, to find other devices advertising Nearby connections for a specific service ID. The service ID parameter you pass into Connections.startDiscovery() should match the value provided in the manifest of the advertising app. The following example shows how to initiate the discovery process in your app:
private void startDiscovery() {
    if (!isConnectedToNetwork()) {
        // Implement logic when device is not connected to a network
    }
    String serviceId = getString(R.string.service_id);

    // Set an appropriate timeout length in milliseconds
    long DISCOVER_TIMEOUT = 1000L;

    // Discover nearby apps that are advertising with the required service ID.
    Nearby.Connections.startDiscovery(mGoogleApiClient, serviceId, DISCOVER_TIMEOUT, this)
            .setResultCallback(new ResultCallback<Status>() {
                @Override
                public void onResult(Status status) {
                    if (status.isSuccess()) {
                        // Device is discovering
                    } else {
                        int statusCode = result.getStatus().getStatusCode();
                        // Advertising failed - see statusCode for more details
                }
            });
}

Managing Nearby Connections

Your app must implement logic for connecting client devices to host devices. This section explains how to complete a connection and identify connected devices. There are two ways that the Nearby Connections API identifies devices:

  • Device ID: Identifies the device uniquely and remains the same even when the device is rebooted.
  • Endpoint ID: Identifies the device locally to a GoogleApiClient instance.

The endpoint ID is required to establish connections and send messages. When a user disconnects from the app, the system may assign a new endpoint ID upon reconnection. However, you can use the device ID for cases such as:
  • Identifying the user and restoring their appropriate data.
  • Reconnecting to a host that the client has previously connected to during discovery (for example, reconnecting to your Android TV to continue any saved progress the following day).
  • Trying to reconnect to a host after losing connectivity with it (for example, returning to an app activity after receiving a phone call).

Completing a connection

Once your app discovers another app that is advertising the requested service ID, you can initiate a connection between the devices. The following example shows you how to handle discovery of a device:
@Override
public void onEndpointFound(final String endpointId, String deviceId,
                            String serviceId, final String endpointName) {
  // This device is discovering endpoints and has located an advertiser.
  // Write your logic to initiate a connection with the device at
  // the endpoint ID
}

Note:
The onEndpointFound() method may be called multiple times if there are multiple devices advertising on the same network. In your app, you can implement a button that presents the user with a list of hosts where they can choose who to connect to.

For apps such as multiplayer games, it is suitable to present a list of hosts to connect to. Alternatively, you could also choose to immediately connect the player, such as when a device connects to Android TV.

Once the user initiates a connection attempt in your app, you can establish the connection as shown in the following example:
private void connectTo(String endpointId, final String endpointName) {
  // Send a connection request to a remote endpoint. By passing 'null' for
  // the name, the Nearby Connections API will construct a default name
  // based on device model such as 'LGE Nexus 5'.
  String myName = null;
  byte[] myPayload = null;
  Nearby.Connections.sendConnectionRequest(mGoogleApiClient, myName,
    remoteEndpointId, myPayload, new Connections.ConnectionResponseCallback() {
      @Override
      public void onConnectionResponse(String remoteEndpointId, Status status,
                                       byte[] bytes) {
        if (status.isSuccess()) {
          // Successful connection
        } else {
          // Failed connection
        }
      }
    }, this);
}
On the host side, you need to handle connection requests from connecting devices. To track messages and send messages to connected devices, store the device endpoint IDs in a data construct of your choosing. Override the onConnectionRequest() method in your activity to handle the requests. The following code snippet shows how you might handle connection requests from devices:
@Override
public void onConnectionRequest(final String remoteEndpointId, String remoteDeviceId,
                                String remoteEndpointName, byte[] payload) {
  if (mIsHost) {
    byte[] myPayload = null;
    // Automatically accept all requests
    Nearby.Connections.acceptConnectionRequest(mGoogleApiClient, remoteEndpointId,
                    myPayload, this).setResultCallback(new ResultCallback<Status>() {
      @Override
        public void onResult(Status status) {
          if (status.isSuccess()) {
            Toast.makeText(mContext, "Connected to " + remoteEndpointName,
                           Toast.LENGTH_SHORT).show();
            } else {
              Toast.makeText(mContext, "Failed to connect to: " + remoteEndpointName,
                             Toast.LENGTH_SHORT).show();
            }
          }
        });
  } else {
    // Clients should not be advertising and will reject all connection requests.
    Nearby.Connections.rejectConnectionRequest(mGoogleApiClient, remoteEndpointId);
  }
}
In the example above, all connections are automatically accepted. Depending on your app setup, you may wish to limit the amount of connections if you have a max user limit. To prevent subsequent connection attempts from devices, call rejectConnectionRequest(). Once the main app activity is running , you can stop advertising your device by calling stopAdvertising().

Note:
The stopAdvertising() method does not prevent an app from sending or receiving messages to and from remote endpoints. In addition, the host device can still receive connection requests from client devices if the client device has stored the host endpoint ID.

Handling lost connections

During the nearby device discovery process, users may encounter disruptions in their network connection. For example, a user might move out of range of the WiFi network. If your app presents a list of hosts to the user, you can override the onEndpointLost() method to simply remove that user from the list when the system detects a lost connection.

Keeping the device screen awake

Because input events are not explicitly required, connected devices may enter sleep mode. As a best practice, call the following code in your activity to keep the device awake:
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
For more information, see Keeping the Device Awake.

Identifying a client device

When a device is connected to the host, it may send messages to other client devices. There are cases where you need to know the endpoint or device ID of the target device. For example, if device X is sending an instruction to device Y, the app would send the message from device X to the host and pass device X’s endpoint ID so that device Y will know where the instruction originated. Use getLocalEndpointId() or getLocalDeviceId() to retrieve this data.

Disconnecting nearby connections

Whenever a user quits the app, call disconnectFromEndpoint(). Your app can also use this method to force a user to quit. The following snippet shows how to disconnect a connection:
Nearby.Connections.disconnectFromEndpoint(mGoogleApiClient, remoteEndpointId);
To stop advertising and disconnect all remote endpoints, call stopAllEndpoints(). You can also call this method from a client device to stop discovery or disconnect from the host. The following snippet shows how to call stopAllEndpoints():
Nearby.Connections.stopAllEndpoints(mGoogleApiClient);
When the Google API client is disconnected on a device, calling disconnectFromEndpoint() or stopAllEndpoints() triggers the onDisconnected() callback method on the device it was connected to. Override this method to gracefully handle disconnection scenarios.

Reconnecting nearby connections

Users can lose connectivity for many reasons (for example network interruptions, or walking outside of the WiFi network range). Depending on your app design, you may want to reconnect a disconnected user while the activity is still ongoing, or pause and restart the activity when the user attempts to reconnect.

To enable your app to reconnect devices, store the device ID and endpoint ID for each user. When storing user data such as a position or score, identify the user by device ID so that the data can be restored after a disconnection even if the endpoint ID changes.

If the host is still advertising, the user can reconnect using the service ID. However, if you disable advertising in your app while the activity is ongoing, you should implement logic to store the endpoint ID so that you can call sendConnectionRequest() to reconnect the user.

Send messages

Once connections are established between devices, they can send and receive messages to update app state and transfer input, data, or events from one device to another. Hosts can send messages to any number of clients, and clients can send messages to the host. If a client needs to communicate with other clients, your app can send a message to the host to relay the information to the recipient client.

Warning: Messages sent through the Nearby Connections API are not encrypted. Do not send sensitive data through this API.

Sending a message

To send a message, call sendReliableMessage() and pass in the appropriate endpoint ID. The payload parameter is a byte array of up to Connections.MAX_RELIABLE_MESSAGE_LEN bytes long that holds your message data. Reliable messages are guaranteed to be received in the order they were sent, and the system retries sending the message until the connection ends.
The following code snippet shows how you can send reliable messages from one device to another:
Nearby.Connections.sendReliableMessage(mGoogleApiClient, remoteEndpointId, payload);
You can also send messages of up to Connections.MAX_UNRELIABLE_MESSAGE_LEN bytes long by calling sendUnreliableMessage(). This method allows the system to deliver messages quickly, but the messages may be lost or sent out of order. Unreliable messaging is suitable if you want to show an opposing player’s character position on a map, or for frequent and less important game notifications.

Receiving a message

After a message is sent, the system receives the message through onMessageReceived(). Based on the requirements of your app, you can override this method to update information such as player scores, on-screen drawings, or the current player's turn.

The following code snippet shows how to override this method to handle received messages:
@Override
public void onMessageReceived(String endpointId, byte[] payload, boolean isReliable) {
    // Implement parsing logic to process message
}

Download Sample Project

No comments:

Post a Comment