24

I am creating an android wear app that extends push notifications. My app downloads approximately 10 images from a server when a push notification comes in and displays these additional images on the watch. These images are specific to the android wear app and are not shown on the handheld device.

How do I tell if the handheld device is paired with an android wear device so that I can determine if it is necessary to download the additional images required for the wear app?

Thanks!

TWilly
  • 4,863
  • 3
  • 43
  • 73
  • I'd just check if Android Wear is installed and then download those assets. If Android Wear is installed, but no watch is paired, there's a good chance it'll be paired in the future. – 323go Jul 22 '14 at 00:56
  • @323go How do you tell if android wear is installed or not? – TWilly Aug 11 '14 at 20:38
  • Through [PackageManager](http://developer.android.com/reference/android/content/pm/PackageManager.html) like all other apps. – 323go Aug 12 '14 at 11:06
  • Here's a full sample for how to check it: stackoverflow.com/a/39513489/878126 – android developer Sep 16 '16 at 18:33

6 Answers6

24

There were already 2 options listed here. They are both valid depending on your use case. I's like to add a 3rd option however incomplete.

Option 1 : find connected nodes using NodeApi

The NodeApi class has a method for retrieving connected nodes. With this you're sure that the user didn't just had a watch in the past or tested one sometime. He really has a watch nearby and connected.

Drawback of this approach is that you will get no results if bluetooth is not enabled or the watch is not connected at the moment.

The method is:

List<Node> connectedNodes =
Wearable.NodeApi.getConnectedNodes(mGoogleApiClient).await().getNodes();

A more complete code example looks like this:

private GoogleApiClient client;
private static final long CONNECTION_TIME_OUT_MS = 1000;

public void checkIfWearableConnected() {

    retrieveDeviceNode(new Callback() {
        @Override
        public void success(String nodeId) {
            Toast.makeText(this, "There was at least one wearable found", Toast.LENGTH_SHORT).show();
        }

        @Override
        public void failed(String message) {
            Toast.makeText(this, "There are no wearables found", Toast.LENGTH_SHORT).show();
        }
    });

}

private GoogleApiClient getGoogleApiClient(Context context) {
        if (client == null)
            client = new GoogleApiClient.Builder(context)
                    .addApi(Wearable.API)
                    .build();
        return client;
    }

private interface Callback {
        public void success(final String nodeId);
        public void failed(final String message);
    }

private void retrieveDeviceNode(final Callback callback) {
        final GoogleApiClient client = getGoogleApiClient(this);
        new Thread(new Runnable() {

            @Override
            public void run() {
                client.blockingConnect(CONNECTION_TIME_OUT_MS, TimeUnit.MILLISECONDS);
                NodeApi.GetConnectedNodesResult result =
                        Wearable.NodeApi.getConnectedNodes(client).await();
                List<Node> nodes = result.getNodes();
                if (nodes.size() > 0) {
                    String nodeId = nodes.get(0).getId();
                    callback.success(nodeId);
                } else {
                    callback.failed("no wearables found");
                }
                client.disconnect();
            }
        }).start();
    }

Option 2 : check for the Android Wear App

And that is the advantage of the second option. If you get a watch the first thing you to do to get it connected is to install the Android Wear app on your handheld. So you can use the PackageManager to verify if this Android Wear app is installed.

Drawback here is that you could get the wear app installed without having a watch.

Code example:

try {
    getPackageManager().getPackageInfo("com.google.android.wearable.app", PackageManager.GET_META_DATA);

    Toast.makeText(this, "The Android Wear App is installed", Toast.LENGTH_SHORT).show();
} catch (PackageManager.NameNotFoundException e) {
    //android wear app is not installed
    Toast.makeText(this, "The Android Wear App is NOT installed", Toast.LENGTH_SHORT).show();
}

Option 3 : Check for paired devices

Not sure if this is possible but in some cases this would be the perfect solution since you can check that the user has a watch paired with his device while it doesn't has to be connected at the time.

Below is a code example, however this doesn't include checking if the paired devices are wearables or not. This is only a starting point.

Code example from: getbondeddevices() not returning paired bluetooth devices

BluetoothAdapter mBtAdapter = BluetoothAdapter.getDefaultAdapter();

Set<BluetoothDevice> pairedDevices = mBtAdapter.getBondedDevices();
if (pairedDevices.size() > 0) {
     Toast.makeText(this, "At least one paired bluetooth device found", Toast.LENGTH_SHORT).show();
    // TODO at this point you'd have to iterate these devices and check if any of them is a wearable (HOW?)
    for (BluetoothDevice device : pairedDevices) {
        Log.d("YOUR_TAG", "Paired device: "+ device.getName() + ", with address: " + device.getAddress());
    }
} else {
    Toast.makeText(this, "No paired bluetooth devices found", Toast.LENGTH_SHORT).show();
}

Note that this code requires bluetooth permissions in your manifest.

<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
Community
  • 1
  • 1
hcpl
  • 17,382
  • 7
  • 72
  • 73
  • 5
    Option 4 : Ask google WTH they made this so difficult to detect? – worked Mar 30 '15 at 20:53
  • Option 3 is what TWilly was looking for, I guess. So this should be the "accepted answer". – Christine May 06 '15 at 11:13
  • "Drawback of this approach is that you will get no results if bluetooth is not enabled or the watch is not connected at the moment." - Why is it a drawback? Isn't it what you really need? To check if the smartwatch is really connected? In which case it's wrong to use it? – android developer Sep 18 '16 at 06:44
  • @androiddeveloper true, it really depends on your use case – hcpl Sep 23 '16 at 09:55
  • "Option 2: check for the Android Wear App" Doesn't take into account any Samsung Gear devices, which use `com.samsung.android.app.watchmanager`. – Niels Feb 25 '19 at 13:11
  • NodeApi is now deprecated, use [NodeClient](https://developers.google.com/android/reference/com/google/android/gms/wearable/NodeClient) instead. In kotlin it would be something like `val nodes: List = Tasks.await(Wearable.getNodeClient(context).connectedNodes)` – James Allen Mar 08 '23 at 11:44
18

You need to use the NodeApi, in particular NodeApi.getConnectedNodes().

For example (from a background thread -- otherwise use setResultCallback() and not await()):

List<Node> connectedNodes =
    Wearable.NodeApi.getConnectedNodes(mGoogleApiClient).await().getNodes();

If the list of nodes returned contains at least one element, then there is a connected Android Wear device, otherwise there isn't.


Update: With the release of Google Play Services 7.3, support for multiple Android Wear devices connected simultaneously has been added. You can use the CapabilityApi to request nodes with specific capabilities.

matiash
  • 54,791
  • 16
  • 125
  • 154
  • Currently, will there ever be more than one node? – Nathan Schwermann Jul 25 '14 at 04:52
  • @schwiz I'm not sure, I guess if you were able to pair more than one device, then there would be multiple nodes (?) However I haven't tested this, since I only have one. :) – matiash Jul 25 '14 at 14:37
  • You can't connect more than one device currently. I learned this when I replaced the poorly designed Gear Live with a G Watch. – 323go Aug 12 '14 at 11:05
  • 1
    Google Glass counts as a node, so you could have a Wear device and a Glass connected at the same time. – Jarett Millard Oct 20 '14 at 00:48
  • 1
    "getConnectedNodes" detects connected nodes, not paired nodes, does it? – Christine May 05 '15 at 20:08
  • @matiash Thank you for pointing out NodeApi and CapabilityApi. NodeApi is working great, but I'm having some problems with CapabilityApi. I opened a separate question and would really appreciate any help: http://stackoverflow.com/q/31328971/1204377 – Anton Cherkashyn Jul 09 '15 at 22:00
  • NodeApi is now deprecated, use NodeClient instead. In kotlin it would be something like val nodes: List = Tasks.await(Wearable.getNodeClient(context).connectedNodes) – James Allen Mar 08 '23 at 11:45
7

Above all answers are correct if handheld device is connected only Android Watch. What if handheld device is connected to more than one device (multiwearables) like Android Watch, HealthBand, Beacon etc then above answers may not work.

The following sections show you how to advertise device nodes that can process activity requests, discover the nodes capable of fulfilling a requested need, and send messages to those nodes,

Advertise capabilities

To launch an activity on a handheld device from a wearable device, use the MessageApi class to send the request. Since multiple wearables can be connected to the handheld device, the wearable app needs to determine that a connected node is capable of launching the activity. In your handheld app, advertise that the node it runs on provides specific capabilities.

To advertise the capabilities of your handheld 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.

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

Retrieve the nodes with the required capabilities

Initially, you can detect the capable nodes by calling the CapabilityApi.getCapability() method. The following example shows how to manually retrieve the results of reachable nodes with the android_wear capability. Use following code in Wear module:

public abstract class BaseActivity extends Activity implements MessageApi.MessageListener, NodeApi.NodeListener,
    DataApi.DataListener, GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener {

private static final String
        SMART_WEAR_CAPABILITY_NAME = "android_wear";

protected GoogleApiClient mGoogleApiClient;
protected ArrayList<String> results;
private String TAG = "BaseActivity::Wear";

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    mGoogleApiClient = new GoogleApiClient.Builder(this)
            .addApi(Wearable.API)
            .addConnectionCallbacks(this)
            .addOnConnectionFailedListener(this)
            .build();
}

private Collection<String> getNodes() {
    results = new ArrayList<>();
    NodeApi.GetConnectedNodesResult nodes =
            Wearable.NodeApi.getConnectedNodes(mGoogleApiClient).await();

    for (Node node : nodes.getNodes()) {
        Log.d(TAG, node.getId());
        results.add(node.getId());
    }

    return results;
}

@Override
protected void onResume() {
    super.onResume();
    mGoogleApiClient.connect();
}

@Override
protected void onPause() {
    super.onPause();
    Wearable.MessageApi.removeListener(mGoogleApiClient, this);
    Wearable.NodeApi.removeListener(mGoogleApiClient, this);
    Wearable.DataApi.removeListener(mGoogleApiClient, this);
    mGoogleApiClient.disconnect();
}

@Override
public void onConnected(Bundle bundle) {
    Log.d(TAG, "onConnected(): Successfully connected to Google API client");
    Wearable.MessageApi.addListener(mGoogleApiClient, this);
    Wearable.DataApi.addListener(mGoogleApiClient, this);
    Wearable.NodeApi.addListener(mGoogleApiClient, this);
    results = new ArrayList<>();

    getNodeIdOfHandheldDevice();
}

@Override
public void onConnectionSuspended(int i) {
    Log.d(TAG, "onConnectionSuspended(): Connection to Google API client was suspended");
}

@Override
public void onConnectionFailed(ConnectionResult connectionResult) {
    Log.e(TAG, "onConnectionFailed(): Failed to connect, with result: " + connectionResult);
}

@Override
public void onPeerConnected(Node node) {
    Log.e(TAG, "onPeerConnected():");
}

@Override
public void onPeerDisconnected(Node node) {
    Log.e(TAG, "onPeerDisconnected():");
}

private void getNodeIdOfHandheldDevice() {
    Wearable.CapabilityApi.getCapability(
            mGoogleApiClient, SMART_WEAR_CAPABILITY_NAME,
            CapabilityApi.FILTER_REACHABLE).setResultCallback(
            new ResultCallback<CapabilityApi.GetCapabilityResult>() {
                @Override
                public void onResult(CapabilityApi.GetCapabilityResult result) {
                    if (result.getStatus().isSuccess()) {
                        updateFindMeCapability(result.getCapability());
                    } else {
                        Log.e(TAG,
                                "setOrUpdateNotification() Failed to get capabilities, "
                                        + "status: "
                                        + result.getStatus().getStatusMessage());
                    }
                }
            });
}

private void updateFindMeCapability(CapabilityInfo capabilityInfo) {
    Set<Node> connectedNodes = capabilityInfo.getNodes();
    if (connectedNodes.isEmpty()) {
        results.clear();
    } else {
        for (Node node : connectedNodes) {
            // we are only considering those nodes that are directly connected
            if (node.isNearby()) {
                results.add(node.getId());
            }
        }
    }
}

}

For more detail please check Android Dev

Umang Kothari
  • 3,674
  • 27
  • 36
  • I've tried to do that but there is something I don't understand : when I call the getCapability() method, I always have a result, even if I change the name of the capability. Wouldn't I be supposed to detect nothing if the name is wrong? – krakig Oct 22 '15 at 09:14
  • If your phone has only one connection(lets say Watch) then capability works normally, but If it has more than two connection (Watch + Health band + Beacons) then system check for suitable name and you will get that node. Please check http://developer.android.com/training/wearables/data-layer/messages.html – Umang Kothari Oct 23 '15 at 04:32
  • 1
    This should have been marked as a correct answer as this actually checks if the connected node is wear or glass or beacon. – Milan Jan 10 '16 at 03:40
2

I'm using a much simpler method that is suitable for my use case, check if the android wear app is installed:

    try {
        getPackageManager().getPackageInfo("com.google.android.wearable.app", PackageManager.GET_META_DATA);
    } catch (PackageManager.NameNotFoundException e) {
        //android wear app is not installed
    }
Oded Breiner
  • 28,523
  • 10
  • 105
  • 71
  • 2
    That's actually more helpful than using bluetoothmanager, and it saves you the permission for Bluetooth. – Christine May 06 '15 at 11:13
  • 1
    Simply checking if another app is installed doesnt mean that they are paired with a wearable device. Checking Connected nodes (in above examples) and going the long way to check paired bluetooth devices is the only way to be absolutely sure – Nlinscott May 28 '15 at 00:02
  • @Nlinscott - as noted, mine is a much simpler method that is suitable for a specific use case. The new answer that was added which includes my solution, and was voted on 6 times at the time of this comment is further proof. – Oded Breiner May 28 '15 at 21:57
  • It also doesn't answer the OP's question. I'm sure it works in some cases but it does not help others who search for a solution to this – Nlinscott May 28 '15 at 23:16
  • 1
    Won't work on Wear OS 3 watches that are paired to a brand-specific app. – Louis CAD Sep 24 '22 at 23:24
1

Check for paired devices

You can check that the user has a watch paired with his device while it doesn't has to be connected at the time. This is better as this way the watch does not have to be connected at the time to be detected.

BluetoothAdapter mBtAdapter = BluetoothAdapter.getDefaultAdapter();

Set<BluetoothDevice> pairedDevices = mBtAdapter.getBondedDevices();
if (pairedDevices.size() > 0) {
    for (BluetoothDevice device : pairedDevices) {
        if(device.getDeviceClass()==BluetoothClass.Device.WEARABLE_WRIST_WATCH){
            Log.d("Found", "Paired wearable: "+ device.getName() + ", with address: " + device.getAddress());
        {
    {
} else {
    Toast.makeText(this, "No paired wearables found", Toast.LENGTH_SHORT).show();
}

Note that this code requires bluetooth permissions in your manifest.

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

Kotlin code from my project:

private val clientDataModel by lazy { ClientDataModel.getClientModelObject(this) }

private fun checkWearableStatus() {
    lifecycleScope.launch {
        try {

            val nodes: List<Node> = nodeClient.connectedNodes.await()
            if (nodes.count() <= 0) {
                // No watch is connected
                Log.d(TAG, "no wear devices was found )-: ")
            }
            else if(nodes.filter{ it.isNearby }.count() == 0)
            {
                Log.d(TAG, "there are paired devices but they are not near by or not connected...")
            }
            else {
                // Display connected devices
                for (n in nodes) {
                    Log.d(TAG, "Wear devices: " + n.displayName)
                }
            }
        } catch (ex: Exception) {
            Log.d(TAG, "Error detecting wearable devices: ${ex.message.toString()}")
        }
    }
}
Jonathan Applebaum
  • 5,738
  • 4
  • 33
  • 52
  • Does it work even if your app isn't installed on the watches? – Louis CAD Sep 24 '22 at 23:24
  • Yes, `connectedNodes()` will return all paired devices (connected and not connected) and you don't need your app to be installed. – Jonathan Applebaum Sep 25 '22 at 05:41
  • I just tested after putting one of my watches in airplane mode, and what you wrote about that is false: the disconnected watch isn't part of the list you get from `connectedNodes()`, which makes sense if you think about it. – Louis CAD Sep 26 '22 at 04:30