Java tutorial
/* * 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.kentph.ttcnextbus; 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.database.sqlite.SQLiteDatabase; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.os.AsyncTask; import android.os.Bundle; import android.preference.PreferenceManager; import android.support.v4.app.NavUtils; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.webkit.WebView; import android.widget.Toast; import com.kentph.ttcnextbus.NextBusRouteConfigXmlParser.RouteConfig; import com.kentph.ttcnextbus.NextBusRouteListXmlParser.Route; import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.URL; import java.util.List; import static android.database.DatabaseUtils.InsertHelper; import static com.kentph.ttcnextbus.NextBusRouteConfigXmlParser.Path; import static com.kentph.ttcnextbus.NextBusRouteConfigXmlParser.PathPoint; import static com.kentph.ttcnextbus.RouteDbHelper.RouteDbContract.PathsTable; import static com.kentph.ttcnextbus.RouteDbHelper.RouteDbContract.StopsTable; /** * 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 CreateDatabaseActivity extends Activity { public static final String WIFI = "Wi-Fi"; public static final String ANY = "Any"; // 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(); } else { showErrorPage(); } } // Displays an error if the app is unable to load content. private void showErrorPage() { setContentView(R.layout.activity_network); // 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; case android.R.id.home: // This ID represents the Home or Up button. In the case of this // activity, the Up button is shown. Use NavUtils to allow users // to navigate up one level in the application structure. For // more details, see the Navigation pattern on Android Design: // // http://developer.android.com/design/patterns/navigation.html#up-vs-back // NavUtils.navigateUpFromSameTask(this); return true; default: return super.onOptionsItemSelected(item); } } // Implementation of AsyncTask used to download XML feed from stackoverflow.com. private class DownloadXmlTask extends AsyncTask<Void, String, String> { // private ProgressDialog pd; @Override protected void onPreExecute() { super.onPreExecute(); // pd = new ProgressDialog(getApplicationContext()); // pd.setMessage("Starting..."); // pd.show(); } @Override protected String doInBackground(Void... params) { try { InputStream stream = null; NextBusRouteListXmlParser nextBusRouteListXmlParser = new NextBusRouteListXmlParser(); NextBusRouteConfigXmlParser nextBusRouteConfigXmlParser = new NextBusRouteConfigXmlParser(); List<Route> routes = null; RouteConfig routeConfig = null; StringBuilder htmlString = new StringBuilder(); RouteDbHelper mDbHelper = new RouteDbHelper(getBaseContext()); // Gets the data repository in write mode SQLiteDatabase db = mDbHelper.getWritableDatabase(); // use insertHelper to speed up insertions InsertHelper ihPathsTable = new InsertHelper(db, "paths"); InsertHelper ihStopsTable = new InsertHelper(db, "stops"); // get column indices final int pathRouteNumberColumn = ihPathsTable.getColumnIndex(PathsTable.COLUMN_NAME_ROUTE_NUMBER); final int pathIdColumn = ihPathsTable.getColumnIndex(PathsTable.COLUMN_NAME_PATH_ID); final int pathLatColumn = ihPathsTable.getColumnIndex(PathsTable.COLUMN_NAME_LAT); final int pathLonColumn = ihPathsTable.getColumnIndex(PathsTable.COLUMN_NAME_LON); final int stopRouteNumberColumn = ihStopsTable.getColumnIndex(StopsTable.COLUMN_NAME_ROUTE_NUMBER); final int stopRouteNameColumn = ihStopsTable.getColumnIndex(StopsTable.COLUMN_NAME_ROUTE_NAME); final int stopIdColumn = ihStopsTable.getColumnIndex(StopsTable.COLUMN_NAME_STOP_ID); final int stopTagColumn = ihStopsTable.getColumnIndex(StopsTable.COLUMN_NAME_STOP_TAG); final int stopTitleColumn = ihStopsTable.getColumnIndex(StopsTable.COLUMN_NAME_STOP_TITLE); final int stopDirectionColumn = ihStopsTable.getColumnIndex(StopsTable.COLUMN_NAME_DIRECTION); final int stopBranchColumn = ihStopsTable.getColumnIndex(StopsTable.COLUMN_NAME_BRANCH); final int stopLatColumn = ihStopsTable.getColumnIndex(StopsTable.COLUMN_NAME_LAT); final int stopLonColumn = ihStopsTable.getColumnIndex(StopsTable.COLUMN_NAME_LON); final int gridLatColumn = ihStopsTable.getColumnIndex(StopsTable.COLUMN_NAME_GRID_LAT); final int gridLonColumn = ihStopsTable.getColumnIndex(StopsTable.COLUMN_NAME_GRID_LON); // create only 1 transaction for speed db.beginTransaction(); // get route list try { stream = downloadUrl( "http://webservices.nextbus.com/service/publicXMLFeed?command=routeList&a=ttc"); routes = nextBusRouteListXmlParser.parse(stream); for (Route route : routes) { stream = downloadUrl( "http://webservices.nextbus.com/service/publicXMLFeed?command=routeConfig&a=ttc&r=" .concat(route.routeNumber)); routeConfig = nextBusRouteConfigXmlParser.parse(stream); // import into path table int pathCount = 0; for (Path path : routeConfig.paths) { for (PathPoint pathPoint : path.path) { ihPathsTable.prepareForInsert(); ihPathsTable.bind(pathRouteNumberColumn, routeConfig.routeNumber); ihPathsTable.bind(pathIdColumn, pathCount); ihPathsTable.bind(pathLatColumn, pathPoint.lat); ihPathsTable.bind(pathLonColumn, pathPoint.lon); ihPathsTable.execute(); } pathCount++; } // import into stop table for (NextBusRouteConfigXmlParser.Stop stop : routeConfig.stops) { ihStopsTable.prepareForInsert(); ihStopsTable.bind(stopRouteNumberColumn, routeConfig.routeNumber); ihStopsTable.bind(stopRouteNameColumn, routeConfig.routeName); ihStopsTable.bind(stopIdColumn, stop.stopId); ihStopsTable.bind(stopTagColumn, stop.tag); ihStopsTable.bind(stopTitleColumn, stop.title); ihStopsTable.bind(stopDirectionColumn, stop.direction); ihStopsTable.bind(stopBranchColumn, stop.branch); ihStopsTable.bind(stopLatColumn, stop.lat); ihStopsTable.bind(stopLonColumn, stop.lon); // we want integer division to get grid IDs ihStopsTable.bind(gridLatColumn, (int) (stop.lat / 0.004)); ihStopsTable.bind(gridLonColumn, (int) (stop.lon / 0.004)); ihStopsTable.execute(); } // publishProgress(routeConfig.routeNumber); } // if all stops/paths imported... db.setTransactionSuccessful(); // Makes sure that the InputStream is closed after the app is // finished using it. } finally { // end transaction whether or not successful db.endTransaction(); //ihPathsTable.close(); // ihStopsTable.close(); if (stream != null) { stream.close(); } } htmlString.append("<p>Done</p>"); return htmlString.toString(); } catch (IOException e) { return getResources().getString(R.string.connection_error); } catch (XmlPullParserException e) { return getResources().getString(R.string.xml_error); } } @Override protected void onProgressUpdate(String... values) { // pd.setMessage("Getting route " + values[0]); // pd.show(); } @Override protected void onPostExecute(String result) { setContentView(R.layout.activity_network); // Displays the HTML string in the UI via a WebView WebView myWebView = (WebView) findViewById(R.id.webview); myWebView.loadData(result, "text/html", null); // pd.setMessage("Done!"); // pd.show(); } } // 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(); } } } }