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