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.findphone; 18 19 import android.app.Notification; 20 import android.app.NotificationManager; 21 import android.os.Bundle; 22 import android.util.Log; 23 24 import com.google.android.gms.common.api.GoogleApiClient; 25 import com.google.android.gms.common.api.ResultCallback; 26 import com.google.android.gms.wearable.CapabilityApi; 27 import com.google.android.gms.wearable.CapabilityInfo; 28 import com.google.android.gms.wearable.Node; 29 import com.google.android.gms.wearable.Wearable; 30 import com.google.android.gms.wearable.WearableListenerService; 31 32 import java.util.List; 33 import java.util.Set; 34 35 /** 36 * Listens for changes in connectivity between this wear device and the phone. More precisely, we 37 * need to distinguish the case that the wear device and the phone are connected directly from all 38 * other possible cases. To this end, the phone app has registered itself to provide the "find_me" 39 * capability and we need to look for connected nodes that provide this capability AND are nearby, 40 * to exclude a connection through the cloud. The proper way would have been to use the 41 * {@code onCapabilitiesChanged()} callback but currently that callback cannot discover the case 42 * where a connection switches from wifi to direct; this shortcoming will be addressed in future 43 * updates but for now we will use the {@code onConnectedNodes()} callback. 44 */ 45 public class DisconnectListenerService extends WearableListenerService 46 implements GoogleApiClient.ConnectionCallbacks { 47 48 private static final String TAG = "ExampleFindPhoneApp"; 49 50 private static final int FORGOT_PHONE_NOTIFICATION_ID = 1; 51 52 /* the capability that the phone app would provide */ 53 private static final String FIND_ME_CAPABILITY_NAME = "find_me"; 54 55 private GoogleApiClient mGoogleApiClient; 56 57 @Override 58 public void onCreate() { 59 super.onCreate(); 60 mGoogleApiClient = new GoogleApiClient.Builder(this) 61 .addApi(Wearable.API) 62 .addConnectionCallbacks(this) 63 .build(); 64 } 65 66 @Override 67 public void onConnectedNodes(List<Node> connectedNodes) { 68 // After we are notified by this callback, we need to query for the nodes that provide the 69 // "find_me" capability and are directly connected. 70 if (mGoogleApiClient.isConnected()) { 71 setOrUpdateNotification(); 72 } else if (!mGoogleApiClient.isConnecting()) { 73 mGoogleApiClient.connect(); 74 } 75 } 76 77 private void setOrUpdateNotification() { 78 Wearable.CapabilityApi.getCapability( 79 mGoogleApiClient, FIND_ME_CAPABILITY_NAME, 80 CapabilityApi.FILTER_REACHABLE).setResultCallback( 81 new ResultCallback<CapabilityApi.GetCapabilityResult>() { 82 @Override 83 public void onResult(CapabilityApi.GetCapabilityResult result) { 84 if (result.getStatus().isSuccess()) { 85 updateFindMeCapability(result.getCapability()); 86 } else { 87 Log.e(TAG, 88 "setOrUpdateNotification() Failed to get capabilities, " 89 + "status: " 90 + result.getStatus().getStatusMessage()); 91 } 92 } 93 }); 94 } 95 96 private void updateFindMeCapability(CapabilityInfo capabilityInfo) { 97 Set<Node> connectedNodes = capabilityInfo.getNodes(); 98 if (connectedNodes.isEmpty()) { 99 setupLostConnectivityNotification(); 100 } else { 101 for (Node node : connectedNodes) { 102 // we are only considering those nodes that are directly connected 103 if (node.isNearby()) { 104 ((NotificationManager) getSystemService(NOTIFICATION_SERVICE)) 105 .cancel(FORGOT_PHONE_NOTIFICATION_ID); 106 } 107 } 108 } 109 } 110 111 /** 112 * Creates a notification to inform user that the connectivity to phone has been lost (possibly 113 * left the phone behind). 114 */ 115 private void setupLostConnectivityNotification() { 116 Notification.Builder notificationBuilder = new Notification.Builder(this) 117 .setContentTitle(getString(R.string.left_phone_title)) 118 .setContentText(getString(R.string.left_phone_content)) 119 .setVibrate(new long[]{0, 200}) // Vibrate for 200 milliseconds. 120 .setSmallIcon(R.drawable.ic_launcher) 121 .setLocalOnly(true) 122 .setPriority(Notification.PRIORITY_MAX); 123 Notification card = notificationBuilder.build(); 124 ((NotificationManager) getSystemService(NOTIFICATION_SERVICE)) 125 .notify(FORGOT_PHONE_NOTIFICATION_ID, card); 126 } 127 128 @Override 129 public void onConnected(Bundle bundle) { 130 setOrUpdateNotification(); 131 } 132 133 @Override 134 public void onConnectionSuspended(int cause) { 135 136 } 137 138 @Override 139 public void onDestroy() { 140 if (mGoogleApiClient.isConnected() || mGoogleApiClient.isConnecting()) { 141 mGoogleApiClient.disconnect(); 142 } 143 super.onDestroy(); 144 } 145 } 146