Android_dev_new
Android_dev_new

Reputation: 73

Not able to send data from mobile app to wear app when both the apps implements the WearableListenerService

I have created a mobile app with an Activity which sends the data to wear. Mobile App Side the code is as below

Please note I have deleted some code to reduce the number of lines.

public class MainActivity extends AppCompatActivity implements
        GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener  {

    GoogleApiClient mGoogleClient;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // Build a new GoogleApiClient for the the Wearable API
        mGoogleClient = new GoogleApiClient.Builder(this)
                .addApi(Wearable.API)
                .addConnectionCallbacks(this)
                .addOnConnectionFailedListener(this)
                .build();

        Button sendBtn = (Button) findViewById(R.id.send_btn);
        sendBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (mGoogleClient.isConnected()) {
                    Log.w("mobile-p", "connected.....sending data");
                    sendData();

                }
            }
        });
    }

    // Connect to the data layer when the Activity starts
    @Override
    protected void onStart() {
        super.onStart();
        mGoogleClient.connect();
    }

    private void constructDataI temsForNotification() {
        //I construct the dataItems here which needs to be passed to the wear
        //and call the sendata.
        sendData();
    }

    private void sendData() {
        //Requires a new thread to avoid blocking the UI
        if (mDataMapList != null && mDataMapList.size() > 0 ) {
            new SendToDataLayerThread().start();
        } else {
            Log.w("mobile", "Nothing to notify");
        }
    }

    @Override
    public void onConnected(Bundle connectionHint) {
    }

    // Disconnect from the data layer when the Activity stops
    @Override
    protected void onStop() {
        if (null != mGoogleClient && mGoogleClient.isConnected()) {
            mGoogleClient.disconnect();
        }
        super.onStop();
    }

    // Placeholders for required connection callbacks
    @Override
    public void onConnectionSuspended(int cause) {
        if (mGoogleClient != null) {
            mGoogleClient.reconnect();
        }
    }

    @Override
    public void onConnectionFailed(ConnectionResult connectionResult) { }

    //Use thread or asynctask.
    class SendToDataLayerThread extends Thread {

        public void run() {
            // Construct a DataRequest and send over the data layer
            PutDataMapRequest putDMR = PutDataMapRequest.create("/weardatapath");
            putDMR.getDataMap().putDataMapArrayList("/weardatapath", mDataMapList);
            PutDataRequest request = putDMR.asPutDataRequest();
            DataApi.DataItemResult result = Wearable.DataApi.putDataItem(mGoogleClient, request).await();
            if (result.getStatus().isSuccess()) {
                for (DataMap mapItem : mDataMapList) {
                    Log.v("mobile-p", "DataMap: " + mapItem + " sent successfully to data layer ");
                }
            } else {
                // Log an error
                Log.v("mobile-p", "ERROR: failed to send DataMap to data layer");
            }
        }
    }
}

On the Wear app side I have created a PhoneListenerService as below

public class PhoneListenerService extends WearableListenerService {

    @Override
    public void onDataChanged(DataEventBuffer dataEvents) {

        DataMap dataMap;
        for (DataEvent event : dataEvents) {
            Log.v("Wear-W", "DataMap received on watch: " + DataMapItem.fromDataItem(event.getDataItem()).getDataMap());
            // Check the data type
            if (event.getType() == DataEvent.TYPE_CHANGED) {
                // Check the data path
                String path = event.getDataItem().getUri().getPath();
                if (path.equals("/weardatapath")) {
                    DataMapItem dataMapItem = DataMapItem.fromDataItem(event.getDataItem());
                    ArrayList<DataMap> dataItems = dataMapItem.getDataMap().getDataMapArrayList
                            ("dataMapItems");
                    // Broadcast DataMap contents to wearable show in notification..
                    if (dataItems != null && dataItems.size() > 0) {
                            for (DataMap item : dataItems) {
                              ........
                            }       
                    }
                }
            }
        }
    }
}

The above code works fine. Whenever I send a data from the MainActivity from mobileApp I do recieve it on the Wear app in PhoneListenerService.

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

From the received data I build notifications on the wear app and for each action taken on the notification I want to notify to the mobile App. To do this I created PhoneSyncService on the wear app which will notify the mobile App on the actions taken..

public class PhoneSyncService extends IntentService implements
        GoogleApiClient.OnConnectionFailedListener, GoogleApiClient.ConnectionCallbacks{

    private static final String "wearsrvc-w" = "PhoneSyncService-W";

    public static final String ACTION_REPLY = "com.test.mobilewearsample.action.REPLY";

    GoogleApiClient mGoogleClient;

    public PhoneSyncService() {
        super("PhoneSyncService");
    }

    @Override
    public void onCreate() {
        super.onCreate();
        mGoogleClient = new GoogleApiClient.Builder(this)
                .addApi(Wearable.API)
                .addConnectionCallbacks(this)
                .addOnConnectionFailedListener(this)
                .build();
    }

    @Override
    public void onDestroy() {
        if (null != mGoogleClient && mGoogleClient.isConnected()) {
            mGoogleClient.disconnect();
        }
        super.onDestroy();
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        if (intent != null) {
            final String action = intent.getAction();
            Log.w("wearsrvc-w", "ACTION : "+action);
            if (ACTION_REPLY.equals(action)) {
                final String uId = intent.getStringExtra("dataId");
                notifyHandheldDevice(uId);
            }
        }
    }

    private void notifyHandheldDevice(String uId) {
        Log.v("wearsrvc-w", "in notify to handheld device :"+uId);

        if (mGoogleClient.isConnected()) {
            DataMap dataMap = new DataMap();
            dataMap.putInt("uId", Integer.valueOf(uId));
            PutDataMapRequest putDMR = PutDataMapRequest.create("/mobiledatapath");
            putDMR.getDataMap().putAll(dataMap);
            PutDataRequest request = putDMR.asPutDataRequest();
            DataApi.DataItemResult result = Wearable.DataApi.putDataItem(mGoogleClient, request).await();
            if (result.getStatus().isSuccess()) {
                Log.v("wearsrvc-w" , "Wear DataMap: " + dataMap + " sent successfully to data layer ");
            } else {
                // Log an error
                Log.v("wearsrvc-w" , "ERROR: failed to send DataMap to data layer");
            }
        }
        Log.v("wearsrvc-w", "done notify to handheld device :"+uId);
    }

    @Override
    public void onConnected(Bundle bundle) {

    }

    @Override
    public void onConnectionSuspended(int i) {

    }

    @Override
    public void onConnectionFailed(ConnectionResult connectionResult) {

    }
}

To get the data sent from wear app to the mobile App I created a WearDataListenerService as well on the mobile app , below is the code.

public class WearDataListenerService  extends WearableListenerService {
    private static final String TAG = "WearDataLstrService-M";


    @Override
    public void onDataChanged(DataEventBuffer dataEvents) {


        for (DataEvent event : dataEvents) {
            Log.v(TAG, "DataMap received on mobile: " + DataMapItem.fromDataItem(event.getDataItem()).getDataMap());
            // Check the data type
            Log.v(TAG, "event type :"+event.getType());
            if (event.getType() == DataEvent.TYPE_CHANGED) {
                // Check the data path
                String path = event.getDataItem().getUri().getPath();
                if (path.equals("/mobiledatapath")) {
                    DataMap dataMap = DataMapItem.fromDataItem(event.getDataItem()).getDataMap();
                    int uId = dataMap.getInt("dataId");
                    Log.v(TAG, "action received for uId :"+uId);
                }

            }
        }
    }

}

When I did the above changes I started to see a strange issue. When I try to send the data from the mobile app to the wear app the data is received on the WearDataListenerService which is on the mobile app side only and is not being received at the wear app side. Its like the data sent from mobile app is received on the mobile app WearableListenerService instead of the one on wear app.

Can someone please help me.

Upvotes: 0

Views: 406

Answers (1)

Ali Naddaf
Ali Naddaf

Reputation: 19034

Your mobile listener service has the following line:

if (path.equals("/mobiledatapath")) {}

I think you are closing the block too early; did you mean to do:

if (path.equals("/mobiledatapath")) {
  // rest of that code
}

Your mobile side is adding/changing data so ANY node can receive a callback, including the mobile device, so that is why your mobile device receives it (and because of your faulty if-clause, it is not filtered out). As for the other part, what is the payload that you are sending in the data (mDataMapList)? Is that the same thing across multiple tries or it changes each time? There are two possible causes:

  • Your payload is the same so there is no "change", hence onDataChanged is not called for a second time (adding a, say, timestamp to data can address that)
  • in Play Services 8.3+, some changes were made to batch the data sync across network and transfer them not immediately (can be delayed up to 20 minutes); if an app needs immediate sync, they need to set the urgent flag, see setUrgent(). On the mobile side, teh change can be seen immediately (it is not crossing wire) but on the wear side, it can take a while unless send as urgent.

Upvotes: 1

Related Questions