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.agendadata; 18 19 import static com.example.android.wearable.agendadata.Constants.TAG; 20 import static com.example.android.wearable.agendadata.Constants.EXTRA_SILENT; 21 22 import static com.example.android.wearable.agendadata.Constants.ALL_DAY; 23 import static com.example.android.wearable.agendadata.Constants.BEGIN; 24 import static com.example.android.wearable.agendadata.Constants.DESCRIPTION; 25 import static com.example.android.wearable.agendadata.Constants.END; 26 import static com.example.android.wearable.agendadata.Constants.PROFILE_PIC; 27 import static com.example.android.wearable.agendadata.Constants.TITLE; 28 29 import android.app.Notification; 30 import android.app.NotificationManager; 31 import android.app.PendingIntent; 32 import android.content.Intent; 33 import android.graphics.Bitmap; 34 import android.graphics.BitmapFactory; 35 import android.net.Uri; 36 import android.text.TextUtils; 37 import android.text.format.DateFormat; 38 import android.util.Log; 39 40 import com.google.android.gms.common.api.GoogleApiClient; 41 import com.google.android.gms.wearable.Asset; 42 import com.google.android.gms.wearable.DataApi; 43 import com.google.android.gms.wearable.DataEvent; 44 import com.google.android.gms.wearable.DataEventBuffer; 45 import com.google.android.gms.wearable.DataItem; 46 import com.google.android.gms.wearable.DataMap; 47 import com.google.android.gms.wearable.DataMapItem; 48 import com.google.android.gms.wearable.MessageEvent; 49 import com.google.android.gms.wearable.Wearable; 50 import com.google.android.gms.wearable.WearableListenerService; 51 52 import java.util.Date; 53 import java.util.HashMap; 54 import java.util.Map; 55 56 /** 57 * Listens to DataItem events on the home device. 58 */ 59 public class HomeListenerService extends WearableListenerService { 60 61 private static final Map<Uri, Integer> sNotificationIdByDataItemUri = 62 new HashMap<Uri, Integer>(); 63 private static int sNotificationId = 1; 64 private GoogleApiClient mGoogleApiClient; 65 66 @Override 67 public void onDataChanged(DataEventBuffer dataEvents) { 68 if (Log.isLoggable(TAG, Log.DEBUG)) { 69 Log.d(TAG, "onDataChanged: " + dataEvents + " for " + getPackageName()); 70 } 71 for (DataEvent event : dataEvents) { 72 if (event.getType() == DataEvent.TYPE_DELETED) { 73 deleteDataItem(event.getDataItem()); 74 } else if (event.getType() == DataEvent.TYPE_CHANGED) { 75 UpdateNotificationForDataItem(event.getDataItem()); 76 } 77 } 78 dataEvents.close(); 79 } 80 81 @Override 82 public void onCreate() { 83 super.onCreate(); 84 mGoogleApiClient = new GoogleApiClient.Builder(this.getApplicationContext()) 85 .addApi(Wearable.API) 86 .build(); 87 mGoogleApiClient.connect(); 88 } 89 90 /** 91 * Posts a local notification to show calendar card. 92 */ 93 private void UpdateNotificationForDataItem(DataItem dataItem) { 94 DataMapItem mapDataItem = DataMapItem.fromDataItem(dataItem); 95 DataMap data = mapDataItem.getDataMap(); 96 97 String description = data.getString(DESCRIPTION); 98 if (TextUtils.isEmpty(description)) { 99 description = ""; 100 } else { 101 // Add a space between the description and the time of the event. 102 description += " "; 103 } 104 String contentText; 105 if (data.getBoolean(ALL_DAY)) { 106 contentText = getString(R.string.desc_all_day, description); 107 } else { 108 String startTime = DateFormat.getTimeFormat(this).format(new Date(data.getLong(BEGIN))); 109 String endTime = DateFormat.getTimeFormat(this).format(new Date(data.getLong(END))); 110 contentText = getString(R.string.desc_time_period, description, startTime, endTime); 111 } 112 113 Intent deleteOperation = new Intent(this, DeleteService.class); 114 // Use a unique identifier for the delete action. 115 String deleteAction = "action_delete" + dataItem.getUri().toString() + sNotificationId; 116 deleteOperation.setAction(deleteAction); 117 deleteOperation.setData(dataItem.getUri()); 118 PendingIntent deleteIntent = PendingIntent.getService(this, 0, deleteOperation, 119 PendingIntent.FLAG_ONE_SHOT); 120 PendingIntent silentDeleteIntent = PendingIntent.getService(this, 1, 121 deleteOperation.putExtra(EXTRA_SILENT, true), PendingIntent.FLAG_ONE_SHOT); 122 123 Notification.Builder notificationBuilder = new Notification.Builder(this) 124 .setContentTitle(data.getString(TITLE)) 125 .setContentText(contentText) 126 .setSmallIcon(R.drawable.ic_launcher) 127 .addAction(R.drawable.ic_menu_delete, getText(R.string.delete), deleteIntent) 128 .setDeleteIntent(silentDeleteIntent) // Delete DataItem if notification dismissed. 129 .setLocalOnly(true) 130 .setPriority(Notification.PRIORITY_MIN); 131 132 // Set the event owner's profile picture as the notification background. 133 Asset asset = data.getAsset(PROFILE_PIC); 134 if (null != asset) { 135 if (mGoogleApiClient.isConnected()) { 136 DataApi.GetFdForAssetResult assetFdResult = 137 Wearable.DataApi.getFdForAsset(mGoogleApiClient, asset).await(); 138 if (assetFdResult.getStatus().isSuccess()) { 139 Bitmap profilePic = BitmapFactory.decodeStream(assetFdResult.getInputStream()); 140 notificationBuilder.extend(new Notification.WearableExtender() 141 .setBackground(profilePic)); 142 } else if (Log.isLoggable(TAG, Log.DEBUG)) { 143 Log.d(TAG, "asset fetch failed with statusCode: " 144 + assetFdResult.getStatus().getStatusCode()); 145 } 146 } else { 147 Log.e(TAG, "Failed to set notification background" 148 + " - Client disconnected from Google Play Services"); 149 } 150 } 151 Notification card = notificationBuilder.build(); 152 153 ((NotificationManager) getSystemService(NOTIFICATION_SERVICE)) 154 .notify(sNotificationId, card); 155 156 sNotificationIdByDataItemUri.put(dataItem.getUri(), sNotificationId++); 157 } 158 159 /** 160 * Deletes the calendar card associated with the data item. 161 */ 162 private void deleteDataItem(DataItem dataItem) { 163 if (Log.isLoggable(TAG, Log.VERBOSE)) { 164 Log.v(TAG, "onDataItemDeleted:DataItem=" + dataItem.getUri()); 165 } 166 Integer notificationId = sNotificationIdByDataItemUri.remove(dataItem.getUri()); 167 if (notificationId != null) { 168 ((NotificationManager) getSystemService(NOTIFICATION_SERVICE)).cancel(notificationId); 169 } 170 } 171 172 @Override 173 public void onMessageReceived(MessageEvent messageEvent) { 174 if (Log.isLoggable(TAG, Log.DEBUG)) { 175 Log.d(TAG, "onMessageReceived: " + messageEvent.getPath() 176 + " " + messageEvent.getData() + " for " + getPackageName()); 177 } 178 } 179 180 } 181