/* * Copyright (C) 2016 Google Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.example.android.wearable.wear.wearhighbandwidthnetworking; import android.app.Activity; import android.content.Context; import android.content.Intent; import android.net.ConnectivityManager; import android.net.Network; import android.net.NetworkCapabilities; import android.net.NetworkRequest; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.util.Log; import android.view.View; import android.view.WindowManager; import android.widget.ImageView; import android.widget.TextView; import java.util.concurrent.TimeUnit; /** * This sample demonstrates how to determine if a high-bandwidth network is available for use cases * that require a minimum network bandwidth, such as streaming media or downloading large files. * In addition, the sample demonstrates best practices for asking a user to add a new Wi-Fi network * for high-bandwidth network operations, if currently available networks are inadequate. */ public class MainActivity extends Activity { private static final String LOG_TAG = MainActivity.class.getSimpleName(); // Intent action for sending the user directly to the add Wi-Fi network activity. private static final String ACTION_ADD_NETWORK_SETTINGS = "com.google.android.clockwork.settings.connectivity.wifi.ADD_NETWORK_SETTINGS"; // Message to notify the network request timout handler that too much time has passed. private static final int MESSAGE_CONNECTIVITY_TIMEOUT = 1; // How long the app should wait trying to connect to a sufficient high-bandwidth network before // asking the user to add a new Wi-Fi network. private static final long NETWORK_CONNECTIVITY_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(10); // The minimum network bandwidth required by the app for high-bandwidth operations. private static final int MIN_NETWORK_BANDWIDTH_KBPS = 10000; private ConnectivityManager mConnectivityManager; private ConnectivityManager.NetworkCallback mNetworkCallback; // Handler for dealing with network connection timeouts. private Handler mHandler; private ImageView mConnectivityIcon; private TextView mConnectivityText; private View mButton; private ImageView mButtonIcon; private TextView mButtonText; private TextView mInfoText; private View mProgressBar; // Tags added to the button in the UI to detect what operation the user has requested. // These are required since the app reuses the button for different states of the app/UI. // See onButtonClick() for how these tags are used. static final String TAG_REQUEST_NETWORK = "REQUEST_NETWORK"; static final String TAG_RELEASE_NETWORK = "RELEASE_NETWORK"; static final String TAG_ADD_WIFI = "ADD_WIFI"; // These constants are used by setUiState() to determine what information to display in the UI, // as this app reuses UI components for the various states of the app, which is dependent on // the state of the network. static final int UI_STATE_REQUEST_NETWORK = 1; static final int UI_STATE_REQUESTING_NETWORK = 2; static final int UI_STATE_NETWORK_CONNECTED = 3; static final int UI_STATE_CONNECTION_TIMEOUT = 4; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); mConnectivityIcon = (ImageView) findViewById(R.id.connectivity_icon); mConnectivityText = (TextView) findViewById(R.id.connectivity_text); mProgressBar = findViewById(R.id.progress_bar); mButton = findViewById(R.id.button); mButton.setTag(TAG_REQUEST_NETWORK); mButtonIcon = (ImageView) findViewById(R.id.button_icon); mButtonText = (TextView) findViewById(R.id.button_label); mInfoText = (TextView) findViewById(R.id.info_text); mConnectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); mHandler = new Handler() { @Override public void handleMessage(Message msg) { switch (msg.what) { case MESSAGE_CONNECTIVITY_TIMEOUT: Log.d(LOG_TAG, "Network connection timeout"); setUiState(UI_STATE_CONNECTION_TIMEOUT); unregisterNetworkCallback(); break; } } }; } @Override public void onStop() { releaseHighBandwidthNetwork(); super.onStop(); } @Override public void onResume() { super.onResume(); if (isNetworkHighBandwidth()) { setUiState(UI_STATE_NETWORK_CONNECTED); } else { setUiState(UI_STATE_REQUEST_NETWORK); } } private void unregisterNetworkCallback() { if (mNetworkCallback != null) { Log.d(LOG_TAG, "Unregistering network callback"); mConnectivityManager.unregisterNetworkCallback(mNetworkCallback); mNetworkCallback = null; } } // Determine if there is a high-bandwidth network exists. Checks both the active // and bound networks. Returns false if no network is available (low or high-bandwidth). private boolean isNetworkHighBandwidth() { Network network = mConnectivityManager.getBoundNetworkForProcess(); network = network == null ? mConnectivityManager.getActiveNetwork() : network; if (network == null) { return false; } // requires android.permission.ACCESS_NETWORK_STATE int bandwidth = mConnectivityManager .getNetworkCapabilities(network).getLinkDownstreamBandwidthKbps(); if (bandwidth >= MIN_NETWORK_BANDWIDTH_KBPS) { return true; } return false; } private void requestHighBandwidthNetwork() { // Before requesting a high-bandwidth network, ensure prior requests are invalidated. unregisterNetworkCallback(); Log.d(LOG_TAG, "Requesting high-bandwidth network"); // Requesting an unmetered network may prevent you from connecting to the cellular // network on the user's watch or phone; however, unless you explicitly ask for permission // to a access the user's cellular network, you should request an unmetered network. NetworkRequest request = new NetworkRequest.Builder() .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED) .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) .addTransportType(NetworkCapabilities.TRANSPORT_WIFI) .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR) .build(); mNetworkCallback = new ConnectivityManager.NetworkCallback() { @Override public void onAvailable(final Network network) { mHandler.removeMessages(MESSAGE_CONNECTIVITY_TIMEOUT); runOnUiThread(new Runnable() { @Override public void run() { // requires android.permission.INTERNET if (!mConnectivityManager.bindProcessToNetwork(network)) { Log.e(LOG_TAG, "ConnectivityManager.bindProcessToNetwork()" + " requires android.permission.INTERNET"); setUiState(UI_STATE_REQUEST_NETWORK); } else { Log.d(LOG_TAG, "Network available"); setUiState(UI_STATE_NETWORK_CONNECTED); } } }); } @Override public void onCapabilitiesChanged(Network network, NetworkCapabilities networkCapabilities) { runOnUiThread(new Runnable() { @Override public void run() { Log.d(LOG_TAG, "Network capabilities changed"); } }); } @Override public void onLost(Network network) { Log.d(LOG_TAG, "Network lost"); runOnUiThread(new Runnable() { @Override public void run() { setUiState(UI_STATE_REQUEST_NETWORK); } }); } }; // requires android.permission.CHANGE_NETWORK_STATE mConnectivityManager.requestNetwork(request, mNetworkCallback); mHandler.sendMessageDelayed( mHandler.obtainMessage(MESSAGE_CONNECTIVITY_TIMEOUT), NETWORK_CONNECTIVITY_TIMEOUT_MS); } private void releaseHighBandwidthNetwork() { mConnectivityManager.bindProcessToNetwork(null); unregisterNetworkCallback(); } private void addWifiNetwork() { // requires android.permission.CHANGE_WIFI_STATE startActivity(new Intent(ACTION_ADD_NETWORK_SETTINGS)); } /** * Click handler for the button in the UI. The view tag is used to determine the specific * function of the button. * * @param view The view that was clicked */ public void onButtonClick(View view) { switch (view.getTag().toString()) { case TAG_REQUEST_NETWORK: requestHighBandwidthNetwork(); setUiState(UI_STATE_REQUESTING_NETWORK); break; case TAG_RELEASE_NETWORK: releaseHighBandwidthNetwork(); setUiState(UI_STATE_REQUEST_NETWORK); break; case TAG_ADD_WIFI: addWifiNetwork(); break; } } // Sets the text and icons the connectivity indicator, button, and info text in the app UI, // which are all reused for the various states of the app and network connectivity. Also, // will show/hide a progress bar, which is dependent on the state of the network connectivity // request. private void setUiState(int uiState) { switch (uiState) { case UI_STATE_REQUEST_NETWORK: if (isNetworkHighBandwidth()) { mConnectivityIcon.setImageResource(R.drawable.ic_cloud_happy); mConnectivityText.setText(R.string.network_fast); } else { mConnectivityIcon.setImageResource(R.drawable.ic_cloud_sad); mConnectivityText.setText(R.string.network_slow); } mButton.setTag(TAG_REQUEST_NETWORK); mButtonIcon.setImageResource(R.drawable.ic_fast_network); mButtonText.setText(R.string.button_request_network); mInfoText.setText(R.string.info_request_network); break; case UI_STATE_REQUESTING_NETWORK: mConnectivityIcon.setImageResource(R.drawable.ic_cloud_disconnected); mConnectivityText.setText(R.string.network_connecting); mProgressBar.setVisibility(View.VISIBLE); mInfoText.setVisibility(View.GONE); mButton.setVisibility(View.GONE); break; case UI_STATE_NETWORK_CONNECTED: if (isNetworkHighBandwidth()) { mConnectivityIcon.setImageResource(R.drawable.ic_cloud_happy); mConnectivityText.setText(R.string.network_fast); } else { mConnectivityIcon.setImageResource(R.drawable.ic_cloud_sad); mConnectivityText.setText(R.string.network_slow); } mProgressBar.setVisibility(View.GONE); mInfoText.setVisibility(View.VISIBLE); mButton.setVisibility(View.VISIBLE); mButton.setTag(TAG_RELEASE_NETWORK); mButtonIcon.setImageResource(R.drawable.ic_no_network); mButtonText.setText(R.string.button_release_network); mInfoText.setText(R.string.info_release_network); break; case UI_STATE_CONNECTION_TIMEOUT: mConnectivityIcon.setImageResource(R.drawable.ic_cloud_disconnected); mConnectivityText.setText(R.string.network_disconnected); mProgressBar.setVisibility(View.GONE); mInfoText.setVisibility(View.VISIBLE); mButton.setVisibility(View.VISIBLE); mButton.setTag(TAG_ADD_WIFI); mButtonIcon.setImageResource(R.drawable.ic_wifi_network); mButtonText.setText(R.string.button_add_wifi); mInfoText.setText(R.string.info_add_wifi); break; } } }