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