Cómo transferir datos de Wear OS a un nuevo dispositivo móvil

Cuando los usuarios configuran un dispositivo Wear OS, lo conectan a un dispositivo móvil particular. Es posible que más adelante el usuario decida obtener un nuevo dispositivo móvil y conectarlo con su dispositivo Wear OS existente. Algunos datos relacionados con un dispositivo Wear OS se almacenan en el dispositivo móvil con el que está conectado actualmente.

A partir de Wear OS 4, cuando los usuarios se conectan a un nuevo dispositivo móvil, pueden transferirle los datos de Wear OS. Los datos se sincronizan automáticamente cuando se transfieren.

Cuando el usuario solicita una transferencia, la capa de datos de wearables entrega objetos DataItem, que en un principio estaban almacenados en un dispositivo móvil, al otro dispositivo móvil. Esto permite una experiencia fluida para los usuarios de tu app.

En este documento, se describe cómo puedes configurar tu app para Wear OS y su app para dispositivos móviles complementaria con el objetivo de brindar compatibilidad ante estos casos.

Preparación

El proceso de transferencia de datos controla los objetos DataItem de manera diferente, según la app que sea propietaria de los datos:

Objetos propiedad de la app para Wear OS
Estos objetos se conservan en el dispositivo Wear OS.
Objetos propiedad de la app para dispositivos móviles

Estos objetos se archivan en el dispositivo anterior. Luego, el sistema empaqueta los datos archivados en un objeto DataItemBuffer y los entrega a la app para dispositivos móviles que se instala en el dispositivo móvil nuevo.

Inmediatamente después de que se entrega el archivo, la capa de datos para wearables invoca el objeto de escucha onNodeMigrated(), de manera similar al modo en que se notifica a tu app cuando los datos se escriben en un dispositivo Wear OS.

Cómo conservar los datos transferidos

Es responsabilidad de tu app preservar los objetos DataItem transferidos. Poco después de que los datos se entregan al dispositivo móvil nuevo, el archivo se borra del dispositivo anterior.

Asegúrate de que se cumpla cada una de las siguientes condiciones:

  1. Tu app se instala en los dos dispositivos móviles que participan en la transferencia.
  2. Las apps para dispositivos móviles, instaladas en cada dispositivo móvil, tienen firmas de paquetes que coinciden.

De lo contrario, los objetos DataItem archivados no se entregan y se descartan.

Cómo recibir datos del dispositivo móvil anterior

Para recibir datos en el dispositivo móvil nuevo que se archivaron en el dispositivo móvil anterior, tu app para dispositivos móviles debe implementar la devolución de llamada onNodeMigrated(), que forma parte de la clase WearableListenerService. Para ello, completa los siguientes pasos:

  1. En el archivo de compilación de tu app para dispositivos móviles, incluye una dependencia en la versión más reciente de la biblioteca de wearables en los Servicios de Google Play:

    dependencies {
        ...
        implementation 'com.google.android.gms:play-services-wearable:18.2.0'
    }
  2. Declara y exporta el WearableListenerService en el archivo de manifiesto de tu app. Para ello, haz lo siguiente:

    <service
    android:name=".MyWearableListenerService"
    android:exported="true">
    <intent-filter>
        ...
        <action android:name="com.google.android.gms.wearable.NODE_MIGRATED" />
        <data android:scheme="wear" />
    </intent-filter>
    </service>
    
  3. Crea una clase de servicio que extienda WearableListenerService y anule onNodeMigrated().

    Kotlin

    class MyWearableListenerService : WearableListenerService() {
        val dataClient: DataClient = Wearable.getDataClient(this)
    
        private fun shouldHandleDataItem(nodeId: String,
                                        dataItem: DataItem): Boolean {
            // Your logic here
            return dataItem.uri.path?.startsWith("/my_feature_path/") == true
        }
    
        private fun handleDataItem(nodeId: String, dataItem: DataItem) {
            val data = dataItem.data ?: return
            val path = dataItem.uri.path ?: return
            // Your logic here
            if (data.toString().startsWith("Please restore")) {
                dataClient.putDataItem(
                    PutDataRequest.create(path).setData(data)
                )
            }
        }
    
        override fun onNodeMigrated(nodeId: String, archive: DataItemBuffer) {
            val dataItemsToHandle = mutableListOf<DataItem>()
    
            for (dataItem in archive) {
                if (shouldHandleDataItem(nodeId, dataItem)) {
                    dataItemsToHandle.add(dataItem.freeze())
                }
            }
    
            // Callback stops automatically after 20 seconds of data processing.
            // If you think you need more time, delegate to a coroutine or thread.
            runBlocking {
                for (dataItem in dataItemsToHandle) {
                    handleDataItem(nodeId, dataItem)
                }
            }
        }
    }

    Java

    public class MyWearableListenerService extends WearableListenerService {
        private final DataClient dataClient = Wearable.getDataClient(this);
    
        private boolean shouldHandleDataItem(String nodeId, DataItem dataItem) {
            // Your logic here
            return Objects.requireNonNull(dataItem.getUri().getPath())
                    .startsWith("/my_feature_path/");
        }
    
        private Task<DataItem> handleDataItem(String nodeId, DataItem dataItem) {
            byte[] data = dataItem.getData();
            String path = dataItem.getUri().getPath();
            // Your logic here
            if (data != null && path != null && Arrays.toString(data)
                    .startsWith("Please restore")) {
                assert path != null;
                return dataClient.putDataItem(
                            PutDataRequest.create(path).setData(data));
        }
    
        @Override
        public void onNodeMigrated(@NonNull String nodeId, DataItemBuffer archive) {
            List<DataItem> dataItemsToHandle = new ArrayList<>();
    
            for (DataItem dataItem : archive) {
                if (shouldHandleDataItem(nodeId, dataItem)) {
                    dataItemsToHandle.add(dataItem.freeze());
                }
            }
    
            for (dataItem in dataItemsToHandle) {
                handleDataItem(nodeId, dataItem);
            }
    
            // Callback stops automatically after 20 seconds of data processing.
            // If you think you need more time, delegate to another thread.
        }
    }