从智能手机向Wear OS智能手表发送通知

时间:2018-08-04 14:47:44

标签: android service bluetooth notifications wear-os

我一直在尝试开发一个简单的应用程序,该应用程序可以将通知从智能手机发送到Wear OS智能手表(更具体地说,是华为Smartwatch 2)。

我已将此代码用作基础:github link。请注意,此代码没有我要开发的“通知部分”。

最重要的是:我可以在智能手表上接收数据,但是在其上运行的应用程序接收。据我所知,ServiceListener服务并未从调试中被激活。基本上,我在该文件上写的任何内容,甚至Log数据都不会显示。

ServiceListener服务:

package com.example.nowor_000.sincronizaciondatos;

import android.app.Notification;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.NotificationManagerCompat;
import android.util.Log;

import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
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.DataMapItem;
import com.google.android.gms.wearable.MessageEvent;
import com.google.android.gms.wearable.Wearable;
import com.google.android.gms.wearable.WearableListenerService;

import static android.content.ContentValues.TAG;

import static com.google.android.gms.wearable.PutDataRequest.WEAR_URI_SCHEME;

/**
 * Created by nowor_000 on 26/11/2015.
 */
public class ServiceListener extends WearableListenerService {

    public static final String NOTIFICATION_PATH = "/notification";
    public static final String NOTIFICATION_TIMESTAMP = "timestamp";
    public static final String NOTIFICATION_TITLE = "title";
    public static final String NOTIFICATION_CONTENT = "content";
    public static final String ACTION_DISMISS = "com.example.nowor_000.sincronizaciondatos.DISMISS";
    //private static final String WEAR_INICIAR_ACTIVIDAD = "/iniciar";
    private static final String START_ACTIVITY_PATH = "/start-activity";
    private int notificationId = 001;


    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.i(TAG, "onStartCommand ServiceListener.");
        if (null != intent) {
            String action = intent.getAction();
            if (ACTION_DISMISS.equals(action)) {
                dismissNotification();
            }
        }
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onDataChanged(DataEventBuffer dataEvents) {
        Log.i(TAG, "onDataChanged ServiceListener.");
        for (DataEvent dataEvent : dataEvents) {
            if (dataEvent.getType() == DataEvent.TYPE_CHANGED) {
                if (NOTIFICATION_PATH.equals(dataEvent.getDataItem().getUri().getPath())) {
                    DataMapItem dataMapItem = DataMapItem.fromDataItem(dataEvent.getDataItem());
                    String title = dataMapItem.getDataMap().getString(NOTIFICATION_TITLE);
                    String content = dataMapItem.getDataMap().getString(NOTIFICATION_CONTENT);
                    sendNotification(title, content);
                }
            }
        }
    }

    private void sendNotification(String title, String content) {

        Log.i(TAG, "Sending notification.");
        // this intent will open the activity when the user taps the "open" action on the notification
        Intent viewIntent = new Intent(this, MainActivity.class);
        PendingIntent pendingViewIntent = PendingIntent.getActivity(this, 0, viewIntent, 0);

        // this intent will be sent when the user swipes the notification to dismiss it
        Intent dismissIntent = new Intent(ACTION_DISMISS);
        PendingIntent pendingDeleteIntent = PendingIntent.getService(this, 0, dismissIntent, PendingIntent.FLAG_UPDATE_CURRENT);

        NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
                .setSmallIcon(R.drawable.ic_launcher)
                .setContentTitle(title)
                .setContentText(content)
                .setDeleteIntent(pendingDeleteIntent)
                .setContentIntent(pendingViewIntent);

        Notification notification = builder.build();

        NotificationManagerCompat notificationManagerCompat = NotificationManagerCompat.from(this);
        notificationManagerCompat.notify(notificationId++, notification);
    }

    private void dismissNotification() {
        new ServiceListener.DismissNotificationCommand(this).execute();
    }


    private class DismissNotificationCommand implements GoogleApiClient.ConnectionCallbacks, ResultCallback<DataApi.DeleteDataItemsResult>, GoogleApiClient.OnConnectionFailedListener {

        private static final String TAG = "DismissNotification";

        private final GoogleApiClient mGoogleApiClient;

        public DismissNotificationCommand(Context context) {
            mGoogleApiClient = new GoogleApiClient.Builder(context)
                    .addApi(Wearable.API)
                    .addConnectionCallbacks(this)
                    .addOnConnectionFailedListener(this)
                    .build();
        }

        public void execute() {
            mGoogleApiClient.connect();
        }

        @Override
        public void onConnected(Bundle bundle) {
            final Uri dataItemUri =
                    new Uri.Builder().scheme(WEAR_URI_SCHEME).path(NOTIFICATION_PATH).build();
            if (Log.isLoggable(TAG, Log.DEBUG)) {
                Log.d(TAG, "Deleting Uri: " + dataItemUri.toString());
            }
            Wearable.DataApi.deleteDataItems(
                    mGoogleApiClient, dataItemUri).setResultCallback(this);
        }

        @Override
        public void onConnectionSuspended(int i) {
            Log.d(TAG, "onConnectionSuspended");
        }

        @Override
        public void onResult(DataApi.DeleteDataItemsResult deleteDataItemsResult) {
            if (!deleteDataItemsResult.getStatus().isSuccess()) {
                Log.e(TAG, "dismissWearableNotification(): failed to delete DataItem");
            }
            mGoogleApiClient.disconnect();
        }

        @Override
        public void onConnectionFailed(ConnectionResult connectionResult) {
            Log.d(TAG, "onConnectionFailed");
        }
    }
}

智能手机上的MainActivity

package com.example.nowor_000.sincronizaciondatos;

import android.os.Bundle;
import android.provider.SyncStateContract;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.text.TextUtils;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

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.MessageApi;
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 static android.content.ContentValues.TAG;

public class MainActivity extends AppCompatActivity implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener, View.OnClickListener {
    public static final String NOTIFICATION_PATH = "/notification";
    public static final String NOTIFICATION_TIMESTAMP = "timestamp";
    public static final String NOTIFICATION_TITLE = "title";
    public static final String NOTIFICATION_CONTENT = "content";
    public static final String ACTION_DISMISS = "de.peterfriese.notificationwithopenactivityonwearableaction.DISMISS";
    /**
     * sincronizacion
     */

    private static final String KEY_CONTADOR = "com.example.key.contador";
    private static final String ITEM_CONTADOR = "/contador";
    /**
     * sincronizacion
     */
    private static final String WEAR_INICIAR_ACTIVIDAD = "/iniciar";
    private static final String WEAR_ENVIAR_TEXTO = "/enviar_texto";
    Button btnEnviarTexto;
    EditText editText;
    private int contador;
    /**
     * CLIENTE
     */
    private GoogleApiClient apiClient;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);


        FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
                        .setAction("Action", null).show();
            }
        });


        /**
         *API CLIENT es clave para establecer la comunicación
         */

        apiClient = new GoogleApiClient.Builder(this)
                .addApi(Wearable.API)
                .addConnectionCallbacks(this)
                .addOnConnectionFailedListener(this)
                .build();


        addViews();

        /**
         * sincronizacion
         */
        final TextView textoContador = (TextView) findViewById(R.id.textoContador);
        textoContador.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                contador++;
                textoContador.setText(Integer.toString(contador));

                PutDataMapRequest putDataMapReq = PutDataMapRequest.create(ITEM_CONTADOR);
                putDataMapReq.getDataMap().putInt(KEY_CONTADOR, contador);

                PutDataRequest putDataReq = putDataMapReq.asPutDataRequest();
                PendingResult<DataApi.DataItemResult> resultado = Wearable.DataApi.putDataItem(apiClient, putDataReq);

                /**
                 * ESTA LINEA SE ME OLVIDO EN EL VIDEOTUTORIAL
                 */
                enviarMensaje(ITEM_CONTADOR, Integer.toString(contador));
            }


        });

        /**
         * sincronizacion
         */
    }


    private void addViews() {

        editText = (EditText) findViewById(R.id.edtitText);

        btnEnviarTexto = (Button) findViewById(R.id.btnEnviarTexto);
        btnEnviarTexto.setOnClickListener(this);


    }


    @Override
    public void onClick(View v) {

        sendNotification();

        switch (v.getId()) {
            case R.id.btnEnviarTexto:

                String text = editText.getText().toString();

                if (!TextUtils.isEmpty(text)) {
                    enviarMensaje(WEAR_ENVIAR_TEXTO, text);
                    editText.getText().clear();
                    editText.requestFocus();


                } else {
                    enviarMensaje(WEAR_ENVIAR_TEXTO, "\n->Escribe un mensaje, en el EditText<-");
                }
                break;


        }

    }


    //<editor-fold desc="ENVIAR MENSAJE EditText">
    private void enviarMensaje(final String path, final String texto) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                NodeApi.GetConnectedNodesResult nodos = Wearable.NodeApi.getConnectedNodes(apiClient).await();

                for (Node nodo : nodos.getNodes()) {
                    Wearable.MessageApi.sendMessage(apiClient, nodo.getId(), path, texto.getBytes())
                            .setResultCallback(
                                    new ResultCallback<MessageApi.SendMessageResult>() {
                                        @Override
                                        public void onResult(MessageApi.SendMessageResult resultado) {
                                            if (!resultado.getStatus().isSuccess()) {
                                                Log.e("sincronizacion", "Error al enviar mensaje. Codigo" + resultado.getStatus().getStatusCode());
                                            }
                                        }
                                    }
                            );
                }

            }
        }).start();

    }


    //</editor-fold>


    //<editor-fold desc="CICLO DE VIDA">
    @Override
    protected void onStart() {
        super.onStart();
        apiClient.connect();
    }


    @Override
    protected void onStop() {

        if (apiClient != null && apiClient.isConnected()) {
            apiClient.disconnect();
        }

        super.onStop();


    }
    //</editor-fold>


    //<editor-fold desc="METODOS API DATA LAYER">
    @Override
    public void onConnected(Bundle bundle) {


    }

    @Override
    public void onConnectionSuspended(int i) {

    }

    @Override
    public void onConnectionFailed(ConnectionResult connectionResult) {

    }
    //</editor-fold>


    //<editor-fold desc="MENU">
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }
    //</editor-fold>


    private void sendNotification() {
        if (apiClient.isConnected()) {
            Log.i(TAG, "Sending data.");
            PutDataMapRequest dataMapRequest = PutDataMapRequest.create(NOTIFICATION_PATH);
            // Make sure the data item is unique. Usually, this will not be required, as the payload
            // (in this case the title and the content of the notification) will be different for almost all
            // situations. However, in this example, the text and the content are always the same, so we need
            // to disambiguate the data item by adding a field that contains teh current time in milliseconds.
            dataMapRequest.getDataMap().putDouble(NOTIFICATION_TIMESTAMP, System.currentTimeMillis());
            dataMapRequest.getDataMap().putString(NOTIFICATION_TITLE, "This is the title");
            dataMapRequest.getDataMap().putString(NOTIFICATION_CONTENT, "This is a notification with some text.");
            PutDataRequest putDataRequest = dataMapRequest.asPutDataRequest().setUrgent();
            Wearable.DataApi.putDataItem(apiClient, putDataRequest);
        } else {
            Log.e(TAG, "No connection to wearable available!");
        }
    }

}

智能手机的AndroidManifest

    <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.nowor_000.sincronizaciondatos">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">

        <!--ETIQUETA PPARA INDICAR LA VERSION DE GOOGLE PLAY QUE ESTAMOS USANDO -->
        <meta-data android:name="com.google.android.gms.version"
            android:value="@integer/google_play_services_version" />

        <activity
            android:name=".MainActivity"
            android:label="@string/app_name"
            android:theme="@style/AppTheme.NoActionBar">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

    </application>

</manifest>

智能手表的AndroidManifest

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.nowor_000.sincronizaciondatos">

    <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" />

        <uses-library android:name="com.google.android.wearable"
            android:required="true" />

        <activity
            android:name=".MainActivity"
            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=".ServiceListener"
            android:enabled="true"
            android:exported="true">
            <intent-filter>
                <action android:name="com.google.android.gms.wearable.DATA_CHANGED" />
                <action android:name="com.google.android.gms.wearable.MESSAGE_RECEIVED" />
                <data android:scheme="wear" android:host="*"
                    android:path="/start-activity" />
            </intent-filter>
        </service>


    </application>

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

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

</manifest>

如果您需要更多代码,请说。谢谢!

0 个答案:

没有答案