1
1
package org .codechimp .ttw ;
2
2
3
+ import android .content .DialogInterface ;
4
+ import android .content .Intent ;
3
5
import android .content .pm .PackageManager ;
6
+ import android .net .Uri ;
4
7
import android .os .Bundle ;
8
+ import android .os .Handler ;
9
+ import android .os .ResultReceiver ;
10
+ import android .support .annotation .NonNull ;
11
+ import android .support .annotation .Nullable ;
5
12
import android .support .v4 .app .Fragment ;
13
+ import android .support .v7 .app .AlertDialog ;
6
14
import android .support .v7 .app .AppCompatActivity ;
7
15
import android .support .v7 .widget .Toolbar ;
16
+ import android .util .Log ;
8
17
import android .view .LayoutInflater ;
9
18
import android .view .Menu ;
10
19
import android .view .MenuItem ;
11
20
import android .view .View ;
12
21
import android .view .ViewGroup ;
13
22
import android .widget .Button ;
23
+ import android .widget .Toast ;
14
24
15
- public class MainActivity extends AppCompatActivity {
25
+ import com .google .android .gms .common .ConnectionResult ;
26
+ import com .google .android .gms .common .api .GoogleApiClient ;
27
+ import com .google .android .gms .common .api .PendingResult ;
28
+ import com .google .android .gms .common .api .ResultCallback ;
29
+ import com .google .android .gms .wearable .CapabilityApi ;
30
+ import com .google .android .gms .wearable .CapabilityInfo ;
31
+ import com .google .android .gms .wearable .Node ;
32
+ import com .google .android .gms .wearable .NodeApi ;
33
+ import com .google .android .gms .wearable .Wearable ;
34
+ import com .google .android .wearable .intent .RemoteIntent ;
35
+
36
+ import java .util .ArrayList ;
37
+ import java .util .List ;
38
+ import java .util .Set ;
39
+
40
+ public class MainActivity extends AppCompatActivity
41
+ implements
42
+ GoogleApiClient .ConnectionCallbacks ,
43
+ GoogleApiClient .OnConnectionFailedListener ,
44
+ CapabilityApi .CapabilityListener {
45
+
46
+ private static final String TAG = "TTW" ;
47
+
48
+ private GoogleApiClient googleApiClient ;
49
+
50
+ // Name of capability listed in Wear app's wear.xml.
51
+ // IMPORTANT NOTE: This should be named differently than your Phone app's capability.
52
+ private static final String CAPABILITY_WEAR_APP = "verify_remote_ttw_wear_app" ;
53
+
54
+ private Set <Node > wearNodesWithApp ;
55
+ private List <Node > allConnectedNodes ;
56
+
57
+ // Result from sending RemoteIntent to wear device(s) to open app in play/app store.
58
+ private final ResultReceiver resultReceiver = new ResultReceiver (new Handler ()) {
59
+ @ Override
60
+ protected void onReceiveResult (int resultCode , Bundle resultData ) {
61
+ Log .d (TAG , "onReceiveResult: " + resultCode );
62
+
63
+ if (resultCode == RemoteIntent .RESULT_OK ) {
64
+ // Do nothing
65
+ } else if (resultCode == RemoteIntent .RESULT_FAILED ) {
66
+ Toast toast = Toast .makeText (
67
+ getApplicationContext (),
68
+ R .string .no_watch_app_remote_error ,
69
+ Toast .LENGTH_LONG );
70
+ toast .show ();
71
+
72
+ } else {
73
+ throw new IllegalStateException ("Unexpected result " + resultCode );
74
+ }
75
+ }
76
+ };
16
77
17
78
@ Override
18
79
protected void onCreate (Bundle savedInstanceState ) {
@@ -39,5 +100,197 @@ public void onClick(View v) {
39
100
finish ();
40
101
}
41
102
});
103
+
104
+ googleApiClient = new GoogleApiClient .Builder (this )
105
+ .addApi (Wearable .API )
106
+ .addConnectionCallbacks (this )
107
+ .build ();
108
+ }
109
+
110
+ @ Override
111
+ protected void onStop () {
112
+ googleApiClient .disconnect ();
113
+ super .onStop ();
114
+ }
115
+
116
+ @ Override
117
+ protected void onPause () {
118
+ super .onPause ();
119
+
120
+ if ((googleApiClient != null ) && googleApiClient .isConnected ()) {
121
+
122
+ Wearable .CapabilityApi .removeCapabilityListener (
123
+ googleApiClient ,
124
+ this ,
125
+ CAPABILITY_WEAR_APP );
126
+
127
+ googleApiClient .disconnect ();
128
+ }
129
+ }
130
+
131
+ @ Override
132
+ protected void onResume () {
133
+ super .onResume ();
134
+
135
+ if (googleApiClient != null ) {
136
+ googleApiClient .connect ();
137
+ }
138
+ }
139
+
140
+ @ Override
141
+ public void onConnected (@ Nullable Bundle bundle ) {
142
+ Log .d (TAG , "onConnected()" );
143
+
144
+ // Set up listeners for capability changes (install/uninstall of remote app).
145
+ Wearable .CapabilityApi .addCapabilityListener (
146
+ googleApiClient ,
147
+ this ,
148
+ CAPABILITY_WEAR_APP );
149
+
150
+ // Initial request for devices with our capability, aka, our Wear app installed.
151
+ findWearDevicesWithApp ();
152
+
153
+ // Initial request for all Wear devices connected (with or without our capability).
154
+ // Additional Note: Because there isn't a listener for ALL Nodes added/removed from network
155
+ // that isn't deprecated, we simply update the full list when the Google API Client is
156
+ // connected and when capability changes come through in the onCapabilityChanged() method.
157
+ findAllWearDevices ();
158
+ }
159
+
160
+
161
+ @ Override
162
+ public void onConnectionSuspended (int i ) {
163
+ Log .d (TAG , "onConnectionSuspended(): connection to location client suspended: " + i );
164
+ }
165
+
166
+ @ Override
167
+ public void onConnectionFailed (@ NonNull ConnectionResult connectionResult ) {
168
+ Log .e (TAG , "onConnectionFailed(): " + connectionResult );
169
+ }
170
+
171
+ @ Override
172
+ public void onCapabilityChanged (CapabilityInfo capabilityInfo ) {
173
+ Log .d (TAG , "onCapabilityChanged(): " + capabilityInfo );
174
+
175
+ wearNodesWithApp = capabilityInfo .getNodes ();
176
+
177
+ // Because we have an updated list of devices with/without our app, we need to also update
178
+ // our list of active Wear devices.
179
+ findAllWearDevices ();
180
+
181
+ verifyNodeAndUpdateUI ();
182
+ }
183
+
184
+ private void findWearDevicesWithApp () {
185
+ Log .d (TAG , "findWearDevicesWithApp()" );
186
+
187
+ // You can filter this by FILTER_REACHABLE if you only want to open Nodes (Wear Devices)
188
+ // directly connect to your phone.
189
+ PendingResult <CapabilityApi .GetCapabilityResult > pendingResult =
190
+ Wearable .CapabilityApi .getCapability (
191
+ googleApiClient ,
192
+ CAPABILITY_WEAR_APP ,
193
+ CapabilityApi .FILTER_ALL );
194
+
195
+ pendingResult .setResultCallback (new ResultCallback <CapabilityApi .GetCapabilityResult >() {
196
+ @ Override
197
+ public void onResult (@ NonNull CapabilityApi .GetCapabilityResult getCapabilityResult ) {
198
+ Log .d (TAG , "onResult(): " + getCapabilityResult );
199
+
200
+ if (getCapabilityResult .getStatus ().isSuccess ()) {
201
+ CapabilityInfo capabilityInfo = getCapabilityResult .getCapability ();
202
+ wearNodesWithApp = capabilityInfo .getNodes ();
203
+ verifyNodeAndUpdateUI ();
204
+
205
+ } else {
206
+ Log .d (TAG , "Failed CapabilityApi: " + getCapabilityResult .getStatus ());
207
+ }
208
+ }
209
+ });
210
+ }
211
+
212
+ private void findAllWearDevices () {
213
+ Log .d (TAG , "findAllWearDevices()" );
214
+
215
+ PendingResult <NodeApi .GetConnectedNodesResult > pendingResult =
216
+ Wearable .NodeApi .getConnectedNodes (googleApiClient );
217
+
218
+ pendingResult .setResultCallback (new ResultCallback <NodeApi .GetConnectedNodesResult >() {
219
+ @ Override
220
+ public void onResult (@ NonNull NodeApi .GetConnectedNodesResult getConnectedNodesResult ) {
221
+
222
+ if (getConnectedNodesResult .getStatus ().isSuccess ()) {
223
+ allConnectedNodes = getConnectedNodesResult .getNodes ();
224
+ verifyNodeAndUpdateUI ();
225
+
226
+ } else {
227
+ Log .d (TAG , "Failed CapabilityApi: " + getConnectedNodesResult .getStatus ());
228
+ }
229
+ }
230
+ });
231
+ }
232
+
233
+ private void verifyNodeAndUpdateUI () {
234
+ Log .d (TAG , "verifyNodeAndUpdateUI()" );
235
+
236
+ if ((wearNodesWithApp == null ) || (allConnectedNodes == null )) {
237
+ Log .d (TAG , "Waiting on Results for both connected nodes and nodes with app" );
238
+
239
+ } else if (allConnectedNodes .isEmpty ()) {
240
+ Log .d (TAG , "No wear devices" );
241
+
242
+ } else if (wearNodesWithApp .isEmpty ()) {
243
+ Log .d (TAG , "No devices with app" );
244
+ showRemoteInstallDialog ();
245
+
246
+ } else if (wearNodesWithApp .size () < allConnectedNodes .size ()) {
247
+ Log .d (TAG , String .format ("Installed on some devices (%s)" , wearNodesWithApp ));
248
+ } else {
249
+ Log .d (TAG , String .format ("Installed on all devices (%s)" , wearNodesWithApp ));
250
+ }
251
+ }
252
+
253
+ protected void showRemoteInstallDialog () {
254
+ new AlertDialog .Builder (this )
255
+ .setTitle (R .string .no_watch_app_title )
256
+ .setMessage (R .string .no_watch_app_description )
257
+ .setNegativeButton (android .R .string .cancel , null )
258
+ .setPositiveButton (R .string .no_watch_app_button , new DialogInterface .OnClickListener () {
259
+ @ Override
260
+ public void onClick (DialogInterface dialog , int which ) {
261
+ openPlayStoreOnWearDevicesWithoutApp ();
262
+ }
263
+ })
264
+ .show ();
265
+ }
266
+
267
+ private void openPlayStoreOnWearDevicesWithoutApp () {
268
+ Log .d (TAG , "openPlayStoreOnWearDevicesWithoutApp()" );
269
+
270
+ // Create a List of Nodes (Wear devices) without your app.
271
+ ArrayList <Node > nodesWithoutApp = new ArrayList <>();
272
+
273
+ for (Node node : allConnectedNodes ) {
274
+ if (!wearNodesWithApp .contains (node )) {
275
+ nodesWithoutApp .add (node );
276
+ }
277
+ }
278
+
279
+ if (!nodesWithoutApp .isEmpty ()) {
280
+ Log .d (TAG , "Number of nodes without app: " + nodesWithoutApp .size ());
281
+
282
+ Intent intent =
283
+ new Intent (Intent .ACTION_VIEW )
284
+ .addCategory (Intent .CATEGORY_BROWSABLE )
285
+ .setData (Uri .parse ("market://details?id=" + getPackageName ()));
286
+
287
+ for (Node node : nodesWithoutApp ) {
288
+ RemoteIntent .startRemoteActivity (
289
+ getApplicationContext (),
290
+ intent ,
291
+ resultReceiver ,
292
+ node .getId ());
293
+ }
294
+ }
42
295
}
43
296
}
0 commit comments