diff options
-rw-r--r-- | .classpath | 7 | ||||
-rw-r--r-- | .gitignore | 2 | ||||
-rw-r--r-- | .project | 39 | ||||
-rw-r--r-- | AndroidManifest.xml | 20 | ||||
-rw-r--r-- | default.properties | 13 | ||||
-rw-r--r-- | res/drawable/icon.png | bin | 0 -> 2574 bytes | |||
-rw-r--r-- | res/layout/list_header.xml | 10 | ||||
-rw-r--r-- | res/layout/list_item.xml | 31 | ||||
-rw-r--r-- | res/values/strings.xml | 5 | ||||
-rw-r--r-- | src/de/animux/android/andmal/AnimeListActivity.java | 91 | ||||
-rw-r--r-- | src/de/animux/android/andmal/api/anime/Anime.java | 197 | ||||
-rw-r--r-- | src/de/animux/android/andmal/api/anime/AnimeList.java | 117 | ||||
-rw-r--r-- | src/de/animux/android/andmal/api/anime/State.java | 36 | ||||
-rw-r--r-- | src/de/animux/android/andmal/util/SeparatedListAdapter.java | 116 | ||||
-rw-r--r-- | src/de/animux/android/andmal/util/SortedLinkedList.java | 40 |
15 files changed, 724 insertions, 0 deletions
diff --git a/.classpath b/.classpath new file mode 100644 index 0000000..609aa00 --- /dev/null +++ b/.classpath @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="UTF-8"?> +<classpath> + <classpathentry kind="src" path="src"/> + <classpathentry kind="src" path="gen"/> + <classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/> + <classpathentry kind="output" path="bin"/> +</classpath> diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d6d6a17 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +bin/* +gen/* diff --git a/.project b/.project new file mode 100644 index 0000000..e8640ee --- /dev/null +++ b/.project @@ -0,0 +1,39 @@ +<?xml version="1.0" encoding="UTF-8"?> +<projectDescription> + <name>AndMAL</name> + <comment></comment> + <projects> + </projects> + <buildSpec> + <buildCommand> + <name>org.eclipse.wst.common.project.facet.core.builder</name> + <arguments> + </arguments> + </buildCommand> + <buildCommand> + <name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name> + <arguments> + </arguments> + </buildCommand> + <buildCommand> + <name>com.android.ide.eclipse.adt.PreCompilerBuilder</name> + <arguments> + </arguments> + </buildCommand> + <buildCommand> + <name>org.eclipse.jdt.core.javabuilder</name> + <arguments> + </arguments> + </buildCommand> + <buildCommand> + <name>com.android.ide.eclipse.adt.ApkBuilder</name> + <arguments> + </arguments> + </buildCommand> + </buildSpec> + <natures> + <nature>com.android.ide.eclipse.adt.AndroidNature</nature> + <nature>org.eclipse.jdt.core.javanature</nature> + <nature>org.eclipse.wst.common.project.facet.core.nature</nature> + </natures> +</projectDescription> diff --git a/AndroidManifest.xml b/AndroidManifest.xml new file mode 100644 index 0000000..a1db31e --- /dev/null +++ b/AndroidManifest.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="de.animux.android.andmal" + android:versionCode="1" + android:versionName="1.0"> + <application android:icon="@drawable/icon" android:label="@string/app_name"> + <activity android:label="@string/app_name" android:name=".AnimeListActivity"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + + </application> + <uses-sdk android:minSdkVersion="3" /> + + + +<uses-permission android:name="android.permission.INTERNET"></uses-permission> +</manifest>
\ No newline at end of file diff --git a/default.properties b/default.properties new file mode 100644 index 0000000..a1ef8e9 --- /dev/null +++ b/default.properties @@ -0,0 +1,13 @@ +# This file is automatically generated by Android Tools. +# Do not modify this file -- YOUR CHANGES WILL BE ERASED! +# +# This file must be checked in Version Control Systems. +# +# To customize properties used by the Ant build system use, +# "build.properties", and override values to adapt the script to your +# project structure. + +# Indicates whether an apk should be generated for each density. +split.density=false +# Project target. +target=android-3 diff --git a/res/drawable/icon.png b/res/drawable/icon.png Binary files differnew file mode 100644 index 0000000..a07c69f --- /dev/null +++ b/res/drawable/icon.png diff --git a/res/layout/list_header.xml b/res/layout/list_header.xml new file mode 100644 index 0000000..fcaf7a4 --- /dev/null +++ b/res/layout/list_header.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="UTF-8"?> +<TextView + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/list_header_title" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:paddingTop="2dip" + android:paddingBottom="2dip" + android:paddingLeft="5dip" + style="?android:attr/listSeparatorTextViewStyle" />
\ No newline at end of file diff --git a/res/layout/list_item.xml b/res/layout/list_item.xml new file mode 100644 index 0000000..6c0641d --- /dev/null +++ b/res/layout/list_item.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<TableLayout + android:id="@+id/TableLayout01" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + xmlns:android="http://schemas.android.com/apk/res/android" + android:stretchColumns="1" + > + <TableRow> + <TextView + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/list_item_title" + android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:paddingTop="10dip" + android:paddingBottom="10dip" + android:paddingLeft="5dip" + android:textAppearance="?android:attr/textAppearanceMedium" + android:layout_column="1" + /> + <TextView + android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:text="@+id/list_item_other" + android:id="@+id/list_item_other" + android:textAppearance="?android:attr/textAppearanceSmall" + android:paddingRight="5dip" + android:gravity="right" /> + </TableRow> +</TableLayout> diff --git a/res/values/strings.xml b/res/values/strings.xml new file mode 100644 index 0000000..89038cf --- /dev/null +++ b/res/values/strings.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="hello">Hello World, List!</string> + <string name="app_name">AndMAL</string> +</resources> diff --git a/src/de/animux/android/andmal/AnimeListActivity.java b/src/de/animux/android/andmal/AnimeListActivity.java new file mode 100644 index 0000000..8db6635 --- /dev/null +++ b/src/de/animux/android/andmal/AnimeListActivity.java @@ -0,0 +1,91 @@ +package de.animux.android.andmal; + +import java.util.Collection; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.Map; + +import de.animux.android.andmal.api.anime.Anime; +import de.animux.android.andmal.api.anime.AnimeList; +import de.animux.android.andmal.api.anime.State; +import de.animux.android.andmal.util.SeparatedListAdapter; + +import android.app.Activity; +import android.app.Dialog; +import android.app.ProgressDialog; +import android.os.Bundle; +import android.widget.ListView; +import android.widget.SimpleAdapter; + +public class AnimeListActivity extends Activity { + + public final static String ITEM_TITLE = "title"; + public final static String ITEM_CAPTION = "caption"; + + public final static int INITIAL_SYNC = 0; + + public Map<String, ?> createItem(String title, String caption) { + Map<String, String> item = new HashMap<String, String>(); + item.put(ITEM_TITLE, title); + item.put(ITEM_CAPTION, caption); + return item; + } + + /** Called when the activity is first created. */ + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + showDialog(INITIAL_SYNC); + + AnimeList animeList = null; + try { + animeList = new AnimeList("AlexanderS"); + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + dismissDialog(INITIAL_SYNC); + + SeparatedListAdapter adapter = new SeparatedListAdapter(this); + Map<State, java.util.List<Anime>> animes = animeList.getAnimes(); + + for (State s : State.values()) + addAnimes(adapter, animes.get(s), s.toString()); + + ListView list = new ListView(this); + list.setAdapter(adapter); + this.setContentView(list); + } + + private void addAnimes(SeparatedListAdapter adapter, + Collection<Anime> animes, String sectionName) { + if (animes != null && animes.size() > 0) { + java.util.List<Map<String, ?>> listSection = new LinkedList<Map<String, ?>>(); + + for (Anime a : animes) { + listSection.add(createItem(a.getTitle(), a.getWatchedEpisodes() + + "/" + a.getEpisodes())); + } + + adapter.addSection(sectionName, new SimpleAdapter(this, + listSection, R.layout.list_item, new String[] { ITEM_TITLE, + ITEM_CAPTION }, new int[] { R.id.list_item_title, + R.id.list_item_other })); + } + } + + @Override + protected Dialog onCreateDialog(int id) { + switch (id) { + case INITIAL_SYNC: + ProgressDialog dialog = ProgressDialog.show(this, "", + "Loading list. Please wait...", true); + return dialog; + default: + return super.onCreateDialog(id); + } + } + +}
\ No newline at end of file diff --git a/src/de/animux/android/andmal/api/anime/Anime.java b/src/de/animux/android/andmal/api/anime/Anime.java new file mode 100644 index 0000000..d4f542f --- /dev/null +++ b/src/de/animux/android/andmal/api/anime/Anime.java @@ -0,0 +1,197 @@ +package de.animux.android.andmal.api.anime; + +public class Anime implements Comparable<Anime> { + + private int id; + private String title; + private String synonyms; + private int type; + private int episodes; + private int status; + private String start; + private String end; + private String image; + private int myId; + private int watchedEpisodes; + private String myStart; + private String myEnd; + private int myScore; + private int myStatus; + private int rewatching; + private int rewatchingEpisodes; + private int lastUpdate; + private String tags; + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getSynonyms() { + return synonyms; + } + + public void setSynonyms(String synonyms) { + this.synonyms = synonyms; + } + + public int getType() { + return type; + } + + public void setType(int type) { + this.type = type; + } + + public int getEpisodes() { + return episodes; + } + + public void setEpisodes(int episodes) { + this.episodes = episodes; + } + + public int getStatus() { + return status; + } + + public void setStatus(int status) { + this.status = status; + } + + public String getStart() { + return start; + } + + public void setStart(String start) { + this.start = start; + } + + public String getEnd() { + return end; + } + + public void setEnd(String end) { + this.end = end; + } + + public String getImage() { + return image; + } + + public void setImage(String image) { + this.image = image; + } + + public int getMyId() { + return myId; + } + + public void setMyId(int myId) { + this.myId = myId; + } + + public int getWatchedEpisodes() { + return watchedEpisodes; + } + + public void setWatchedEpisodes(int watchedEpisodes) { + this.watchedEpisodes = watchedEpisodes; + } + + public String getMyStart() { + return myStart; + } + + public void setMyStart(String myStart) { + this.myStart = myStart; + } + + public String getMyEnd() { + return myEnd; + } + + public void setMyEnd(String myEnd) { + this.myEnd = myEnd; + } + + public int getMyScore() { + return myScore; + } + + public void setMyScore(int myScore) { + this.myScore = myScore; + } + + public int getMyStatus() { + return myStatus; + } + + public void setMyStatus(int myStatus) { + this.myStatus = myStatus; + } + + public int getRewatching() { + return rewatching; + } + + public void setRewatching(int rewatching) { + this.rewatching = rewatching; + } + + public int getRewatchingEpisodes() { + return rewatchingEpisodes; + } + + public void setRewatchingEpisodes(int rewatchingEpisodes) { + this.rewatchingEpisodes = rewatchingEpisodes; + } + + public int getLastUpdate() { + return lastUpdate; + } + + public void setLastUpdate(int lastUpdate) { + this.lastUpdate = lastUpdate; + } + + public String getTags() { + return tags; + } + + public void setTags(String tags) { + this.tags = tags; + } + + @Override + public String toString() { + return getTitle(); + } + + @Override + public int compareTo(Anime arg0) { + int compareTitle = arg0.getTitle().compareTo(getTitle()); + if (compareTitle != 0) { + return compareTitle; + } + else { + if (arg0.getId() != getId()) { + return getId() - arg0.getId(); + } + } + + return 0; + } + +} diff --git a/src/de/animux/android/andmal/api/anime/AnimeList.java b/src/de/animux/android/andmal/api/anime/AnimeList.java new file mode 100644 index 0000000..78a1e09 --- /dev/null +++ b/src/de/animux/android/andmal/api/anime/AnimeList.java @@ -0,0 +1,117 @@ +package de.animux.android.andmal.api.anime; + +import java.io.IOException; +import java.io.InputStream; + +import java.net.URL; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.xml.parsers.FactoryConfigurationError; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; + +import org.xml.sax.Attributes; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.DefaultHandler; + +import de.animux.android.andmal.api.anime.State; +import de.animux.android.andmal.util.SortedLinkedList; + +public class AnimeList extends DefaultHandler { + + private Anime currentAnime; + private StringBuffer currentValue; + + private HashMap<State,List<Anime>> animes; + + public AnimeList(String user) throws IOException, + ParserConfigurationException, SAXException, + FactoryConfigurationError { + currentAnime = null; + currentValue = new StringBuffer(); + + animes = new HashMap<State, List<Anime>>(); + animes.put(State.WATCHING, new SortedLinkedList<Anime>()); + animes.put(State.COMPLETED, new SortedLinkedList<Anime>()); + animes.put(State.ONHOLD, new SortedLinkedList<Anime>()); + animes.put(State.DROPPED, new SortedLinkedList<Anime>()); + animes.put(State.PLANTOWATCH, new SortedLinkedList<Anime>()); + + URL url = new URL("http://myanimelist.net/malappinfo.php?status=all&type=anime&u=" + user); + InputStream stream = url.openStream(); + + SAXParser sax = SAXParserFactory.newInstance().newSAXParser(); + sax.parse(stream, this); + } + + public Anime getCurrentAnime() { + return currentAnime; + } + + public StringBuffer getCurrentValue() { + return currentValue; + } + + public Map<State, List<Anime>> getAnimes() { + return animes; + } + + @Override + public void characters(char[] ch, int start, int length) + throws SAXException { + currentValue.append(new String(ch).substring(start, length - start) + .replaceAll("&", "&")); + } + + @Override + public void endElement(String uri, String localName, String qName) + throws SAXException { + + if (localName.equals("anime")) { + if (animes.containsKey(State.valueOf(currentAnime.getMyStatus()))) { + animes.get(State.valueOf(currentAnime.getMyStatus())).add(currentAnime); + } + + } else if (localName.equals("series_animedb_id")) { + currentAnime.setId(Integer.valueOf(currentValue.toString())); + } else if (localName.equals("series_title")) { + currentAnime.setTitle(currentValue.toString()); + } else if (localName.equals("series_synonyms")) { + currentAnime.setSynonyms(currentValue.toString()); + } else if (localName.equals("series_type")) { + currentAnime.setType(Integer.valueOf(currentValue.toString())); + } else if (localName.equals("series_episodes")) { + currentAnime.setEpisodes(Integer.valueOf(currentValue.toString())); + } else if (localName.equals("series_status")) { + currentAnime.setStatus(Integer.valueOf(currentValue.toString())); + } else if (localName.equals("series_start")) { + currentAnime.setStart(currentValue.toString()); + } else if (localName.equals("series_end")) { + currentAnime.setEnd(currentValue.toString()); + } else if (localName.equals("series_image")) { + currentAnime.setImage(currentValue.toString()); + } else if (localName.equals("my_id")) { + currentAnime.setMyId(Integer.valueOf(currentValue.toString())); + } else if (localName.equals("my_watched_episodes")) { + currentAnime.setWatchedEpisodes(Integer.valueOf(currentValue + .toString())); + } else if (localName.equals("my_status")) { + currentAnime.setMyStatus(Integer.valueOf(currentValue.toString())); + } + + currentValue.setLength(0); + } + + @Override + public void startElement(String uri, String localName, String qName, + Attributes attributes) throws SAXException { + + currentValue.setLength(0); + if (localName.equals("anime")) { + currentAnime = new Anime(); + } + } +} diff --git a/src/de/animux/android/andmal/api/anime/State.java b/src/de/animux/android/andmal/api/anime/State.java new file mode 100644 index 0000000..639b761 --- /dev/null +++ b/src/de/animux/android/andmal/api/anime/State.java @@ -0,0 +1,36 @@ +package de.animux.android.andmal.api.anime; + +public enum State { + WATCHING (1, "Watching"), + COMPLETED (2, "Completed"), + ONHOLD (3, "On Hold"), + DROPPED (4, "Dropped"), + PLANTOWATCH (6, "Plan to watch"); + + private final int id; + private final String name; + + State(int id, String name) { + this.id = id; + this.name = name; + } + + @Override + public String toString() { + return name; + } + + public int getId() { + return id; + } + + static State valueOf(int id) { + for (State s : State.values()) { + if (s.getId() == id) { + return s; + } + } + + return null; + } +}
\ No newline at end of file diff --git a/src/de/animux/android/andmal/util/SeparatedListAdapter.java b/src/de/animux/android/andmal/util/SeparatedListAdapter.java new file mode 100644 index 0000000..6320b0f --- /dev/null +++ b/src/de/animux/android/andmal/util/SeparatedListAdapter.java @@ -0,0 +1,116 @@ +package de.animux.android.andmal.util; + +import java.util.LinkedHashMap; +import java.util.Map; + +import de.animux.android.andmal.R; + +import android.content.Context; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Adapter; +import android.widget.ArrayAdapter; +import android.widget.BaseAdapter; + +public class SeparatedListAdapter extends BaseAdapter { + + public final Map<String, Adapter> sections = new LinkedHashMap<String, Adapter>(); + + public final ArrayAdapter<String> headers; + + public final static int TYPE_SECTION_HEADER = 0; + + public SeparatedListAdapter(Context context) { + headers = new ArrayAdapter<String>(context, R.layout.list_header); + } + + public void addSection(String section, Adapter adapter) { + this.headers.add(section); + this.sections.put(section, adapter); + } + + public Object getItem(int position) { + for (Object section : this.sections.keySet()) { + Adapter adapter = sections.get(section); + int size = adapter.getCount() + 1; + + // check if position inside this section + if (position == 0) + return section; + if (position < size) + return adapter.getItem(position - 1); + + // otherwise jump into next section + position -= size; + } + return null; + } + + public int getCount() { + // total together all sections, plus one for each section header + int total = 0; + for (Adapter adapter : this.sections.values()) + total += adapter.getCount() + 1; + return total; + } + + public int getViewTypeCount() { + // assume that headers count as one, then total all sections + int total = 1; + for (Adapter adapter : this.sections.values()) + total += adapter.getViewTypeCount(); + return total; + } + + public int getItemViewType(int position) { + int type = 1; + for (Object section : this.sections.keySet()) { + Adapter adapter = sections.get(section); + int size = adapter.getCount() + 1; + + // check if position inside this section + if (position == 0) + return TYPE_SECTION_HEADER; + if (position < size) + return type + adapter.getItemViewType(position - 1); + + // otherwise jump into next section + position -= size; + type += adapter.getViewTypeCount(); + } + return -1; + } + + public boolean areAllItemsSelectable() { + return false; + } + + public boolean isEnabled(int position) { + return (getItemViewType(position) != TYPE_SECTION_HEADER); + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + int sectionnum = 0; + for (Object section : this.sections.keySet()) { + Adapter adapter = sections.get(section); + int size = adapter.getCount() + 1; + + // check if position inside this section + if (position == 0) + return headers.getView(sectionnum, convertView, parent); + if (position < size) + return adapter.getView(position - 1, convertView, parent); + + // otherwise jump into next section + position -= size; + sectionnum++; + } + return null; + } + + @Override + public long getItemId(int position) { + return position; + } +}
\ No newline at end of file diff --git a/src/de/animux/android/andmal/util/SortedLinkedList.java b/src/de/animux/android/andmal/util/SortedLinkedList.java new file mode 100644 index 0000000..974d3e5 --- /dev/null +++ b/src/de/animux/android/andmal/util/SortedLinkedList.java @@ -0,0 +1,40 @@ +package de.animux.android.andmal.util; + +import java.util.Collection; +import java.util.LinkedList; +import java.util.ListIterator; + +public class SortedLinkedList<T extends Comparable<T>> extends LinkedList<T> { + + private static final long serialVersionUID = 8263372892230475461L; + + @Override + public boolean add(T object) { + ListIterator<T> it = listIterator(); + while (it.hasNext()) { + if (object.compareTo(it.next()) > 0) { + break; + } + } + + if (it.hasPrevious()) { + it.previous(); + it.add(object); + } + else { + // insert at first position + listIterator().add(object); + } + + return true; + } + + @Override + public boolean addAll(Collection<? extends T> collection) { + for (T object : collection) { + add(object); + } + + return true; + } +} |