Initial Commit

This commit is contained in:
Daniel Oeh 2011-12-23 19:22:06 +01:00
commit c9283f09dc
28 changed files with 1275 additions and 0 deletions

19
.gitignore vendored Normal file
View File

@ -0,0 +1,19 @@
# built application files
*.apk
*.ap_
# files for the dex VM
*.dex
# Java class files
*.class
# generated files
bin/
gen/
# Local configuration file (sdk path, etc)
local.properties
# Backup files
*~

27
AndroidManifest.xml Normal file
View File

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="de.podfetcher"
android:versionCode="1"
android:versionName="1.0" >
<!-- <uses-permission android:name="android.permission.ACCESS_ALL_DOWNLOADS" /> -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-sdk android:minSdkVersion="10" />
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name" >
<activity
android:label="@string/app_name"
android:name=".PodfetcherActivity" >
<intent-filter >
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

85
build.xml Normal file
View File

@ -0,0 +1,85 @@
<?xml version="1.0" encoding="UTF-8"?>
<project name="Podfetcher" default="help">
<!-- The local.properties file is created and updated by the 'android' tool.
It contains the path to the SDK. It should *NOT* be checked into
Version Control Systems. -->
<property file="local.properties" />
<!-- The ant.properties file can be created by you. It is only edited by the
'android' tool to add properties to it.
This is the place to change some Ant specific build properties.
Here are some properties you may want to change/update:
source.dir
The name of the source directory. Default is 'src'.
out.dir
The name of the output directory. Default is 'bin'.
For other overridable properties, look at the beginning of the rules
files in the SDK, at tools/ant/build.xml
Properties related to the SDK location or the project target should
be updated using the 'android' tool with the 'update' action.
This file is an integral part of the build system for your
application and should be checked into Version Control Systems.
-->
<property file="ant.properties" />
<!-- The project.properties file is created and updated by the 'android'
tool, as well as ADT.
This contains project specific properties such as project target, and library
dependencies. Lower level build properties are stored in ant.properties
(or in .classpath for Eclipse projects).
This file is an integral part of the build system for your
application and should be checked into Version Control Systems. -->
<loadproperties srcFile="project.properties" />
<!-- quick check on sdk.dir -->
<fail
message="sdk.dir is missing. Make sure to generate local.properties using 'android update project' or to inject it through an env var"
unless="sdk.dir"
/>
<!-- extension targets. Uncomment the ones where you want to do custom work
in between standard targets -->
<!--
<target name="-pre-build">
</target>
<target name="-pre-compile">
</target>
/* This is typically used for code obfuscation.
Compiled code location: ${out.classes.absolute.dir}
If this is not done in place, override ${out.dex.input.absolute.dir} */
<target name="-post-compile">
</target>
-->
<!-- Import the actual build file.
To customize existing targets, there are two options:
- Customize only one target:
- copy/paste the target into this file, *before* the
<import> task.
- customize it to your needs.
- Customize the whole content of build.xml
- copy/paste the content of the rules files (minus the top node)
into this file, replacing the <import> task.
- customize to your needs.
***********************
****** IMPORTANT ******
***********************
In all cases you must update the value of version-tag below to read 'custom' instead of an integer,
in order to avoid having your file be overridden by tools such as "android update project"
-->
<!-- version-tag: 1 -->
<import file="${sdk.dir}/tools/ant/build.xml" />
</project>

40
proguard.cfg Normal file
View File

@ -0,0 +1,40 @@
-optimizationpasses 5
-dontusemixedcaseclassnames
-dontskipnonpubliclibraryclasses
-dontpreverify
-verbose
-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*
-keep public class * extends android.app.Activity
-keep public class * extends android.app.Application
-keep public class * extends android.app.Service
-keep public class * extends android.content.BroadcastReceiver
-keep public class * extends android.content.ContentProvider
-keep public class * extends android.app.backup.BackupAgentHelper
-keep public class * extends android.preference.Preference
-keep public class com.android.vending.licensing.ILicensingService
-keepclasseswithmembernames class * {
native <methods>;
}
-keepclasseswithmembers class * {
public <init>(android.content.Context, android.util.AttributeSet);
}
-keepclasseswithmembers class * {
public <init>(android.content.Context, android.util.AttributeSet, int);
}
-keepclassmembers class * extends android.app.Activity {
public void *(android.view.View);
}
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}
-keep class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator *;
}

11
project.properties Normal file
View File

@ -0,0 +1,11 @@
# 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,
# "ant.properties", and override values to adapt the script to your
# project structure.
# Project target.
target=android-10

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<ImageView
android:id="@+id/imgvFeedimage"
android:layout_height="fill_parent"
android:layout_width="50dip"
android:padding="10dip"
android:cropToPadding="true"
android:layout_alignParentLeft="true"
/>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="fill_parent"
android:layout_width="wrap_content"
android:layout_alignParentRight="true">
<TextView
android:id="@+id/txtvFeedname"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
<TextView
android:id="@+id/txtvNewEpisodes"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
</LinearLayout>
</RelativeLayout>

16
res/layout/main.xml Normal file
View File

@ -0,0 +1,16 @@
<?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" >
<EditText
android:id="@+id/textedit"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<Button
android:id="@+id/testbutton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Test2" />
</LinearLayout>

7
res/values/strings.xml Normal file
View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="hello">Hello World, PodfetcherActivity!</string>
<string name="app_name">Podfetcher</string>
</resources>

View File

@ -0,0 +1,25 @@
package de.podfetcher;
import de.podfetcher.feed.FeedManager;
import android.app.Application;
public class PodcastApp extends Application {
private static PodcastApp singleton;
public static PodcastApp getInstance() {
return singleton;
}
@Override
public void onCreate() {
super.onCreate();
singleton = this;
FeedManager manager = FeedManager.getInstance();
manager.loadDBData(getApplicationContext());
}
}

View File

@ -0,0 +1,41 @@
package de.podfetcher;
import javax.xml.parsers.ParserConfigurationException;
import org.xml.sax.SAXException;
import de.podfetcher.feed.*;
import de.podfetcher.storage.DownloadRequester;
import android.app.Activity;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.os.Bundle;
public class PodfetcherActivity extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
final DownloadRequester requester = DownloadRequester.getInstance();
final FeedHandler handler = new FeedHandler();
final FeedManager manager = FeedManager.getInstance();
final Button button = (Button)findViewById(R.id.testbutton);
final EditText edittext = (EditText)findViewById(R.id.textedit);
button.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
final String s = edittext.getText().toString();
manager.addFeed(v.getContext(), s);
edittext.setText("Receiving...");
}
});
}
}

View File

@ -0,0 +1,45 @@
package de.podfetcher.feed;
import java.util.ArrayList;
/**
* Data Object for a whole feed
* @author daniel
*
*/
public class Feed extends FeedFile{
public String title;
public String link;
public String description;
public FeedImage image;
public FeedCategory category;
public ArrayList<FeedItem> items;
public Feed() {
items = new ArrayList<FeedItem>();
}
public Feed(String url) {
this.download_url = url;
}
public Feed(String title, String link, String description, String download_url,
FeedCategory category) {
super();
this.title = title;
this.link = link;
this.description = description;
this.download_url = download_url;
this.category = category;
items = new ArrayList<FeedItem>();
}
}

View File

@ -0,0 +1,12 @@
package de.podfetcher.feed;
public class FeedCategory extends FeedComponent{
public String name;
public FeedCategory(String name) {
super();
this.name = name;
}
}

View File

@ -0,0 +1,16 @@
package de.podfetcher.feed;
/**
* Represents every possible component of a feed
* @author daniel
*
*/
public class FeedComponent {
public long id;
public FeedComponent() {
super();
}
}

View File

@ -0,0 +1,7 @@
package de.podfetcher.feed;
public abstract class FeedFile extends FeedComponent {
public String file_url;
public String download_url;
}

View File

@ -0,0 +1,41 @@
package de.podfetcher.feed;
import java.io.File;
import java.io.IOException;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.xml.sax.SAXException;
public class FeedHandler {
public final static String CHANNEL = "channel";
public final static String ITEM = "item";
public final static String TITLE = "title";
public final static String LINK = "link";
public final static String DESCR = "description";
public final static String PUBDATE = "pubDate";
public final static String ENCLOSURE = "enclosure";
public final static String IMAGE = "image";
public final static String URL = "url";
public final static String ENC_URL = "url";
public final static String ENC_LEN = "length";
public final static String ENC_TYPE = "type";
public Feed parseFeed(String file) throws ParserConfigurationException, SAXException {
SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser saxParser = factory.newSAXParser();
RSSHandler handler = new RSSHandler();
try {
saxParser.parse(new File(file), handler);
} catch (SAXException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return handler.feed;
}
}

View File

@ -0,0 +1,26 @@
package de.podfetcher.feed;
public class FeedImage extends FeedFile {
public String title;
public FeedImage(String download_url, String title) {
super();
this.download_url = download_url;
this.title = title;
}
public FeedImage(long id, String title, String file_url, String download_url) {
this.id = id;
this.title = title;
this.file_url = file_url;
this.download_url = download_url;
}
public FeedImage() {
}
}

View File

@ -0,0 +1,33 @@
package de.podfetcher.feed;
/**
* Data Object for a XML message
* @author daniel
*
*/
public class FeedItem extends FeedComponent{
public String title;
public String description;
public String link;
public String pubDate;
public FeedMedia media;
public Feed feed;
public boolean read;
public FeedItem() {
this.read = false;
}
public FeedItem(String title, String description, String link,
String pubDate, FeedMedia media, Feed feed) {
super();
this.title = title;
this.description = description;
this.link = link;
this.pubDate = pubDate;
this.media = media;
this.feed = feed;
this.read = false;
}
}

View File

@ -0,0 +1,116 @@
package de.podfetcher.feed;
import java.util.ArrayList;
import de.podfetcher.storage.*;
import android.content.Context;
import android.database.Cursor;
/**
* Singleton class
* Manages all feeds, categories and feeditems
*
*
* */
public class FeedManager {
private static FeedManager singleton;
public ArrayList<Feed> feeds;
public ArrayList<FeedCategory> categories;
Cursor feedlistCursor;
private FeedManager() {
feeds = new ArrayList<Feed>();
categories = new ArrayList<FeedCategory>();
}
public static FeedManager getInstance(){
if(singleton == null) {
singleton = new FeedManager();
}
return singleton;
}
/** Add and Download a new Feed */
public void addFeed(Context context, String url) {
// TODO Check if URL is correct
PodDBAdapter adapter = new PodDBAdapter(context);
Feed feed = new Feed(url);
feed.id = adapter.setFeed(feed);
DownloadRequester req = DownloadRequester.getInstance();
req.downloadFeed(context, feed);
}
/** Reads the database */
public void loadDBData(Context context) {
PodDBAdapter adapter = new PodDBAdapter(context);
feedlistCursor = adapter.getAllFeedsCursor();
updateArrays(context);
}
public void updateArrays(Context context) {
feedlistCursor.requery();
PodDBAdapter adapter = new PodDBAdapter(context);
feeds.clear();
categories.clear();
extractFeedlistFromCursor(context);
}
private void extractFeedlistFromCursor(Context context) {
PodDBAdapter adapter = new PodDBAdapter(context);
if(feedlistCursor.moveToFirst()) {
do {
Feed feed = new Feed();
feed.id = feedlistCursor.getLong(feedlistCursor.getColumnIndex(PodDBAdapter.KEY_ID));
feed.title = feedlistCursor.getString(feedlistCursor.getColumnIndex(PodDBAdapter.KEY_TITLE));
feed.link = feedlistCursor.getString(feedlistCursor.getColumnIndex(PodDBAdapter.KEY_LINK));
feed.description = feedlistCursor.getString(feedlistCursor.getColumnIndex(PodDBAdapter.KEY_DESCRIPTION));
feed.image = adapter.getFeedImage(feed);
feed.file_url = feedlistCursor.getString(feedlistCursor.getColumnIndex(PodDBAdapter.KEY_FILE_URL));
feed.download_url = feedlistCursor.getString(feedlistCursor.getColumnIndex(PodDBAdapter.KEY_DOWNLOAD_URL));
// Get FeedItem-Object
Cursor itemlistCursor = adapter.getAllItemsOfFeedCursor(feed);
feed.items = extractFeedItemsFromCursor(context, itemlistCursor);
feeds.add(feed);
}while(feedlistCursor.moveToNext());
}
}
private ArrayList<FeedItem> extractFeedItemsFromCursor(Context context, Cursor itemlistCursor) {
ArrayList<FeedItem> items = new ArrayList<FeedItem>();
PodDBAdapter adapter = new PodDBAdapter(context);
if(itemlistCursor.moveToFirst()) {
do {
FeedItem item = new FeedItem();
item.id = itemlistCursor.getLong(itemlistCursor.getColumnIndex(PodDBAdapter.KEY_ID));
item.title = itemlistCursor.getString(itemlistCursor.getColumnIndex(PodDBAdapter.KEY_TITLE));
item.link = itemlistCursor.getString(itemlistCursor.getColumnIndex(PodDBAdapter.KEY_LINK));
item.description = itemlistCursor.getString(itemlistCursor.getColumnIndex(PodDBAdapter.KEY_DESCRIPTION));
item.pubDate = itemlistCursor.getString(itemlistCursor.getColumnIndex(PodDBAdapter.KEY_PUBDATE));
item.media = adapter.getFeedMedia(itemlistCursor.getLong(itemlistCursor.getColumnIndex(PodDBAdapter.KEY_MEDIA)));
item.read = (itemlistCursor.getInt(itemlistCursor.getColumnIndex(PodDBAdapter.KEY_READ)) > 0) ? true : false;
items.add(item);
} while(itemlistCursor.moveToNext());
}
return items;
}
}

View File

@ -0,0 +1,33 @@
package de.podfetcher.feed;
public class FeedMedia extends FeedFile{
public long length;
public long position;
public long size; // File size in Byte
public String mime_type;
public FeedItem item; // TODO remove
public FeedMedia(FeedItem i, String download_url, long size, String mime_type) {
this.item = i;
this.download_url = download_url;
this.size = size;
this.mime_type = mime_type;
}
public FeedMedia(long id, long length, long position, long size, String mime_type,
String file_url, String download_url) {
super();
this.id = id;
this.length = length;
this.position = position;
this.size = size;
this.mime_type = mime_type;
this.file_url = file_url;
this.download_url = download_url;
}
}

View File

@ -0,0 +1,116 @@
package de.podfetcher.feed;
import java.util.ArrayList;
import de.podfetcher.feed.FeedHandler;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
/**
* SAX-Parser for reading RSS-Feeds
*
* @author daniel
*
*/
public class RSSHandler extends DefaultHandler {
public ArrayList<FeedItem> items;
public FeedItem currentItem;
public StringBuilder strBuilder;
public Feed feed;
public String active_root_element; // channel or item or image
public String active_sub_element; // Not channel or item
@Override
public void characters(char[] ch, int start, int length)
throws SAXException {
if (active_sub_element != null) {
strBuilder.append(ch, start, length);
}
}
@Override
public void endDocument() throws SAXException {
feed.items = items;
}
@Override
public void endElement(String uri, String localName, String qName)
throws SAXException {
if (qName.equalsIgnoreCase(FeedHandler.ITEM)) {
currentItem.feed = feed;
items.add(currentItem);
} else if (qName.equalsIgnoreCase(FeedHandler.TITLE)) {
if (active_root_element.equalsIgnoreCase(FeedHandler.CHANNEL)) {
feed.title = strBuilder.toString();
} else if(active_root_element.equalsIgnoreCase(FeedHandler.TITLE)) {
currentItem.title = strBuilder.toString();
} else if(active_root_element.equalsIgnoreCase(FeedHandler.IMAGE)) {
feed.image.title = strBuilder.toString();
}
} else if (qName.equalsIgnoreCase(FeedHandler.DESCR)) {
if (active_root_element.equalsIgnoreCase(FeedHandler.CHANNEL)) {
feed.description = strBuilder.toString();
} else {
currentItem.description = strBuilder.toString();
}
} else if (qName.equalsIgnoreCase(FeedHandler.LINK)) {
if (active_root_element.equalsIgnoreCase(FeedHandler.CHANNEL)) {
feed.link = strBuilder.toString();
} else if(active_root_element.equalsIgnoreCase(FeedHandler.TITLE)){
currentItem.link = strBuilder.toString();
}
} else if (qName.equalsIgnoreCase(FeedHandler.PUBDATE)) {
if (active_root_element.equalsIgnoreCase(FeedHandler.ITEM)) {
currentItem.pubDate = strBuilder.toString();
}
} else if (qName.equalsIgnoreCase(FeedHandler.URL)) {
if(active_root_element.equalsIgnoreCase(FeedHandler.IMAGE)) {
feed.image.download_url = strBuilder.toString();
}
} else if(qName.equalsIgnoreCase(FeedHandler.IMAGE)) {
active_root_element = FeedHandler.CHANNEL;
}
active_sub_element = null;
strBuilder = new StringBuilder();
}
@Override
public void startDocument() throws SAXException {
items = new ArrayList<FeedItem>();
strBuilder = new StringBuilder();
}
@Override
public void startElement(String uri, String localName, String qName,
Attributes attributes) throws SAXException {
if (qName.equalsIgnoreCase(FeedHandler.CHANNEL)) {
feed = new Feed();
active_root_element = qName;
} else if (qName.equalsIgnoreCase(FeedHandler.ITEM)) {
currentItem = new FeedItem();
active_root_element = qName;
} else if (qName.equalsIgnoreCase(FeedHandler.TITLE)) {
active_sub_element = qName;
} else if (qName.equalsIgnoreCase(FeedHandler.DESCR)) {
active_sub_element = qName;
} else if (qName.equalsIgnoreCase(FeedHandler.LINK)) {
active_sub_element = qName;
} else if (qName.equalsIgnoreCase(FeedHandler.PUBDATE)) {
active_sub_element = qName;
} else if (qName.equalsIgnoreCase(FeedHandler.ENCLOSURE)) {
currentItem.media = new FeedMedia(currentItem,
attributes.getValue(FeedHandler.ENC_URL),
Long.parseLong(attributes.getValue(FeedHandler.ENC_LEN)),
attributes.getValue(FeedHandler.ENC_TYPE));
} else if(qName.equalsIgnoreCase(FeedHandler.IMAGE)) {
feed.image = new FeedImage();
active_root_element = qName;
} else if(qName.equalsIgnoreCase(FeedHandler.URL)) {
active_sub_element = qName;
}
}
}

View File

@ -0,0 +1,15 @@
package de.podfetcher.gui;
import android.app.Activity;
import android.os.Bundle;
public class AddFeedActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
}

View File

@ -0,0 +1,57 @@
package de.podfetcher.gui;
import java.io.File;
import java.util.List;
import de.podfetcher.R;
import de.podfetcher.feed.Feed;
import android.content.Context;
import android.net.Uri;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
public class FeedlistAdapter extends ArrayAdapter<Feed> {
int resource;
public FeedlistAdapter(Context context, int resource,
int textViewResourceId, List<Feed> objects) {
super(context, resource, textViewResourceId, objects);
this.resource = resource;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
LinearLayout feedlistView;
Feed feed = getItem(position);
// Inflate Layout
if (convertView == null) {
feedlistView = new LinearLayout(getContext());
String inflater = Context.LAYOUT_INFLATER_SERVICE;
LayoutInflater vi = (LayoutInflater)getContext().getSystemService(inflater);
vi.inflate(resource, feedlistView, true);
} else {
feedlistView = (LinearLayout) convertView;
}
ImageView imageView = (ImageView)feedlistView.findViewById(R.id.imgvFeedimage);
TextView txtvFeedname = (TextView)feedlistView.findViewById(R.id.txtvFeedname);
TextView txtvNewEpisodes = (TextView)feedlistView.findViewById(R.id.txtvNewEpisodes);
imageView.setImageURI(Uri.fromFile(new File(feed.file_url))); // TODO select default picture when no image downloaded
txtvFeedname.setText(feed.title);
// TODO find new Episodes txtvNewEpisodes.setText(feed)
return feedlistView;
}
}

View File

@ -0,0 +1,27 @@
package de.podfetcher.storage;
import de.podfetcher.PodcastApp;
import android.app.DownloadManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
public class DownloadReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
long id = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, 0);
DownloadRequester requester = DownloadRequester.getInstance();
Intent item_intent = requester.getItemIntent(id);
String action = item_intent.getAction();
if(action.equals(DownloadRequester.ACTION_FEED_DOWNLOAD_COMPLETED)) {
requester.removeFeedByID(item_intent.getLongExtra(DownloadRequester.EXTRA_ITEM_ID, -1));
} else if(action.equals(DownloadRequester.ACTION_MEDIA_DOWNLOAD_COMPLETED)) {
requester.removeMediaByID(item_intent.getLongExtra(DownloadRequester.EXTRA_ITEM_ID, -1));
} else if(action.equals(DownloadRequester.ACTION_IMAGE_DOWNLOAD_COMPLETED)) {
requester.removeImageByID(item_intent.getLongExtra(DownloadRequester.EXTRA_ITEM_ID, -1));
}
PodcastApp.getInstance().getApplicationContext().sendBroadcast(item_intent);
}
}

View File

@ -0,0 +1,131 @@
package de.podfetcher.storage;
import java.util.ArrayList;
import java.io.File;
import de.podfetcher.feed.Feed;
import de.podfetcher.feed.FeedImage;
import de.podfetcher.feed.FeedMedia;
import android.app.DownloadManager;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
public class DownloadRequester {
public static String EXTRA_DOWNLOAD_ID = "extra.de.podfetcher.storage.download_id";
public static String EXTRA_ITEM_ID = "extra.de.podfetcher.storage.item_id";
public static String ACTION_FEED_DOWNLOAD_COMPLETED = "action.de.podfetcher.storage.feed_download_completed";
public static String ACTION_MEDIA_DOWNLOAD_COMPLETED = "action.de.podfetcher.storage.media_download_completed";
public static String ACTION_IMAGE_DOWNLOAD_COMPLETED = "action.de.podfetcher.storage.image_download_completed";
private static boolean STORE_ON_SD = true;
public static String IMAGE_DOWNLOADPATH = "images";
public static String FEED_DOWNLOADPATH = "cache";
public static String MEDIA_DOWNLOADPATH = "media";
private static DownloadRequester downloader;
public ArrayList<Intent> feeds;
public ArrayList<Intent> images;
public ArrayList<Intent> media;
private DownloadRequester(){
feeds = new ArrayList<Intent>();
images = new ArrayList<Intent>();
media = new ArrayList<Intent>();
}
public static DownloadRequester getInstance() {
if(downloader == null) {
downloader = new DownloadRequester();
}
return downloader;
}
private void download(Context context, ArrayList<Intent> type, String str_uri, File dest, boolean visibleInUI, String action, long id) {
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(str_uri));
//request.allowScanningByMediaScanner();
request.setDestinationUri(Uri.fromFile(dest));
request.setVisibleInDownloadsUi(visibleInUI);
// TODO Set Allowed Network Types
DownloadManager manager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
Intent i = new Intent(action);
i.putExtra(EXTRA_DOWNLOAD_ID, manager.enqueue(request));
i.putExtra(EXTRA_ITEM_ID, id);
type.add(i);
}
public void downloadFeed(Context context, Feed feed) {
download(context, feeds, feed.download_url,
new File(context.getExternalFilesDir(FEED_DOWNLOADPATH), "feed-" + feed.id),
true, ACTION_FEED_DOWNLOAD_COMPLETED, feed.id);
}
public void downloadImage(Context context, FeedImage image) {
download(context, images, image.download_url,
new File(context.getExternalFilesDir(IMAGE_DOWNLOADPATH), "image-" + image.id),
true, ACTION_IMAGE_DOWNLOAD_COMPLETED, image.id);
}
public void downloadMedia(Context context, FeedMedia feedmedia) {
download(context, media, feedmedia.download_url,
new File(context.getExternalFilesDir(MEDIA_DOWNLOADPATH), "media-" + feedmedia.id),
true, ACTION_MEDIA_DOWNLOAD_COMPLETED, feedmedia.id);
}
public void removeFeedByID(long id) {
int len = feeds.size();
for(int x = 0; x < len; x++) {
if(feeds.get(x).getLongExtra(EXTRA_ITEM_ID, -1) == id) {
feeds.remove(x);
break;
}
}
}
public void removeMediaByID(long id) {
int len = media.size();
for(int x = 0; x < len; x++) {
if(media.get(x).getLongExtra(EXTRA_ITEM_ID, -1) == id) {
media.remove(x);
break;
}
}
}
public void removeImageByID(long id) {
int len = images.size();
for(int x = 0; x < len; x++) {
if(images.get(x).getLongExtra(EXTRA_ITEM_ID, -1) == id) {
images.remove(x);
break;
}
}
}
/* Returns the stored intent by looking for the right download id */
public Intent getItemIntent(long id) {
for(Intent i : feeds) {
if(i.getLongExtra(EXTRA_DOWNLOAD_ID, -1) == id) {
return i;
}
}
for(Intent i : media) {
if(i.getLongExtra(EXTRA_DOWNLOAD_ID, -1) == id) {
return i;
}
}
for(Intent i : images) {
if(i.getLongExtra(EXTRA_DOWNLOAD_ID, -1) == id) {
return i;
}
}
return null;
}
}

View File

@ -0,0 +1,295 @@
package de.podfetcher.storage;
import de.podfetcher.feed.Feed;
import de.podfetcher.feed.FeedCategory;
import de.podfetcher.feed.FeedImage;
import de.podfetcher.feed.FeedItem;
import de.podfetcher.feed.FeedMedia;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
/**
* Implements methods for accessing the database
* */
public class PodDBAdapter {
private static final int DATABASE_VERSION = 1;
private static final String DATABASE_NAME = "Podfetcher.db";
// Key-constants
public static final String KEY_ID = "id";
public static final String KEY_TITLE = "title";
public static final String KEY_NAME = "name";
public static final String KEY_LINK = "link";
public static final String KEY_DESCRIPTION = "description";
public static final String KEY_FILE_URL = "file_url";
public static final String KEY_DOWNLOAD_URL = "download_url";
public static final String KEY_PUBDATE = "pubDate";
public static final String KEY_READ = "read";
public static final String KEY_LENGTH = "length";
public static final String KEY_POSITION = "position";
public static final String KEY_SIZE = "filesize";
public static final String KEY_MIME_TYPE = "mime_type";
public static final String KEY_IMAGE = "image";
public static final String KEY_CATEGORY = "category";
public static final String KEY_FEED = "feed";
public static final String KEY_MEDIA = "media";
// Table names
public static final String TABLE_NAME_FEEDS = "Feeds";
public static final String TABLE_NAME_FEED_ITEMS = "FeedItems";
public static final String TABLE_NAME_FEED_CATEGORIES = "FeedCategories";
public static final String TABLE_NAME_FEED_IMAGES = "FeedImages";
public static final String TABLE_NAME_FEED_MEDIA = "FeedMedia";
// SQL Statements for creating new tables
private static final String TABLE_PRIMARY_KEY = KEY_ID
+ " INTEGER PRIMARY KEY AUTOINCREMENT ,";
private static final String CREATE_TABLE_FEEDS = "CREATE TABLE "
+ TABLE_NAME_FEEDS + " (" + TABLE_PRIMARY_KEY + KEY_TITLE
+ " TEXT," + KEY_LINK + " TEXT," + KEY_DESCRIPTION
+ " TEXT," + KEY_IMAGE + " INTEGER," + KEY_CATEGORY
+ " INTEGER," + KEY_FILE_URL + " TEXT," + KEY_DOWNLOAD_URL
+ " TEXT)";
private static final String CREATE_TABLE_FEED_ITEMS = "CREATE TABLE "
+ TABLE_NAME_FEED_ITEMS + " (" + TABLE_PRIMARY_KEY + KEY_TITLE
+ " TEXT," + KEY_LINK + " TEXT," + KEY_DESCRIPTION
+ " TEXT," + KEY_PUBDATE + " TEXT," + KEY_MEDIA
+ " INTEGER," + KEY_FEED + " INTEGER," + KEY_READ
+ " INTEGER)";
private static final String CREATE_TABLE_FEED_CATEGORIES = "CREATE TABLE "
+ TABLE_NAME_FEED_CATEGORIES + " (" + TABLE_PRIMARY_KEY + KEY_NAME
+ " TEXT)";
private static final String CREATE_TABLE_FEED_IMAGES = "CREATE TABLE "
+ TABLE_NAME_FEED_IMAGES + " (" + TABLE_PRIMARY_KEY + KEY_TITLE
+ " TEXT," + KEY_FILE_URL + " TEXT,"
+ KEY_DOWNLOAD_URL + " TEXT)";
private static final String CREATE_TABLE_FEED_MEDIA = "CREATE TABLE "
+ TABLE_NAME_FEED_MEDIA + " (" + TABLE_PRIMARY_KEY + KEY_LENGTH
+ " INTEGER," + KEY_POSITION + " INTEGER,"
+ KEY_SIZE + " INTEGER," + KEY_MIME_TYPE + " TEXT,"
+ KEY_FILE_URL + " TEXT," + KEY_DOWNLOAD_URL + " TEXT)";
private SQLiteDatabase db;
private final Context context;
private PodDBHelper helper;
public PodDBAdapter(Context c) {
this.context = c;
helper = new PodDBHelper(context, DATABASE_NAME, null, DATABASE_VERSION);
}
public PodDBAdapter open() {
try {
db = helper.getWritableDatabase();
} catch (SQLException ex) {
db = helper.getReadableDatabase();
}
return this;
}
public void close() {
db.close();
}
/** Inserts or updates a feed entry
* @return the id of the entry
* */
public long setFeed(Feed feed) {
open();
ContentValues values = new ContentValues();
values.put(KEY_TITLE, feed.title);
values.put(KEY_LINK, feed.link);
values.put(KEY_DESCRIPTION, feed.description);
if (feed.image != null) {
if (feed.image.id == 0) {
setImage(feed.image);
}
values.put(KEY_IMAGE, feed.image.id);
}
if(feed.category != null) {
if(feed.category.id == 0) {
setCategory(feed.category);
}
values.put(KEY_CATEGORY, feed.category.id);
}
if(feed.file_url != null) {
values.put(KEY_FILE_URL, feed.file_url);
}
values.put(KEY_DOWNLOAD_URL, feed.download_url);
if(feed.id == 0) {
// Create new entry
feed.id = db.insert(TABLE_NAME_FEEDS, null, values);
} else {
db.update(TABLE_NAME_FEEDS, values, KEY_ID+"=?", new String[]{String.valueOf(feed.id)});
}
close();
return feed.id;
}
/** Inserts or updates a category entry
* @return the id of the entry
* */
public long setCategory(FeedCategory category) {
ContentValues values = new ContentValues();
values.put(KEY_NAME, category.name);
if(category.id == 0) {
category.id = db.insert(TABLE_NAME_FEED_CATEGORIES, null, values);
} else {
db.update(TABLE_NAME_FEED_CATEGORIES, values, KEY_ID+"=?", new String[]{String.valueOf(category.id)});
}
return category.id;
}
/**
* Inserts or updates an image entry
* @return the id of the entry
* */
public long setImage(FeedImage image) {
ContentValues values = new ContentValues();
values.put(KEY_TITLE, image.title);
values.put(KEY_DOWNLOAD_URL, image.download_url);
if(image.file_url != null) {
values.put(KEY_FILE_URL, image.file_url);
}
if(image.id == 0) {
image.id = db.insert(TABLE_NAME_FEED_IMAGES, null, values);
} else {
db.update(TABLE_NAME_FEED_IMAGES, values, KEY_ID+"=?", new String[]{String.valueOf(image.id)});
}
return image.id;
}
/**
* Inserts or updates an image entry
* @return the id of the entry
*/
public long setMedia(FeedMedia media) {
ContentValues values = new ContentValues();
values.put(KEY_LENGTH, media.length);
values.put(KEY_POSITION, media.position);
values.put(KEY_SIZE, media.size);
values.put(KEY_MIME_TYPE, media.mime_type);
values.put(KEY_DOWNLOAD_URL, media.download_url);
if(media.file_url != null) {
values.put(KEY_FILE_URL, media.file_url);
}
if(media.id == 0) {
media.id = db.insert(TABLE_NAME_FEED_MEDIA, null, values);
} else {
db.update(TABLE_NAME_FEED_MEDIA, values, KEY_ID+"=?", new String[]{String.valueOf(media.id)});
}
return media.id;
}
/**
* Inserts or updates a feeditem entry
* @return the id of the entry
*/
public long setFeedItem(FeedItem item) {
ContentValues values = new ContentValues();
values.put(KEY_TITLE, item.title);
values.put(KEY_LINK, item.link);
values.put(KEY_DESCRIPTION, item.description);
values.put(KEY_PUBDATE, item.pubDate);
if(item.media != null) {
if(item.media.id == 0) {
setMedia(item.media);
}
values.put(KEY_MEDIA, item.media.id);
}
if(item.feed.id == 0) {
setFeed(item.feed);
}
values.put(KEY_FEED, item.feed.id);
values.put(KEY_READ, (item.read) ? 1 : 0);
return item.id;
}
public Cursor getAllCategoriesCursor() {
return db.query(TABLE_NAME_FEED_CATEGORIES, null, null, null, null, null, null);
}
public Cursor getAllFeedsCursor() {
return db.query(TABLE_NAME_FEEDS, null, null, null, null, null, null);
}
public Cursor getAllItemsOfFeedCursor(Feed feed) {
return db.query(TABLE_NAME_FEED_ITEMS, null, KEY_FEED+"=?", new String[]{String.valueOf(feed.id)}, null, null, null);
}
public Cursor getFeedMediaOfItemCursor(FeedItem item) {
return db.query(TABLE_NAME_FEED_MEDIA, null, KEY_ID+"=?", new String[]{String.valueOf(item.media.id)}, null, null, null);
}
public Cursor getImageOfFeedCursor(Feed feed) {
return db.query(TABLE_NAME_FEED_IMAGES, null, KEY_ID+"=?", new String[]{String.valueOf(feed.image.id)}, null, null, null);
}
public FeedMedia getFeedMedia(long row_index) throws SQLException{
Cursor cursor = db.query(TABLE_NAME_FEED_MEDIA, null, KEY_ID+"=?", new String[]{String.valueOf(row_index)}, null, null, null);
if((cursor.getCount() == 0) || !cursor.moveToFirst()) {
throw new SQLException("No FeedMedia found at index: "+ row_index);
}
return new FeedMedia(row_index,
cursor.getLong(cursor.getColumnIndex(KEY_LENGTH)),
cursor.getLong(cursor.getColumnIndex(KEY_POSITION)),
cursor.getLong(cursor.getColumnIndex(KEY_SIZE)),
cursor.getString(cursor.getColumnIndex(KEY_MIME_TYPE)),
cursor.getString(cursor.getColumnIndex(KEY_FILE_URL)),
cursor.getString(cursor.getColumnIndex(KEY_DOWNLOAD_URL)));
}
public FeedImage getFeedImage(Feed feed) throws SQLException {
Cursor cursor = this.getImageOfFeedCursor(feed);
if((cursor.getCount() == 0) || !cursor.moveToFirst()) {
throw new SQLException("No FeedImage found at index: "+ feed.image.id);
}
return new FeedImage(feed.image.id, cursor.getString(cursor.getColumnIndex(KEY_TITLE)),
cursor.getString(cursor.getColumnIndex(KEY_FILE_URL)),
cursor.getString(cursor.getColumnIndex(KEY_DOWNLOAD_URL)));
}
private static class PodDBHelper extends SQLiteOpenHelper {
public PodDBHelper(Context context, String name, CursorFactory factory,
int version) {
super(context, name, factory, version);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(CREATE_TABLE_FEEDS);
db.execSQL(CREATE_TABLE_FEED_ITEMS);
db.execSQL(CREATE_TABLE_FEED_CATEGORIES);
db.execSQL(CREATE_TABLE_FEED_IMAGES);
db.execSQL(CREATE_TABLE_FEED_MEDIA);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Log.w("DBAdapter", "Upgrading from version " + oldVersion + " to "
+ newVersion + ".");
// TODO delete Database
}
}
}