I want to do bi-directional data transfer between Android Wear and Handheld. Everything seems good, except running onDataChanged on Handheld. It only starts when I connect the USB cable connected to the PC. Therefore, I do not understand why this is happening.
Here is my code:
WearListenerService on Handheld:
import android.content.Intent; import android.os.Bundle; import android.support.v4.content.LocalBroadcastManager; import com.google.android.gms.common.ConnectionResult; import com.google.android.gms.common.api.GoogleApiClient; import com.google.android.gms.wearable.DataApi; import com.google.android.gms.wearable.DataEvent; import com.google.android.gms.wearable.DataEventBuffer; import com.google.android.gms.wearable.DataMap; import com.google.android.gms.wearable.DataMapItem; import com.google.android.gms.wearable.Node; import com.google.android.gms.wearable.NodeApi; import com.google.android.gms.wearable.PutDataMapRequest; import com.google.android.gms.wearable.PutDataRequest; import com.google.android.gms.wearable.Wearable; import com.google.android.gms.wearable.WearableListenerService; import java.util.List; import ru.orangesoftware.financisto.db.DatabaseAdapter; import ru.orangesoftware.financisto.model.Category; import ru.orangesoftware.financisto.model.CategoryTree; import ru.orangesoftware.financisto.utils.Log; public class WearService extends WearableListenerService implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener { private static final String WEARABLE_DATA_PATH = "/wearable_data"; private static final String HANDHELD_DATA_PATH = "/handheld_data"; private SendToDataLayerThread s; GoogleApiClient googleClient; private DatabaseAdapter db; @Override public void onCreate() { super.onCreate(); Log.d("WearService Created"); db = new DatabaseAdapter(this); db.open(); initGoogleApiClient(); } @Override public void onDataChanged(DataEventBuffer dataEvents) { Log.d("In dataChanged"); DataMap dataMap; for (DataEvent event : dataEvents) {
WearListenerService on Wear :
import android.content.Intent; import android.os.Bundle; import com.google.android.gms.common.ConnectionResult; import com.google.android.gms.common.api.GoogleApiClient; import com.google.android.gms.common.api.PendingResult; import com.google.android.gms.common.api.ResultCallback; import com.google.android.gms.wearable.DataApi; import com.google.android.gms.wearable.DataEvent; import com.google.android.gms.wearable.DataEventBuffer; import com.google.android.gms.wearable.DataMap; import com.google.android.gms.wearable.DataMapItem; import com.google.android.gms.wearable.Node; import com.google.android.gms.wearable.NodeApi; import com.google.android.gms.wearable.PutDataMapRequest; import com.google.android.gms.wearable.PutDataRequest; import com.google.android.gms.wearable.Wearable; import com.google.android.gms.wearable.WearableListenerService; public class ListenerService extends WearableListenerService implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener { private static final String WEARABLE_DATA_PATH = "/wearable_data"; private static final String HANDHELD_DATA_PATH = "/handheld_data"; GoogleApiClient googleClient; private SendToDataLayerThread s; @Override public void onDataChanged(DataEventBuffer dataEvents) { Log.d("In dataChanged"); DataMap dataMap; for (DataEvent event : dataEvents) {
The main activities of both Handheld and Wear are simply launching services. Data path: 1) Serves to transfer data to the service. onDataChanged for wear caused as it should 2) Manual onDataChanged triggers only after disconnecting or connecting the USB cable. Handheld sends data for wear. 3) onDataChanged triggers during wear, as they should, and receive data.
Additional Information. Manifest Handheld:
<?xml version="1.0" encoding="utf-8"?> <manifest package="ru.orangesoftware.financisto" xmlns:android="http://schemas.android.com/apk/res/android" android:installLocation="internalOnly"> <supports-screens android:anyDensity="true" android:largeScreens="true" android:normalScreens="true" android:resizeable="true" android:smallScreens="true"/> <uses-feature android:name="android.hardware.touchscreen" android:required="false"/> <uses-feature android:name="android.hardware.camera" android:required="false"/> <uses-feature android:name="android.hardware.location" android:required="false"/> <uses-feature android:name="android.hardware.location.network" android:required="false"/> <uses-feature android:name="android.hardware.location.gps" android:required="false"/> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> <uses-permission android:name="android.permission.CAMERA"/> <uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/> <uses-permission android:name="android.permission.VIBRATE"/> <uses-permission android:name="android.permission.WAKE_LOCK"/> <uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.GET_ACCOUNTS"/> <uses-permission android:name="android.permission.USE_CREDENTIALS"/> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> <application android:allowBackup="true" android:description="@string/app_description" android:icon="@drawable/icon" android:label="@string/app_name" android:theme="@android:style/Theme.DeviceDefault"> <uses-library android:name="com.google.android.maps" android:required="false"/> <meta-data android:name="com.google.android.gms.version" android:value="@integer/google_play_services_version"/> <activity android:name=".activity.MainActivity" android:configChanges="orientation|keyboardHidden" android:label="@string/app_name" android:taskAffinity=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> <service android:name=".service.WearService"> <intent-filter> <action android:name="com.google.android.gms.wearable.BIND_LISTENER"/> </intent-filter> </service> </application> </manifest>
Clothing Manifest:
<?xml version="1.0" encoding="utf-8"?> <manifest package="ru.orangesoftware.financisto" xmlns:android="http://schemas.android.com/apk/res/android"> <uses-feature android:name="android.hardware.type.watch"/> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:supportsRtl="true" android:theme="@android:style/Theme.DeviceDefault"> <meta-data android:name="com.google.android.gms.version" android:value="@integer/google_play_services_version"/> <activity android:name=".MainWearActivity" android:label="@string/app_name"> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> <service android:name="ru.orangesoftware.financisto.ListenerService" android:enabled="true"> <intent-filter> <action android:name="com.google.android.gms.wearable.BIND_LISTENER"/> </intent-filter> </service> </application> </manifest>
Gradle Handheld :
buildscript { repositories { mavenCentral() } dependencies { classpath 'com.android.tools.build:gradle:1.3.0' classpath 'com.neenbedankt.gradle.plugins:android-apt:1.4' } } apply plugin: 'com.android.application' apply plugin: 'com.neenbedankt.android-apt' repositories { maven { url "http://repo.commonsware.com.s3.amazonaws.com" } maven { url "https://repository-achartengine.forge.cloudbees.com/snapshot/" } mavenCentral() } android { compileSdkVersion 22 buildToolsVersion "23.0.2" defaultConfig { applicationId "ru.orangesoftware.financisto" minSdkVersion 19 targetSdkVersion 22 versionCode 92 versionName "1.6.8" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt' } } packagingOptions { exclude 'META-INF/LICENSE' exclude 'META-INF/LICENSE.txt' exclude 'META-INF/NOTICE' exclude 'META-INF/NOTICE.txt' } } def googlePlayVersion = '8.3.0' dependencies { compile "com.google.android.gms:play-services-base:$googlePlayVersion" compile "com.google.android.gms:play-services-drive:$googlePlayVersion" compile "com.google.android.gms:play-services-wearable:$googlePlayVersion" compile files('libs/dropbox-android-sdk-1.6.1/dropbox-android-sdk-1.6.1.jar') compile files('libs/google-rfc-2445/rfc2445-no-joda.jar') compile 'com.google.code.gson:gson:2.3' compile 'com.commonsware.cwac:wakeful:1.0.1' compile 'org.achartengine:achartengine:1.2.0' compile 'net.sf.trove4j:trove4j:3.0.3' compile 'com.wdullaer:materialdatetimepicker:2.0.0' }
Gradle Wear :
apply plugin: 'com.android.application' android { compileSdkVersion 22 buildToolsVersion "23.0.2" defaultConfig { applicationId "ru.orangesoftware.financisto" minSdkVersion 20 targetSdkVersion 22 versionCode 1 versionName "1.0" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } } def googlePlayVersion = '8.3.0' dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) compile 'com.google.android.support:wearable:1.3.0' compile "com.google.android.gms:play-services-wearable:$googlePlayVersion" }
Login to Handheld after starting the application :
11-15 12:20:20.616 29043-29043/? D/Financisto: WearService Created [WearService.onCreate:44] 11-15 12:20:20.616 29043-29043/? D/Financisto: Initialaizing GoogleClient [WearService.initGoogleApiClient:94] 11-15 12:20:20.636 29043-29043/? D/Financisto: Tring to connect to GoogleApi... [WearService.initGoogleApiClient:107] 11-15 12:20:20.636 29043-29043/? D/Financisto: Google Client ID = com.google.android.gms.internal.zzmg@4344d5c0 [WearService.initGoogleApiClient:113] 11-15 12:20:21.016 29043-29043/ru.orangesoftware.financisto D/Financisto: onConnected entered [WearService.onConnected:139] 11-15 12:20:21.016 29043-29043/ru.orangesoftware.financisto D/Financisto: GoogleAPI now status:true [WearService.onConnected:140] -------now I send data from watch and unplug usb cable after 30 seconds 11-15 12:24:31.986 29043-29091/? D/Financisto: In dataChanged [WearService.onDataChanged:54] 11-15 12:24:31.986 29043-29091/? V/Financisto: Path phone: /handheld_data [WearService.onDataChanged:68] 11-15 12:24:31.986 29043-29091/? V/Financisto: DataMap received from watch: {time=1447565065308, ping=ping1447565065306} [WearService.onDataChanged:69] 11-15 12:24:32.036 29043-29091/? D/Financisto: In dataChanged [WearService.onDataChanged:54] 11-15 12:24:32.046 29043-1493/? V/Financisto: DataMap: {Pong=Pong1447565071992, time=1447565071992} sent to: Gear 2 76A1; path=/wearable_data [SendToDataLayerThread.run:179]
Log in after starting the application and sending data:
11-15 12:24:25.301 2460-2460/ru.orangesoftware.financisto D/FinancistoWear: onStartCommand: Service was started. 11-15 12:24:25.377 2460-2460/ru.orangesoftware.financisto V/FinancistoWear: DataMap: {time=1447565065308, ping=ping1447565065306} sent to: Tolive GN3 11-15 12:24:25.379 2460-3309/ru.orangesoftware.financisto D/FinancistoWear: In dataChanged 11-15 12:24:25.379 2460-3309/ru.orangesoftware.financisto D/FinancistoWear: DataChanged: path = /handheld_data
Please tell me what am I doing wrong?
Decision
I am rewriting SendToDataLayerThread. Now this is a regular class without the Thread and DataRequest extensions becoming urgent:
class SendToDataLayerThread { String path; DataMap dataMap; // Constructor for sending data objects to the data layer SendToDataLayerThread(String p, DataMap data) { path = p; dataMap = data; } public void run() { //NodeApi.GetConnectedNodesResult nodes = Wearable.NodeApi.getConnectedNodes(googleClient); PendingResult<NodeApi.GetConnectedNodesResult> nodes = Wearable.NodeApi.getConnectedNodes(googleClient); nodes.setResultCallback(new ResultCallback<NodeApi.GetConnectedNodesResult>() { @Override public void onResult(NodeApi.GetConnectedNodesResult getConnectedNodesResult) { for (Node node : getConnectedNodesResult.getNodes()) { final Node node2 = node; // Construct a DataRequest and send over the data layer PutDataMapRequest putDMR = PutDataMapRequest.create(path); putDMR.getDataMap().putAll(dataMap); PutDataRequest request = putDMR.asPutDataRequest(); request.setUrgent(); PendingResult<DataApi.DataItemResult> pendingResult = Wearable.DataApi.putDataItem(googleClient, request); pendingResult.setResultCallback(new ResultCallback<DataApi.DataItemResult>() { @Override public void onResult(DataApi.DataItemResult dataItemResult) { if (dataItemResult.getStatus().isSuccess()) { Log.v("DataMap: " + dataMap + " sent to: " + node2.getDisplayName()); } else { // Log an error Log.v("ERROR: failed to send DataMap"); } } }); } } }); } }
Thanks for the help!