Home | History | Annotate | Download | only in networkusage
      1 /*
      2  * Copyright (C) 2012 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
      5  * in compliance with the License. You may obtain a copy of the License at
      6  *
      7  * http://www.apache.org/licenses/LICENSE-2.0
      8  *
      9  * Unless required by applicable law or agreed to in writing, software distributed under the License
     10  * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
     11  * or implied. See the License for the specific language governing permissions and limitations under
     12  * the License.
     13  */
     14 
     15 package com.example.android.networkusage;
     16 
     17 import android.app.Activity;
     18 import android.content.BroadcastReceiver;
     19 import android.content.Context;
     20 import android.content.Intent;
     21 import android.content.IntentFilter;
     22 import android.content.SharedPreferences;
     23 import android.net.ConnectivityManager;
     24 import android.net.NetworkInfo;
     25 import android.os.AsyncTask;
     26 import android.os.Bundle;
     27 import android.preference.PreferenceManager;
     28 import android.view.Menu;
     29 import android.view.MenuInflater;
     30 import android.view.MenuItem;
     31 import android.webkit.WebView;
     32 import android.widget.Toast;
     33 
     34 import com.example.android.networkusage.R;
     35 import com.example.android.networkusage.StackOverflowXmlParser.Entry;
     36 
     37 import org.xmlpull.v1.XmlPullParserException;
     38 
     39 import java.io.IOException;
     40 import java.io.InputStream;
     41 import java.net.HttpURLConnection;
     42 import java.net.URL;
     43 import java.text.DateFormat;
     44 import java.text.SimpleDateFormat;
     45 import java.util.Calendar;
     46 import java.util.List;
     47 
     48 
     49 /**
     50  * Main Activity for the sample application.
     51  *
     52  * This activity does the following:
     53  *
     54  * o Presents a WebView screen to users. This WebView has a list of HTML links to the latest
     55  *   questions tagged 'android' on stackoverflow.com.
     56  *
     57  * o Parses the StackOverflow XML feed using XMLPullParser.
     58  *
     59  * o Uses AsyncTask to download and process the XML feed.
     60  *
     61  * o Monitors preferences and the device's network connection to determine whether
     62  *   to refresh the WebView content.
     63  */
     64 public class NetworkActivity extends Activity {
     65     public static final String WIFI = "Wi-Fi";
     66     public static final String ANY = "Any";
     67     private static final String URL =
     68             "http://stackoverflow.com/feeds/tag?tagnames=android&sort=newest";
     69 
     70     // Whether there is a Wi-Fi connection.
     71     private static boolean wifiConnected = false;
     72     // Whether there is a mobile connection.
     73     private static boolean mobileConnected = false;
     74     // Whether the display should be refreshed.
     75     public static boolean refreshDisplay = true;
     76 
     77     // The user's current network preference setting.
     78     public static String sPref = null;
     79 
     80     // The BroadcastReceiver that tracks network connectivity changes.
     81     private NetworkReceiver receiver = new NetworkReceiver();
     82 
     83     @Override
     84     public void onCreate(Bundle savedInstanceState) {
     85         super.onCreate(savedInstanceState);
     86 
     87         // Register BroadcastReceiver to track connection changes.
     88         IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
     89         receiver = new NetworkReceiver();
     90         this.registerReceiver(receiver, filter);
     91     }
     92 
     93     // Refreshes the display if the network connection and the
     94     // pref settings allow it.
     95     @Override
     96     public void onStart() {
     97         super.onStart();
     98 
     99         // Gets the user's network preference settings
    100         SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this);
    101 
    102         // Retrieves a string value for the preferences. The second parameter
    103         // is the default value to use if a preference value is not found.
    104         sPref = sharedPrefs.getString("listPref", "Wi-Fi");
    105 
    106         updateConnectedFlags();
    107 
    108         // Only loads the page if refreshDisplay is true. Otherwise, keeps previous
    109         // display. For example, if the user has set "Wi-Fi only" in prefs and the
    110         // device loses its Wi-Fi connection midway through the user using the app,
    111         // you don't want to refresh the display--this would force the display of
    112         // an error page instead of stackoverflow.com content.
    113         if (refreshDisplay) {
    114             loadPage();
    115         }
    116     }
    117 
    118     @Override
    119     public void onDestroy() {
    120         super.onDestroy();
    121         if (receiver != null) {
    122             this.unregisterReceiver(receiver);
    123         }
    124     }
    125 
    126     // Checks the network connection and sets the wifiConnected and mobileConnected
    127     // variables accordingly.
    128     private void updateConnectedFlags() {
    129         ConnectivityManager connMgr =
    130                 (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
    131 
    132         NetworkInfo activeInfo = connMgr.getActiveNetworkInfo();
    133         if (activeInfo != null && activeInfo.isConnected()) {
    134             wifiConnected = activeInfo.getType() == ConnectivityManager.TYPE_WIFI;
    135             mobileConnected = activeInfo.getType() == ConnectivityManager.TYPE_MOBILE;
    136         } else {
    137             wifiConnected = false;
    138             mobileConnected = false;
    139         }
    140     }
    141 
    142     // Uses AsyncTask subclass to download the XML feed from stackoverflow.com.
    143     // This avoids UI lock up. To prevent network operations from
    144     // causing a delay that results in a poor user experience, always perform
    145     // network operations on a separate thread from the UI.
    146     private void loadPage() {
    147         if (((sPref.equals(ANY)) && (wifiConnected || mobileConnected))
    148                 || ((sPref.equals(WIFI)) && (wifiConnected))) {
    149             // AsyncTask subclass
    150             new DownloadXmlTask().execute(URL);
    151         } else {
    152             showErrorPage();
    153         }
    154     }
    155 
    156     // Displays an error if the app is unable to load content.
    157     private void showErrorPage() {
    158         setContentView(R.layout.main);
    159 
    160         // The specified network connection is not available. Displays error message.
    161         WebView myWebView = (WebView) findViewById(R.id.webview);
    162         myWebView.loadData(getResources().getString(R.string.connection_error),
    163                 "text/html", null);
    164     }
    165 
    166     // Populates the activity's options menu.
    167     @Override
    168     public boolean onCreateOptionsMenu(Menu menu) {
    169         MenuInflater inflater = getMenuInflater();
    170         inflater.inflate(R.menu.mainmenu, menu);
    171         return true;
    172     }
    173 
    174     // Handles the user's menu selection.
    175     @Override
    176     public boolean onOptionsItemSelected(MenuItem item) {
    177         switch (item.getItemId()) {
    178         case R.id.settings:
    179                 Intent settingsActivity = new Intent(getBaseContext(), SettingsActivity.class);
    180                 startActivity(settingsActivity);
    181                 return true;
    182         case R.id.refresh:
    183                 loadPage();
    184                 return true;
    185         default:
    186                 return super.onOptionsItemSelected(item);
    187         }
    188     }
    189 
    190     // Implementation of AsyncTask used to download XML feed from stackoverflow.com.
    191     private class DownloadXmlTask extends AsyncTask<String, Void, String> {
    192 
    193         @Override
    194         protected String doInBackground(String... urls) {
    195             try {
    196                 return loadXmlFromNetwork(urls[0]);
    197             } catch (IOException e) {
    198                 return getResources().getString(R.string.connection_error);
    199             } catch (XmlPullParserException e) {
    200                 return getResources().getString(R.string.xml_error);
    201             }
    202         }
    203 
    204         @Override
    205         protected void onPostExecute(String result) {
    206             setContentView(R.layout.main);
    207             // Displays the HTML string in the UI via a WebView
    208             WebView myWebView = (WebView) findViewById(R.id.webview);
    209             myWebView.loadData(result, "text/html", null);
    210         }
    211     }
    212 
    213     // Uploads XML from stackoverflow.com, parses it, and combines it with
    214     // HTML markup. Returns HTML string.
    215     private String loadXmlFromNetwork(String urlString) throws XmlPullParserException, IOException {
    216         InputStream stream = null;
    217         StackOverflowXmlParser stackOverflowXmlParser = new StackOverflowXmlParser();
    218         List<Entry> entries = null;
    219         String title = null;
    220         String url = null;
    221         String summary = null;
    222         Calendar rightNow = Calendar.getInstance();
    223         DateFormat formatter = new SimpleDateFormat("MMM dd h:mmaa");
    224 
    225         // Checks whether the user set the preference to include summary text
    226         SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this);
    227         boolean pref = sharedPrefs.getBoolean("summaryPref", false);
    228 
    229         StringBuilder htmlString = new StringBuilder();
    230         htmlString.append("<h3>" + getResources().getString(R.string.page_title) + "</h3>");
    231         htmlString.append("<em>" + getResources().getString(R.string.updated) + " " +
    232                 formatter.format(rightNow.getTime()) + "</em>");
    233 
    234         try {
    235             stream = downloadUrl(urlString);
    236             entries = stackOverflowXmlParser.parse(stream);
    237         // Makes sure that the InputStream is closed after the app is
    238         // finished using it.
    239         } finally {
    240             if (stream != null) {
    241                 stream.close();
    242             }
    243         }
    244 
    245         // StackOverflowXmlParser returns a List (called "entries") of Entry objects.
    246         // Each Entry object represents a single post in the XML feed.
    247         // This section processes the entries list to combine each entry with HTML markup.
    248         // Each entry is displayed in the UI as a link that optionally includes
    249         // a text summary.
    250         for (Entry entry : entries) {
    251             htmlString.append("<p><a href='");
    252             htmlString.append(entry.link);
    253             htmlString.append("'>" + entry.title + "</a></p>");
    254             // If the user set the preference to include summary text,
    255             // adds it to the display.
    256             if (pref) {
    257                 htmlString.append(entry.summary);
    258             }
    259         }
    260         return htmlString.toString();
    261     }
    262 
    263     // Given a string representation of a URL, sets up a connection and gets
    264     // an input stream.
    265     private InputStream downloadUrl(String urlString) throws IOException {
    266         URL url = new URL(urlString);
    267         HttpURLConnection conn = (HttpURLConnection) url.openConnection();
    268         conn.setReadTimeout(10000 /* milliseconds */);
    269         conn.setConnectTimeout(15000 /* milliseconds */);
    270         conn.setRequestMethod("GET");
    271         conn.setDoInput(true);
    272         // Starts the query
    273         conn.connect();
    274         InputStream stream = conn.getInputStream();
    275         return stream;
    276     }
    277 
    278     /**
    279      *
    280      * This BroadcastReceiver intercepts the android.net.ConnectivityManager.CONNECTIVITY_ACTION,
    281      * which indicates a connection change. It checks whether the type is TYPE_WIFI.
    282      * If it is, it checks whether Wi-Fi is connected and sets the wifiConnected flag in the
    283      * main activity accordingly.
    284      *
    285      */
    286     public class NetworkReceiver extends BroadcastReceiver {
    287 
    288         @Override
    289         public void onReceive(Context context, Intent intent) {
    290             ConnectivityManager connMgr =
    291                     (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
    292             NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();
    293 
    294             // Checks the user prefs and the network connection. Based on the result, decides
    295             // whether
    296             // to refresh the display or keep the current display.
    297             // If the userpref is Wi-Fi only, checks to see if the device has a Wi-Fi connection.
    298             if (WIFI.equals(sPref) && networkInfo != null
    299                     && networkInfo.getType() == ConnectivityManager.TYPE_WIFI) {
    300                 // If device has its Wi-Fi connection, sets refreshDisplay
    301                 // to true. This causes the display to be refreshed when the user
    302                 // returns to the app.
    303                 refreshDisplay = true;
    304                 Toast.makeText(context, R.string.wifi_connected, Toast.LENGTH_SHORT).show();
    305 
    306                 // If the setting is ANY network and there is a network connection
    307                 // (which by process of elimination would be mobile), sets refreshDisplay to true.
    308             } else if (ANY.equals(sPref) && networkInfo != null) {
    309                 refreshDisplay = true;
    310 
    311                 // Otherwise, the app can't download content--either because there is no network
    312                 // connection (mobile or Wi-Fi), or because the pref setting is WIFI, and there
    313                 // is no Wi-Fi connection.
    314                 // Sets refreshDisplay to false.
    315             } else {
    316                 refreshDisplay = false;
    317                 Toast.makeText(context, R.string.lost_connection, Toast.LENGTH_SHORT).show();
    318             }
    319         }
    320     }
    321 }
    322