如何检测Android设备是否已与Android Wear手表配对

24
我正在创建一个Android Wear应用程序,它可以扩展推送通知。当推送通知进来时,我的应用程序从服务器下载大约10张图片,并在手表上显示这些额外的图片。这些图片是特定于Android Wear应用程序的,不会显示在手持设备上。
我如何判断手持设备是否与Android Wear设备配对,以便确定是否需要下载针对Android Wear应用程序所需的额外图片?
谢谢!

我只需要检查 Android Wear 是否已安装,然后下载这些资源。如果已安装 Android Wear,但没有配对手表,则很有可能将来会进行配对。 - 323go
@323go 您如何判断 Android Wear 是否已安装? - TWilly
通过像所有其他应用程序一样使用PackageManager - 323go
这里是如何检查的完整示例:stackoverflow.com/a/39513489/878126 - android developer
6个回答

24

这里已经列出了两个选项。取决于您的用例,它们都是有效的。但我想添加第三个选项,但是它不完整。

选项1:使用NodeApi查找连接节点

NodeApi类有一个检索连接节点的方法。使用此方法,您可以确定用户过去确实拥有手表或测试过手表。他真的在附近并连接了。

这种方法的缺点是,如果蓝牙未启用或手表当前未连接,则不会得到任何结果。

该方法为:

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

一个更完整的代码示例如下:

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();
    }

选项2:检查Android Wear应用程序

这就是第二个选项的优势。如果你购买了一块手表并想要将其连接到手机,首先需要做的就是在移动设备上安装Android Wear应用程序。因此,您可以使用PackageManager来验证是否已安装此Android Wear应用程序。

缺点在于您可能会在没有手表的情况下安装Wear应用程序。

代码示例:

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();
}

选项三:检查配对设备

不确定是否可行,但在某些情况下,这将是完美的解决方案,因为您可以检查用户是否将手表与其设备配对,而此时它不必连接。

以下是一个代码示例,但这并不包括检查配对设备是否为可穿戴设备。这只是一个起点。

代码示例来自: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();
}

请注意,此代码需要在您的清单中设置蓝牙权限。

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

5
选项4:问问谷歌他们为什么让这个如此难以检测? - worked
选项3应该就是TWilly在寻找的答案,我猜。因此这应该是"已接受的答案"。 - Christine
使用此方法的缺点是如果蓝牙未启用或手表当前未连接,则将无法获得结果。这为什么是一个缺点?难道不是你真正需要的吗?在这种情况下,它是错误使用的吗? - android developer
@androiddeveloper 真的,这取决于你的使用情况。 - hcpl
选项2:检查Android Wear应用程序,不考虑使用com.samsung.android.app.watchmanager的任何三星Gear设备。 - Niels
NodeApi现已不推荐使用,请改用NodeClient。在Kotlin中,可以这样写:val nodes: List<Node> = Tasks.await(Wearable.getNodeClient(context).connectedNodes) - James Allen

18
你需要使用NodeApi,特别是NodeApi.getConnectedNodes()
例如(在后台线程中—否则请使用setResultCallback()而不是await()):
List<Node> connectedNodes =
    Wearable.NodeApi.getConnectedNodes(mGoogleApiClient).await().getNodes();
如果返回的节点列表至少包含一个元素,则存在已连接的Android Wear设备;否则,不存在。
更新:随着 Google Play服务7.3版本 的发布,支持同时连接多个Android Wear设备。您可以使用CapabilityApi 来请求具有特定功能的节点。

目前,会有多个节点吗? - Nathan Schwermann
@schwiz 我不确定,如果您能够配对多个设备,那么可能会有多个节点(?)但是我没有测试过,因为我只有一个。 :) - matiash
1
Google Glass算作一个节点,因此您可以同时连接一个Wear设备和一个Glass。 - Jarett Millard
1
“getConnectedNodes” 检测的是已连接的节点,而不是配对的节点,是吗? - Christine
@matiash 感谢你指出NodeApi和CapabilityApi。NodeApi很好用,但我在使用CapabilityApi时遇到了一些问题。我打开了一个单独的问题,并非常感激任何帮助:http://stackoverflow.com/q/31328971/1204377 - Anton Cherkashyn
显示剩余2条评论

7
如果手持设备仅连接Android手表,则以上所有答案都是正确的。如果手持设备连接到多个设备(多个可穿戴设备),例如Android手表、HealthBand、Beacon等,则上述答案可能无法正常工作。
以下部分向您展示如何广告处理活动请求的设备节点,发现能够满足所需请求的节点,并向这些节点发送消息。
广告功能
要从可穿戴设备启动手持设备上的活动,请使用MessageApi类发送请求。由于多个可穿戴设备可以连接到手持设备,因此可穿戴设备应确定连接的节点是否能够启动该活动。在您的手持应用程序中,广告它运行的节点提供特定的功能。
要广告您的手持应用程序的功能:
1. 在项目的res/values/目录中创建一个名为wear.xml的XML配置文件。 2. 向wear.xml添加名为android_wear_capabilities的资源。 3. 定义设备提供的功能。
<resources>
    <string-array name="android_wear_capabilities">
        <item>android_wear</item>
    </string-array> 
</resources>

获取具备所需能力的节点

首先,您可以通过调用CapabilityApi.getCapability()方法来检测具备能力的节点。以下示例展示了如何手动检索具备android_wear能力的可达节点结果。在Wear模块中使用以下代码:

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());
            }
        }
    }
}

}

请查看Android Dev以获取更详细信息。


我曾尝试过这么做,但有些事情我不太明白:当我调用getCapability()方法时,无论我更改能力的名称,我始终会得到结果。如果名称错误,我难道不应该检测到没有任何输出吗? - krakig
如果您的手机只有一个连接(比如手表),则功能正常工作,但如果它有两个以上的连接(手表+健康手环+信标),则系统会检查适当的名称,并返回相应节点。请参阅http://developer.android.com/training/wearables/data-layer/messages.html。 - Umang Kothari
1
这个答案应该被标记为正确的,因为它实际上检查了连接节点是否为磨损、玻璃或信标。 - Milan

2
我使用了一种更简单的方法,适用于我的使用情况,检查 Android Wear 应用是否已安装:
    try {
        getPackageManager().getPackageInfo("com.google.android.wearable.app", PackageManager.GET_META_DATA);
    } catch (PackageManager.NameNotFoundException e) {
        //android wear app is not installed
    }

2
这实际上比使用BluetoothManager更有帮助,而且它还可以节省您的蓝牙权限。 - Christine
1
仅仅检查另一个应用程序是否已安装并不意味着它们已经与可穿戴设备配对。检查已连接的节点(如上例所示)并通过长途跋涉来检查已配对的蓝牙设备是唯一绝对确定的方法。 - Nlinscott
@Nlinscott - 如注明的那样,我的方法更简单,适用于特定的用例。新添加的答案包括我的解决方案,并在此评论时获得了6次投票,这进一步证明了这一点。 - Oded Breiner
它也没有回答楼主的问题。我相信它在某些情况下有效,但对于寻找解决方案的其他人并没有帮助。 - Nlinscott
1
无法在配对了品牌特定应用的 Wear OS 3 手表上运行。 - Louis CAD

1

检查配对设备

您可以检查用户是否将手表与设备配对,即使手表当前未连接也可以进行检查。这样做更好,因为这样手表不必在连接时被检测到。

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();
}

请注意,此代码需要在您的清单中拥有蓝牙权限。
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>

0

我的项目中的 Kotlin 代码:

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()}")
        }
    }
}

即使您的应用程序未安装在手表上,它是否有效? - Louis CAD
是的,connectedNodes() 將返回所有已配對的設備(已連接和未連接),您不需要安裝您的應用程式。 - Jonathan Applebaum
我刚刚测试了一下,把我的手表设置为飞行模式后,你所写的内容是错误的:断开连接的手表不会出现在connectedNodes()返回的列表中,如果你仔细想想也是有道理的。 - Louis CAD

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接