1 /*
2  * Copyright 2015 Google Inc. All rights reserved.
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.xyztouristattractions.service;
18 
19 import android.app.IntentService;
20 import android.app.NotificationManager;
21 import android.content.Context;
22 import android.content.Intent;
23 import android.util.Log;
24 
25 import com.example.android.xyztouristattractions.R;
26 import com.example.android.xyztouristattractions.common.Constants;
27 import com.example.android.xyztouristattractions.common.Utils;
28 import com.google.android.gms.common.ConnectionResult;
29 import com.google.android.gms.common.api.GoogleApiClient;
30 import com.google.android.gms.wearable.CapabilityApi;
31 import com.google.android.gms.wearable.Node;
32 import com.google.android.gms.wearable.Wearable;
33 
34 import java.util.Iterator;
35 import java.util.Set;
36 import java.util.concurrent.TimeUnit;
37 
38 /**
39  * A utility IntentService, used for a variety of asynchronous background
40  * operations that do not necessarily need to be tied to a UI.
41  */
42 public class UtilityService extends IntentService {
43 
44     private static final String TAG = UtilityService.class.getSimpleName();
45 
46     private static final String ACTION_CLEAR_NOTIFICATION = "clear_notification";
47     private static final String ACTION_CLEAR_REMOTE_NOTIFICATIONS = "clear_remote_notifications";
48     private static final String ACTION_START_DEVICE_ACTIVITY = "start_device_activity";
49     private static final String EXTRA_START_PATH = "start_path";
50     private static final String EXTRA_START_ACTIVITY_INFO = "start_activity_info";
51     private static final long GET_CAPABILITY_TIMEOUT_S = 10;
52 
clearNotification(Context context)53     public static void clearNotification(Context context) {
54         Intent intent = new Intent(context, UtilityService.class);
55         intent.setAction(UtilityService.ACTION_CLEAR_NOTIFICATION);
56         context.startService(intent);
57     }
58 
clearRemoteNotifications(Context context)59     public static void clearRemoteNotifications(Context context) {
60         context.startService(getClearRemoteNotificationsIntent(context));
61     }
62 
getClearRemoteNotificationsIntent(Context context)63     public static Intent getClearRemoteNotificationsIntent(Context context) {
64         Intent intent = new Intent(context, UtilityService.class);
65         intent.setAction(UtilityService.ACTION_CLEAR_REMOTE_NOTIFICATIONS);
66         return intent;
67     }
68 
69     /**
70      * Trigger a message that asks the master device to start an activity.
71      *
72      * @param context the context
73      * @param path the path that will be sent via the wearable message API
74      * @param name the tourist attraction name
75      * @param city the tourist attraction city
76      */
startDeviceActivity(Context context, String path, String name, String city)77     public static void startDeviceActivity(Context context, String path, String name, String city) {
78         Intent intent = new Intent(context, UtilityService.class);
79         intent.setAction(UtilityService.ACTION_START_DEVICE_ACTIVITY);
80         String extraInfo;
81         if (Constants.START_ATTRACTION_PATH.equals(path)) {
82             extraInfo = name;
83         } else {
84             extraInfo = name + ", " + city;
85         }
86         intent.putExtra(EXTRA_START_ACTIVITY_INFO, extraInfo);
87         intent.putExtra(EXTRA_START_PATH, path);
88         context.startService(intent);
89     }
90 
UtilityService()91     public UtilityService() {
92         super(TAG);
93     }
94 
95     @Override
onHandleIntent(Intent intent)96     protected void onHandleIntent(Intent intent) {
97         String action = intent != null ? intent.getAction() : null;
98         if (ACTION_CLEAR_NOTIFICATION.equals(action)) {
99             clearNotificationInternal();
100         } else if (ACTION_CLEAR_REMOTE_NOTIFICATIONS.equals(action)) {
101             clearRemoteNotificationsInternal();
102         } else if (ACTION_START_DEVICE_ACTIVITY.equals(action)) {
103             startDeviceActivityInternal(intent.getStringExtra(EXTRA_START_PATH),
104                     intent.getStringExtra(EXTRA_START_ACTIVITY_INFO));
105         }
106     }
107 
108     /**
109      * Clear the local notifications
110      */
clearNotificationInternal()111     private void clearNotificationInternal() {
112         NotificationManager notificationManager =
113                 (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
114         notificationManager.cancel(Constants.WEAR_NOTIFICATION_ID);
115     }
116 
117     /**
118      * Trigger a message to ask other devices to clear their notifications
119      */
clearRemoteNotificationsInternal()120     private void clearRemoteNotificationsInternal() {
121         GoogleApiClient googleApiClient = new GoogleApiClient.Builder(this)
122                 .addApi(Wearable.API)
123                 .build();
124 
125         ConnectionResult connectionResult = googleApiClient.blockingConnect(
126                 Constants.GOOGLE_API_CLIENT_TIMEOUT_S, TimeUnit.SECONDS);
127 
128         if (connectionResult.isSuccess() && googleApiClient.isConnected()) {
129             Iterator<String> itr = Utils.getNodes(googleApiClient).iterator();
130             while (itr.hasNext()) {
131                 // Loop through all connected nodes
132                 Wearable.MessageApi.sendMessage(
133                         googleApiClient, itr.next(), Constants.CLEAR_NOTIFICATIONS_PATH, null);
134             }
135         }
136 
137         googleApiClient.disconnect();
138     }
139 
140     /**
141      * Sends the actual message to ask other devices that are capable of showing "details" to start
142      * the appropriate activity
143      *
144      * @param path the path to pass to the wearable message API
145      * @param extraInfo extra info that varies based on the path being sent
146      */
startDeviceActivityInternal(String path, String extraInfo)147     private void startDeviceActivityInternal(String path, String extraInfo) {
148         GoogleApiClient googleApiClient = new GoogleApiClient.Builder(this)
149                 .addApi(Wearable.API)
150                 .build();
151 
152         ConnectionResult connectionResult = googleApiClient.blockingConnect(
153                 Constants.GOOGLE_API_CLIENT_TIMEOUT_S, TimeUnit.SECONDS);
154 
155         if (connectionResult.isSuccess() && googleApiClient.isConnected()) {
156             CapabilityApi.GetCapabilityResult result = Wearable.CapabilityApi.getCapability(
157                     googleApiClient,
158                     getApplicationContext().getString(R.string.show_detail_capability_name),
159                     CapabilityApi.FILTER_REACHABLE)
160                     .await(GET_CAPABILITY_TIMEOUT_S, TimeUnit.SECONDS);
161             if (result.getStatus().isSuccess()) {
162                 Set<Node> nodes = result.getCapability().getNodes();
163                 for (Node node : nodes) {
164                     Wearable.MessageApi.sendMessage(
165                             googleApiClient, node.getId(), path, extraInfo.getBytes());
166                 }
167             } else {
168                 Log.e(TAG, "startDeviceActivityInternal() Failed to get capabilities, status: "
169                         + result.getStatus().getStatusMessage());
170             }
171 
172             googleApiClient.disconnect();
173         }
174     }
175 
176 }
177