Home | History | Annotate | Download | only in com.example.android.wearable.wear.wearverifyremoteapp
      1 /*
      2  * Copyright (C) 2016 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 package com.example.android.wearable.wear.wearverifyremoteapp;
     17 
     18 import android.content.Intent;
     19 import android.net.Uri;
     20 import android.os.Bundle;
     21 import android.os.Handler;
     22 import android.os.ResultReceiver;
     23 import android.support.annotation.NonNull;
     24 import android.support.annotation.Nullable;
     25 import android.support.v7.app.AppCompatActivity;
     26 import android.util.Log;
     27 import android.view.View;
     28 import android.widget.Button;
     29 import android.widget.TextView;
     30 import android.widget.Toast;
     31 
     32 import com.google.android.gms.common.ConnectionResult;
     33 import com.google.android.gms.common.api.GoogleApiClient;
     34 import com.google.android.gms.common.api.PendingResult;
     35 import com.google.android.gms.common.api.ResultCallback;
     36 import com.google.android.gms.wearable.CapabilityApi;
     37 import com.google.android.gms.wearable.CapabilityInfo;
     38 import com.google.android.gms.wearable.Node;
     39 import com.google.android.gms.wearable.NodeApi;
     40 import com.google.android.gms.wearable.Wearable;
     41 import com.google.android.wearable.intent.RemoteIntent;
     42 
     43 import java.util.ArrayList;
     44 import java.util.List;
     45 import java.util.Set;
     46 
     47 /**
     48  * Checks if the sample's Wear app is installed on remote Wear device(s). If it is not, allows the
     49  * user to open the app listing on the Wear devices' Play Store.
     50  */
     51 public class MainMobileActivity extends AppCompatActivity implements
     52         GoogleApiClient.ConnectionCallbacks,
     53         GoogleApiClient.OnConnectionFailedListener,
     54         CapabilityApi.CapabilityListener {
     55 
     56     private static final String TAG = "MainMobileActivity";
     57 
     58     private static final String WELCOME_MESSAGE = "Welcome to our Mobile app!\n\n";
     59 
     60     private static final String CHECKING_MESSAGE =
     61             WELCOME_MESSAGE + "Checking for Wear Devices for app...\n";
     62 
     63     private static final String NO_DEVICES =
     64             WELCOME_MESSAGE
     65                     + "You have no Wear devices linked to your phone at this time.\n";
     66 
     67     private static final String MISSING_ALL_MESSAGE =
     68             WELCOME_MESSAGE
     69                     + "You are missing the Wear app on all your Wear Devices, please click on the "
     70                     + "button below to install it on those device(s).\n";
     71 
     72     private static final String INSTALLED_SOME_DEVICES_MESSAGE =
     73             WELCOME_MESSAGE
     74                     + "Wear app installed on some your device(s) (%s)!\n\nYou can now use the "
     75                     + "MessageApi, DataApi, etc.\n\n"
     76                     + "To install the Wear app on the other devices, please click on the button "
     77                     + "below.\n";
     78 
     79     private static final String INSTALLED_ALL_DEVICES_MESSAGE =
     80             WELCOME_MESSAGE
     81                     + "Wear app installed on all your devices (%s)!\n\nYou can now use the "
     82                     + "MessageApi, DataApi, etc.";
     83 
     84     // Name of capability listed in Wear app's wear.xml.
     85     // IMPORTANT NOTE: This should be named differently than your Phone app's capability.
     86     private static final String CAPABILITY_WEAR_APP = "verify_remote_example_wear_app";
     87 
     88     // Links to Wear app (Play Store).
     89     // TODO: Replace with your links/packages.
     90     private static final String PLAY_STORE_APP_URI =
     91             "market://details?id=com.example.android.wearable.wear.wearverifyremoteapp";
     92 
     93     // Result from sending RemoteIntent to wear device(s) to open app in play/app store.
     94     private final ResultReceiver mResultReceiver = new ResultReceiver(new Handler()) {
     95         @Override
     96         protected void onReceiveResult(int resultCode, Bundle resultData) {
     97             Log.d(TAG, "onReceiveResult: " + resultCode);
     98 
     99             if (resultCode == RemoteIntent.RESULT_OK) {
    100                 Toast toast = Toast.makeText(
    101                         getApplicationContext(),
    102                         "Play Store Request to Wear device successful.",
    103                         Toast.LENGTH_SHORT);
    104                 toast.show();
    105 
    106             } else if (resultCode == RemoteIntent.RESULT_FAILED) {
    107                 Toast toast = Toast.makeText(
    108                         getApplicationContext(),
    109                         "Play Store Request Failed. Wear device(s) may not support Play Store, "
    110                                 + " that is, the Wear device may be version 1.0.",
    111                         Toast.LENGTH_LONG);
    112                 toast.show();
    113 
    114             } else {
    115                 throw new IllegalStateException("Unexpected result " + resultCode);
    116             }
    117         }
    118     };
    119 
    120     private TextView mInformationTextView;
    121     private Button mRemoteOpenButton;
    122 
    123     private Set<Node> mWearNodesWithApp;
    124     private List<Node> mAllConnectedNodes;
    125 
    126     private GoogleApiClient mGoogleApiClient;
    127 
    128     @Override
    129     protected void onCreate(Bundle savedInstanceState) {
    130         Log.d(TAG, "onCreate()");
    131         super.onCreate(savedInstanceState);
    132         setContentView(R.layout.activity_main);
    133 
    134         mInformationTextView = (TextView) findViewById(R.id.information_text_view);
    135         mRemoteOpenButton = (Button) findViewById(R.id.remote_open_button);
    136 
    137         mInformationTextView.setText(CHECKING_MESSAGE);
    138 
    139         mRemoteOpenButton.setOnClickListener(new View.OnClickListener() {
    140             @Override
    141             public void onClick(View v) {
    142                 openPlayStoreOnWearDevicesWithoutApp();
    143             }
    144         });
    145 
    146         mGoogleApiClient = new GoogleApiClient.Builder(this)
    147                 .addApi(Wearable.API)
    148                 .addConnectionCallbacks(this)
    149                 .addOnConnectionFailedListener(this)
    150                 .build();
    151     }
    152 
    153 
    154     @Override
    155     protected void onPause() {
    156         Log.d(TAG, "onPause()");
    157         super.onPause();
    158 
    159         if ((mGoogleApiClient != null) && mGoogleApiClient.isConnected()) {
    160 
    161             Wearable.CapabilityApi.removeCapabilityListener(
    162                     mGoogleApiClient,
    163                     this,
    164                     CAPABILITY_WEAR_APP);
    165 
    166             mGoogleApiClient.disconnect();
    167         }
    168     }
    169 
    170     @Override
    171     protected void onResume() {
    172         Log.d(TAG, "onResume()");
    173         super.onResume();
    174         if (mGoogleApiClient != null) {
    175             mGoogleApiClient.connect();
    176         }
    177     }
    178 
    179     @Override
    180     public void onConnected(@Nullable Bundle bundle) {
    181         Log.d(TAG, "onConnected()");
    182 
    183         // Set up listeners for capability changes (install/uninstall of remote app).
    184         Wearable.CapabilityApi.addCapabilityListener(
    185                 mGoogleApiClient,
    186                 this,
    187                 CAPABILITY_WEAR_APP);
    188 
    189         // Initial request for devices with our capability, aka, our Wear app installed.
    190         findWearDevicesWithApp();
    191 
    192         // Initial request for all Wear devices connected (with or without our capability).
    193         // Additional Note: Because there isn't a listener for ALL Nodes added/removed from network
    194         // that isn't deprecated, we simply update the full list when the Google API Client is
    195         // connected and when capability changes come through in the onCapabilityChanged() method.
    196         findAllWearDevices();
    197     }
    198 
    199     @Override
    200     public void onConnectionSuspended(int i) {
    201         Log.d(TAG, "onConnectionSuspended(): connection to location client suspended: " + i);
    202     }
    203 
    204     @Override
    205     public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
    206         Log.e(TAG, "onConnectionFailed(): " + connectionResult);
    207     }
    208 
    209     /*
    210      * Updates UI when capabilities change (install/uninstall wear app).
    211      */
    212     public void onCapabilityChanged(CapabilityInfo capabilityInfo) {
    213         Log.d(TAG, "onCapabilityChanged(): " + capabilityInfo);
    214 
    215         mWearNodesWithApp = capabilityInfo.getNodes();
    216 
    217         // Because we have an updated list of devices with/without our app, we need to also update
    218         // our list of active Wear devices.
    219         findAllWearDevices();
    220 
    221         verifyNodeAndUpdateUI();
    222     }
    223 
    224     private void findWearDevicesWithApp() {
    225         Log.d(TAG, "findWearDevicesWithApp()");
    226 
    227         // You can filter this by FILTER_REACHABLE if you only want to open Nodes (Wear Devices)
    228         // directly connect to your phone.
    229         PendingResult<CapabilityApi.GetCapabilityResult> pendingResult =
    230                 Wearable.CapabilityApi.getCapability(
    231                         mGoogleApiClient,
    232                         CAPABILITY_WEAR_APP,
    233                         CapabilityApi.FILTER_ALL);
    234 
    235         pendingResult.setResultCallback(new ResultCallback<CapabilityApi.GetCapabilityResult>() {
    236             @Override
    237             public void onResult(@NonNull CapabilityApi.GetCapabilityResult getCapabilityResult) {
    238                 Log.d(TAG, "onResult(): " + getCapabilityResult);
    239 
    240                 if (getCapabilityResult.getStatus().isSuccess()) {
    241                     CapabilityInfo capabilityInfo = getCapabilityResult.getCapability();
    242                     mWearNodesWithApp = capabilityInfo.getNodes();
    243                     verifyNodeAndUpdateUI();
    244 
    245                 } else {
    246                     Log.d(TAG, "Failed CapabilityApi: " + getCapabilityResult.getStatus());
    247                 }
    248             }
    249         });
    250     }
    251 
    252     private void findAllWearDevices() {
    253         Log.d(TAG, "findAllWearDevices()");
    254 
    255         PendingResult<NodeApi.GetConnectedNodesResult> pendingResult =
    256                 Wearable.NodeApi.getConnectedNodes(mGoogleApiClient);
    257 
    258         pendingResult.setResultCallback(new ResultCallback<NodeApi.GetConnectedNodesResult>() {
    259             @Override
    260             public void onResult(@NonNull NodeApi.GetConnectedNodesResult getConnectedNodesResult) {
    261 
    262                 if (getConnectedNodesResult.getStatus().isSuccess()) {
    263                     mAllConnectedNodes = getConnectedNodesResult.getNodes();
    264                     verifyNodeAndUpdateUI();
    265 
    266                 } else {
    267                     Log.d(TAG, "Failed CapabilityApi: " + getConnectedNodesResult.getStatus());
    268                 }
    269             }
    270         });
    271     }
    272 
    273     private void verifyNodeAndUpdateUI() {
    274         Log.d(TAG, "verifyNodeAndUpdateUI()");
    275 
    276         if ((mWearNodesWithApp == null) || (mAllConnectedNodes == null)) {
    277             Log.d(TAG, "Waiting on Results for both connected nodes and nodes with app");
    278 
    279         } else if (mAllConnectedNodes.isEmpty()) {
    280             Log.d(TAG, NO_DEVICES);
    281             mInformationTextView.setText(NO_DEVICES);
    282             mRemoteOpenButton.setVisibility(View.INVISIBLE);
    283 
    284         } else if (mWearNodesWithApp.isEmpty()) {
    285             Log.d(TAG, MISSING_ALL_MESSAGE);
    286             mInformationTextView.setText(MISSING_ALL_MESSAGE);
    287             mRemoteOpenButton.setVisibility(View.VISIBLE);
    288 
    289         } else if (mWearNodesWithApp.size() < mAllConnectedNodes.size()) {
    290             // TODO: Add your code to communicate with the wear app(s) via
    291             // Wear APIs (MessageApi, DataApi, etc.)
    292 
    293             String installMessage =
    294                     String.format(INSTALLED_SOME_DEVICES_MESSAGE, mWearNodesWithApp);
    295             Log.d(TAG, installMessage);
    296             mInformationTextView.setText(installMessage);
    297             mRemoteOpenButton.setVisibility(View.VISIBLE);
    298 
    299         } else {
    300             // TODO: Add your code to communicate with the wear app(s) via
    301             // Wear APIs (MessageApi, DataApi, etc.)
    302 
    303             String installMessage =
    304                     String.format(INSTALLED_ALL_DEVICES_MESSAGE, mWearNodesWithApp);
    305             Log.d(TAG, installMessage);
    306             mInformationTextView.setText(installMessage);
    307             mRemoteOpenButton.setVisibility(View.INVISIBLE);
    308 
    309         }
    310     }
    311 
    312     private void openPlayStoreOnWearDevicesWithoutApp() {
    313         Log.d(TAG, "openPlayStoreOnWearDevicesWithoutApp()");
    314 
    315         // Create a List of Nodes (Wear devices) without your app.
    316         ArrayList<Node> nodesWithoutApp = new ArrayList<>();
    317 
    318         for (Node node : mAllConnectedNodes) {
    319             if (!mWearNodesWithApp.contains(node)) {
    320                 nodesWithoutApp.add(node);
    321             }
    322         }
    323 
    324         if (!nodesWithoutApp.isEmpty()) {
    325             Log.d(TAG, "Number of nodes without app: " + nodesWithoutApp.size());
    326 
    327             Intent intent =
    328                     new Intent(Intent.ACTION_VIEW)
    329                             .addCategory(Intent.CATEGORY_BROWSABLE)
    330                             .setData(Uri.parse(PLAY_STORE_APP_URI));
    331 
    332             for (Node node : nodesWithoutApp) {
    333                 RemoteIntent.startRemoteActivity(
    334                         getApplicationContext(),
    335                         intent,
    336                         mResultReceiver,
    337                         node.getId());
    338             }
    339         }
    340     }
    341 }