original source : https://developer.android.com/training/wearables/data-layer/messages.html

Sending and Receiving Messages

You send messages using the MessageApi and attach the following items to the message:

  • An arbitrary payload (optional)
  • A path that uniquely identifies the message’s action

syncing 은 양방향이라면 message는 일방이며 

remote procedure calls (RPC)에 유용하다. 예를 들어 sending a message to the wearable to start an activity.

Multiple wearable devices can be connected to a user’s handheld device. Each connected device in the network is considered a node. 그러므로 message를 보내는 경우 목적지를 명확하게 해야 한다. google play 7.3 이전에는 하나의 mobile에 하나의 wearable기기만 연결할수 있으나 그 이후 버전에는 여러개를 연결해서 사용가능하므로 새로운 기능에대한 업데이트를 잘못하면 제대로 전달되지 않는 문제가 발생할수 있다.

Send a Message

       

Advertise capabilities

To launch an activity on a handheld device from a wearable device, use the MessageApi class to send the request.

the wearable app needs to determine that a connected node is capable of launching the activity. wearable app에서 작업을 수행할수 있는지 확인 하는 과정에서 작업을 수행할수 있는 기기의 app에서는 wearable app에게 본기기가 작업수행이 가능하다는 것을 알려야 하는 데 그방법은 아래와 같다.

  1. Create an XML configuration file in the res/values/ directory of your project and name it wear.xml.
  2. Add a resource named android_wear_capabilities to wear.xml.
  3. Define capabilities that the device provides.

Note: Capabilities are custom strings that you define and must be unique within your app.

<resources>
   <string-array name="android_wear_capabilities">
       <item>voice_transcription</item>
   </string-array>
</resources>

        Retrieve the nodes with the required capabilities (어느 app이 작업을 처리가능한지 확인하는 과정)

CapabilityApi.getCapability() method 를 이용한다.

private static final String
       VOICE_TRANSCRIPTION_CAPABILITY_NAME = "voice_transcription";

private GoogleApiClient mGoogleApiClient;

...

private void setupVoiceTranscription() {
   CapabilityApi.GetCapabilityResult result =
           Wearable.CapabilityApi.getCapability(
                   mGoogleApiClient, VOICE_TRANSCRIPTION_CAPABILITY_NAME,
                   CapabilityApi.FILTER_REACHABLE).await();

   updateTranscriptionCapability(result.getCapability());
}

To detect capable nodes as they connect to the wearable device, register a CapabilityApi.CapabilityListener() instance to your GoogleApiClient.

private void setupVoiceTranscription() {
   ...

   CapabilityApi.CapabilityListener capabilityListener =
           new CapabilityApi.CapabilityListener() {
               @Override
               public void onCapabilityChanged(CapabilityInfo capabilityInfo) {
                   updateTranscriptionCapability(capabilityInfo);
               }
           };

   Wearable.CapabilityApi.addCapabilityListener(
           mGoogleApiClient,
           capabilityListener,
           VOICE_TRANSCRIPTION_CAPABILITY_NAME);
}

Note: If you create a service that extends WearableListenerService to detect capability changes, you may want to override the onConnectedNodes()method to listen to finer-grained connectivity details, such as when a wearable device switches from Wi-Fi to a Bluetooth connection to the handset. For more information on how to listen for important events, see Listen for Data Layer Events.

After detecting the capable nodes, determine where to send the message. You should pick a node that is in close proximity to your wearable device to minimize message routing through multiple nodes. A nearby node is defined as one that is directly connected to the device. To determine if a node is nearby, call the Node.isNearby() method.

private String transcriptionNodeId = null;

private void updateTranscriptionCapability(CapabilityInfo capabilityInfo) {
   Set<Node> connectedNodes = capabilityInfo.getNodes();

   transcriptionNodeId = pickBestNodeId(connectedNodes);
}

private String pickBestNodeId(Set<Node> nodes) {
   String bestNodeId = null;
   // Find a nearby node or pick one arbitrarily
   for (Node node : nodes) {
       if (node.isNearby()) {
           return node.getId();
        }
        bestNodeId = node.getId();
   }
   return bestNodeId;
}

       Deliver the message

Once you’ve identified the best node to use, send the message using the MessageApi class.

Verify that the node is available before you attempt to send the message. This call is synchronous and blocks processing until the system queues the message for delivery.

Note: A successful result code does not guarantee delivery of the message. If your app requires data reliability, consider using DataItem objects or the ChannelApi class to send data between devices.

public static final String VOICE_TRANSCRIPTION_MESSAGE_PATH = "/voice_transcription";

private void requestTranscription(byte[] voiceData) {
   if (transcriptionNodeId != null) {
       Wearable.MessageApi.sendMessage(googleApiClient, transcriptionNodeId,
           VOICE_TRANSCRIPTION_MESSAGE_PATH, voiceData).setResultCallback(
                 new ResultCallback() {
                     @Override
                     public void onResult(SendMessageResult sendMessageResult) {
                         if (!sendMessageResult.getStatus().isSuccess()) {
                             // Failed to send message
                         }
                     }
                 }
           );
   } else {
       // Unable to retrieve node with transcription capability
   }
}

Note: To learn more about asynchronous and synchronous calls to Google Play services and when to use each, see Communicate with Google Play Services.

You can also broadcast messages to all connected nodes.

private Collection<String> getNodes() {
   HashSet <String>results = new HashSet<String>();
   NodeApi.GetConnectedNodesResult nodes =
           Wearable.NodeApi.getConnectedNodes(mGoogleApiClient).await();
   for (Node node : nodes.getNodes()) {
       results.add(node.getId());
   }
   return results;
}

Receive a Message

To be notified of received messages, implement the MessageListener interface to provide a listener for message events. Then, register the listener with the MessageApi.addListener() method.

@Override
public void onMessageReceived(MessageEvent messageEvent) {
   if (messageEvent.getPath().equals(VOICE_TRANSCRIPTION_MESSAGE_PATH)) {
       Intent startIntent = new Intent(this, MainActivity.class);
       startIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
       startIntent.putExtra("VOICE_DATA", messageEvent.getData());
       startActivity(startIntent);
   }
}

app server 를 통해 FCM을 이용하는 경우 단 한번에 모든 사용장에게 notification을 보낼 방법은 없다. 각각의 기기들에게 (token을 이용)보내거나 topic 별로 보낼수 있다. ref) https://stackoverflow.com/q/38237559

New Firebase token is generated (onTokenRefresh() is called) when:

  • The app deletes Instance ID
  • The app is restored on a new device
  • The user uninstalls/reinstall the app
  • The user clears app data.

ref) https://stackoverflow.com/a/38727643

notification을 통해 전달된 data를 다른 activity로 전달해서 처리하는 경우

(notification을 클릭했을때 다른 activity에서 처리하게하는 방법)

app server(fcm server 로 data를 보내고 결과를 받아낼) 설정및 사용방법    ref) https://firebase.google.com/docs/cloud-messaging/server

 http을 이용 FCM과 커뮤니케이션하는 경우 전달 되어지는 data형태 ref) https://firebase.google.com/docs/cloud-messaging/http-server-ref

FCM사용의 기본 ref) https://firebase.google.com/docs/cloud-messaging/concept-options

firebase UI 를 통하는 경우

  • backgound시에는 무조건 onMessageReceived()를 bypass한다. 어드밴스항목을 통해 정보를 넣어도 bypass한다.(notification을 클릭하면 무조건 launcher로 이동)
  • foreground시에는 어드밴스 항목에서작성한 항목은 data payload로 받아진다.
  • foreground시에서 받아진 data payload는 사용자가 앱에서 background로 이동한 다음 notification을 클릭해도 foreground에서 받은것처럼 유지 행동하게 된다.
  • 어드밴스항목을 사용하지 않고 작성하는 경우는 foreground에서는

    onMessageReceived() 를 거치며 data payload는 없는 것으로 처리된다.

App server를 이용하는 경우

data = { “notification”:
               {“title”: “test title from app server”,
                   "body": “test messge from app server”},

           "to" : “asdfghjkl……”
           }  

위와같이 notification방식으로하는 경우 foreground일때는 

onMessageReceived() 를 거친다. background의 경우 bypass 한다. 그리고 notification을 클릭하면 launcher로 이동

data = { “notification”:
              {“title”: “test title from app server”,
                  “body”: “test messge from app server”, “click_action”:”액션이름”},

          “to” : “asdfghjkl……”
          }

위와같이 action name을 지정해 주고<intent-filter> 와 그안의 <action android:name>을 이용 notification을 사용자가 클릭하면 특정 activity로 이동하게 할수 있지만 title, body값에 접근 할수 없다. click_action은 data전달이 필요없는 작업을 수행할때만 사용가능.

사용자가 foreground, background 든 어느쪽에서든 받은 notification 클릭시 내가 원하는 방향으로 이동하게 하기위해서는 app server에서 data payload를 넣어서 작성해야한다. 즉 내가 단순한 message (notification)만을 전달하더라도app server datapayload 방식을 택해야 사용자가 클릭하고 app안으로 이동 읽을수 있게 된다.

How do you send a Firebase Notification to all devices via CURL?

Receive Messages in an Android App  |  Firebase

Notification Icon with the new Firebase Cloud Messaging system – Stack Overflow

How to handle notification when app in background in Firebase – Stack Overflow