MAJOR code refucktoring.

This commit is contained in:
Robert Kolner committed 2012-01-17 21:03:04 +01:00
1 parent 3acf6bb8b7
commit 542a8e891e
12 files changed
+320 -229

No files matched your search

@@ -1,13 +1,17 @@
package com.studentersamfundet.app;
import java.io.Serializable;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
public class DataHandler {
private List<Event> events = new LinkedList<Event>();
private List<News> news = new LinkedList<News>();
public class DataHandler implements Serializable {
private static final long serialVersionUID = 6787541405689146002L;
private Collection<Event> events = new LinkedHashSet<Event>();
private Collection<News> news = new LinkedList<News>();
private Set<String> eventCategories = new TreeSet<String>();
public void insertEvent(String id,
@@ -22,8 +26,6 @@ public class DataHandler {
String priceMem,
String ticketUriStr) {
int intId = Integer.parseInt(id);
if (getEvent(intId) != null)
return;
Event event = new Event(intId, title, description, date, location, text, category, imageUri);
+15 -3
View File
@@ -8,11 +8,11 @@ import java.util.Date;
import android.net.Uri;
public class Event implements Serializable {
private static final long serialVersionUID = 1L;
public static final String ALL = "Alle";
private static final long serialVersionUID = -5222901012434000257L;
private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm");
public static final String ALL = "Alle";
public final int id;
public final String title;
private String description;
@@ -118,4 +118,16 @@ public class Event implements Serializable {
this.memberPrice = memberPrice;
this.ticketUriStr = ticketUri;
}
public int hashCode() {
return this.id;
}
public boolean equals(Object o2) {
if (o2 instanceof Event) {
Event e2 = (Event)o2;
return this.id == e2.id;
}
return false;
}
}
+66 -54
View File
@@ -5,6 +5,7 @@ import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Serializable;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
@@ -19,14 +20,12 @@ import org.xml.sax.SAXException;
import android.content.Context;
public class FeedFetcher {
private final String LOCAL_FILENAME = "dns_events";
private Context context;
public class FeedFetcher implements Serializable {
private static final long serialVersionUID = 3408406671996232103L;
private static final String LOCAL_FILENAME = "dns_events";
private String url;
public FeedFetcher(Context context, String url) {
this.context = context;
public FeedFetcher(String url) {
this.url = url;
}
@@ -61,54 +60,67 @@ public class FeedFetcher {
* @throws IOException Is thrown if the app cannot download the file from
* the internet and there is no local copy.
*/
public NodeList fetch(boolean forcedUpdate) throws IOException {
InputStream in = null;
NodeList itemNodes = null;
/* Update the local file if possible. */
try {
if (forcedUpdate || doesFileNeedUpdate(context)) {
in = openHttpConnection();
saveFile(in, context);
}
} catch(IOException e) {
/* OK, so we failed to update it. It doesn't matter yet, because we can try to... */
} finally {
if (in != null)
in.close();
}
/* ...load local file: */
in = loadFile(context); // if we fail here, we fail ultimately. Throw the Exception!
Document doc = null;
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db;
try {
db = dbf.newDocumentBuilder();
doc = db.parse(in);
} catch (ParserConfigurationException e) {
/* This should never happen. */
} catch (SAXException e) {
/* This shouldn't happen unless the xml returned by the server
* is somehow invalid. If it happens, the best course of action
* is to pretend we failed to download the event list (it's
* close enough and it simplifies exception-handling). */
throw new IOException("Bad response from server.");
}
doc.getDocumentElement().normalize();
// Retrieve all the <item> nodes.
itemNodes = doc.getElementsByTagName("item");
in.close();
return itemNodes;
public NodeList fetch(Context context, boolean forcedUpdate) throws IOException {
NodeList itemNodes = null;
InputStream in = null;
/* Update the local file if possible. */
try {
if (forcedUpdate || doesFileNeedUpdate(context)) {
in = openHttpConnection();
saveFile(in, context);
}
} catch(IOException e) {
/* OK, so we failed to update it. It doesn't matter yet, because we can try to... */
} finally {
if (in != null)
in.close();
}
/* ...load local file: */
in = loadFile(context); // if we fail here, we fail ultimately. Throw the Exception!
itemNodes = parseInputStream(in);
in.close();
return itemNodes;
}
private boolean doesFileNeedUpdate(Context c) throws IOException {
public static NodeList loadLocalData(Context c) throws IOException {
NodeList nodes;
InputStream is = loadFile(c);
nodes = parseInputStream(is);
is.close();
return nodes;
}
private static NodeList parseInputStream(InputStream iStream) throws IOException {
Document doc = null;
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db;
try {
db = dbf.newDocumentBuilder();
doc = db.parse(iStream);
} catch (ParserConfigurationException e) {
/* This should never happen. */
} catch (SAXException e) {
/* This shouldn't happen unless the xml returned by the server
* is somehow invalid. If it happens, the best course of action
* is to pretend we failed to download the event list (it's
* close enough and it simplifies exception-handling). */
throw new IOException("Bad response from server.");
}
doc.getDocumentElement().normalize();
// Retrieve all the <item> nodes.
return doc.getElementsByTagName("item");
}
private static boolean doesFileNeedUpdate(Context c) throws IOException {
/** Maximum amount of time before the file needs updating, in ms: */
final long maxInterval = 1000 * 60 * 60; // one hour
@@ -121,7 +133,7 @@ public class FeedFetcher {
return now - lastMod > maxInterval;
}
private void saveFile(InputStream in, Context c) throws IOException, FileNotFoundException {
private static void saveFile(InputStream in, Context c) throws IOException, FileNotFoundException {
final int BUFFER_SIZE = 128;
OutputStream os = c.openFileOutput(LOCAL_FILENAME, Context.MODE_PRIVATE);
@@ -135,7 +147,7 @@ public class FeedFetcher {
os.close();
}
private InputStream loadFile(Context c) throws FileNotFoundException {
private static InputStream loadFile(Context c) throws FileNotFoundException {
return c.openFileInput(LOCAL_FILENAME);
}
}
@@ -0,0 +1,7 @@
package com.studentersamfundet.app;
import org.w3c.dom.NodeList;
public interface IRSSParser {
public DataHandler parse(NodeList nodes);
}
+6 -5
View File
@@ -1,16 +1,17 @@
package com.studentersamfundet.app;
import java.io.Serializable;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import android.net.Uri;
public class News {
public class News implements Serializable {
private static final long serialVersionUID = 4015441794784305793L;
public String title;
public String description;
public String content;
public Uri link;
public String link;
public Date pubDate;
private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
@@ -19,7 +20,7 @@ public class News {
this.title = title.trim();
this.description = description.trim();
this.content = content.trim();
this.link = Uri.parse(link.trim());
this.link = link.trim();
try {
this.pubDate = sdf.parse(pubDate.trim());
@@ -4,7 +4,7 @@ import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
public class RSSParserNews {
public class RSSParserNews implements IRSSParser {
private final DataHandler dh;
public RSSParserNews() {
@@ -4,7 +4,7 @@ import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
public class RSSParserProgram {
public class RSSParserProgram implements IRSSParser {
private final DataHandler dh;
public RSSParserProgram() {
@@ -11,30 +11,21 @@ public abstract class BaseDnsActivity extends Activity {
protected Handler handler = new Handler();
public void clickHandler(View v) {
Intent intent = new Intent();
Intent intent = null;
/* Program button was pushed: */
if (v.getId() == R.id.main_menu_program) {
intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT
| Intent.FLAG_ACTIVITY_SINGLE_TOP);
intent.setClass(this, EventListActivity.class);
intent = setupNewIntent(EventListActivity.class);
startActivity(intent);
/* Join button was pushed: */
} else if (v.getId() == R.id.main_menu_join) {
intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY
| Intent.FLAG_ACTIVITY_SINGLE_TOP);
intent.setClass(this, JoinUsActivity.class);
intent = setupNewIntent(JoinUsActivity.class);
startActivity(intent);
/* The header was pushed: */
} else if (v.getId() == R.id.header) {
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP
| Intent.FLAG_ACTIVITY_SINGLE_TOP);
intent.setClass(this, DnsActivity.class);
intent = setupNewIntent(DnsActivity.class);
startActivity(intent);
}
}
@@ -42,4 +33,13 @@ public abstract class BaseDnsActivity extends Activity {
public void doNothing(View v) {
/* As promised. */
}
protected Intent setupNewIntent(Class<?> cl) {
Intent intent = new Intent();
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP
| Intent.FLAG_ACTIVITY_SINGLE_TOP);
intent.setClass(this, cl);
return intent;
}
}
@@ -0,0 +1,138 @@
package com.studentersamfundet.app.ui;
import java.io.IOException;
import org.w3c.dom.NodeList;
import com.studentersamfundet.app.DataHandler;
import com.studentersamfundet.app.Event;
import com.studentersamfundet.app.FeedFetcher;
import com.studentersamfundet.app.IRSSParser;
import com.studentersamfundet.app.R;
import com.studentersamfundet.app.RSSParserProgram;
import android.content.Context;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.View;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.Toast;
public abstract class BaseListActivity extends BaseDnsActivity {
private DataHandler dataHandler;
private FeedFetcher feed;
private IRSSParser parser;
private boolean isUpdated = false;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
/* Fetch the feed fetcher: */
Intent i = getIntent();
this.feed = (FeedFetcher)i.getExtras().get("feed");
if (this.feed == null)
throw new RuntimeException("This activity must be fed a feed feeder.");
/* Create the parser: */
this.parser = new RSSParserProgram();
/* DataHandler will be set later. */
}
protected final DataHandler getDataHandler() {
if (!isUpdated && dataHandler == null)
throw new RuntimeException("You have to create and run list creator first!");
return this.dataHandler;
}
protected abstract <T> ListAdapter createAdapter(T[] objects);
protected abstract <T> ListCreator<?> getListCreator(Context c, int idList, int idProgressBar);
protected abstract class ListCreator<T> extends AsyncTask<Void, Void, Boolean> {
private String category = Event.ALL;
private boolean forcedUpdate = false;
private final Context context;
private final int idList;
private final int idProgressBar;
public ListCreator(Context context, int idList, int idProgressBar) {
this.context = context;
this.idList = idList;
this.idProgressBar = idProgressBar;
}
protected String getCategory() {
return this.category;
}
protected void setCategory(String category) {
this.category = category;
}
protected boolean getForcedUpdate() {
return this.forcedUpdate;
}
protected void setForcedUpdate(boolean forcedUpdate) {
this.forcedUpdate = forcedUpdate;
}
@Override
protected void onPreExecute() {
if (forcedUpdate) {
dataHandler = null;
}
ListView list = (ListView)findViewById(idList);
list.setVisibility(View.GONE);
ProgressBar pb = (ProgressBar)findViewById(idProgressBar);
pb.setVisibility(View.VISIBLE);
}
@Override
protected Boolean doInBackground(Void ... v) {
// Do we have intarwebs?
// YAY = Fetch the feed.
// NAY = Inform about no connection.
try {
if (dataHandler == null) {
NodeList itemNodes = feed.fetch(BaseListActivity.this, this.forcedUpdate);
dataHandler = parser.parse(itemNodes);
}
return true;
} catch (IOException e) {
return false;
}
}
@Override
protected void onPostExecute(Boolean result) {
isUpdated = true;
if (result) {
ListView list = (ListView)findViewById(idList);
T[] objects = getObjects();
ListAdapter adapter = createAdapter(objects);
list.setAdapter(adapter);
list.setVisibility(View.VISIBLE);
ProgressBar pb = (ProgressBar)findViewById(idProgressBar);
pb.setVisibility(View.GONE);
} else {
Toast toast = Toast.makeText(context, R.string.error_noconnection_noupdate, Toast.LENGTH_LONG);
toast.show();
}
}
abstract protected T[] getObjects();
}
}
@@ -12,10 +12,15 @@ import android.view.animation.Animation;
import android.widget.TextView;
import android.widget.Toast;
import com.studentersamfundet.app.FeedFetcher;
import com.studentersamfundet.app.R;
public class DnsActivity extends BaseDnsActivity {
String[] openingHoursHouse = {
public static final String feedURL = "http://studentersamfundet.no/rss/lars_program_feed.php";
private FeedFetcher feedFetcher;
private String[] openingHoursHouse = {
"10.00 - 01.00",
"10.00 - 01.00",
"10.00 - 01.00",
@@ -24,7 +29,7 @@ public class DnsActivity extends BaseDnsActivity {
"12.00 - 03.00",
"12.00 - 20.00"};
String[] openingHoursGB = {
private String[] openingHoursGB = {
"13.00 - 01.00",
"13.00 - 01.00",
"13.00 - 01.00",
@@ -33,7 +38,7 @@ public class DnsActivity extends BaseDnsActivity {
"16.00 - 03.00",
"Stengt"};
String[] openingHoursBC = {
private String[] openingHoursBC = {
"19.00 - 00.00",
"19.00 - 00.00",
"19.00 - 00.00",
@@ -48,6 +53,9 @@ public class DnsActivity extends BaseDnsActivity {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
/* Initialize necessary variables: */
feedFetcher = new FeedFetcher(feedURL);
/* Set opening hours: */
TextView tvNeuf = (TextView)findViewById(R.id.main_menu_hours_neuf);
TextView tvBC = (TextView)findViewById(R.id.main_menu_hours_bc);
@@ -138,22 +146,12 @@ public class DnsActivity extends BaseDnsActivity {
}
public void programButton(View v) {
Intent intent = new Intent();
intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT
| Intent.FLAG_ACTIVITY_SINGLE_TOP);
intent.setClass(this, EventListActivity.class);
Intent intent = setupNewIntent(EventListActivity.class);
startActivity(intent);
}
public void ticketButton(View v) {
Intent intent = new Intent();
intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT
| Intent.FLAG_ACTIVITY_SINGLE_TOP);
intent.setClass(this, EventListTicketsActivity.class);
Intent intent = setupNewIntent(EventListTicketsActivity.class);
startActivity(intent);
}
@@ -162,12 +160,15 @@ public class DnsActivity extends BaseDnsActivity {
}
public void joinButton(View v) {
Intent intent = new Intent();
intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
intent.setClass(this, JoinUsActivity.class);
Intent intent = setupNewIntent(JoinUsActivity.class);
startActivity(intent);
}
protected Intent setupNewIntent(Class<?> cl) {
Intent intent = super.setupNewIntent(cl);
intent.putExtra("feed", feedFetcher);
return intent;
}
}
@@ -7,13 +7,11 @@ import java.net.MalformedURLException;
import java.net.URL;
import java.text.SimpleDateFormat;
import org.w3c.dom.NodeList;
import android.app.Dialog;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.os.Bundle;
import android.text.Html;
import android.view.LayoutInflater;
@@ -26,26 +24,16 @@ import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import com.studentersamfundet.app.DataHandler;
import com.studentersamfundet.app.Event;
import com.studentersamfundet.app.FeedFetcher;
import com.studentersamfundet.app.R;
import com.studentersamfundet.app.RSSParserProgram;
import com.studentersamfundet.app.ui.ChooseCategoryDialog.Callback;
public class EventListActivity extends BaseDnsActivity {
public static final String feedURL = "http://studentersamfundet.no/rss/lars_program_feed.php";
public class EventListActivity extends BaseListActivity {
public static final int imageSize = 80;
protected FeedFetcher feed;
protected RSSParserProgram parser;
protected DataHandler dataHandler;
private String currentCategory = Event.ALL;
@Override
@@ -53,10 +41,7 @@ public class EventListActivity extends BaseDnsActivity {
super.onCreate(savedInstanceState);
setContentView(R.layout.event_list);
this.feed = new FeedFetcher(this, feedURL);
this.parser = new RSSParserProgram();
ListCreator lc = getListCreator(Event.ALL, false);
ListCreator<?> lc = getListCreator(this, R.id.event_list, R.id.event_list_progress_bar);
lc.execute();
}
@@ -72,7 +57,8 @@ public class EventListActivity extends BaseDnsActivity {
switch (item.getItemId()) {
case R.id.menu_refresh:
ListCreator lc = getListCreator(Event.ALL, true);
ListCreator<?> lc = getListCreator(this, R.id.event_list, R.id.event_list_progress_bar);
lc.setForcedUpdate(true);
lc.execute();
return true;
@@ -90,13 +76,13 @@ public class EventListActivity extends BaseDnsActivity {
if (currentCategory.equals(Event.ALL)) {
super.onBackPressed();
} else {
ListCreator lc = getListCreator(Event.ALL, false);
ListCreator<?> lc = getListCreator(this, R.id.event_list, R.id.event_list_progress_bar);
lc.execute();
}
}
protected ListAdapter createAdapter(Event[] events) {
protected <T> ListAdapter createAdapter(T[] objects) {
Event[] events = (Event[])objects;
ListAdapter adapter = new ArrayAdapter<Event>(this, R.layout.event_list_row, R.id.event_list_row_text, events) {
@Override
@@ -167,84 +153,30 @@ public class EventListActivity extends BaseDnsActivity {
if (message.length < 1)
message = new String[] { Event.ALL };
ListCreator lc = getListCreator(message[0], false);
ListCreator<?> lc = getListCreator(EventListActivity.this, R.id.event_list, R.id.event_list_progress_bar);
lc.setCategory(message[0]);
lc.execute();
}
};
if (dataHandler != null) {
Dialog dialog = new ChooseCategoryDialog(this, callback, dataHandler.getEventCategories());
if (getDataHandler() != null) {
Dialog dialog = new ChooseCategoryDialog(this, callback, getDataHandler().getEventCategories());
dialog.show();
} else {
Toast.makeText(this, R.string.error_loading, Toast.LENGTH_SHORT);
}
}
protected class ListCreator extends AsyncTask<Void, Void, Boolean> {
protected String category;
protected boolean forcedUpdate;
public ListCreator(String category, boolean forcedUpdate) {
this.category = category;
this.forcedUpdate = forcedUpdate;
}
@Override
protected void onPreExecute() {
if (forcedUpdate) {
dataHandler = null;
}
ListView list = (ListView)findViewById(R.id.event_list);
list.setVisibility(View.GONE);
ProgressBar pb = (ProgressBar)findViewById(R.id.event_list_progress_bar);
pb.setVisibility(View.VISIBLE);
}
@Override
protected Boolean doInBackground(Void ... v) {
// Do we have intarwebs?
// YAY = Fetch the feed.
// NAY = Inform about no connection.
try {
if (dataHandler == null) {
NodeList itemNodes = feed.fetch(this.forcedUpdate);
dataHandler = parser.parse(itemNodes);
}
return true;
} catch (IOException e) {
return false;
}
}
@Override
protected void onPostExecute(Boolean result) {
if (result) {
ListView list = (ListView)findViewById(R.id.event_list);
Event[] events = getEvents();
ListAdapter adapter = createAdapter(events);
list.setAdapter(adapter);
list.setVisibility(View.VISIBLE);
ProgressBar pb = (ProgressBar)findViewById(R.id.event_list_progress_bar);
pb.setVisibility(View.GONE);
currentCategory = category;
} else {
Toast toast = Toast.makeText(EventListActivity.this, R.string.error_noconnection_noupdate, Toast.LENGTH_LONG);
toast.show();
}
}
protected Event[] getEvents() {
return dataHandler.getEvents(this.category);
}
}
protected ListCreator getListCreator(String category, boolean forcedUpdate) {
return new ListCreator(category, forcedUpdate);
protected <T> ListCreator<?> getListCreator(Context context, int idList, int idProgressBar) {
return new ListCreator<Event>(context, idList, idProgressBar) {
@Override
protected Event[] getObjects() {
return getDataHandler().getEvents(Event.ALL);
}
};
}
protected OnClickListener getShortClickListener (final Event e) {
@@ -1,48 +1,34 @@
package com.studentersamfundet.app.ui;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnLongClickListener;
import com.studentersamfundet.app.Event;
public class EventListTicketsActivity extends EventListActivity {
class TicketListCreator extends ListCreator {
public TicketListCreator(String category, boolean forcedUpdate) {
super(category, forcedUpdate);
}
@Override
protected Event[] getEvents() {
return dataHandler.getEventsWithTickets();
}
}
@Override
protected ListCreator getListCreator(String category, boolean forcedUpdate) {
return new TicketListCreator(category, forcedUpdate);
}
protected OnClickListener getShortClickListener (final Event e) {
return new OnClickListener() {
public void onClick(View v) {
Uri webDestination = e.getTicketUri();
Intent webIntent = new Intent(Intent.ACTION_VIEW, webDestination);
startActivity(webIntent);
}
};
}
protected OnLongClickListener getLongClickListener (final Event e) {
return new OnLongClickListener() {
public boolean onLongClick(View v) {
return false;
}
};
}
protected ListCreator<?> getListCreator(Context context, int idList, int idProgressBar) {
return new ListCreator<Event>(context, idList, idProgressBar) {
@Override
protected Event[] getObjects() {
return getDataHandler().getEventsWithTickets();
}
};
}
@Override
protected OnClickListener getShortClickListener (final Event e) {
return new OnClickListener() {
public void onClick(View v) {
Uri webDestination = e.getTicketUri();
Intent webIntent = new Intent(Intent.ACTION_VIEW, webDestination);
startActivity(webIntent);
}
};
}
}