From 2f8cc17f5fbc2e05ac0889fbbddf4e530750087b Mon Sep 17 00:00:00 2001 From: Katie McCormick Date: Fri, 16 Mar 2012 17:47:55 -0700 Subject: [PATCH] cherrypick from ics-mr1 docs: source for nw app Change-Id: If50f407a0e56fa802fe9beedaa650e3a131872b2 Change-Id: I55d8668f4065129c844ada239f268a6621df4780 --- samples/training/network-usage/AndroidManifest.xml | 48 +++ samples/training/network-usage/README.txt | 14 + .../res/drawable-hdpi/ic_launcher.png | Bin 0 -> 4147 bytes .../res/drawable-ldpi/ic_launcher.png | Bin 0 -> 1723 bytes .../res/drawable-mdpi/ic_launcher.png | Bin 0 -> 2574 bytes samples/training/network-usage/res/layout/main.xml | 12 + .../training/network-usage/res/menu/mainmenu.xml | 24 ++ .../training/network-usage/res/values/arrays.xml | 28 ++ .../training/network-usage/res/values/strings.xml | 35 +++ .../training/network-usage/res/xml/preferences.xml | 33 +++ .../android/networkusage/NetworkActivity.java | 321 +++++++++++++++++++++ .../android/networkusage/SettingsActivity.java | 66 +++++ .../networkusage/StackOverflowXmlParser.java | 169 +++++++++++ 13 files changed, 750 insertions(+) create mode 100644 samples/training/network-usage/AndroidManifest.xml create mode 100644 samples/training/network-usage/README.txt create mode 100644 samples/training/network-usage/res/drawable-hdpi/ic_launcher.png create mode 100644 samples/training/network-usage/res/drawable-ldpi/ic_launcher.png create mode 100644 samples/training/network-usage/res/drawable-mdpi/ic_launcher.png create mode 100644 samples/training/network-usage/res/layout/main.xml create mode 100644 samples/training/network-usage/res/menu/mainmenu.xml create mode 100644 samples/training/network-usage/res/values/arrays.xml create mode 100644 samples/training/network-usage/res/values/strings.xml create mode 100644 samples/training/network-usage/res/xml/preferences.xml create mode 100644 samples/training/network-usage/src/com/example/android/networkusage/NetworkActivity.java create mode 100644 samples/training/network-usage/src/com/example/android/networkusage/SettingsActivity.java create mode 100644 samples/training/network-usage/src/com/example/android/networkusage/StackOverflowXmlParser.java diff --git a/samples/training/network-usage/AndroidManifest.xml b/samples/training/network-usage/AndroidManifest.xml new file mode 100644 index 000000000000..4b96d148b5d2 --- /dev/null +++ b/samples/training/network-usage/AndroidManifest.xml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/training/network-usage/README.txt b/samples/training/network-usage/README.txt new file mode 100644 index 000000000000..cc9a7a06fe78 --- /dev/null +++ b/samples/training/network-usage/README.txt @@ -0,0 +1,14 @@ +README +====== + +This Network Usage sample app does the following: + +-- Downloads an XML feed from StackOverflow.com for the most recent posts tagged "android". + +-- Parses the XML feed, combines feed elements with HTML markup, and displays the resulting HTML in the UI. + +-- Lets users control their network data usage through a settings UI. Users can choose to fetch the feed + when any network connection is available, or only when a Wi-Fi connection is available. + +-- Detects when there is a change in the device's connection status and responds accordingly. For example, if + the device loses its network connection, the app will not attempt to download the feed. diff --git a/samples/training/network-usage/res/drawable-hdpi/ic_launcher.png b/samples/training/network-usage/res/drawable-hdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..8074c4c571b8cd19e27f4ee5545df367420686d7 GIT binary patch literal 4147 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 literal 0 HcmV?d00001 diff --git a/samples/training/network-usage/res/drawable-ldpi/ic_launcher.png b/samples/training/network-usage/res/drawable-ldpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..1095584ec21f71cd0afc9e0993aa2209671b590c GIT binary patch literal 1723 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 + + + + diff --git a/samples/training/network-usage/res/menu/mainmenu.xml b/samples/training/network-usage/res/menu/mainmenu.xml new file mode 100644 index 000000000000..17d44db16d9a --- /dev/null +++ b/samples/training/network-usage/res/menu/mainmenu.xml @@ -0,0 +1,24 @@ + + + + + + + + diff --git a/samples/training/network-usage/res/values/arrays.xml b/samples/training/network-usage/res/values/arrays.xml new file mode 100644 index 000000000000..2e8b8a76b173 --- /dev/null +++ b/samples/training/network-usage/res/values/arrays.xml @@ -0,0 +1,28 @@ + + + + + + + Only when on Wi-Fi + On any network + + + Wi-Fi + Any + + diff --git a/samples/training/network-usage/res/values/strings.xml b/samples/training/network-usage/res/values/strings.xml new file mode 100644 index 000000000000..d7c702f4a04a --- /dev/null +++ b/samples/training/network-usage/res/values/strings.xml @@ -0,0 +1,35 @@ + + + + + + + NetworkUsage + + + Settings + Refresh + + + Newest StackOverflow questions tagged \'android\' + Last updated: + Lost connection. + Wi-Fi reconnected. + Unable to load content. Check your network connection. + Error parsing XML. + + diff --git a/samples/training/network-usage/res/xml/preferences.xml b/samples/training/network-usage/res/xml/preferences.xml new file mode 100644 index 000000000000..801ba79971b4 --- /dev/null +++ b/samples/training/network-usage/res/xml/preferences.xml @@ -0,0 +1,33 @@ + + + + + + + + diff --git a/samples/training/network-usage/src/com/example/android/networkusage/NetworkActivity.java b/samples/training/network-usage/src/com/example/android/networkusage/NetworkActivity.java new file mode 100644 index 000000000000..b7ed3314ef1c --- /dev/null +++ b/samples/training/network-usage/src/com/example/android/networkusage/NetworkActivity.java @@ -0,0 +1,321 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * 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.networkusage; + +import android.app.Activity; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.SharedPreferences; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; +import android.os.AsyncTask; +import android.os.Bundle; +import android.preference.PreferenceManager; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.webkit.WebView; +import android.widget.Toast; + +import com.example.android.networkusage.R; +import com.example.android.networkusage.StackOverflowXmlParser.Entry; + +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; +import java.io.InputStream; +import java.net.HttpURLConnection; +import java.net.URL; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.List; + + +/** + * Main Activity for the sample application. + * + * This activity does the following: + * + * o Presents a WebView screen to users. This WebView has a list of HTML links to the latest + * questions tagged 'android' on stackoverflow.com. + * + * o Parses the StackOverflow XML feed using XMLPullParser. + * + * o Uses AsyncTask to download and process the XML feed. + * + * o Monitors preferences and the device's network connection to determine whether + * to refresh the WebView content. + */ +public class NetworkActivity extends Activity { + public static final String WIFI = "Wi-Fi"; + public static final String ANY = "Any"; + private static final String URL = + "http://stackoverflow.com/feeds/tag?tagnames=android&sort=newest"; + + // Whether there is a Wi-Fi connection. + private static boolean wifiConnected = false; + // Whether there is a mobile connection. + private static boolean mobileConnected = false; + // Whether the display should be refreshed. + public static boolean refreshDisplay = true; + + // The user's current network preference setting. + public static String sPref = null; + + // The BroadcastReceiver that tracks network connectivity changes. + private NetworkReceiver receiver = new NetworkReceiver(); + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // Register BroadcastReceiver to track connection changes. + IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION); + receiver = new NetworkReceiver(); + this.registerReceiver(receiver, filter); + } + + // Refreshes the display if the network connection and the + // pref settings allow it. + @Override + public void onStart() { + super.onStart(); + + // Gets the user's network preference settings + SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this); + + // Retrieves a string value for the preferences. The second parameter + // is the default value to use if a preference value is not found. + sPref = sharedPrefs.getString("listPref", "Wi-Fi"); + + updateConnectedFlags(); + + // Only loads the page if refreshDisplay is true. Otherwise, keeps previous + // display. For example, if the user has set "Wi-Fi only" in prefs and the + // device loses its Wi-Fi connection midway through the user using the app, + // you don't want to refresh the display--this would force the display of + // an error page instead of stackoverflow.com content. + if (refreshDisplay) { + loadPage(); + } + } + + @Override + public void onDestroy() { + super.onDestroy(); + if (receiver != null) { + this.unregisterReceiver(receiver); + } + } + + // Checks the network connection and sets the wifiConnected and mobileConnected + // variables accordingly. + private void updateConnectedFlags() { + ConnectivityManager connMgr = + (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); + + NetworkInfo activeInfo = connMgr.getActiveNetworkInfo(); + if (activeInfo != null && activeInfo.isConnected()) { + wifiConnected = activeInfo.getType() == ConnectivityManager.TYPE_WIFI; + mobileConnected = activeInfo.getType() == ConnectivityManager.TYPE_MOBILE; + } else { + wifiConnected = false; + mobileConnected = false; + } + } + + // Uses AsyncTask subclass to download the XML feed from stackoverflow.com. + // This avoids UI lock up. To prevent network operations from + // causing a delay that results in a poor user experience, always perform + // network operations on a separate thread from the UI. + private void loadPage() { + if (((sPref.equals(ANY)) && (wifiConnected || mobileConnected)) + || ((sPref.equals(WIFI)) && (wifiConnected))) { + // AsyncTask subclass + new DownloadXmlTask().execute(URL); + } else { + showErrorPage(); + } + } + + // Displays an error if the app is unable to load content. + private void showErrorPage() { + setContentView(R.layout.main); + + // The specified network connection is not available. Displays error message. + WebView myWebView = (WebView) findViewById(R.id.webview); + myWebView.loadData(getResources().getString(R.string.connection_error), + "text/html", null); + } + + // Populates the activity's options menu. + @Override + public boolean onCreateOptionsMenu(Menu menu) { + MenuInflater inflater = getMenuInflater(); + inflater.inflate(R.menu.mainmenu, menu); + return true; + } + + // Handles the user's menu selection. + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.settings: + Intent settingsActivity = new Intent(getBaseContext(), SettingsActivity.class); + startActivity(settingsActivity); + return true; + case R.id.refresh: + loadPage(); + return true; + default: + return super.onOptionsItemSelected(item); + } + } + + // Implementation of AsyncTask used to download XML feed from stackoverflow.com. + private class DownloadXmlTask extends AsyncTask { + + @Override + protected String doInBackground(String... urls) { + try { + return loadXmlFromNetwork(urls[0]); + } catch (IOException e) { + return getResources().getString(R.string.connection_error); + } catch (XmlPullParserException e) { + return getResources().getString(R.string.xml_error); + } + } + + @Override + protected void onPostExecute(String result) { + setContentView(R.layout.main); + // Displays the HTML string in the UI via a WebView + WebView myWebView = (WebView) findViewById(R.id.webview); + myWebView.loadData(result, "text/html", null); + } + } + + // Uploads XML from stackoverflow.com, parses it, and combines it with + // HTML markup. Returns HTML string. + private String loadXmlFromNetwork(String urlString) throws XmlPullParserException, IOException { + InputStream stream = null; + StackOverflowXmlParser stackOverflowXmlParser = new StackOverflowXmlParser(); + List entries = null; + String title = null; + String url = null; + String summary = null; + Calendar rightNow = Calendar.getInstance(); + DateFormat formatter = new SimpleDateFormat("MMM dd h:mmaa"); + + // Checks whether the user set the preference to include summary text + SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this); + boolean pref = sharedPrefs.getBoolean("summaryPref", false); + + StringBuilder htmlString = new StringBuilder(); + htmlString.append("

" + getResources().getString(R.string.page_title) + "

"); + htmlString.append("" + getResources().getString(R.string.updated) + " " + + formatter.format(rightNow.getTime()) + ""); + + try { + stream = downloadUrl(urlString); + entries = stackOverflowXmlParser.parse(stream); + // Makes sure that the InputStream is closed after the app is + // finished using it. + } finally { + if (stream != null) { + stream.close(); + } + } + + // StackOverflowXmlParser returns a List (called "entries") of Entry objects. + // Each Entry object represents a single post in the XML feed. + // This section processes the entries list to combine each entry with HTML markup. + // Each entry is displayed in the UI as a link that optionally includes + // a text summary. + for (Entry entry : entries) { + htmlString.append("

" + entry.title + "

"); + // If the user set the preference to include summary text, + // adds it to the display. + if (pref) { + htmlString.append(entry.summary); + } + } + return htmlString.toString(); + } + + // Given a string representation of a URL, sets up a connection and gets + // an input stream. + private InputStream downloadUrl(String urlString) throws IOException { + URL url = new URL(urlString); + HttpURLConnection conn = (HttpURLConnection) url.openConnection(); + conn.setReadTimeout(10000 /* milliseconds */); + conn.setConnectTimeout(15000 /* milliseconds */); + conn.setRequestMethod("GET"); + conn.setDoInput(true); + // Starts the query + conn.connect(); + InputStream stream = conn.getInputStream(); + return stream; + } + + /** + * + * This BroadcastReceiver intercepts the android.net.ConnectivityManager.CONNECTIVITY_ACTION, + * which indicates a connection change. It checks whether the type is TYPE_WIFI. + * If it is, it checks whether Wi-Fi is connected and sets the wifiConnected flag in the + * main activity accordingly. + * + */ + public class NetworkReceiver extends BroadcastReceiver { + + @Override + public void onReceive(Context context, Intent intent) { + ConnectivityManager connMgr = + (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); + NetworkInfo networkInfo = connMgr.getActiveNetworkInfo(); + + // Checks the user prefs and the network connection. Based on the result, decides + // whether + // to refresh the display or keep the current display. + // If the userpref is Wi-Fi only, checks to see if the device has a Wi-Fi connection. + if (WIFI.equals(sPref) && networkInfo != null + && networkInfo.getType() == ConnectivityManager.TYPE_WIFI) { + // If device has its Wi-Fi connection, sets refreshDisplay + // to true. This causes the display to be refreshed when the user + // returns to the app. + refreshDisplay = true; + Toast.makeText(context, R.string.wifi_connected, Toast.LENGTH_SHORT).show(); + + // If the setting is ANY network and there is a network connection + // (which by process of elimination would be mobile), sets refreshDisplay to true. + } else if (ANY.equals(sPref) && networkInfo != null) { + refreshDisplay = true; + + // Otherwise, the app can't download content--either because there is no network + // connection (mobile or Wi-Fi), or because the pref setting is WIFI, and there + // is no Wi-Fi connection. + // Sets refreshDisplay to false. + } else { + refreshDisplay = false; + Toast.makeText(context, R.string.lost_connection, Toast.LENGTH_SHORT).show(); + } + } + } +} diff --git a/samples/training/network-usage/src/com/example/android/networkusage/SettingsActivity.java b/samples/training/network-usage/src/com/example/android/networkusage/SettingsActivity.java new file mode 100644 index 000000000000..73b72d26f7db --- /dev/null +++ b/samples/training/network-usage/src/com/example/android/networkusage/SettingsActivity.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * 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.networkusage; + +import android.content.SharedPreferences; +import android.content.SharedPreferences.OnSharedPreferenceChangeListener; +import android.os.Bundle; +import android.preference.PreferenceActivity; +import com.example.android.networkusage.R; + +/** + * This preference activity has in its manifest declaration an intent filter for + * the ACTION_MANAGE_NETWORK_USAGE action. This activity provides a settings UI + * for users to specify network settings to control data usage. + */ +public class SettingsActivity extends PreferenceActivity + implements + OnSharedPreferenceChangeListener { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // Loads the XML preferences file. + addPreferencesFromResource(R.xml.preferences); + } + + @Override + protected void onResume() { + super.onResume(); + + // Registers a callback to be invoked whenever a user changes a preference. + getPreferenceScreen().getSharedPreferences().registerOnSharedPreferenceChangeListener(this); + } + + @Override + protected void onPause() { + super.onPause(); + + // Unregisters the listener set in onResume(). + // It's best practice to unregister listeners when your app isn't using them to cut down on + // unnecessary system overhead. You do this in onPause(). + getPreferenceScreen() + .getSharedPreferences().unregisterOnSharedPreferenceChangeListener(this); + } + + // Fires when the user changes a preference. + @Override + public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { + // Sets refreshDisplay to true so that when the user returns to the main + // activity, the display refreshes to reflect the new settings. + NetworkActivity.refreshDisplay = true; + } +} diff --git a/samples/training/network-usage/src/com/example/android/networkusage/StackOverflowXmlParser.java b/samples/training/network-usage/src/com/example/android/networkusage/StackOverflowXmlParser.java new file mode 100644 index 000000000000..6a010986f97e --- /dev/null +++ b/samples/training/network-usage/src/com/example/android/networkusage/StackOverflowXmlParser.java @@ -0,0 +1,169 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * 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.networkusage; + +import android.util.Xml; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; + +/** + * This class parses XML feeds from stackoverflow.com. + * Given an InputStream representation of a feed, it returns a List of entries, + * where each list element represents a single entry (post) in the XML feed. + */ +public class StackOverflowXmlParser { + private static final String ns = null; + + // We don't use namespaces + + public List parse(InputStream in) throws XmlPullParserException, IOException { + try { + XmlPullParser parser = Xml.newPullParser(); + parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false); + parser.setInput(in, null); + parser.nextTag(); + return readFeed(parser); + } finally { + in.close(); + } + } + + private List readFeed(XmlPullParser parser) throws XmlPullParserException, IOException { + List entries = new ArrayList(); + + parser.require(XmlPullParser.START_TAG, ns, "feed"); + while (parser.next() != XmlPullParser.END_TAG) { + if (parser.getEventType() != XmlPullParser.START_TAG) { + continue; + } + String name = parser.getName(); + // Starts by looking for the entry tag + if (name.equals("entry")) { + entries.add(readEntry(parser)); + } else { + skip(parser); + } + } + return entries; + } + + // This class represents a single entry (post) in the XML feed. + // It includes the data members "title," "link," and "summary." + public static class Entry { + public final String title; + public final String link; + public final String summary; + + private Entry(String title, String summary, String link) { + this.title = title; + this.summary = summary; + this.link = link; + } + } + + // Parses the contents of an entry. If it encounters a title, summary, or link tag, hands them + // off + // to their respective "read" methods for processing. Otherwise, skips the tag. + private Entry readEntry(XmlPullParser parser) throws XmlPullParserException, IOException { + parser.require(XmlPullParser.START_TAG, ns, "entry"); + String title = null; + String summary = null; + String link = null; + while (parser.next() != XmlPullParser.END_TAG) { + if (parser.getEventType() != XmlPullParser.START_TAG) { + continue; + } + String name = parser.getName(); + if (name.equals("title")) { + title = readTitle(parser); + } else if (name.equals("summary")) { + summary = readSummary(parser); + } else if (name.equals("link")) { + link = readLink(parser); + } else { + skip(parser); + } + } + return new Entry(title, summary, link); + } + + // Processes title tags in the feed. + private String readTitle(XmlPullParser parser) throws IOException, XmlPullParserException { + parser.require(XmlPullParser.START_TAG, ns, "title"); + String title = readText(parser); + parser.require(XmlPullParser.END_TAG, ns, "title"); + return title; + } + + // Processes link tags in the feed. + private String readLink(XmlPullParser parser) throws IOException, XmlPullParserException { + String link = ""; + parser.require(XmlPullParser.START_TAG, ns, "link"); + String tag = parser.getName(); + String relType = parser.getAttributeValue(null, "rel"); + if (tag.equals("link")) { + if (relType.equals("alternate")) { + link = parser.getAttributeValue(null, "href"); + parser.nextTag(); + } + } + parser.require(XmlPullParser.END_TAG, ns, "link"); + return link; + } + + // Processes summary tags in the feed. + private String readSummary(XmlPullParser parser) throws IOException, XmlPullParserException { + parser.require(XmlPullParser.START_TAG, ns, "summary"); + String summary = readText(parser); + parser.require(XmlPullParser.END_TAG, ns, "summary"); + return summary; + } + + // For the tags title and summary, extracts their text values. + private String readText(XmlPullParser parser) throws IOException, XmlPullParserException { + String result = ""; + if (parser.next() == XmlPullParser.TEXT) { + result = parser.getText(); + parser.nextTag(); + } + return result; + } + + // Skips tags the parser isn't interested in. Uses depth to handle nested tags. i.e., + // if the next tag after a START_TAG isn't a matching END_TAG, it keeps going until it + // finds the matching END_TAG (as indicated by the value of "depth" being 0). + private void skip(XmlPullParser parser) throws XmlPullParserException, IOException { + if (parser.getEventType() != XmlPullParser.START_TAG) { + throw new IllegalStateException(); + } + int depth = 1; + while (depth != 0) { + switch (parser.next()) { + case XmlPullParser.END_TAG: + depth--; + break; + case XmlPullParser.START_TAG: + depth++; + break; + } + } + } +} -- 2.11.0