Home | History | Annotate | Download | only in wearhighbandwidthnetworking
      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.wearhighbandwidthnetworking;
     17 
     18 import android.app.Activity;
     19 import android.content.Context;
     20 import android.content.Intent;
     21 import android.net.ConnectivityManager;
     22 import android.net.Network;
     23 import android.net.NetworkCapabilities;
     24 import android.net.NetworkRequest;
     25 import android.os.Bundle;
     26 import android.os.Handler;
     27 import android.os.Message;
     28 import android.util.Log;
     29 import android.view.View;
     30 import android.view.WindowManager;
     31 import android.widget.ImageView;
     32 import android.widget.TextView;
     33 
     34 import java.util.concurrent.TimeUnit;
     35 
     36 /**
     37  * This sample demonstrates how to determine if a high-bandwidth network is available for use cases
     38  * that require a minimum network bandwidth, such as streaming media or downloading large files.
     39  * In addition, the sample demonstrates best practices for asking a user to add a new Wi-Fi network
     40  * for high-bandwidth network operations, if currently available networks are inadequate.
     41  */
     42 public class MainActivity extends Activity  {
     43     private static final String LOG_TAG = MainActivity.class.getSimpleName();
     44 
     45     // Intent action for sending the user directly to the add Wi-Fi network activity.
     46     private static final String ACTION_ADD_NETWORK_SETTINGS =
     47             "com.google.android.clockwork.settings.connectivity.wifi.ADD_NETWORK_SETTINGS";
     48 
     49     // Message to notify the network request timout handler that too much time has passed.
     50     private static final int MESSAGE_CONNECTIVITY_TIMEOUT = 1;
     51 
     52     // How long the app should wait trying to connect to a sufficient high-bandwidth network before
     53     // asking the user to add a new Wi-Fi network.
     54     private static final long NETWORK_CONNECTIVITY_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(10);
     55 
     56     // The minimum network bandwidth required by the app for high-bandwidth operations.
     57     private static final int MIN_NETWORK_BANDWIDTH_KBPS = 10000;
     58 
     59     private ConnectivityManager mConnectivityManager;
     60     private ConnectivityManager.NetworkCallback mNetworkCallback;
     61 
     62     // Handler for dealing with network connection timeouts.
     63     private Handler mHandler;
     64 
     65     private ImageView mConnectivityIcon;
     66     private TextView mConnectivityText;
     67 
     68     private View mButton;
     69     private ImageView mButtonIcon;
     70     private TextView mButtonText;
     71     private TextView mInfoText;
     72     private View mProgressBar;
     73 
     74     // Tags added to the button in the UI to detect what operation the user has requested.
     75     // These are required since the app reuses the button for different states of the app/UI.
     76     // See onButtonClick() for how these tags are used.
     77     static final String TAG_REQUEST_NETWORK = "REQUEST_NETWORK";
     78     static final String TAG_RELEASE_NETWORK = "RELEASE_NETWORK";
     79     static final String TAG_ADD_WIFI = "ADD_WIFI";
     80 
     81     // These constants are used by setUiState() to determine what information to display in the UI,
     82     // as this app reuses UI components for the various states of the app, which is dependent on
     83     // the state of the network.
     84     static final int UI_STATE_REQUEST_NETWORK = 1;
     85     static final int UI_STATE_REQUESTING_NETWORK = 2;
     86     static final int UI_STATE_NETWORK_CONNECTED = 3;
     87     static final int UI_STATE_CONNECTION_TIMEOUT = 4;
     88 
     89     @Override
     90     protected void onCreate(Bundle savedInstanceState) {
     91         super.onCreate(savedInstanceState);
     92         setContentView(R.layout.activity_main);
     93 
     94         getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
     95 
     96         mConnectivityIcon = (ImageView) findViewById(R.id.connectivity_icon);
     97         mConnectivityText = (TextView) findViewById(R.id.connectivity_text);
     98 
     99         mProgressBar = findViewById(R.id.progress_bar);
    100 
    101         mButton = findViewById(R.id.button);
    102         mButton.setTag(TAG_REQUEST_NETWORK);
    103         mButtonIcon = (ImageView) findViewById(R.id.button_icon);
    104         mButtonText = (TextView) findViewById(R.id.button_label);
    105 
    106         mInfoText = (TextView) findViewById(R.id.info_text);
    107 
    108         mConnectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
    109 
    110         mHandler = new Handler() {
    111             @Override
    112             public void handleMessage(Message msg) {
    113                 switch (msg.what) {
    114                     case MESSAGE_CONNECTIVITY_TIMEOUT:
    115                         Log.d(LOG_TAG, "Network connection timeout");
    116                         setUiState(UI_STATE_CONNECTION_TIMEOUT);
    117                         unregisterNetworkCallback();
    118                         break;
    119                 }
    120             }
    121         };
    122     }
    123 
    124     @Override
    125     public void onStop() {
    126         releaseHighBandwidthNetwork();
    127         super.onStop();
    128     }
    129 
    130     @Override
    131     public void onResume() {
    132         super.onResume();
    133 
    134         if (isNetworkHighBandwidth()) {
    135             setUiState(UI_STATE_NETWORK_CONNECTED);
    136         } else {
    137             setUiState(UI_STATE_REQUEST_NETWORK);
    138         }
    139     }
    140 
    141     private void unregisterNetworkCallback() {
    142         if (mNetworkCallback != null) {
    143             Log.d(LOG_TAG, "Unregistering network callback");
    144             mConnectivityManager.unregisterNetworkCallback(mNetworkCallback);
    145             mNetworkCallback = null;
    146         }
    147     }
    148 
    149     // Determine if there is a high-bandwidth network exists. Checks both the active
    150     // and bound networks. Returns false if no network is available (low or high-bandwidth).
    151     private boolean isNetworkHighBandwidth() {
    152         Network network = mConnectivityManager.getBoundNetworkForProcess();
    153         network = network == null ? mConnectivityManager.getActiveNetwork() : network;
    154         if (network == null) {
    155             return false;
    156         }
    157 
    158         // requires android.permission.ACCESS_NETWORK_STATE
    159         int bandwidth = mConnectivityManager
    160                 .getNetworkCapabilities(network).getLinkDownstreamBandwidthKbps();
    161 
    162         if (bandwidth >= MIN_NETWORK_BANDWIDTH_KBPS) {
    163             return true;
    164         }
    165 
    166         return false;
    167     }
    168 
    169     private void requestHighBandwidthNetwork() {
    170         // Before requesting a high-bandwidth network, ensure prior requests are invalidated.
    171         unregisterNetworkCallback();
    172 
    173         Log.d(LOG_TAG, "Requesting high-bandwidth network");
    174 
    175         // Requesting an unmetered network may prevent you from connecting to the cellular
    176         // network on the user's watch or phone; however, unless you explicitly ask for permission
    177         // to a access the user's cellular network, you should request an unmetered network.
    178         NetworkRequest request = new NetworkRequest.Builder()
    179                 .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED)
    180                 .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
    181                 .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
    182                 .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
    183                 .build();
    184 
    185         mNetworkCallback = new ConnectivityManager.NetworkCallback() {
    186             @Override
    187             public void onAvailable(final Network network) {
    188                 mHandler.removeMessages(MESSAGE_CONNECTIVITY_TIMEOUT);
    189 
    190                 runOnUiThread(new Runnable() {
    191                     @Override
    192                     public void run() {
    193                         // requires android.permission.INTERNET
    194                         if (!mConnectivityManager.bindProcessToNetwork(network)) {
    195                             Log.e(LOG_TAG, "ConnectivityManager.bindProcessToNetwork()"
    196                                     + " requires android.permission.INTERNET");
    197                             setUiState(UI_STATE_REQUEST_NETWORK);
    198                         } else {
    199                             Log.d(LOG_TAG, "Network available");
    200                             setUiState(UI_STATE_NETWORK_CONNECTED);
    201                         }
    202                     }
    203                 });
    204             }
    205 
    206             @Override
    207             public void onCapabilitiesChanged(Network network,
    208                                               NetworkCapabilities networkCapabilities) {
    209                 runOnUiThread(new Runnable() {
    210                     @Override
    211                     public void run() {
    212                         Log.d(LOG_TAG, "Network capabilities changed");
    213                     }
    214                 });
    215             }
    216 
    217             @Override
    218             public void onLost(Network network) {
    219                 Log.d(LOG_TAG, "Network lost");
    220 
    221                 runOnUiThread(new Runnable() {
    222                     @Override
    223                     public void run() {
    224                         setUiState(UI_STATE_REQUEST_NETWORK);
    225                     }
    226                 });
    227             }
    228         };
    229 
    230         // requires android.permission.CHANGE_NETWORK_STATE
    231         mConnectivityManager.requestNetwork(request, mNetworkCallback);
    232 
    233         mHandler.sendMessageDelayed(
    234                 mHandler.obtainMessage(MESSAGE_CONNECTIVITY_TIMEOUT),
    235                 NETWORK_CONNECTIVITY_TIMEOUT_MS);
    236     }
    237 
    238     private void releaseHighBandwidthNetwork() {
    239         mConnectivityManager.bindProcessToNetwork(null);
    240         unregisterNetworkCallback();
    241     }
    242 
    243     private void addWifiNetwork() {
    244         // requires android.permission.CHANGE_WIFI_STATE
    245         startActivity(new Intent(ACTION_ADD_NETWORK_SETTINGS));
    246     }
    247 
    248     /**
    249      * Click handler for the button in the UI. The view tag is used to determine the specific
    250      * function of the button.
    251      *
    252      * @param view The view that was clicked
    253      */
    254     public void onButtonClick(View view) {
    255         switch (view.getTag().toString()) {
    256             case TAG_REQUEST_NETWORK:
    257                 requestHighBandwidthNetwork();
    258                 setUiState(UI_STATE_REQUESTING_NETWORK);
    259                 break;
    260 
    261             case TAG_RELEASE_NETWORK:
    262                 releaseHighBandwidthNetwork();
    263                 setUiState(UI_STATE_REQUEST_NETWORK);
    264                 break;
    265 
    266             case TAG_ADD_WIFI:
    267                 addWifiNetwork();
    268                 break;
    269         }
    270     }
    271 
    272     // Sets the text and icons the connectivity indicator, button, and info text in the app UI,
    273     // which are all reused for the various states of the app and network connectivity. Also,
    274     // will show/hide a progress bar, which is dependent on the state of the network connectivity
    275     // request.
    276     private void setUiState(int uiState) {
    277         switch (uiState) {
    278             case UI_STATE_REQUEST_NETWORK:
    279                 if (isNetworkHighBandwidth()) {
    280                     mConnectivityIcon.setImageResource(R.drawable.ic_cloud_happy);
    281                     mConnectivityText.setText(R.string.network_fast);
    282                 } else {
    283                     mConnectivityIcon.setImageResource(R.drawable.ic_cloud_sad);
    284                     mConnectivityText.setText(R.string.network_slow);
    285                 }
    286 
    287                 mButton.setTag(TAG_REQUEST_NETWORK);
    288                 mButtonIcon.setImageResource(R.drawable.ic_fast_network);
    289                 mButtonText.setText(R.string.button_request_network);
    290                 mInfoText.setText(R.string.info_request_network);
    291 
    292                 break;
    293 
    294             case UI_STATE_REQUESTING_NETWORK:
    295                 mConnectivityIcon.setImageResource(R.drawable.ic_cloud_disconnected);
    296                 mConnectivityText.setText(R.string.network_connecting);
    297 
    298                 mProgressBar.setVisibility(View.VISIBLE);
    299                 mInfoText.setVisibility(View.GONE);
    300                 mButton.setVisibility(View.GONE);
    301 
    302                 break;
    303 
    304             case UI_STATE_NETWORK_CONNECTED:
    305                 if (isNetworkHighBandwidth()) {
    306                     mConnectivityIcon.setImageResource(R.drawable.ic_cloud_happy);
    307                     mConnectivityText.setText(R.string.network_fast);
    308                 } else {
    309                     mConnectivityIcon.setImageResource(R.drawable.ic_cloud_sad);
    310                     mConnectivityText.setText(R.string.network_slow);
    311                 }
    312 
    313                 mProgressBar.setVisibility(View.GONE);
    314                 mInfoText.setVisibility(View.VISIBLE);
    315                 mButton.setVisibility(View.VISIBLE);
    316 
    317                 mButton.setTag(TAG_RELEASE_NETWORK);
    318                 mButtonIcon.setImageResource(R.drawable.ic_no_network);
    319                 mButtonText.setText(R.string.button_release_network);
    320                 mInfoText.setText(R.string.info_release_network);
    321 
    322                 break;
    323 
    324             case UI_STATE_CONNECTION_TIMEOUT:
    325                 mConnectivityIcon.setImageResource(R.drawable.ic_cloud_disconnected);
    326                 mConnectivityText.setText(R.string.network_disconnected);
    327 
    328                 mProgressBar.setVisibility(View.GONE);
    329                 mInfoText.setVisibility(View.VISIBLE);
    330                 mButton.setVisibility(View.VISIBLE);
    331 
    332                 mButton.setTag(TAG_ADD_WIFI);
    333                 mButtonIcon.setImageResource(R.drawable.ic_wifi_network);
    334                 mButtonText.setText(R.string.button_add_wifi);
    335                 mInfoText.setText(R.string.info_add_wifi);
    336 
    337                 break;
    338         }
    339     }
    340 }