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 onCreate(Bundle savedInstanceState)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 onStop()125 public void onStop() { 126 releaseHighBandwidthNetwork(); 127 super.onStop(); 128 } 129 130 @Override onResume()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 unregisterNetworkCallback()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). isNetworkHighBandwidth()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 requestHighBandwidthNetwork()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 releaseHighBandwidthNetwork()238 private void releaseHighBandwidthNetwork() { 239 mConnectivityManager.bindProcessToNetwork(null); 240 unregisterNetworkCallback(); 241 } 242 addWifiNetwork()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 */ onButtonClick(View view)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. setUiState(int uiState)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 }