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