我一直在尝试开发一个简单的应用程序,该应用程序可以将通知从智能手机发送到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>
如果您需要更多代码,请说。谢谢!