blob: 27d230b5737b856efb64d844f34d87bac4582671 [file] [log] [blame]
Katie McCormick2f8cc172012-03-16 17:47:55 -07001/*
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
15package com.example.android.networkusage;
16
17import android.app.Activity;
18import android.content.BroadcastReceiver;
19import android.content.Context;
20import android.content.Intent;
21import android.content.IntentFilter;
22import android.content.SharedPreferences;
23import android.net.ConnectivityManager;
24import android.net.NetworkInfo;
25import android.os.AsyncTask;
26import android.os.Bundle;
27import android.preference.PreferenceManager;
28import android.view.Menu;
29import android.view.MenuInflater;
30import android.view.MenuItem;
31import android.webkit.WebView;
32import android.widget.Toast;
33
34import com.example.android.networkusage.R;
35import com.example.android.networkusage.StackOverflowXmlParser.Entry;
36
37import org.xmlpull.v1.XmlPullParserException;
38
39import java.io.IOException;
40import java.io.InputStream;
41import java.net.HttpURLConnection;
42import java.net.URL;
43import java.text.DateFormat;
44import java.text.SimpleDateFormat;
45import java.util.Calendar;
46import 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 */
64public 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.
Alan Viverette51efddb2017-04-05 10:00:01 -0400161 WebView myWebView = findViewById(R.id.webview);
Katie McCormick2f8cc172012-03-16 17:47:55 -0700162 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
Alan Viverette51efddb2017-04-05 10:00:01 -0400208 WebView myWebView = findViewById(R.id.webview);
Katie McCormick2f8cc172012-03-16 17:47:55 -0700209 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}