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.delayedconfirmation; 18 19 import android.app.Activity; 20 import android.app.Notification; 21 import android.app.NotificationManager; 22 import android.os.Bundle; 23 import android.support.wearable.view.DelayedConfirmationView; 24 import android.util.Log; 25 import android.view.View; 26 import android.widget.Toast; 27 28 import com.google.android.gms.common.ConnectionResult; 29 import com.google.android.gms.common.api.GoogleApiClient; 30 import com.google.android.gms.common.api.ResultCallback; 31 import com.google.android.gms.wearable.CapabilityApi; 32 import com.google.android.gms.wearable.CapabilityInfo; 33 import com.google.android.gms.wearable.MessageApi; 34 import com.google.android.gms.wearable.Node; 35 import com.google.android.gms.wearable.Wearable; 36 37 import java.util.Set; 38 39 public class MainActivity extends Activity implements 40 DelayedConfirmationView.DelayedConfirmationListener, 41 GoogleApiClient.OnConnectionFailedListener, GoogleApiClient.ConnectionCallbacks, 42 CapabilityApi.CapabilityListener { 43 44 private static final String TAG = "DelayedConfirmation"; 45 private static final int NUM_SECONDS = 5; 46 47 private static final String TIMER_SELECTED_PATH = "/timer_selected"; 48 private static final String TIMER_FINISHED_PATH = "/timer_finished"; 49 50 /* name of the capability that the phone side provides */ 51 private static final String CONFIRMATION_HANDLER_CAPABILITY_NAME = "confirmation_handler"; 52 53 private DelayedConfirmationView delayedConfirmationView; 54 private GoogleApiClient mGoogleApiClient; 55 56 /* the preferred note that can handle the confirmation capability */ 57 private Node mConfirmationHandlerNode; 58 59 @Override onCreate(Bundle b)60 public void onCreate(Bundle b) { 61 super.onCreate(b); 62 setContentView(R.layout.main_activity); 63 delayedConfirmationView = (DelayedConfirmationView) findViewById(R.id.delayed_confirmation); 64 delayedConfirmationView.setTotalTimeMs(NUM_SECONDS * 1000); 65 mGoogleApiClient = new GoogleApiClient.Builder(this) 66 .addApi(Wearable.API) 67 .addOnConnectionFailedListener(this) 68 .addConnectionCallbacks(this) 69 .build(); 70 } 71 72 @Override onResume()73 protected void onResume() { 74 super.onResume(); 75 if (!mGoogleApiClient.isConnected()) { 76 mGoogleApiClient.connect(); 77 } 78 } 79 80 @Override onPause()81 protected void onPause() { 82 if (mGoogleApiClient.isConnected()) { 83 Wearable.CapabilityApi.removeCapabilityListener(mGoogleApiClient, this, 84 CONFIRMATION_HANDLER_CAPABILITY_NAME); 85 mGoogleApiClient.disconnect(); 86 } 87 super.onPause(); 88 } 89 90 /** 91 * Starts the DelayedConfirmationView when user presses "Start Timer" button. 92 */ onStartTimer(View view)93 public void onStartTimer(View view) { 94 delayedConfirmationView.start(); 95 delayedConfirmationView.setListener(this); 96 } 97 98 @Override onTimerSelected(View v)99 public void onTimerSelected(View v) { 100 v.setPressed(true); 101 Notification notification = new Notification.Builder(this) 102 .setSmallIcon(R.drawable.ic_launcher) 103 .setContentTitle(getString(R.string.notification_title)) 104 .setContentText(getString(R.string.notification_timer_selected)) 105 .build(); 106 ((NotificationManager) getSystemService(NOTIFICATION_SERVICE)).notify(0, notification); 107 sendMessageToCompanion(TIMER_SELECTED_PATH); 108 // Prevent onTimerFinished from being heard. 109 ((DelayedConfirmationView) v).setListener(null); 110 finish(); 111 } 112 113 @Override onTimerFinished(View v)114 public void onTimerFinished(View v) { 115 Notification notification = new Notification.Builder(this) 116 .setSmallIcon(R.drawable.ic_launcher) 117 .setContentTitle(getString(R.string.notification_title)) 118 .setContentText(getString(R.string.notification_timer_finished)) 119 .build(); 120 ((NotificationManager) getSystemService(NOTIFICATION_SERVICE)).notify(0, notification); 121 sendMessageToCompanion(TIMER_FINISHED_PATH); 122 finish(); 123 } 124 125 @Override onConnectionFailed(ConnectionResult connectionResult)126 public void onConnectionFailed(ConnectionResult connectionResult) { 127 Log.e(TAG, "Failed to connect to Google Api Client"); 128 mConfirmationHandlerNode = null; 129 } 130 sendMessageToCompanion(final String path)131 private void sendMessageToCompanion(final String path) { 132 if (mConfirmationHandlerNode != null) { 133 Wearable.MessageApi.sendMessage(mGoogleApiClient, mConfirmationHandlerNode.getId(), 134 path, new byte[0]) 135 .setResultCallback(getSendMessageResultCallback(mConfirmationHandlerNode)); 136 } else { 137 Toast.makeText(this, R.string.no_device_found, Toast.LENGTH_SHORT).show(); 138 } 139 } 140 getSendMessageResultCallback( final Node node)141 private ResultCallback<MessageApi.SendMessageResult> getSendMessageResultCallback( 142 final Node node) { 143 return new ResultCallback<MessageApi.SendMessageResult>() { 144 @Override 145 public void onResult(MessageApi.SendMessageResult sendMessageResult) { 146 if (!sendMessageResult.getStatus().isSuccess()) { 147 Log.e(TAG, "Failed to send message with status " 148 + sendMessageResult.getStatus()); 149 } else { 150 Log.d(TAG, "Sent confirmation message to node " + node.getDisplayName()); 151 } 152 } 153 }; 154 } 155 156 private void setupConfirmationHandlerNode() { 157 Wearable.CapabilityApi.addCapabilityListener( 158 mGoogleApiClient, this, CONFIRMATION_HANDLER_CAPABILITY_NAME); 159 160 Wearable.CapabilityApi.getCapability( 161 mGoogleApiClient, CONFIRMATION_HANDLER_CAPABILITY_NAME, 162 CapabilityApi.FILTER_REACHABLE).setResultCallback( 163 new ResultCallback<CapabilityApi.GetCapabilityResult>() { 164 @Override 165 public void onResult(CapabilityApi.GetCapabilityResult result) { 166 if (!result.getStatus().isSuccess()) { 167 Log.e(TAG, "setupConfirmationHandlerNode() Failed to get capabilities, " 168 + "status: " + result.getStatus().getStatusMessage()); 169 return; 170 } 171 updateConfirmationCapability(result.getCapability()); 172 } 173 }); 174 } 175 176 private void updateConfirmationCapability(CapabilityInfo capabilityInfo) { 177 Set<Node> connectedNodes = capabilityInfo.getNodes(); 178 if (connectedNodes.isEmpty()) { 179 mConfirmationHandlerNode = null; 180 } else { 181 mConfirmationHandlerNode = pickBestNode(connectedNodes); 182 } 183 } 184 185 /** 186 * We pick a node that is capabale of handling the confirmation. If there is more than one, 187 * then we would prefer the one that is directly connected to this device. In general, 188 * depending on the situation and requirements, the "best" node might be picked based on other 189 * criteria. 190 */ 191 private Node pickBestNode(Set<Node> connectedNodes) { 192 Node best = null; 193 if (connectedNodes != null) { 194 for (Node node : connectedNodes) { 195 if (node.isNearby()) { 196 return node; 197 } 198 best = node; 199 } 200 } 201 return best; 202 } 203 204 @Override 205 public void onConnected(Bundle bundle) { 206 setupConfirmationHandlerNode(); 207 } 208 209 @Override 210 public void onConnectionSuspended(int cause) { 211 mConfirmationHandlerNode = null; 212 } 213 214 @Override 215 public void onCapabilityChanged(CapabilityInfo capabilityInfo) { 216 updateConfirmationCapability(capabilityInfo); 217 } 218 } 219