Friday 9 September 2016

Android XMLPullParsing in Webview




STEP-1  Add 3 permission to AndroidManifest.xml

AndroidManifest.xml

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
 
STEP-2  Add below string to values/strings.xml
 

Strings.xml

<resources>

    <string name="app_name">NetworkUsage</string>
    <!--  Menu items -->    <string name="settings">Settings</string>
    <string name="refresh">Refresh</string>

    <!--  NetworkActivity -->
    <string name="page_title">Newest StackOverflow questions tagged \'android\'</string>
    <string name="updated">Last updated:</string>
    <string name="lost_connection">Lost connection.</string>
    <string name="wifi_connected">Wi-Fi reconnected.</string>
    <string name="connection_error">Unable to load content. Check your network connection.</string>
    <string name="xml_error">Error parsing XML.</string>

</resources>

 STEP-3   Now create an resource file in res/values/ directory and name it - arrays.xml
 

arrays.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string-array name="listArray">
        <item>Only when on Wi-Fi</item>
        <item>On any network</item>
    </string-array>
    <string-array name="listValues">
        <item>Wi-Fi</item>
        <item>Any</item>
    </string-array>
</resources>a
 
STEP-4   Now, create a directory under res and name it - xml
Goto app --> res --> right click --> new --> directory --> Name it : xml
Now, create xml resource file under xml directory.
 

preferences.xml 


<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
    <ListPreference 
 android:title="Download Feed"         
android:summary="Network connectivity required to download the feed."         
android:key="listPref"        android:defaultValue="Wi-Fi" 
 android:entries="@array/listArray" 
 android:entryValues="@array/listValues"     />
    <CheckBoxPreference 
 android:title="Show Summaries" 
 android:defaultValue="false"         
android:summary="Show a summary for each link." 
 android:key="summaryPref" />
</PreferenceScreen>


STEP-5 Now create a java resource file in app/java/ and name it - SettingsActivity
 

SettingsActivity.java

 
 
        import android.content.SharedPreferences;
        import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
        import android.os.Bundle;
        import android.preference.PreferenceActivity;
        import com.example.divakar.xmlparsing.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 t he 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.         
MainActivity.refreshDisplay = true;
    }
}
 
 
 
 STEP-6 Now create an another java resource file in app/java/ and name it - 
StackOverflowXmlParser
 

StackOverflowXmlParser.java


        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;

public class StackOverflowXmlParser {
    private static final String ns = null;

    // We don't use namespaces
    public List<Entry> 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<Entry> readFeed(XmlPullParser parser) throws XmlPullParserException, IOException {
        List<Entry> entries = new ArrayList<Entry>();

        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 &quot;read&quot; 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;
            }
        }
    }
}




STEP-7   Now, goto your activity_main.xml and paste the below code

activity_main.xml


<?xml version="1.0" encoding="utf-8"?>
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"     
android:layout_width="fill_parent"    android:layout_height="fill_parent"     
android:orientation="vertical" >

    <WebView  xmlns:android="http://schemas.android.com/apk/res/android"         
android:id="@+id/webview" 
 android:layout_width="fill_parent" 
 android:layout_height="fill_parent"/>
</LinearLayout>

STEP-8  Now, paste this below code to java res file.

MainActivity.java


        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.divakar.xmlparsing.R;
        import com.example.divakar.xmlparsing.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;


public class MainActivity 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.    p
rivate 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.activity_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<String, Void, String> {

        @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.activity_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<Entry> 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("<h3>" + getResources().getString(R.string.page_title) + "</h3>");
        htmlString.append("<em>" + getResources().getString(R.string.updated) + " " +
                formatter.format(rightNow.getTime()) + "</em>");

        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("<p><a href='");
            htmlString.append(entry.link);
            htmlString.append("'>" + entry.title + "</a></p>");
            // 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();
            }
        }
    }
}

No comments:

Post a Comment