• After 15+ years, we've made a big change: Android Forums is now Early Bird Club. Learn more here.

Apps RSS reader showing html tags in feed feed text

cr5315

Android Enthusiast
I have an RSS reader app that I'm working on and it's working fine except for the fact that when a user would click on the name of an article to see more about it, it has html tags in the text. For example, if a user clicks on the story "PJ Larsen Finishes 8th an Indianapolis Supercross", the following activity will say, "<p>JDR/J-STAR/KJM rider PJ Larsen earned eighth overall in the... in the Super class. </p>". The same thing happens it the article would have an image. The app shows "<img src="http://example.com/image.jpg">. I've looked at a lot of example on RSS readers and I can't figure out how to either get my reader to use the tags or omit them from the text.

AndroidRssReader.java
Code:
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.List;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;

import android.app.ListActivity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.widget.TextView;

public class AndroidRssReader extends ListActivity {

private RSSFeed myRssFeed = null;

public class MyCustomAdapter extends ArrayAdapter<RSSItem> {

 public MyCustomAdapter(Context context, int textViewResourceId,
   List<RSSItem> list) {
  super(context, textViewResourceId, list);
 }

 @Override
 public View getView(int position, View convertView, ViewGroup parent) {
  // TODO Auto-generated method stub
  //return super.getView(position, convertView, parent);
 
  View row = convertView;
 
  if(row==null){
   LayoutInflater inflater=getLayoutInflater();
   row=inflater.inflate(R.layout.row, parent, false);
  }
 
  TextView listTitle=(TextView)row.findViewById(R.id.listtitle);
  listTitle.setText(myRssFeed.getList().get(position).getTitle());
  TextView listPubdate=(TextView)row.findViewById(R.id.listpubdate);
  listPubdate.setText(myRssFeed.getList().get(position).getPubdate());

  if (position%2 == 0){
   listTitle.setBackgroundColor(0xff101010);
   listPubdate.setBackgroundColor(0xff101010);
  }
  else{
   listTitle.setBackgroundColor(0xff080808);
   listPubdate.setBackgroundColor(0xff080808);
  }
 
  return row;
 }
}

  /** Called when the activity is first created. */
  @Override
  public void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.main);
      
      Button raceButton = (Button) findViewById(R.id.race_button);
      raceButton.setOnClickListener(new View.OnClickListener() {
		
		@Override
		public void onClick(View v) {
			// TODO Auto-generated method stub
			Intent raceIntent = new Intent(AndroidRssReader.this, raceActivity.class);
			startActivity(raceIntent);
		}
	});
    
      try {
  URL rssUrl = new URL("http://www.supercross.com/index.php?option=com_ninjarsssyndicator&feed_id=2&format=raw");
  SAXParserFactory mySAXParserFactory = SAXParserFactory.newInstance();
  SAXParser mySAXParser = mySAXParserFactory.newSAXParser();
  XMLReader myXMLReader = mySAXParser.getXMLReader();
  RSSHandler myRSSHandler = new RSSHandler();
  myXMLReader.setContentHandler(myRSSHandler);
  InputSource myInputSource = new InputSource(rssUrl.openStream());
  myXMLReader.parse(myInputSource);
 
  myRssFeed = myRSSHandler.getFeed();
 
 } catch (MalformedURLException e) {
  // TODO Auto-generated catch block
  e.printStackTrace();
 } catch (ParserConfigurationException e) {
  // TODO Auto-generated catch block
  e.printStackTrace();
 } catch (SAXException e) {
  // TODO Auto-generated catch block
  e.printStackTrace();
 } catch (IOException e) {
  // TODO Auto-generated catch block
  e.printStackTrace();
 }

 if (myRssFeed!=null)
 {
  TextView feedDescribtion = (TextView)findViewById(R.id.feeddescribtion);
  feedDescribtion.setText(myRssFeed.getDescription());
 
  MyCustomAdapter adapter =
   new MyCustomAdapter(this, R.layout.row, myRssFeed.getList());
  setListAdapter(adapter);
 }
 
 }
  
 @Override
 protected void onListItemClick(ListView l, View v, final int position, long id) {
  // TODO Auto-generated method stub
	 Intent intent = new Intent(AndroidRssReader.this, ShowDetails.class);
	   Bundle bundle = new Bundle();
	   bundle.putString("keyTitle", myRssFeed.getItem(position).getTitle());
	   bundle.putString("keyDescription", myRssFeed.getItem(position).getDescription());
	   bundle.putString("keyLink", myRssFeed.getItem(position).getLink());
	   bundle.putString("keyPubdate", myRssFeed.getItem(position).getPubdate());
	   intent.putExtras(bundle);
	        startActivity(intent);
          
      };
 
  



@Override
public boolean onCreateOptionsMenu(Menu menu) {
    MenuInflater inflater = getMenuInflater();
    inflater.inflate(R.menu.menu, menu);
    return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    // Handle item selection
    switch (item.getItemId()) {
    case R.id.settings:
    	Intent settings = new Intent (this,settingsActivity.class);
    	startActivity(settings);
    case R.id.about:
    	Intent about = new Intent (this, aboutActivity.class);
	    startActivity(about);
    case R.id.exit:
    	finish();
    	return true;
    default:
        return super.onOptionsItemSelected(item);
    }
}
}

ShowDetails.java
Code:
package com.bba.supercross;

import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;

public class ShowDetails extends Activity {

@Override
protected void onCreate(Bundle savedInstanceState) {
 // TODO Auto-generated method stub
 super.onCreate(savedInstanceState);
 setContentView(R.layout.details);
 TextView detailsTitle = (TextView)findViewById(R.id.detailstitle);
 TextView detailsDescription = (TextView)findViewById(R.id.detailsdescription);
 TextView detailsPubdate = (TextView)findViewById(R.id.detailspubdate);
 TextView detailsLink = (TextView)findViewById(R.id.detailslink);

 Bundle bundle = this.getIntent().getExtras();
    
      detailsTitle.setText(bundle.getString("keyTitle"));
      detailsDescription.setText(bundle.getString("keyDescription"));
      detailsPubdate.setText(bundle.getString("keyPubdate"));
      detailsLink.setText(bundle.getString("keyLink"));
    
}
}

There are more .java's dealing with the RSS reader. If you think they would help, let me know and I will post them.

Here is an example of what it looks like

demo8i.png
 
The deescription field is often HTML and you need to convert it to plain text.
A simple solution is to use a regular expression:

String nohtml = htmlDescription.replaceAll("\\<.*?>","");

You will also find the need to unescape special charactes, such as &amp; to &

I wrote this simple code:

Code:
    public static String decodeXML(String in)
    {
        if (in==null) return null;
        StringBuilder out = new StringBuilder();
        int i = 0;

        try
        {
            for(i = 0; i < in.length(); i++)
            {
                char c = in.charAt(i);
                if(c == '&')
                {
                    i++;
                    if(in.startsWith("amp;", i))        {out.append('&');  i += 3;}
                    else if (in.startsWith("lt;", i))   {out.append('<');  i += 2;}
                    else if (in.startsWith("gt;", i))   {out.append('>');  i += 2;}
                    else if (in.startsWith("apos;", i)) {out.append('\''); i += 4;}
                    else if (in.startsWith("quot;", i)) {out.append('"');  i += 4;}
                    else if (in.startsWith("rarr;", i)) {out.append('\u2192');  i += 4;}
                    else if (in.startsWith("larr;", i)) {out.append('\u2190');  i += 4;}
                    else if (in.startsWith("lsquo;", i)) {out.append('\u2018');  i += 5;}
                    else if (in.startsWith("rsquo;", i)) {out.append('\u2019');  i += 5;}
                    else if (in.startsWith("mdash;", i)) {out.append('-');  i += 5;}
                    
                    else if (in.startsWith("#038;", i)) {out.append('&');  i += 4;}
                    else if (in.startsWith("#38;", i)) {out.append('&');  i += 3;}
                    else if (in.startsWith("#039;", i)) {out.append('\'');  i += 4;}
                    else if (in.startsWith("#39;", i)) {out.append('\'');  i += 3;}
                    
                    else if (in.startsWith("#034;", i)) {out.append('"');  i += 4;}
                    else if (in.startsWith("#34;", i)) {out.append('"');  i += 3;}
                    else if (in.startsWith("#060;", i)) {out.append('<');  i += 4;}
                    else if (in.startsWith("#062;", i)) {out.append('>');  i += 4;}
                    
                    else if (in.startsWith("#047", i)) {out.append('/');  i += 3;}
                    else if (in.startsWith("#47", i)) {out.append('/');  i += 2;}
                    else if (in.startsWith("#47;", i)) {out.append('/');  i += 3;}
                    
                    else if (in.startsWith("#8216;", i)) {out.append('\u2018');  i += 5;}
                    else if (in.startsWith("#8217;", i)) {out.append('\u2019');  i += 5;}
                    else if (in.startsWith("#8220;", i)) {out.append('\u201c');  i += 5;}                    
                    else if (in.startsWith("#8211;", i)) {out.append('\u201c');  i += 5;}                    
                    else if (in.startsWith("#8221;", i)) {out.append('\u201d');  i += 5;}
                    else if (in.startsWith("#8230;", i)) {out.append('\u2026');  i += 5;}
                    else if (in.startsWith("#0146;", i)) {out.append('\u0092');  i += 5;}
                    else if (in.startsWith("#x2014;", i)) {out.append('\u2014');  i += 6;}
                    //http://htmlhelp.com/reference/html40/entities/special.html
                    
                    else {out.append('&'); i--;}
                }
                else if(c == '\\')
                {
                    i++;
                    if(in.charAt(i) == 'u')
                    {
                        i++;
                        out.append((char)Integer.parseInt(in.substring(i,i+4),16));
                         i += 3;
                    }
                    else if(in.charAt(i) == '\\')
                         out.append('\\');
                    else
                    {
                        out.append('\\');
                        i--; // unread the last char
                    }
                }
                else
                    out.append(c);
            }
        }
        catch (Exception e)
        {
            return in;
        }

        return out.toString();
    }
We use this technique in the RSS Reader Task of the BuzzBox SDK for Android. Check out this:
BuzzBox SDK Library - Demo App




That should work unless the html is badly malformed. It that case and in more complecated cases you need to use a html parser. I used a modified version HtmlCleaner on Android and then I wrote some simple code to extract the text.
I can share if you need it.
 
I put it in the app, but it doesn't seem to be working. Am I doing something wrong?

Code:
package com.bba.supercross;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.List;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;

import android.app.ListActivity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.widget.TextView;

public class AndroidRssReader extends ListActivity {

private RSSFeed myRssFeed = null;

public class MyCustomAdapter extends ArrayAdapter<RSSItem> {

 public MyCustomAdapter(Context context, int textViewResourceId,
   List<RSSItem> list) {
  super(context, textViewResourceId, list);
 }

 public String decodeXML(String in)
 {
     if (in==null) return null;
     StringBuilder out = new StringBuilder();
     int i = 0;

     try
     {
         for(i = 0; i < in.length(); i++)
         {
             char c = in.charAt(i);
             if(c == '&')
             {
                 i++;
                 if(in.startsWith("amp;", i))        {out.append('&');  i += 3;}
                 else if (in.startsWith("lt;", i))   {out.append('<');  i += 2;}
                 else if (in.startsWith("gt;", i))   {out.append('>');  i += 2;}
                 else if (in.startsWith("apos;", i)) {out.append('\''); i += 4;}
                 else if (in.startsWith("quot;", i)) {out.append('"');  i += 4;}
                 else if (in.startsWith("rarr;", i)) {out.append('\u2192');  i += 4;}
                 else if (in.startsWith("larr;", i)) {out.append('\u2190');  i += 4;}
                 else if (in.startsWith("lsquo;", i)) {out.append('\u2018');  i += 5;}
                 else if (in.startsWith("rsquo;", i)) {out.append('\u2019');  i += 5;}
                 else if (in.startsWith("mdash;", i)) {out.append('-');  i += 5;}
                 
                 else if (in.startsWith("#038;", i)) {out.append('&');  i += 4;}
                 else if (in.startsWith("#38;", i)) {out.append('&');  i += 3;}
                 else if (in.startsWith("#039;", i)) {out.append('\'');  i += 4;}
                 else if (in.startsWith("#39;", i)) {out.append('\'');  i += 3;}
                 
                 else if (in.startsWith("#034;", i)) {out.append('"');  i += 4;}
                 else if (in.startsWith("#34;", i)) {out.append('"');  i += 3;}
                 else if (in.startsWith("#060;", i)) {out.append('<');  i += 4;}
                 else if (in.startsWith("#062;", i)) {out.append('>');  i += 4;}
                 
                 else if (in.startsWith("#047", i)) {out.append('/');  i += 3;}
                 else if (in.startsWith("#47", i)) {out.append('/');  i += 2;}
                 else if (in.startsWith("#47;", i)) {out.append('/');  i += 3;}
                 
                 else if (in.startsWith("#8216;", i)) {out.append('\u2018');  i += 5;}
                 else if (in.startsWith("#8217;", i)) {out.append('\u2019');  i += 5;}
                 else if (in.startsWith("#8220;", i)) {out.append('\u201c');  i += 5;}                    
                 else if (in.startsWith("#8211;", i)) {out.append('\u201c');  i += 5;}                    
                 else if (in.startsWith("#8221;", i)) {out.append('\u201d');  i += 5;}
                 else if (in.startsWith("#8230;", i)) {out.append('\u2026');  i += 5;}
                 else if (in.startsWith("#0146;", i)) {out.append('\u0092');  i += 5;}
                 else if (in.startsWith("#x2014;", i)) {out.append('\u2014');  i += 6;}
                 //http://htmlhelp.com/reference/html40/entities/special.html
                 
                 else {out.append('&'); i--;}
             }
             else if(c == '\\')
             {
                 i++;
                 if(in.charAt(i) == 'u')
                 {
                     i++;
                     out.append((char)Integer.parseInt(in.substring(i,i+4),16));
                      i += 3;
                 }
                 else if(in.charAt(i) == '\\')
                      out.append('\\');
                 else
                 {
                     out.append('\\');
                     i--; // unread the last char
                 }
             }
             else
                 out.append(c);
         }
     }
     catch (Exception e)
     {
         return in;
     }

     return out.toString();
 }
 
 @Override
 public View getView(int position, View convertView, ViewGroup parent) {
  // TODO Auto-generated method stub
  //return super.getView(position, convertView, parent);
 
  View row = convertView;
 
  if(row==null){
   LayoutInflater inflater=getLayoutInflater();
   row=inflater.inflate(R.layout.row, parent, false);
  }
 
  TextView listTitle=(TextView)row.findViewById(R.id.listtitle);
  listTitle.setText(myRssFeed.getList().get(position).getTitle());
  TextView listPubdate=(TextView)row.findViewById(R.id.listpubdate);
  listPubdate.setText(myRssFeed.getList().get(position).getPubdate());

  if (position%2 == 0){
   listTitle.setBackgroundColor(0xff101010);
   listPubdate.setBackgroundColor(0xff101010);
  }
  else{
   listTitle.setBackgroundColor(0xff080808);
   listPubdate.setBackgroundColor(0xff080808);
  }
 
  return row;
 }
}

  /** Called when the activity is first created. */
  @Override
  public void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.main);
      
      Button raceButton = (Button) findViewById(R.id.race_button);
      raceButton.setOnClickListener(new View.OnClickListener() {
		
		@Override
		public void onClick(View v) {
			// TODO Auto-generated method stub
			Intent raceIntent = new Intent(AndroidRssReader.this, raceActivity.class);
			startActivity(raceIntent);
		}
	});
    
      try {
  URL rssUrl = new URL("http://www.supercross.com/index.php?option=com_ninjarsssyndicator&feed_id=2&format=raw");
  SAXParserFactory mySAXParserFactory = SAXParserFactory.newInstance();
  SAXParser mySAXParser = mySAXParserFactory.newSAXParser();
  XMLReader myXMLReader = mySAXParser.getXMLReader();
  RSSHandler myRSSHandler = new RSSHandler();
  myXMLReader.setContentHandler(myRSSHandler);
  InputSource myInputSource = new InputSource(rssUrl.openStream());
  myXMLReader.parse(myInputSource);
 
  myRssFeed = myRSSHandler.getFeed();
 
 } catch (MalformedURLException e) {
  // TODO Auto-generated catch block
  e.printStackTrace();
 } catch (ParserConfigurationException e) {
  // TODO Auto-generated catch block
  e.printStackTrace();
 } catch (SAXException e) {
  // TODO Auto-generated catch block
  e.printStackTrace();
 } catch (IOException e) {
  // TODO Auto-generated catch block
  e.printStackTrace();
 }

 if (myRssFeed!=null)
 {
  TextView feedDescribtion = (TextView)findViewById(R.id.feeddescribtion);
  feedDescribtion.setText(myRssFeed.getDescription());
 
  MyCustomAdapter adapter =
   new MyCustomAdapter(this, R.layout.row, myRssFeed.getList());
  setListAdapter(adapter);
 }
 
 }
  
 @Override
 protected void onListItemClick(ListView l, View v, final int position, long id) {
  // TODO Auto-generated method stub
	 Intent intent = new Intent(AndroidRssReader.this, ShowDetails.class);
	   Bundle bundle = new Bundle();
	   bundle.putString("keyTitle", myRssFeed.getItem(position).getTitle());
	   bundle.putString("keyDescription", myRssFeed.getItem(position).getDescription());
	   bundle.putString("keyLink", myRssFeed.getItem(position).getLink());
	   bundle.putString("keyPubdate", myRssFeed.getItem(position).getPubdate());
	   intent.putExtras(bundle);
	        startActivity(intent);
          
      };
 
  



@Override
public boolean onCreateOptionsMenu(Menu menu) {
    MenuInflater inflater = getMenuInflater();
    inflater.inflate(R.menu.menu, menu);
    return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    // Handle item selection
    switch (item.getItemId()) {
    case R.id.about:
    	Intent about = new Intent (this, aboutActivity.class);
	    startActivity(about);
    case R.id.exit:
    	finish();
    	return true;
    default:
        return super.onOptionsItemSelected(item);
    }
}
}
 
Back
Top Bottom