1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.example.android.wearable.synchronizednotifications; 18 19 import static com.google.android.gms.wearable.PutDataRequest.WEAR_URI_SCHEME; 20 21 import android.app.Notification; 22 import android.app.NotificationManager; 23 import android.app.PendingIntent; 24 import android.content.Intent; 25 import android.net.Uri; 26 import android.os.Bundle; 27 import android.util.Log; 28 29 import com.example.android.wearable.synchronizednotifications.common.Constants; 30 import com.google.android.gms.common.ConnectionResult; 31 import com.google.android.gms.common.api.GoogleApiClient; 32 import com.google.android.gms.common.api.ResultCallback; 33 import com.google.android.gms.wearable.DataApi; 34 import com.google.android.gms.wearable.DataEvent; 35 import com.google.android.gms.wearable.DataEventBuffer; 36 import com.google.android.gms.wearable.DataMap; 37 import com.google.android.gms.wearable.DataMapItem; 38 import com.google.android.gms.wearable.Wearable; 39 import com.google.android.gms.wearable.WearableListenerService; 40 41 /** 42 * A {@link com.google.android.gms.wearable.WearableListenerService} that will be invoked when a 43 * DataItem is added or deleted. The creation of a new DataItem will be interpreted as a request to 44 * create a new notification and the removal of that DataItem is interpreted as a request to 45 * dismiss that notification. 46 */ 47 public class NotificationUpdateService extends WearableListenerService 48 implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener, 49 ResultCallback<DataApi.DeleteDataItemsResult> { 50 51 private static final String TAG = "NotificationUpdate"; 52 private GoogleApiClient mGoogleApiClient; 53 54 @Override onCreate()55 public void onCreate() { 56 super.onCreate(); 57 mGoogleApiClient = new GoogleApiClient.Builder(this) 58 .addApi(Wearable.API) 59 .addConnectionCallbacks(this) 60 .addOnConnectionFailedListener(this) 61 .build(); 62 } 63 64 @Override onStartCommand(Intent intent, int flags, int startId)65 public int onStartCommand(Intent intent, int flags, int startId) { 66 if (null != intent) { 67 String action = intent.getAction(); 68 if (Constants.ACTION_DISMISS.equals(action)) { 69 // We need to dismiss the wearable notification. We delete the data item that 70 // created the notification and that is how we inform the phone 71 int notificationId = intent.getIntExtra(Constants.KEY_NOTIFICATION_ID, -1); 72 if (notificationId == Constants.BOTH_ID) { 73 dismissPhoneNotification(notificationId); 74 } 75 } 76 } 77 return super.onStartCommand(intent, flags, startId); 78 } 79 80 /** 81 * Dismisses the phone notification, via a {@link android.app.PendingIntent} that is triggered 82 * when the user dismisses the local notification. Deleting the corresponding data item notifies 83 * the {@link com.google.android.gms.wearable.WearableListenerService} on the phone that the 84 * matching notification on the phone side should be removed. 85 */ dismissPhoneNotification(int id)86 private void dismissPhoneNotification(int id) { 87 mGoogleApiClient.connect(); 88 } 89 90 @Override onDataChanged(DataEventBuffer dataEvents)91 public void onDataChanged(DataEventBuffer dataEvents) { 92 for (DataEvent dataEvent : dataEvents) { 93 if (dataEvent.getType() == DataEvent.TYPE_CHANGED) { 94 DataMap dataMap = DataMapItem.fromDataItem(dataEvent.getDataItem()).getDataMap(); 95 String content = dataMap.getString(Constants.KEY_CONTENT); 96 String title = dataMap.getString(Constants.KEY_TITLE); 97 if (Constants.WATCH_ONLY_PATH.equals(dataEvent.getDataItem().getUri().getPath())) { 98 buildWearableOnlyNotification(title, content, false); 99 } else if (Constants.BOTH_PATH.equals(dataEvent.getDataItem().getUri().getPath())) { 100 buildWearableOnlyNotification(title, content, true); 101 } 102 } else if (dataEvent.getType() == DataEvent.TYPE_DELETED) { 103 if (Log.isLoggable(TAG, Log.DEBUG)) { 104 Log.d(TAG, "DataItem deleted: " + dataEvent.getDataItem().getUri().getPath()); 105 } 106 if (Constants.BOTH_PATH.equals(dataEvent.getDataItem().getUri().getPath())) { 107 // Dismiss the corresponding notification 108 ((NotificationManager) getSystemService(NOTIFICATION_SERVICE)) 109 .cancel(Constants.WATCH_ONLY_ID); 110 } 111 } 112 } 113 } 114 115 /** 116 * Builds a simple notification on the wearable. 117 */ buildWearableOnlyNotification(String title, String content, boolean withDismissal)118 private void buildWearableOnlyNotification(String title, String content, 119 boolean withDismissal) { 120 Notification.Builder builder = new Notification.Builder(this) 121 .setSmallIcon(R.drawable.ic_launcher) 122 .setContentTitle(title) 123 .setContentText(content); 124 125 if (withDismissal) { 126 Intent dismissIntent = new Intent(Constants.ACTION_DISMISS); 127 dismissIntent.putExtra(Constants.KEY_NOTIFICATION_ID, Constants.BOTH_ID); 128 PendingIntent pendingIntent = PendingIntent 129 .getService(this, 0, dismissIntent, PendingIntent.FLAG_UPDATE_CURRENT); 130 builder.setDeleteIntent(pendingIntent); 131 } 132 133 ((NotificationManager) getSystemService(NOTIFICATION_SERVICE)) 134 .notify(Constants.WATCH_ONLY_ID, builder.build()); 135 } 136 137 @Override onConnected(Bundle bundle)138 public void onConnected(Bundle bundle) { 139 final Uri dataItemUri = 140 new Uri.Builder().scheme(WEAR_URI_SCHEME).path(Constants.BOTH_PATH).build(); 141 if (Log.isLoggable(TAG, Log.DEBUG)) { 142 Log.d(TAG, "Deleting Uri: " + dataItemUri.toString()); 143 } 144 Wearable.DataApi.deleteDataItems( 145 mGoogleApiClient, dataItemUri).setResultCallback(this); 146 } 147 148 @Override onConnectionSuspended(int i)149 public void onConnectionSuspended(int i) { 150 } 151 152 @Override onConnectionFailed(ConnectionResult connectionResult)153 public void onConnectionFailed(ConnectionResult connectionResult) { 154 } 155 156 @Override onResult(DataApi.DeleteDataItemsResult deleteDataItemsResult)157 public void onResult(DataApi.DeleteDataItemsResult deleteDataItemsResult) { 158 if (!deleteDataItemsResult.getStatus().isSuccess()) { 159 Log.e(TAG, "dismissWearableNotification(): failed to delete DataItem"); 160 } 161 mGoogleApiClient.disconnect(); 162 } 163 } 164