Skip to content

Instantly share code, notes, and snippets.

@gabrielemariotti
Last active September 15, 2020 11:33
Show Gist options
  • Star 38 You must be signed in to star a gist
  • Fork 5 You must be signed in to fork a gist
  • Save gabrielemariotti/117b05aad4db251f7534 to your computer and use it in GitHub Desktop.
Save gabrielemariotti/117b05aad4db251f7534 to your computer and use it in GitHub Desktop.
Android Wear: small gist to start an Activity on the mobile handheld from the Android Wear device.
<service android:name=".ListenerServiceFromWear">
<intent-filter>
<action android:name="com.google.android.gms.wearable.BIND_LISTENER" />
</intent-filter>
</service>
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
wearApp project(':wear')
compile 'com.google.android.gms:play-services-wearable:+'
}
public class ListenerServiceFromWear extends WearableListenerService {
private static final String HELLO_WORLD_WEAR_PATH = "/hello-world-wear";
@Override
public void onMessageReceived(MessageEvent messageEvent) {
/*
* Receive the message from wear
*/
if (messageEvent.getPath().equals(HELLO_WORLD_WEAR_PATH)) {
Intent startIntent = new Intent(this, MyActivity.class);
startIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(startIntent);
}
}
}
<meta-data android:name="com.google.android.gms.version" android:value="@integer/google_play_services_version" />
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile "com.google.android.support:wearable:1.0.+"
compile 'com.google.android.gms:play-services-wearable:+'
}
public class LaunchActivity extends Activity implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener {
Node mNode; // the connected device to send the message to
GoogleApiClient mGoogleApiClient;
private static final String HELLO_WORLD_WEAR_PATH = "/hello-world-wear";
private boolean mResolvingError=false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_launch);
//Connect the GoogleApiClient
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addApi(Wearable.API)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.build();
//UI elements with a simple CircleImageView
final WatchViewStub stub = (WatchViewStub) findViewById(R.id.watch_view_stub);
stub.setOnLayoutInflatedListener(new WatchViewStub.OnLayoutInflatedListener() {
@Override
public void onLayoutInflated(WatchViewStub stub) {
CircledImageView mCircledImageView = (CircledImageView) stub.findViewById(R.id.circle);
//Listener to send the message (it is just an example)
mCircledImageView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
sendMessage();
}
});
}
});
}
/**
* Send message to mobile handheld
*/
private void sendMessage() {
if (mNode != null && mGoogleApiClient!=null && mGoogleApiClient.isConnected()) {
Wearable.MessageApi.sendMessage(
mGoogleApiClient, mNode.getId(), HELLO_WORLD_WEAR_PATH, null).setResultCallback(
new ResultCallback<MessageApi.SendMessageResult>() {
@Override
public void onResult(MessageApi.SendMessageResult sendMessageResult) {
if (!sendMessageResult.getStatus().isSuccess()) {
Log.e("TAG", "Failed to send message with status code: "
+ sendMessageResult.getStatus().getStatusCode());
}
}
}
);
}else{
//Improve your code
}
}
@Override
protected void onStart() {
super.onStart();
if (!mResolvingError) {
mGoogleApiClient.connect();
}
}
/*
* Resolve the node = the connected device to send the message to
*/
private void resolveNode() {
Wearable.NodeApi.getConnectedNodes(mGoogleApiClient).setResultCallback(new ResultCallback<NodeApi.GetConnectedNodesResult>() {
@Override
public void onResult(NodeApi.GetConnectedNodesResult nodes) {
for (Node node : nodes.getNodes()) {
mNode = node;
}
}
});
}
@Override
public void onConnected(Bundle bundle) {
resolveNode();
}
@Override
public void onConnectionSuspended(int i) {
//Improve your code
}
@Override
public void onConnectionFailed(ConnectionResult connectionResult) {
//Improve your code
}
}
@reisepass
Copy link

Nevermind my last comment, I got it working 😄

@joshardt
Copy link

In line 82 of wear-LaunchActivity.java you set your Node member variable to the last node that is inside the list of all connected Nodes. There should be not more than one connected device with the wear, so this seems correct. However, if you test this code it shows that there is also a "Cloud" Node connected. So what you need is to check, if the Node is nearby with node.isNearby(). Then you can break the loop.

In your sendMessage method, you don't need to check, if mGoogleApiClient is null because you set it in your onCreate method. Therefore mGoogleApiClient != null is always true. Furthermore, what I did in my implementation is that I call Wearable.NodeApi.getConnectedNodes every time when I want to send a message. So only if there are currently connected nodes that are also nearby, a message will be sent.

I am also not using the MessageApi to send messages between the devices but the ChannelApi. I do this because in the MessageApi documentation is written:

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

I came here to see how I can start activities from the wear device on the handheld device. Your solution makes sense. So thanks for that 👍

Btw, this is my implementation of the ChannelApi:

/**
 * Send a message to a connected and nearby device.
 *
 * @param message The text to send to the connected device.
 * @param path The path to identify the message on the receivers side.
 */
private void sendMessageToDevice(final String message, final String path) {
    Log.d(TAG, "sendMessageToDevice");

    Wearable.NodeApi.getConnectedNodes(googleApiClient).setResultCallback(new ResultCallback<NodeApi.GetConnectedNodesResult>() {
        @Override
        public void onResult(final GetConnectedNodesResult getConnectedNodesResult) {
            Log.d(TAG, "sendMessageToDevice: onResult");

            final List<Node> nodes = getConnectedNodesResult.getNodes();
            for (final Node node : nodes) {
                if (node.isNearby()) {
                    Wearable.ChannelApi.openChannel(googleApiClient, node.getId(), path).setResultCallback(new ResultCallback<ChannelApi.OpenChannelResult>() {
                        @Override
                        public void onResult(ChannelApi.OpenChannelResult openChannelResult) {
                            Log.d(TAG, "sendMessageToDevice: onResult: onResult");
                            final Channel channel = openChannelResult.getChannel();
                            channel.getOutputStream(googleApiClient).setResultCallback(new ResultCallback<Channel.GetOutputStreamResult>() {
                                @Override
                                public void onResult(final Channel.GetOutputStreamResult getOutputStreamResult) {
                                    Log.d(TAG, "sendMessageToDevice: onResult: onResult: onResult");

                                    OutputStream outputStream = null;
                                    try {
                                        outputStream = getOutputStreamResult.getOutputStream();
                                        outputStream.write(message.getBytes());
                                        Log.d(TAG, "sendMessageToDevice: onResult: onResult: onResult: Message sent: " + message);
                                    } catch (final IOException ioexception) {
                                        Log.w(TAG, "sendMessageToDevice: onResult: onResult: onResult: Could not send message from smartwatch to given node.\n" +
                                                "Node ID: " + channel.getNodeId() + "\n" +
                                                "Path: " + channel.getPath() + "\n" +
                                                "Error message: " + ioexception.getMessage() + "\n" +
                                                "Error cause: " + ioexception.getCause());
                                    } finally {
                                        try {
                                            if (outputStream != null) {
                                                outputStream.close();
                                            }
                                        } catch (final IOException ioexception) {
                                            Log.w(TAG, "sendMessageToDevice: onResult: onResult: onResult: Could not close Output Stream from smartwatch to given node.\n" +
                                                    "Node ID: " + channel.getNodeId() + "\n" +
                                                    "Path: " + channel.getPath() + "\n" +
                                                    "Error message: " + ioexception.getMessage() + "\n" +
                                                    "Error cause: " + ioexception.getCause());
                                        } finally {
                                            // Will call onChannelClosed
                                            channel.close(googleApiClient);
                                        }
                                    }
                                }
                            });
                        }
                    });
                    break;
                }
            }
        }
    });
}

Hope it can help anyone :-)

EDIT:
This inspired me to make my own gist to demonstrate the ChannelApi.

@mitchross
Copy link

I got burned for a week trying to get this to work only to find out the wear app and mobile app in android studio need to have THE SAME EXACT PACKAGE NAME or the messages won't get received. Great gist!

@BaN4NaJ0e
Copy link

Thank you for this nice example. Works like a charm!

But please keep in mind: The Manifest IntentFilter BIND_LISTENER is now deprecated. You should not use:
<action android:name="com.google.android.gms.wearable.BIND_LISTENER" />
anymore.
More informations here: http://android-developers.blogspot.de/2016/04/deprecation-of-bindlistener.html

The service declaration in the Manifest should look like this now:

        <service android:name=".ListenerServiceFromWear">
            <intent-filter>
                <action android:name="com.google.android.gms.wearable.MESSAGE_RECEIVED" />
                <data android:scheme="wear" android:host="*"
                    android:path="/hello-world-wear" />
            </intent-filter>
        </service>

@khushwinder
Copy link

This is the only working app currently on google which call handheld activity from wearable. Thanks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment