{{announcement.body}}
{{announcement.title}}

Messaging Text Through HMS Nearby Connection

DZone 's Guide to

Messaging Text Through HMS Nearby Connection

Nearby Connection is an API of HUAWEI Nearby Service that lets applications to easily discover and connect to nearby devices and exchange data without Internet.

· Open Source Zone ·
Free Resource

Introduction to Nearby Connection

Nearby Connection is one of the API of HUAWEI Nearby Service that allows applications to easily discover and connect to nearby devices, and exchange data with nearby devices without having Internet.

Example: We can chat with a friend and can share files such as photos and videos without connecting to the Internet.

Prerequisites:

  • Java JDK (1.8 or later)
  • Android (API level 21 or higher)
  • HMS Core (APK) 4.0.0.300 or later
  • Android Studio 3.X
  • Two Huawei phones (with USB cables) or Non Huawei Phone with updated version of HMS Core App for app running and debugging.

Integration

  1. Create a project in android studio and Huawei AGC.
  2. Provide the SHA-256 Key in App Information Section.
  3. Enable Huawei Nearby Service.
  4. Download the agconnect-services.json from AGCand save into app directory.
  5. In root build.gradle  

Navigate to allprojects->repositories and buildscript->repositories and add the given line.

1

maven { url 'http://developer.huawei.com/repo/' }

In dependency add class path.

1

classpath 'com.huawei.agconnect:agcp:1.2.1.301'

6. In app build.gradle

Add compatibility mode to JDK 1.8 

1

2

3

4

compileOptions {

    sourceCompatibility = 1.8

    targetCompatibility = 1.8

}

Add Implementation

1

implementation 'com.huawei.hms:nearby:5.0.1.300'

Apply plugin

1

apply plugin: 'com.huawei.agconnect'

7. Permissions in Manifest

1

2

3

4

5

6

<uses-permission android:name="android.permission.BLUETOOTH" />

<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />

<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />

<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />

<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />


Nearby Connection Process

Nearby connection process can be divided into four phases.

1.     Broadcast and scanning

2.     Connection setup

3.     Data transmission

4.     Disconnection

Let us go to the details.

1) Broadcast and Scanning

This is the initial phase, here the broadcaster starts broadcasting, and the discoverer starts scanning to discover the broadcaster.

We can classify the functionalities of the initial phase as given:

a) Starting Broadcasting:

The broadcaster calls startBroadcasting() to start broadcasting

1

2

3

4

5

public void doStartBroadcasting() {

    BroadcastOption.Builder advBuilder = new BroadcastOption.Builder();

    advBuilder.setPolicy(Policy.POLICY_STAR);

    discoveryEngine.startBroadcasting(myNameStr, myServiceId, connectCallback, advBuilder.build());

}

b) Starting Scanning:

The discoverer calls startScan() to start scanning to discover nearby devices.

1

2

3

4

5

public void doStartScan() {

    ScanOption.Builder discBuilder = new ScanOption.Builder();

    discBuilder.setPolicy(Policy.POLICY_STAR);

    discoveryEngine.startScan(myServiceId, scanEndpointCallback, discBuilder.build());

}

c) Notifies the scanning result:

The scanning listening callback class instance notifies the discoverer of the scanning result. For example, a device is discovered or a device is lost, onFound()  and  onLost() methods are used here for the purpose.

1

2

3

4

5

6

7

8

9

10

11

12

13

private ScanEndpointCallback scanEndpointCallback =

        new ScanEndpointCallback() {

            @Override

            public void onFound(String endpointId, ScanEndpointInfo discoveryEndpointInfo) {

                mEndpointId = endpointId;

                discoveryEngine.requestConnect(myNameStr, mEndpointId, connectCallback);

            }

 

            @Override

            public void onLost(String endpointId) {

                Log.d(TAG, "Nearby Connection Demo app: Lost endpoint: " + endpointId);

            }

        };

d) Stopping Broadcasting:

To stop broadcasting, we can call stopBroadcasting() method. The broadcaster cannot receive a connection request from the discoverer afterward.

1

discoveryEngine.stopBroadcasting();

e) Stopping Scanning:

To stop scanning, we can call stopScan() method. Once you find the device you want to connect to, call stopScan() to stop scanning.

1

discoveryEngine.stopScan();


2) Connection Setup

In this phase the discoverer initiates a connection and starts a symmetric authentication process, and the two endpoints independently accept or reject the connection request.

We can classify the functionalities of the second phase as given:

a) Initiating a Connection:

When nearby devices are found, the discoverer can call requestConnect() to initiate connections request to the broadcaster.

1

discoveryEngine.requestConnect(myNameStr, mEndpointId, connectCallback);

b) Confirming the Connection:

After the discoverer requests to set up a connection with the broadcaster, the discoverer notifies the two parties of the connection setup by calling back the onEstablish() method of connectCallback. Both parties must accept the connection by calling acceptConnect() or reject the connection by calling rejectConnect(). The connection can be set up only when both parties accept the connection. If one or both of them choose to reject the connection, the connection fails. In either mode, the connection result is notified through the onResult() method. 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

private ConnectCallback connectCallback =

        new ConnectCallback() {

            @Override

            public void onEstablish(String endpointId, ConnectInfo connectionInfo) {

                transferEngine = Nearby.getTransferEngine(getApplicationContext());

                discoveryEngine.acceptConnect(endpointId, dataCallback);

                ToastUtil.showShortToastTop(getApplicationContext(), "Let's chat!");

                sendBtn.setEnabled(true);

                msgEt.setEnabled(true);         

                connectBtn.setEnabled(false);

                connectTaskResult = StatusCode.STATUS_SUCCESS;

                if (myNameStr.compareTo(friendNameStr) > 0) {

                    discoveryEngine.stopScan();

                else {

                    discoveryEngine.stopBroadcasting();

                }

            }

 

            @Override

            public void onResult(String endpointId, ConnectResult resolution) {

                mEndpointId = endpointId;

            }

 

            @Override

            public void onDisconnected(String endpointId) {

                ToastUtil.showShortToastTop(getApplicationContext(), "Disconnect.");

                connectTaskResult = StatusCode.STATUS_NOT_CONNECTED;

                sendBtn.setEnabled(false);

                connectBtn.setEnabled(true);

                msgEt.setEnabled(false);

                myNameEt.setEnabled(true);

                friendNameEt.setEnabled(true);              

            }

        };


3) Data Transmission

After the connection is set up, the two endpoints start data exchange in this phase.

We can classify the functionalities of the third phase as given:

a) Sending data:

After the connection is set up, both endpoints can call sendData() to send data to the remote endpoint.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

private void sendMessage() {

    msgStr = msgEt.getText().toString() ;

    Data data = Data.fromBytes(msgStr.getBytes(Charset.defaultCharset()));

    Log.d(TAG, "myEndpointId " + mEndpointId);

    transferEngine.sendData(mEndpointId, data).addOnCompleteListener(task -> {

        task.addOnSuccessListener(su -> {

            Log.i(TAG, "sendData success. Message:" + msgStr);

        }).addOnFailureListener(e -> {

            Log.e(TAG, "sendData failed, Message:" + msgStr + "cause: " + e.getMessage());

        });

    });

    MessageBean item = new MessageBean();

    item.setMyName(myNameStr);

    item.setFriendName(friendNameStr);

    item.setMsg(msgStr.split(":")[0]);

    item.setType(MessageBean.TYPE_SEND_TEXT);

    msgList.add(item);

    adapter.notifyDataSetChanged();

    msgEt.setText("");

    messageListView.setSelection(messageListView.getBottom());

}

b) Receiving data:

The data subscriber is notified by onReceived() that the data is received.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

@Override

public void onReceived(String string, Data data) {

    Log.d(TAG, "onPayloadReceived, payload.getType() = " + data.getType());

    Log.d(TAG, "onPayloadReceived, string=" + string);

    switch (data.getType()) {

        case Data.Type.BYTES:

            msgStr = new String(data.asBytes(), UTF_8);

            Log.d(TAG, "onReceived success. msgSt: " + msgStr);

            MessageBean item = new MessageBean();

            item.setMyName(myNameStr);

            item.setFriendName(friendNameStr);

            item.setMsg(msgStr);

            item.setType(MessageBean.TYPE_RECEIVE_TEXT);

            msgList.add(item);

            adapter.notifyDataSetChanged();

            messageListView.setSelection(messageListView.getBottom());                           

            break;

        default:

            Log.i(TAG, "the other Unknown data type.");

            return;

    }

c) Updating the Progress:

The onTransferUpdate() method provides the data sending or receiving progress update. 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

@Override

public void onTransferUpdate(String string, TransferStateUpdate update) {

 

    long payloadId = update.getDataId();

    Log.d(TAG, "onTransferUpdate.payloadId:" + payloadId);

    if (update.getStatus() == TransferStateUpdate.Status.TRANSFER_STATE_SUCCESS) {

        Log.d(TAG, "PayloadTransferUpdate.Status.SUCCESS");

        Data payload = incomingFilePayloads.remove(payloadId);

 

        if (payload != null) {

 

            Log.d(TAG, "payload.getType() " + payload.getType());

            completedFilePayloads.put(payloadId, payload);

        }

    else {

        Log.d(TAG, "PayloadTransferUpdate.Status=" + update.getStatus());

    }

}

d) Cancelling Transmission:

To cancel the transmission during data receiving or sending, we can call the cancelDataTransfer() method of the TransferEngine class. 


4) Disconnection

This is final phase, here we can call the disconnect() method of the DiscoveryEngine class to disconnect from the remote endpoint.

1

discoveryEngine.disconnectAll();

The remote endpoint is notified by onDisconnected() of the disconnection. Once this API is called, this endpoint cannot send or receive data.

1

2

3

4

5

6

7

8

9

10

@Override

public void onDisconnected(String endpointId) {

    ToastUtil.showShortToastTop(getApplicationContext(), "Disconnect.");

    connectTaskResult = StatusCode.STATUS_NOT_CONNECTED;

    sendBtn.setEnabled(false);

    connectBtn.setEnabled(true);

    msgEt.setEnabled(false);

    myNameEt.setEnabled(true);

    friendNameEt.setEnabled(true);   

}


Reference:

https://developer.huawei.com/consumer/en/doc/development/HMS-Guides/Nearby-Service-Nearby-Connection-Service-Nearby-Connection

Topics:
android, application, integration, mobile, opensource

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}