Merge branch 'develop' of https://github.com/danieloeh/AntennaPod into move-to-top

This commit is contained in:
Tom Hennen 2013-09-02 19:35:07 -04:00
commit d538e3899c
75 changed files with 2134 additions and 690 deletions

1
.gitignore vendored
View File

@ -41,3 +41,4 @@ proguard
libs
*.DS_Store
src/de/danoeh/antennapod/util/flattr/FlattrConfig.java
gradle.properties

View File

@ -61,6 +61,7 @@
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data android:scheme="http"/>
<data android:scheme="https"/>
<data android:host="*"/>
<data android:pathPattern=".*\\.xml"/>
<data android:pathPattern=".*\\.rss"/>
@ -71,6 +72,7 @@
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data android:scheme="http"/>
<data android:scheme="https"/>
<data android:host="feeds.feedburner.com"/>
<data android:host="feedproxy.google.com"/>
<data android:host="feeds2.feedburner.com"/>
@ -82,6 +84,7 @@
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data android:scheme="http"/>
<data android:scheme="https"/>
<data android:mimeType="text/xml"/>
<data android:mimeType="application/rss+xml"/>
<data android:mimeType="application/atom+xml"/>

View File

@ -3,7 +3,7 @@ buildscript {
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:0.5.5'
classpath 'com.android.tools.build:gradle:0.5.6'
}
}
apply plugin: 'android'
@ -12,6 +12,17 @@ repositories {
mavenCentral()
}
dependencies {
def libsdir = new File('libs');
if (!libsdir.exists()) {
println "Creating libs directory"
libsdir.mkdir()
}
def prestoLib = new File('libs/presto_client-0.8.5.jar')
if (!prestoLib.exists()) {
println "Downloading presto library into libs folder"
new URL('http://www.aocate.com/presto/client/presto_client-0.8.5.jar').withInputStream{ i -> prestoLib.withOutputStream{ it << i }}
}
compile 'com.android.support:appcompat-v7:18.0.+'
compile 'org.apache.commons:commons-lang3:3.1'
compile ('org.shredzone.flattr4j:flattr4j-core:2.4') {
@ -22,6 +33,7 @@ dependencies {
compile 'commons-io:commons-io:2.4'
compile 'com.nineoldandroids:library:2.4.0'
compile project(':submodules:dslv:library')
compile files('libs/presto_client-0.8.5.jar')
}
android {
@ -35,6 +47,31 @@ android {
testInstrumentationRunner "instrumentationTest.de.test.antennapod.AntennaPodTestRunner"
}
signingConfigs {
releaseConfig {
if (project.hasProperty('releaseStoreFile')) {
storeFile file(releaseStoreFile)
} else {
storeFile file('keystore')
}
if (project.hasProperty('releaseStorePassword')) {
storePassword releaseStorePassword
} else {
storePassword "password"
}
if (project.hasProperty('releaseKeyAlias')) {
keyAlias releaseKeyAlias
} else {
keyAlias "alias"
}
if (project.hasProperty('releaseKeyPassword')) {
keyPassword releaseKeyPassword
} else {
keyPassword "password"
}
}
}
sourceSets {
main {
manifest.srcFile 'AndroidManifest.xml'
@ -51,5 +88,10 @@ android {
debug {
packageNameSuffix ".debug"
}
release {
runProguard true
proguardFile 'proguard.cfg'
signingConfig signingConfigs.releaseConfig
}
}
}

10
pom.xml
View File

@ -84,6 +84,14 @@
<artifactId>library</artifactId>
<version>2.4.0</version>
</dependency>
<dependency>
<groupId>com.aocate</groupId>
<artifactId>presto_client</artifactId>
<version>0.8.5</version>
<type>jar</type>
<scope>system</scope>
<systemPath>${project.basedir}/libs/presto_client-0.8.5.jar</systemPath>
</dependency>
</dependencies>
<build>
@ -212,7 +220,7 @@
</manifest>
<proguard>
<skip>false</skip>
<config>proguard.cfg</config>
<config>proguard-mvn.cfg</config>
</proguard>
</configuration>
<executions>

66
proguard-mvn.cfg Normal file
View File

@ -0,0 +1,66 @@
-printmapping out.map
-renamesourcefileattribute SourceFile
-keepattributes SourceFile,LineNumberTable
-dontpreverify
-repackageclasses ''
-allowaccessmodification
-optimizations !code/simplification/arithmetic
-keepattributes *Annotation*
-injars libs/presto_client-0.8.5.jar
-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.view.View {
public <init>(android.content.Context);
public <init>(android.content.Context, android.util.AttributeSet);
public <init>(android.content.Context, android.util.AttributeSet, int);
public void set*(...);
}
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}
-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.content.Context {
public void *(android.view.View);
public void *(android.view.MenuItem);
}
-keepclassmembers class * implements android.os.Parcelable {
static android.os.Parcelable$Creator CREATOR;
}
-keepclassmembers class **.R$* {
public static <fields>;
}
-keep class android.support.v4.** { *; }
-keep interface android.support.v4.** { *; }
-keep class android.support.v7.** { *; }
-keep interface android.support.v7.** { *; }
-dontwarn android.support.v4.**
-dontwarn android.support.v7.**
-keepattributes *Annotation*
-keep class org.shredzone.flattr4j.** { *; }
-dontwarn org.shredzone.flattr4j.**
-keep class org.apache.commons.** { *; }
-dontskipnonpubliclibraryclassmembers

View File

@ -8,10 +8,7 @@
-optimizations !code/simplification/arithmetic
-keepattributes *Annotation*
#-libraryjars libs/android-support-v4.jar
#-libraryjars libs/commons-lang3-3.1.jar
#-libraryjars libs/flattr4j-core-2.4.jar
#-libraryjars libs/commons-io-2.4.jar
#-injars libs/presto_client-0.8.5.jar
-keep public class * extends android.app.Activity
-keep public class * extends android.app.Application
@ -52,10 +49,12 @@
public static <fields>;
}
-keep class android.support.v4.app.** { *; }
-keep interface android.support.v4.app.** { *; }
-keep class com.actionbarsherlock.** { *; }
-keep interface com.actionbarsherlock.** { *; }
-keep class android.support.v4.** { *; }
-keep interface android.support.v4.** { *; }
-keep class android.support.v7.** { *; }
-keep interface android.support.v7.** { *; }
-dontwarn android.support.v4.**
-dontwarn android.support.v7.**
-keepattributes *Annotation*

View File

@ -92,14 +92,12 @@
android:layout_width="80dp"
android:layout_height="match_parent"
android:layout_centerHorizontal="true"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:background="?attr/borderless_button"
android:src="?attr/av_pause" />
<ImageButton
android:id="@+id/butRev"
android:layout_width="80dp"
android:layout_width="60dp"
android:layout_height="match_parent"
android:layout_toLeftOf="@id/butPlay"
android:background="?attr/borderless_button"
@ -107,11 +105,22 @@
<ImageButton
android:id="@+id/butFF"
android:layout_width="80dp"
android:layout_width="60dp"
android:layout_height="match_parent"
android:layout_toRightOf="@id/butPlay"
android:background="?attr/borderless_button"
android:src="?attr/av_fast_forward" />
<Button
android:id="@+id/butPlaybackSpeed"
android:layout_width="60dp"
android:layout_height="match_parent"
android:layout_toRightOf="@id/butFF"
android:background="?attr/borderless_button"
android:src="?attr/av_fast_forward"
android:textColor="@color/gray"
android:textSize="@dimen/text_size_medium"
android:visibility="gone" />
</RelativeLayout>
<RelativeLayout

View File

@ -79,8 +79,6 @@
android:layout_width="80dp"
android:layout_height="match_parent"
android:layout_centerHorizontal="true"
android:layout_marginLeft="12dp"
android:layout_marginRight="12dp"
android:background="?attr/borderless_button"
android:src="?attr/av_pause" />
@ -99,6 +97,17 @@
android:layout_toRightOf="@id/butPlay"
android:background="?attr/borderless_button"
android:src="?attr/av_fast_forward" />
<Button
android:id="@+id/butPlaybackSpeed"
android:layout_width="80dp"
android:layout_height="match_parent"
android:layout_toRightOf="@id/butFF"
android:background="?attr/borderless_button"
android:src="?attr/av_fast_forward"
android:textColor="@color/gray"
android:textSize="@dimen/text_size_medium"
android:visibility="gone" />
</RelativeLayout>
<RelativeLayout

View File

@ -1,6 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="update_intervall_options">
<item>Manual</item>
<item>1 hour</item>
<item>2 hours</item>
<item>4 hours</item>
<item>8 hours</item>
<item>12 hours</item>
<item>24 hours</item>
</string-array>
<string-array name="update_intervall_values">
<item>0</item>
<item>1</item>
@ -28,6 +38,50 @@
<item>80</item>
<item>100</item>
</string-array>
<string-array name="playback_speed_values">
<item>1.0</item>
<item>1.05</item>
<item>1.10</item>
<item>1.15</item>
<item>1.20</item>
<item>1.25</item>
<item>1.30</item>
<item>1.35</item>
<item>1.40</item>
<item>1.45</item>
<item>1.50</item>
<item>1.55</item>
<item>1.60</item>
<item>1.65</item>
<item>1.70</item>
<item>1.75</item>
<item>1.80</item>
<item>1.85</item>
<item>1.90</item>
<item>1.95</item>
<item>2.00</item>
<item>2.10</item>
<item>2.20</item>
<item>2.30</item>
<item>2.40</item>
<item>2.50</item>
<item>2.60</item>
<item>2.70</item>
<item>2.80</item>
<item>2.90</item>
<item>3.00</item>
<item>3.10</item>
<item>3.20</item>
<item>3.30</item>
<item>3.40</item>
<item>3.50</item>
<item>3.60</item>
<item>3.70</item>
<item>3.80</item>
<item>3.90</item>
<item>4.00</item>
</string-array>
<string-array name="autodl_select_networks_default_entries">
<item>N/A</item>
</string-array>
@ -42,5 +96,6 @@
<item>0</item>
<item>1</item>
</string-array>
</resources>

View File

@ -47,6 +47,8 @@
<string name="processing_label">Processing</string>
<string name="loading_label">Loading...</string>
<string name="image_of_prefix">Image of:\u0020</string>
<string name="close_label">Close</string>
<!-- 'Add Feed' Activity labels -->
<string name="feedurl_label">Feed URL</string>
@ -143,6 +145,12 @@
<string name="access_revoked_info">You have successfully revoked AntennaPod\'s access token to your account. In order to complete the process, you have to remove this app from the list of approved applications in your account settings on the flattr website.</string>
<string name="flattr_click_success">Successfully flattred this thing!</string>
<string name="flattring_label">Flattring</string>
<!-- Variable Speed -->
<string name="download_plugin_label">Download Plugin</string>
<string name="no_playback_plugin_title">Plugin Not Installed</string>
<string name="no_playback_plugin_msg">For variable speed playback to work, a third party library must be installed.\n\nTap \'Download Plugin\' to download a free plugin from the Play Store\n\nAny problems found using this plugin are not the responsibility of AntennaPod and should be reported to the plugin owner.</string>
<string name="set_playback_speed_label">Playback Speeds</string>
<!-- Empty list labels -->
<string name="no_items_label">There are no items in this list.</string>
@ -185,9 +193,11 @@
<string name="pref_theme_title_light">Light</string>
<string name="pref_theme_title_dark">Dark</string>
<string name="pref_episode_cache_unlimited">Unlimited</string>
<string name="pref_update_interval_hours_plural">hours</string>
<string name="pref_update_interval_hours_singular">hour</string>
<string name="pref_update_interval_hours_manual">Manual</string>
<string name="pref_update_interval_hours_plural">hours</string>
<string name="pref_update_interval_hours_singular">hour</string>
<string name="pref_update_interval_hours_manual">Manual</string>
<string name="pref_playback_speed_title">Playback Speeds</string>
<string name="pref_playback_speed_sum">Customize the speeds available for variable speed audio playback</string>
<!-- Search -->
@ -253,4 +263,4 @@
<string name="pref_pausePlaybackForFocusLoss_sum">Pause playback instead of lowering volume when another app wants to play sounds</string>
<string name="pref_pausePlaybackForFocusLoss_title">Pause for interruptions</string>
</resources>
</resources>

View File

@ -17,6 +17,11 @@
android:key="prefFollowQueue"
android:summary="@string/pref_followQueue_sum"
android:title="@string/pref_followQueue_title" />
<Preference
android:key="prefPlaybackSpeedLauncher"
android:summary="@string/pref_playback_speed_sum"
android:title="@string/pref_playback_speed_title" />
<CheckBoxPreference
android:defaultValue="false"
android:enabled="true"

View File

@ -12,7 +12,9 @@ import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.Window;
import android.view.View.OnLongClickListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ImageButton;
import android.widget.ImageView.ScaleType;
import android.widget.ListView;
@ -22,11 +24,14 @@ import de.danoeh.antennapod.AppConfig;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.adapter.ChapterListAdapter;
import de.danoeh.antennapod.asynctask.ImageLoader;
import de.danoeh.antennapod.dialog.VariableSpeedDialog;
import de.danoeh.antennapod.feed.Chapter;
import de.danoeh.antennapod.feed.MediaType;
import de.danoeh.antennapod.feed.SimpleChapter;
import de.danoeh.antennapod.fragment.CoverFragment;
import de.danoeh.antennapod.fragment.ItemDescriptionFragment;
import de.danoeh.antennapod.preferences.UserPreferences;
import de.danoeh.antennapod.preferences.UserPreferences;
import de.danoeh.antennapod.service.PlaybackService;
import de.danoeh.antennapod.util.playback.ExternalMedia;
import de.danoeh.antennapod.util.playback.Playable;
@ -56,6 +61,7 @@ public class AudioplayerActivity extends MediaplayerActivity {
private TextView txtvTitle;
private TextView txtvFeed;
private Button butPlaybackSpeed;
private ImageButton butNavLeft;
private ImageButton butNavRight;
@ -218,7 +224,7 @@ public class AudioplayerActivity extends MediaplayerActivity {
if (savedPosition != -1) {
switchToFragment(savedPosition);
}
}
@Override
@ -363,6 +369,7 @@ public class AudioplayerActivity extends MediaplayerActivity {
txtvFeed = (TextView) findViewById(R.id.txtvFeed);
butNavLeft = (ImageButton) findViewById(R.id.butNavLeft);
butNavRight = (ImageButton) findViewById(R.id.butNavRight);
butPlaybackSpeed = (Button) findViewById(R.id.butPlaybackSpeed);
butNavLeft.setOnClickListener(new OnClickListener() {
@ -390,6 +397,65 @@ public class AudioplayerActivity extends MediaplayerActivity {
}
}
});
butPlaybackSpeed.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (controller != null && controller.canSetPlaybackSpeed()) {
String[] availableSpeeds = UserPreferences
.getPlaybackSpeedArray();
String currentSpeed = UserPreferences.getPlaybackSpeed();
// Provide initial value in case the speed list has changed
// out from under us
// and our current speed isn't in the new list
String newSpeed;
if (availableSpeeds.length > 0) {
newSpeed = availableSpeeds[0];
} else {
newSpeed = "1.0";
}
for (int i = 0; i < availableSpeeds.length; i++) {
if (availableSpeeds[i].equals(currentSpeed)) {
if (i == availableSpeeds.length - 1) {
newSpeed = availableSpeeds[0];
} else {
newSpeed = availableSpeeds[i + 1];
}
break;
}
}
UserPreferences.setPlaybackSpeed(newSpeed);
controller.setPlaybackSpeed(Float.parseFloat(newSpeed));
}
}
});
butPlaybackSpeed.setOnLongClickListener(new OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
VariableSpeedDialog.showDialog(AudioplayerActivity.this);
return true;
}
});
}
@Override
protected void onPlaybackSpeedChange() {
super.onPlaybackSpeedChange();
updateButPlaybackSpeed();
}
private void updateButPlaybackSpeed() {
if (controller == null
|| (controller.getCurrentPlaybackSpeedMultiplier() == -1)) {
butPlaybackSpeed.setVisibility(View.GONE);
} else {
butPlaybackSpeed.setVisibility(View.VISIBLE);
butPlaybackSpeed.setText(UserPreferences.getPlaybackSpeed());
}
}
@Override
@ -421,7 +487,7 @@ public class AudioplayerActivity extends MediaplayerActivity {
((AudioplayerContentFragment) currentlyShownFragment)
.onDataSetChanged(media);
}
updateButPlaybackSpeed();
}
public void notifyMediaPositionChanged() {

View File

@ -347,7 +347,9 @@ public class DirectoryChooserActivity extends ActionBarActivity {
* CREATE_DIRECTORY_NAME.
*/
private int createFolder() {
if (selectedDir != null && selectedDir.canWrite()) {
if (selectedDir == null) {
return R.string.create_folder_error;
} else if (selectedDir.canWrite()) {
File newDir = new File(selectedDir, CREATE_DIRECTORY_NAME);
if (!newDir.exists()) {
boolean result = newDir.mkdir();
@ -359,10 +361,8 @@ public class DirectoryChooserActivity extends ActionBarActivity {
} else {
return R.string.create_folder_error_already_exists;
}
} else if (selectedDir.canWrite() == false) {
return R.string.create_folder_error_no_write_access;
} else {
return R.string.create_folder_error;
return R.string.create_folder_error_no_write_access;
}
}

View File

@ -121,7 +121,7 @@ public class DownloadActivity extends ActionBarActivity implements
contentRefresher.cancel(true);
}
contentRefresher = new AsyncTask<Void, Void, Void>() {
private final int WAITING_INTERVALL = 1000;
private static final int WAITING_INTERVAL = 1000;
@Override
protected void onProgressUpdate(Void... values) {
@ -137,7 +137,7 @@ public class DownloadActivity extends ActionBarActivity implements
protected Void doInBackground(Void... params) {
while (!isCancelled()) {
try {
Thread.sleep(WAITING_INTERVALL);
Thread.sleep(WAITING_INTERVAL);
publishProgress();
} catch (InterruptedException e) {
return null;

View File

@ -37,7 +37,7 @@ public class DownloadLogActivity extends ActionBarActivity {
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
listview = (ListView) findViewById(R.layout.listview_activity);
listview = (ListView) findViewById(R.id.listview);
dla = new DownloadLogAdapter(this, itemAccess);
listview.setAdapter(dla);

View File

@ -7,6 +7,7 @@ import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.TypedArray;
import android.media.AudioManager;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v4.app.FragmentManager;
@ -57,6 +58,7 @@ public class FeedItemlistActivity extends ActionBarActivity {
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
setContentView(R.layout.feeditemlist_activity);
setVolumeControlStream(AudioManager.STREAM_MUSIC);
long feedId = getIntent().getLongExtra(
FeedlistFragment.EXTRA_SELECTED_FEED, -1);
@ -122,6 +124,8 @@ public class FeedItemlistActivity extends ActionBarActivity {
SearchManager searchManager =
(SearchManager) getSystemService(Context.SEARCH_SERVICE);
SearchView searchView = (SearchView) MenuItemCompat.getActionView(menu.findItem(R.id.search_item));
searchView.setIconifiedByDefault(true);
searchView.setSearchableInfo(

View File

@ -1,5 +1,6 @@
package de.danoeh.antennapod.activity;
import android.media.AudioManager;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v4.app.FragmentManager;
@ -45,6 +46,7 @@ public class ItemviewActivity extends ActionBarActivity {
requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
getSupportActionBar().setDisplayShowTitleEnabled(false);
EventDistributor.getInstance().register(contentUpdate);
setVolumeControlStream(AudioManager.STREAM_MUSIC);
long itemId = getIntent().getLongExtra(
ItemlistFragment.EXTRA_SELECTED_FEEDITEM, -1);

View File

@ -6,6 +6,7 @@ import android.app.SearchManager;
import android.app.SearchableInfo;
import android.content.Context;
import android.content.Intent;
import android.media.AudioManager;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentPagerAdapter;
@ -55,8 +56,9 @@ public class MainActivity extends ActionBarActivity {
StorageUtils.checkStorageAvailability(this);
requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
setContentView(R.layout.main);
setVolumeControlStream(AudioManager.STREAM_MUSIC);
getSupportActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
getSupportActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
viewpager = (ViewPager) findViewById(R.id.viewpager);
pagerAdapter = new TabsAdapter(this, viewpager);
@ -181,7 +183,12 @@ public class MainActivity extends ActionBarActivity {
SearchManager searchManager =
(SearchManager) getSystemService(Context.SEARCH_SERVICE);
SearchView searchView = (SearchView) MenuItemCompat.getActionView(menu.findItem(R.id.search_item));
MenuItem searchItem = menu.findItem(R.id.search_item);
SearchView searchView = (SearchView) MenuItemCompat.getActionView(searchItem);
if (searchView == null) {
MenuItemCompat.setActionView(searchItem, new SearchView(this));
searchView = (SearchView) MenuItemCompat.getActionView(searchItem);
}
searchView.setIconifiedByDefault(true);
SearchableInfo info = searchManager.getSearchableInfo(getComponentName());

View File

@ -4,6 +4,7 @@ import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.PixelFormat;
import android.media.AudioManager;
import android.net.Uri;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
@ -128,10 +129,19 @@ public abstract class MediaplayerActivity extends ActionBarActivity
public void onPlaybackEnd() {
finish();
}
@Override
public void onPlaybackSpeedChange() {
MediaplayerActivity.this.onPlaybackSpeedChange();
}
};
}
protected void onPlaybackSpeedChange() {
}
protected void onServiceQueried() {
supportInvalidateOptionsMenu();
}
@ -143,8 +153,9 @@ public abstract class MediaplayerActivity extends ActionBarActivity
if (AppConfig.DEBUG)
Log.d(TAG, "Creating Activity");
StorageUtils.checkStorageAvailability(this);
setVolumeControlStream(AudioManager.STREAM_MUSIC);
orientation = getResources().getConfiguration().orientation;
orientation = getResources().getConfiguration().orientation;
getWindow().setFormat(PixelFormat.TRANSPARENT);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
}

View File

@ -22,7 +22,7 @@ import de.danoeh.antennapod.preferences.UserPreferences;
public class MiroGuideCategoryActivity extends ActionBarActivity {
private static final String TAG = "MiroGuideCategoryActivity";
public static String EXTRA_CATEGORY = "category";
public static final String EXTRA_CATEGORY = "category";
private ViewPager viewpager;
private CategoryPagerAdapter pagerAdapter;

View File

@ -157,7 +157,7 @@ public abstract class OnlineFeedViewActivity extends ActionBarActivity {
@Override
public void run() {
String reasonDetailed = new String();
String reasonDetailed = "";
boolean successful = false;
FeedHandler handler = new FeedHandler();
try {

View File

@ -7,6 +7,7 @@ import java.net.URL;
import android.app.AlertDialog;
import android.os.Bundle;
import de.danoeh.antennapod.preferences.UserPreferences;
import de.danoeh.antennapod.util.LangUtils;
/** Lets the user start the OPML-import process. */
public class OpmlImportFromIntentActivity extends OpmlImportBaseActivity {
@ -20,7 +21,8 @@ public class OpmlImportFromIntentActivity extends OpmlImportBaseActivity {
try {
URL mOpmlURL = new URL(getIntent().getData().toString());
BufferedReader in = new BufferedReader(new InputStreamReader(mOpmlURL.openStream()));
BufferedReader in = new BufferedReader(new InputStreamReader(mOpmlURL.openStream(),
LangUtils.UTF_8));
startImport(in);
} catch (Exception e) {
new AlertDialog.Builder(this).setMessage("Cannot open XML - Reason: " + e.getMessage()).show();

View File

@ -1,8 +1,10 @@
package de.danoeh.antennapod.activity;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.InputStreamReader;
import java.io.IOException;
import java.io.Reader;
import android.app.AlertDialog;
@ -20,6 +22,7 @@ import android.widget.Toast;
import de.danoeh.antennapod.AppConfig;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.preferences.UserPreferences;
import de.danoeh.antennapod.util.LangUtils;
import de.danoeh.antennapod.util.StorageUtils;
/**
@ -125,8 +128,10 @@ public class OpmlImportFromPathActivity extends OpmlImportBaseActivity {
}
private void startImport(File file) {
Reader mReader = null;
try {
Reader mReader = new FileReader(file);
mReader = new InputStreamReader(new FileInputStream(file),
LangUtils.UTF_8);
if (AppConfig.DEBUG) Log.d(TAG, "Parsing " + file.toString());
startImport(mReader);
} catch (FileNotFoundException e) {

View File

@ -51,6 +51,7 @@ public class OrganizeQueueActivity extends ActionBarActivity implements
listView = (DragSortListView) findViewById(android.R.id.list);
listView.setDropListener(dropListener);
listView.setRemoveListener(removeListener);
listView.setEmptyView(findViewById(android.R.id.empty));
loadData();
undoBarController = new UndoBarController(findViewById(R.id.undobar),
@ -155,9 +156,11 @@ public class OrganizeQueueActivity extends ActionBarActivity implements
public void onUndo(Parcelable token) {
// Perform the undo
UndoToken undoToken = (UndoToken) token;
long itemId = undoToken.getFeedItemId();
int position = undoToken.getPosition();
DBWriter.addQueueItemAt(OrganizeQueueActivity.this, itemId, position, false);
if (token != null) {
long itemId = undoToken.getFeedItemId();
int position = undoToken.getPosition();
DBWriter.addQueueItemAt(OrganizeQueueActivity.this, itemId, position, false);
}
}
private static class OrganizeAdapter extends BaseAdapter {

View File

@ -25,6 +25,7 @@ import de.danoeh.antennapod.AppConfig;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.asynctask.FlattrClickWorker;
import de.danoeh.antennapod.asynctask.OpmlExportWorker;
import de.danoeh.antennapod.dialog.VariableSpeedDialog;
import de.danoeh.antennapod.preferences.UserPreferences;
import de.danoeh.antennapod.util.flattr.FlattrUtils;
@ -41,7 +42,8 @@ public class PreferenceActivity extends android.preference.PreferenceActivity {
private static final String PREF_ABOUT = "prefAbout";
private static final String PREF_CHOOSE_DATA_DIR = "prefChooseDataDir";
private static final String AUTO_DL_PREF_SCREEN = "prefAutoDownloadSettings";
private static final String PREF_PLAYBACK_SPEED_LAUNCHER = "prefPlaybackSpeedLauncher";
private CheckBoxPreference[] selectedNetworks;
@SuppressWarnings("deprecation")
@ -156,6 +158,14 @@ public class PreferenceActivity extends android.preference.PreferenceActivity {
return true;
}
});
findPreference(PREF_PLAYBACK_SPEED_LAUNCHER)
.setOnPreferenceClickListener(new OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
VariableSpeedDialog.showDialog(PreferenceActivity.this);
return true;
}
});
buildUpdateIntervalPreference();
buildAutodownloadSelectedNetworsPreference();
setSelectedNetworksEnabled(UserPreferences

View File

@ -4,6 +4,7 @@ import java.util.ArrayList;
import java.util.List;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.SearchManager;
import android.content.Intent;
import android.os.Bundle;
@ -140,32 +141,34 @@ public class SearchActivity extends ActionBarActivity implements AdapterView.OnI
@Override
public void run() {
Log.d(TAG, "Starting background work");
final Activity activity = SearchActivity.this;
final List<SearchResult> result = FeedSearcher
.performSearch(SearchActivity.this, query, feedID);
if (SearchActivity.this != null) {
SearchActivity.this.runOnUiThread(new Runnable() {
.performSearch(activity, query, feedID);
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
if (AppConfig.DEBUG)
Log.d(TAG, "Background work finished");
if (AppConfig.DEBUG)
Log.d(TAG, "Found " + result.size()
+ " results");
@Override
public void run() {
if (AppConfig.DEBUG)
Log.d(TAG, "Background work finished");
if (AppConfig.DEBUG)
Log.d(TAG, "Found " + result.size()
+ " results");
searchAdapter.clear();
searchAdapter.addAll(result);
searchAdapter.notifyDataSetChanged();
txtvStatus
.setText(R.string.search_status_no_results);
if (!searchAdapter.isEmpty()) {
txtvStatus.setVisibility(View.GONE);
} else {
txtvStatus.setVisibility(View.VISIBLE);
}
searchAdapter.clear();
for (SearchResult s : result) {
searchAdapter.add(s);
}
});
}
searchAdapter.notifyDataSetChanged();
txtvStatus
.setText(R.string.search_status_no_results);
if (!searchAdapter.isEmpty()) {
txtvStatus.setVisibility(View.GONE);
} else {
txtvStatus.setVisibility(View.VISIBLE);
}
}
});
}
};
thread.start();

View File

@ -144,53 +144,6 @@ public class ChapterListAdapter extends ArrayAdapter<Chapter> {
TextView link;
}
private LinkMovementMethod linkMovementMethod = new LinkMovementMethod() {
@Override
public boolean onTouchEvent(TextView widget, Spannable buffer,
MotionEvent event) {
Object text = widget.getText();
if (text instanceof Spanned) {
int action = event.getAction();
if (action == MotionEvent.ACTION_UP
|| action == MotionEvent.ACTION_DOWN) {
int x = (int) event.getX();
int y = (int) event.getY();
x -= widget.getTotalPaddingLeft();
y -= widget.getTotalPaddingTop();
x += widget.getScrollX();
y += widget.getScrollY();
Layout layout = widget.getLayout();
int line = layout.getLineForVertical(y);
int off = layout.getOffsetForHorizontal(line, x);
ClickableSpan[] link = buffer.getSpans(off, off,
ClickableSpan.class);
if (link.length != 0) {
if (action == MotionEvent.ACTION_UP) {
link[0].onClick(widget);
} else if (action == MotionEvent.ACTION_DOWN) {
Selection.setSelection(buffer,
buffer.getSpanStart(link[0]),
buffer.getSpanEnd(link[0]));
}
return true;
}
}
}
return false;
}
};
@Override
public int getCount() {
// ignore invalid chapters

View File

@ -47,7 +47,7 @@ public class BitmapDecodeWorkerTask extends Thread {
*/
protected boolean tagsMatching(ImageView target) {
return target.getTag() == null
|| target.getTag() == imageResource.getImageLoaderCacheKey();
|| target.getTag().equals(imageResource.getImageLoaderCacheKey());
}
protected void onPostExecute() {

View File

@ -77,7 +77,7 @@ public class ImageLoader {
});
}
public static ImageLoader getInstance() {
public static synchronized ImageLoader getInstance() {
if (singleton == null) {
singleton = new ImageLoader();
}

View File

@ -1,8 +1,9 @@
package de.danoeh.antennapod.asynctask;
import java.io.File;
import java.io.FileWriter;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.Arrays;
import android.annotation.SuppressLint;
@ -16,6 +17,7 @@ import de.danoeh.antennapod.PodcastApp;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.opml.OpmlWriter;
import de.danoeh.antennapod.preferences.UserPreferences;
import de.danoeh.antennapod.util.LangUtils;
import de.danoeh.antennapod.storage.DBReader;
/** Writes an OPML file into the export directory in the background. */
@ -49,13 +51,21 @@ public class OpmlExportWorker extends AsyncTask<Void, Void, Void> {
output.delete();
}
}
OutputStreamWriter writer = null;
try {
FileWriter writer = new FileWriter(output);
writer = new OutputStreamWriter(new FileOutputStream(output), LangUtils.UTF_8);
opmlWriter.writeDocument(DBReader.getFeedList(context), writer);
writer.close();
} catch (IOException e) {
e.printStackTrace();
exception = e;
} finally {
if (writer != null) {
try {
writer.close();
} catch (IOException ioe) {
exception = ioe;
}
}
}
return null;
}

View File

@ -1,5 +1,6 @@
package de.danoeh.antennapod.asynctask;
import java.util.Arrays;
import java.util.Date;
import android.annotation.SuppressLint;
@ -22,7 +23,7 @@ public class OpmlFeedQueuer extends AsyncTask<Void, Void, Void> {
public OpmlFeedQueuer(Context context, int[] selection) {
super();
this.context = context;
this.selection = selection;
this.selection = Arrays.copyOf(selection, selection.length);
}
@Override

View File

@ -64,6 +64,13 @@ public class OpmlImportWorker extends
@Override
protected void onPostExecute(ArrayList<OpmlElement> result) {
if (mReader != null) {
try {
mReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
progDialog.dismiss();
if (exception != null) {
if (AppConfig.DEBUG)

View File

@ -0,0 +1,100 @@
package de.danoeh.antennapod.dialog;
import java.util.Arrays;
import java.util.List;
import android.app.AlertDialog;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.preferences.UserPreferences;
public class VariableSpeedDialog {
private VariableSpeedDialog() {
}
public static void showDialog(final Context context) {
if (com.aocate.media.MediaPlayer.isPrestoLibraryInstalled(context)) {
showSpeedSelectorDialog(context);
} else {
showGetPluginDialog(context);
}
}
private static void showGetPluginDialog(final Context context) {
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle(R.string.no_playback_plugin_title);
builder.setMessage(R.string.no_playback_plugin_msg);
builder.setNegativeButton(R.string.close_label, null);
builder.setPositiveButton(R.string.download_plugin_label,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
try {
Intent playStoreIntent = new Intent(
Intent.ACTION_VIEW,
Uri.parse("market://details?id=com.falconware.prestissimo"));
context.startActivity(playStoreIntent);
} catch (ActivityNotFoundException e) {
// this is usually thrown on an emulator if the Android market is not installed
e.printStackTrace();
}
}
});
builder.create().show();
}
private static void showSpeedSelectorDialog(final Context context) {
final String[] speedValues = context.getResources().getStringArray(
R.array.playback_speed_values);
// According to Java spec these get initialized to false on creation
final boolean[] speedChecked = new boolean[speedValues.length];
// Build the "isChecked" array so that multiChoice dialog is
// populated correctly
List<String> selectedSpeedList = Arrays.asList(UserPreferences
.getPlaybackSpeedArray());
for (int i = 0; i < speedValues.length; i++) {
speedChecked[i] = selectedSpeedList.contains(speedValues[i]);
}
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle(R.string.set_playback_speed_label);
builder.setMultiChoiceItems(R.array.playback_speed_values,
speedChecked, new DialogInterface.OnMultiChoiceClickListener() {
@Override
public void onClick(DialogInterface dialog, int which,
boolean isChecked) {
speedChecked[which] = isChecked;
}
});
builder.setNegativeButton(android.R.string.cancel, null);
builder.setPositiveButton(android.R.string.ok,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
int choiceCount = 0;
for (int i = 0; i < speedChecked.length; i++) {
if (speedChecked[i]) {
choiceCount++;
}
}
String[] newSpeedValues = new String[choiceCount];
int newSpeedIndex = 0;
for (int i = 0; i < speedChecked.length; i++) {
if (speedChecked[i]) {
newSpeedValues[newSpeedIndex++] = speedValues[i];
}
}
UserPreferences.setPlaybackSpeedArray(newSpeedValues);
}
});
builder.create().show();
}
}

View File

@ -39,7 +39,7 @@ public class EventDistributor extends Observable {
events = new ConcurrentLinkedQueue<Integer>();
}
public static EventDistributor getInstance() {
public static synchronized EventDistributor getInstance() {
if (instance == null) {
instance = new EventDistributor();
}

View File

@ -55,7 +55,11 @@ public class Feed extends FeedFile {
super(fileUrl, downloadUrl, downloaded);
this.id = id;
this.title = title;
this.lastUpdate = lastUpdate;
if (lastUpdate != null) {
this.lastUpdate = (Date) lastUpdate.clone();
} else {
this.lastUpdate = null;
}
this.link = link;
this.description = description;
this.paymentLink = paymentLink;
@ -83,7 +87,7 @@ public class Feed extends FeedFile {
*/
public Feed(String url, Date lastUpdate) {
super(null, url, false);
this.lastUpdate = lastUpdate;
this.lastUpdate = (lastUpdate != null) ? (Date) lastUpdate.clone() : null;
}
/**
@ -314,19 +318,12 @@ public class Feed extends FeedFile {
this.items = list;
}
/**
* Returns an array that contains all the feeditems of this feed.
*/
public FeedItem[] getItemsArray() {
return items.toArray(new FeedItem[items.size()]);
}
public Date getLastUpdate() {
return lastUpdate;
return (lastUpdate != null) ? (Date) lastUpdate.clone() : null;
}
public void setLastUpdate(Date lastUpdate) {
this.lastUpdate = lastUpdate;
this.lastUpdate = (lastUpdate != null) ? (Date) lastUpdate.clone() : null;
}
public String getFeedIdentifier() {

View File

@ -48,6 +48,19 @@ public class FeedItem extends FeedComponent implements
this.read = true;
}
/**
* This constructor should be used for creating test objects.
* */
public FeedItem(long id, String title, String itemIdentifier, String link, Date pubDate, boolean read, Feed feed) {
this.id = id;
this.title = title;
this.itemIdentifier = itemIdentifier;
this.link = link;
this.pubDate = (pubDate != null) ? (Date) pubDate.clone() : null;
this.read = read;
this.feed = feed;
}
public void updateFromOther(FeedItem other) {
super.updateFromOther(other);
if (other.title != null) {
@ -123,19 +136,35 @@ public class FeedItem extends FeedComponent implements
}
public Date getPubDate() {
return pubDate;
if (pubDate != null) {
return (Date) pubDate.clone();
} else {
return null;
}
}
public void setPubDate(Date pubDate) {
this.pubDate = pubDate;
if (pubDate != null) {
this.pubDate = (Date) pubDate.clone();
} else {
this.pubDate = null;
}
}
public FeedMedia getMedia() {
return media;
}
/**
* Sets the media object of this FeedItem. If the given
* FeedMedia object is not null, it's 'item'-attribute value
* will also be set to this item.
* */
public void setMedia(FeedMedia media) {
this.media = media;
if (media != null && media.getItem() != this) {
media.setItem(this);
}
}
public Feed getFeed() {

View File

@ -53,7 +53,8 @@ public class FeedMedia extends FeedFile implements Playable {
this.position = position;
this.size = size;
this.mime_type = mime_type;
this.playbackCompletionDate = playbackCompletionDate;
this.playbackCompletionDate = playbackCompletionDate == null
? null : (Date) playbackCompletionDate.clone();
}
public FeedMedia(long id, FeedItem item) {
@ -164,16 +165,25 @@ public class FeedMedia extends FeedFile implements Playable {
return item;
}
/**
* Sets the item object of this FeedMedia. If the given
* FeedItem object is not null, it's 'media'-attribute value
* will also be set to this media object.
* */
public void setItem(FeedItem item) {
this.item = item;
if (item != null && item.getMedia() != this) {
item.setMedia(this);
}
}
public Date getPlaybackCompletionDate() {
return playbackCompletionDate;
}
return playbackCompletionDate == null
? null : (Date) playbackCompletionDate.clone(); }
public void setPlaybackCompletionDate(Date playbackCompletionDate) {
this.playbackCompletionDate = playbackCompletionDate;
this.playbackCompletionDate = playbackCompletionDate == null
? null : (Date) playbackCompletionDate.clone();
}
public boolean isInProgress() {
@ -304,7 +314,7 @@ public class FeedMedia extends FeedFile implements Playable {
@Override
public void saveCurrentPosition(SharedPreferences pref, int newPosition) {
position = newPosition;
DBWriter.setFeedMediaPosition(PodcastApp.getInstance(), this);
DBWriter.setFeedMediaPlaybackInformation(PodcastApp.getInstance(), this);
}
@Override

View File

@ -173,6 +173,12 @@ public class ExternalPlayerFragment extends Fragment {
.newOnPlayButtonClickListener());
}
}
@Override
public void onPlaybackSpeedChange() {
// TODO Auto-generated method stub
}
};
}

View File

@ -3,6 +3,7 @@ package de.danoeh.antennapod.fragment;
import java.util.List;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.AsyncTask;
@ -29,9 +30,9 @@ import de.danoeh.antennapod.storage.FeedItemStatistics;
import de.danoeh.antennapod.util.menuhandler.FeedMenuHandler;
public class FeedlistFragment extends Fragment implements
ActionMode.Callback, AdapterView.OnItemClickListener,
AdapterView.OnItemLongClickListener {
private static final String TAG = "FeedlistFragment";
ActionMode.Callback, AdapterView.OnItemClickListener,
AdapterView.OnItemLongClickListener {
private static final String TAG = "FeedlistFragment";
private static final int EVENTS = EventDistributor.DOWNLOAD_HANDLED
| EventDistributor.DOWNLOAD_QUEUED
@ -94,12 +95,16 @@ public class FeedlistFragment extends Fragment implements
AsyncTask<Void, Void, List[]> loadTask = new AsyncTask<Void, Void, List[]>() {
@Override
protected List[] doInBackground(Void... params) {
return new List[]{DBReader.getFeedList(getActivity()),
DBReader.getFeedStatisticsList(getActivity())};
Context context = getActivity();
if (context != null) {
return new List[]{DBReader.getFeedList(context),
DBReader.getFeedStatisticsList(context)};
} else {
return null;
}
}
@Override
protected void onPostExecute(List[] result) {
super.onPostExecute(result);
@ -159,10 +164,15 @@ public class FeedlistFragment extends Fragment implements
EventDistributor.getInstance().register(contentUpdate);
}
@Override
public void onDestroy() {
super.onDestroy();
EventDistributor.getInstance().unregister(contentUpdate);
}
@Override
public void onPause() {
super.onPause();
EventDistributor.getInstance().unregister(contentUpdate);
if (mActionMode != null) {
mActionMode.finish();
}
@ -255,9 +265,9 @@ public class FeedlistFragment extends Fragment implements
public boolean onItemLongClick(AdapterView<?> parent, View view,
int position, long id) {
Feed selection = fla.getItem(position);
if (AppConfig.DEBUG)
Log.d(TAG, "Selected Feed with title " + selection.getTitle());
if (selection != null) {
if (AppConfig.DEBUG)
Log.d(TAG, "Selected Feed with title " + selection.getTitle());
if (mActionMode != null) {
mActionMode.finish();
}

View File

@ -1,5 +1,6 @@
package de.danoeh.antennapod.fragment;
import android.content.*;
import android.support.v4.app.Fragment;
import android.support.v7.app.ActionBarActivity;
import de.danoeh.antennapod.feed.FeedItem;
@ -9,10 +10,6 @@ import org.apache.commons.lang3.StringEscapeUtils;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.ClipData;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.TypedArray;
import android.net.Uri;
import android.os.AsyncTask;
@ -117,7 +114,12 @@ public class ItemDescriptionFragment extends Fragment {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
startActivity(intent);
try {
startActivity(intent);
} catch (ActivityNotFoundException e) {
e.printStackTrace();
return false;
}
return true;
}
@ -138,6 +140,7 @@ public class ItemDescriptionFragment extends Fragment {
}
});
registerForContextMenu(webvDescription);
return webvDescription;
}
@ -371,11 +374,10 @@ public class ItemDescriptionFragment extends Fragment {
Callable<String> shownotesLoadTask = shownotesProvider.loadShownotes();
final String shownotes = shownotesLoadTask.call();
data = "";
data = StringEscapeUtils.unescapeHtml4(shownotes);
Activity activity = getActivity();
if (activity != null) {
TypedArray res = getActivity()
TypedArray res = activity
.getTheme()
.obtainStyledAttributes(
new int[]{android.R.attr.textColorPrimary});

View File

@ -50,7 +50,6 @@ public class ItemlistFragment extends ListFragment {
public static final String EXTRA_SELECTED_FEEDITEM = "extra.de.danoeh.antennapod.activity.selected_feeditem";
public static final String ARGUMENT_FEED_ID = "argument.de.danoeh.antennapod.feed_id";
protected InternalFeedItemlistAdapter fila;
protected DownloadRequester requester = DownloadRequester.getInstance();
private Feed feed;
protected List<Long> queue;
@ -61,6 +60,8 @@ public class ItemlistFragment extends ListFragment {
/** Argument for FeeditemlistAdapter */
protected boolean showFeedtitle;
private AsyncTask<Long, Void, Feed> currentLoadTask;
public ItemlistFragment(boolean showFeedtitle) {
super();
this.showFeedtitle = showFeedtitle;
@ -116,11 +117,21 @@ public class ItemlistFragment extends ListFragment {
return inflater.inflate(R.layout.feeditemlist, container, false);
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
loadData();
}
@Override
public void onStart() {
super.onStart();
EventDistributor.getInstance().register(contentUpdate);
loadData();
}
@Override
public void onDestroyView() {
super.onDestroyView();
EventDistributor.getInstance().unregister(contentUpdate);
if (currentLoadTask != null) {
currentLoadTask.cancel(true);
}
}
protected void loadData() {
final long feedId;
@ -156,8 +167,6 @@ public class ItemlistFragment extends ListFragment {
} else {
Log.e(TAG, "Could not load queue");
}
if (result.getItems().isEmpty()) {
}
setEmptyViewIfListIsEmpty();
if (fila != null) {
fila.notifyDataSetChanged();
@ -171,6 +180,7 @@ public class ItemlistFragment extends ListFragment {
}
}
};
currentLoadTask = loadTask;
loadTask.execute(feedId);
}
@ -187,17 +197,6 @@ public class ItemlistFragment extends ListFragment {
adapterCallback, showFeedtitle);
}
@Override
public void onPause() {
super.onPause();
}
@Override
public void onDestroy() {
super.onDestroy();
EventDistributor.getInstance().unregister(contentUpdate);
}
@Override
public void onResume() {
super.onResume();
@ -209,7 +208,6 @@ public class ItemlistFragment extends ListFragment {
}
});
updateProgressBarVisibility();
EventDistributor.getInstance().register(contentUpdate);
}
@Override

View File

@ -16,6 +16,8 @@ import org.json.JSONObject;
import android.net.Uri;
import de.danoeh.antennapod.util.LangUtils;
/** Executes HTTP requests and returns the results. */
public class MiroGuideConnector {
private HttpClient httpClient;
@ -73,12 +75,14 @@ public class MiroGuideConnector {
if (response.getStatusLine().getStatusCode() == 200) {
HttpEntity entity = response.getEntity();
if (entity != null) {
InputStream in = entity.getContent();
BufferedReader reader = new BufferedReader(
new InputStreamReader(in));
result = reader.readLine();
in.close();
new InputStreamReader(entity.getContent(),
LangUtils.UTF_8));
try {
result = reader.readLine();
} finally {
reader.close();
}
}
} else {
throw new MiroGuideException(response.getStatusLine()

View File

@ -12,7 +12,7 @@ public class MiroGuideItem {
super();
this.name = name;
this.description = description;
this.date = date;
this.date = (Date) date.clone();
this.url = url;
}
@ -30,7 +30,7 @@ public class MiroGuideItem {
}
public Date getDate() {
return date;
return (Date) date.clone();
}
public String getUrl() {

View File

@ -2,9 +2,13 @@ package de.danoeh.antennapod.preferences;
import java.io.File;
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import org.apache.commons.lang3.StringUtils;
import org.json.JSONArray;
import org.json.JSONException;
import android.app.AlarmManager;
import android.app.PendingIntent;
@ -41,12 +45,14 @@ public class UserPreferences implements
public static final String PREF_ENABLE_AUTODL_WIFI_FILTER = "prefEnableAutoDownloadWifiFilter";
private static final String PREF_AUTODL_SELECTED_NETWORKS = "prefAutodownloadSelectedNetworks";
public static final String PREF_EPISODE_CACHE_SIZE = "prefEpisodeCacheSize";
private static final String PREF_PLAYBACK_SPEED = "prefPlaybackSpeed";
private static final String PREF_PLAYBACK_SPEED_ARRAY = "prefPlaybackSpeedArray";
public static final String PREF_PAUSE_PLAYBACK_FOR_FOCUS_LOSS = "prefPauseForFocusLoss";
private static int EPISODE_CACHE_SIZE_UNLIMITED = -1;
private static UserPreferences instance;
private Context context;
private final Context context;
// Preferences
private boolean pauseOnHeadsetDisconnect;
@ -61,6 +67,8 @@ public class UserPreferences implements
private boolean enableAutodownloadWifiFilter;
private String[] autodownloadSelectedNetworks;
private int episodeCacheSize;
private String playbackSpeed;
private String[] playbackSpeedArray;
private boolean pauseForFocusLoss;
private UserPreferences(Context context) {
@ -85,6 +93,7 @@ public class UserPreferences implements
createNoMediaFile();
PreferenceManager.getDefaultSharedPreferences(context)
.registerOnSharedPreferenceChangeListener(instance);
}
private void loadPreferences() {
@ -110,6 +119,9 @@ public class UserPreferences implements
episodeCacheSize = readEpisodeCacheSize(sp.getString(
PREF_EPISODE_CACHE_SIZE, "20"));
enableAutodownload = sp.getBoolean(PREF_ENABLE_AUTODL, false);
playbackSpeed = sp.getString(PREF_PLAYBACK_SPEED, "1.0");
playbackSpeedArray = readPlaybackSpeedArray(sp.getString(
PREF_PLAYBACK_SPEED_ARRAY, null));
pauseForFocusLoss = sp.getBoolean(PREF_PAUSE_PLAYBACK_FOR_FOCUS_LOSS, false);
}
@ -138,6 +150,36 @@ public class UserPreferences implements
}
}
private String[] readPlaybackSpeedArray(String valueFromPrefs) {
String[] selectedSpeeds = null;
// If this preference hasn't been set yet, return the default options
if (valueFromPrefs == null) {
String[] allSpeeds = context.getResources().getStringArray(
R.array.playback_speed_values);
List<String> speedList = new LinkedList<String>();
for (String speedStr : allSpeeds) {
float speed = Float.parseFloat(speedStr);
if (speed < 2.0001 && speed * 10 % 1 == 0) {
speedList.add(speedStr);
}
}
selectedSpeeds = speedList.toArray(new String[speedList.size()]);
} else {
try {
JSONArray jsonArray = new JSONArray(valueFromPrefs);
selectedSpeeds = new String[jsonArray.length()];
for (int i = 0; i < jsonArray.length(); i++) {
selectedSpeeds[i] = jsonArray.getString(i);
}
} catch (JSONException e) {
Log.e(TAG,
"Got JSON error when trying to get speeds from JSONArray");
e.printStackTrace();
}
}
return selectedSpeeds;
}
private static void instanceAvailable() {
if (instance == null) {
throw new IllegalStateException(
@ -172,7 +214,8 @@ public class UserPreferences implements
public static boolean isDisplayOnlyEpisodes() {
instanceAvailable();
return instance.displayOnlyEpisodes;
//return instance.displayOnlyEpisodes;
return false;
}
public static boolean isAutoDelete() {
@ -199,6 +242,16 @@ public class UserPreferences implements
return EPISODE_CACHE_SIZE_UNLIMITED;
}
public static String getPlaybackSpeed() {
instanceAvailable();
return instance.playbackSpeed;
}
public static String[] getPlaybackSpeedArray() {
instanceAvailable();
return instance.playbackSpeedArray;
}
/**
* Returns the capacity of the episode cache. This method will return the
* negative integer EPISODE_CACHE_SIZE_UNLIMITED if the cache size is set to
@ -258,11 +311,31 @@ public class UserPreferences implements
PREF_EPISODE_CACHE_SIZE, "20"));
} else if (key.equals(PREF_ENABLE_AUTODL)) {
enableAutodownload = sp.getBoolean(PREF_ENABLE_AUTODL, false);
} else if (key.equals(PREF_PLAYBACK_SPEED)) {
playbackSpeed = sp.getString(PREF_PLAYBACK_SPEED, "1.0");
} else if (key.equals(PREF_PLAYBACK_SPEED_ARRAY)) {
playbackSpeedArray = readPlaybackSpeedArray(sp.getString(
PREF_PLAYBACK_SPEED_ARRAY, null));
} else if (key.equals(PREF_PAUSE_PLAYBACK_FOR_FOCUS_LOSS)) {
pauseForFocusLoss = sp.getBoolean(PREF_PAUSE_PLAYBACK_FOR_FOCUS_LOSS, false);
}
}
public static void setPlaybackSpeed(String speed) {
PreferenceManager.getDefaultSharedPreferences(instance.context).edit()
.putString(PREF_PLAYBACK_SPEED, speed).apply();
}
public static void setPlaybackSpeedArray(String[] speeds) {
JSONArray jsonArray = new JSONArray();
for (String speed : speeds) {
jsonArray.put(speed);
}
PreferenceManager.getDefaultSharedPreferences(instance.context).edit()
.putString(PREF_PLAYBACK_SPEED_ARRAY, jsonArray.toString())
.apply();
}
public static void setAutodownloadSelectedNetworks(Context context,
String[] value) {
SharedPreferences.Editor editor = PreferenceManager

View File

@ -45,9 +45,13 @@ import de.danoeh.antennapod.storage.DBTasks;
import de.danoeh.antennapod.storage.DBWriter;
import de.danoeh.antennapod.util.BitmapDecoder;
import de.danoeh.antennapod.util.QueueAccess;
import de.danoeh.antennapod.util.DuckType;
import de.danoeh.antennapod.util.flattr.FlattrUtils;
import de.danoeh.antennapod.util.playback.AudioPlayer;
import de.danoeh.antennapod.util.playback.IPlayer;
import de.danoeh.antennapod.util.playback.Playable;
import de.danoeh.antennapod.util.playback.Playable.PlayableException;
import de.danoeh.antennapod.util.playback.VideoPlayer;
import de.danoeh.antennapod.util.playback.PlaybackController;
/**
@ -119,7 +123,12 @@ public class PlaybackService extends Service {
*/
public static final int NOTIFICATION_TYPE_PLAYBACK_END = 7;
/**
/**
* Playback speed has changed
* */
public static final int NOTIFICATION_TYPE_PLAYBACK_SPEED_CHANGE = 8;
/**
* Returned by getPositionSafe() or getDurationSafe() if the playbackService
* is in an invalid state.
*/
@ -132,13 +141,12 @@ public class PlaybackService extends Service {
private static final int NOTIFICATION_ID = 1;
private volatile IPlayer player;
private RemoteControlClient remoteControlClient;
private AudioManager audioManager;
private ComponentName mediaButtonReceiver;
private MediaPlayer player;
private RemoteControlClient remoteControlClient;
private Playable media;
private volatile Playable media;
/**
* True if media should be streamed (Extracted from Intent Extra) .
@ -252,7 +260,6 @@ public class PlaybackService extends Service {
}
);
dbLoaderExecutor = Executors.newSingleThreadExecutor();
player = createMediaPlayer();
mediaButtonReceiver = new ComponentName(getPackageName(),
MediaButtonReceiver.class.getName());
@ -273,21 +280,42 @@ public class PlaybackService extends Service {
loadQueue();
}
private MediaPlayer createMediaPlayer() {
return createMediaPlayer(new MediaPlayer());
private IPlayer createMediaPlayer() {
IPlayer player;
if (media == null || media.getMediaType() == MediaType.VIDEO) {
player = new VideoPlayer();
} else {
player = new AudioPlayer(this);
}
return createMediaPlayer(player);
}
private MediaPlayer createMediaPlayer(MediaPlayer mp) {
if (mp != null) {
mp.setOnPreparedListener(preparedListener);
mp.setOnCompletionListener(completionListener);
mp.setOnSeekCompleteListener(onSeekCompleteListener);
mp.setOnErrorListener(onErrorListener);
mp.setOnBufferingUpdateListener(onBufferingUpdateListener);
mp.setOnInfoListener(onInfoListener);
}
return mp;
}
private IPlayer createMediaPlayer(IPlayer mp) {
if (mp != null && media != null) {
if (media.getMediaType() == MediaType.AUDIO) {
((AudioPlayer) mp).setOnPreparedListener(audioPreparedListener);
((AudioPlayer) mp)
.setOnCompletionListener(audioCompletionListener);
((AudioPlayer) mp)
.setOnSeekCompleteListener(audioSeekCompleteListener);
((AudioPlayer) mp).setOnErrorListener(audioErrorListener);
((AudioPlayer) mp)
.setOnBufferingUpdateListener(audioBufferingUpdateListener);
((AudioPlayer) mp).setOnInfoListener(audioInfoListener);
} else {
((VideoPlayer) mp).setOnPreparedListener(videoPreparedListener);
((VideoPlayer) mp)
.setOnCompletionListener(videoCompletionListener);
((VideoPlayer) mp)
.setOnSeekCompleteListener(videoSeekCompleteListener);
((VideoPlayer) mp).setOnErrorListener(videoErrorListener);
((VideoPlayer) mp)
.setOnBufferingUpdateListener(videoBufferingUpdateListener);
((VideoPlayer) mp).setOnInfoListener(videoInfoListener);
}
}
return mp;
}
@SuppressLint("NewApi")
@Override
@ -482,7 +510,7 @@ public class PlaybackService extends Service {
seekDelta(-PlaybackController.DEFAULT_SEEK_DELTA);
break;
}
}
}
}
/**
@ -575,6 +603,7 @@ public class PlaybackService extends Service {
Log.d(TAG, "Setting up media player");
try {
MediaType mediaType = media.getMediaType();
player = createMediaPlayer();
if (mediaType == MediaType.AUDIO) {
if (AppConfig.DEBUG)
Log.d(TAG, "Mime type is audio");
@ -669,105 +698,169 @@ public class PlaybackService extends Service {
}
}
private MediaPlayer.OnPreparedListener preparedListener = new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mp) {
if (AppConfig.DEBUG)
Log.d(TAG, "Resource prepared");
mp.seekTo(media.getPosition());
if (media.getDuration() == 0) {
if (AppConfig.DEBUG)
Log.d(TAG, "Setting duration of media");
media.setDuration(mp.getDuration());
}
setStatus(PlayerStatus.PREPARED);
if (chapterLoader != null) {
chapterLoader.interrupt();
}
chapterLoader = new Thread() {
@Override
public void run() {
if (AppConfig.DEBUG)
Log.d(TAG, "Chapter loader started");
if (media != null && media.getChapters() == null) {
media.loadChapterMarks();
if (!isInterrupted() && media.getChapters() != null) {
sendNotificationBroadcast(NOTIFICATION_TYPE_RELOAD,
0);
}
}
if (AppConfig.DEBUG)
Log.d(TAG, "Chapter loader stopped");
}
};
chapterLoader.start();
private final com.aocate.media.MediaPlayer.OnPreparedListener audioPreparedListener = new com.aocate.media.MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(com.aocate.media.MediaPlayer mp) {
genericOnPrepared(mp);
}
};
if (startWhenPrepared) {
play();
}
}
};
private final android.media.MediaPlayer.OnPreparedListener videoPreparedListener = new android.media.MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(android.media.MediaPlayer mp) {
genericOnPrepared(mp);
}
};
private MediaPlayer.OnSeekCompleteListener onSeekCompleteListener = new MediaPlayer.OnSeekCompleteListener() {
private final void genericOnPrepared(Object inObj) {
IPlayer mp = DuckType.coerce(inObj).to(IPlayer.class);
if (AppConfig.DEBUG)
Log.d(TAG, "Resource prepared");
mp.seekTo(media.getPosition());
if (media.getDuration() == 0) {
if (AppConfig.DEBUG)
Log.d(TAG, "Setting duration of media");
media.setDuration(mp.getDuration());
}
setStatus(PlayerStatus.PREPARED);
if (chapterLoader != null) {
chapterLoader.interrupt();
}
chapterLoader = new Thread() {
@Override
public void run() {
if (AppConfig.DEBUG)
Log.d(TAG, "Chapter loader started");
if (media != null && media.getChapters() == null) {
media.loadChapterMarks();
if (!isInterrupted() && media.getChapters() != null) {
sendNotificationBroadcast(NOTIFICATION_TYPE_RELOAD,
0);
}
}
if (AppConfig.DEBUG)
Log.d(TAG, "Chapter loader stopped");
}
};
chapterLoader.start();
@Override
public void onSeekComplete(MediaPlayer mp) {
if (status == PlayerStatus.SEEKING) {
setStatus(statusBeforeSeek);
}
if (startWhenPrepared) {
play();
}
}
}
};
private final com.aocate.media.MediaPlayer.OnSeekCompleteListener audioSeekCompleteListener = new com.aocate.media.MediaPlayer.OnSeekCompleteListener() {
@Override
public void onSeekComplete(com.aocate.media.MediaPlayer mp) {
genericSeekCompleteListener();
}
};
private MediaPlayer.OnInfoListener onInfoListener = new MediaPlayer.OnInfoListener() {
private final android.media.MediaPlayer.OnSeekCompleteListener videoSeekCompleteListener = new android.media.MediaPlayer.OnSeekCompleteListener() {
@Override
public void onSeekComplete(android.media.MediaPlayer mp) {
genericSeekCompleteListener();
}
};
@Override
public boolean onInfo(MediaPlayer mp, int what, int extra) {
switch (what) {
case MediaPlayer.MEDIA_INFO_BUFFERING_START:
sendNotificationBroadcast(NOTIFICATION_TYPE_BUFFER_START, 0);
return true;
case MediaPlayer.MEDIA_INFO_BUFFERING_END:
sendNotificationBroadcast(NOTIFICATION_TYPE_BUFFER_END, 0);
return true;
default:
return false;
}
}
};
private final void genericSeekCompleteListener() {
if (status == PlayerStatus.SEEKING) {
setStatus(statusBeforeSeek);
}
}
private MediaPlayer.OnErrorListener onErrorListener = new MediaPlayer.OnErrorListener() {
private static final String TAG = "PlaybackService.onErrorListener";
private final com.aocate.media.MediaPlayer.OnInfoListener audioInfoListener = new com.aocate.media.MediaPlayer.OnInfoListener() {
@Override
public boolean onInfo(com.aocate.media.MediaPlayer mp, int what,
int extra) {
return genericInfoListener(what);
}
};
@Override
public boolean onError(MediaPlayer mp, int what, int extra) {
Log.w(TAG, "An error has occured: " + what);
if (mp.isPlaying()) {
pause(true, true);
}
sendNotificationBroadcast(NOTIFICATION_TYPE_ERROR, what);
setCurrentlyPlayingMedia(PlaybackPreferences.NO_MEDIA_PLAYING);
stopSelf();
return true;
}
};
private final android.media.MediaPlayer.OnInfoListener videoInfoListener = new android.media.MediaPlayer.OnInfoListener() {
@Override
public boolean onInfo(android.media.MediaPlayer mp, int what, int extra) {
return genericInfoListener(what);
}
};
private MediaPlayer.OnCompletionListener completionListener = new MediaPlayer.OnCompletionListener() {
private boolean genericInfoListener(int what) {
switch (what) {
case MediaPlayer.MEDIA_INFO_BUFFERING_START:
sendNotificationBroadcast(NOTIFICATION_TYPE_BUFFER_START, 0);
return true;
case MediaPlayer.MEDIA_INFO_BUFFERING_END:
sendNotificationBroadcast(NOTIFICATION_TYPE_BUFFER_END, 0);
return true;
default:
return false;
}
}
@Override
public void onCompletion(MediaPlayer mp) {
endPlayback(true);
}
};
private final com.aocate.media.MediaPlayer.OnErrorListener audioErrorListener = new com.aocate.media.MediaPlayer.OnErrorListener() {
@Override
public boolean onError(com.aocate.media.MediaPlayer mp, int what,
int extra) {
return genericOnError(mp, what, extra);
}
};
private MediaPlayer.OnBufferingUpdateListener onBufferingUpdateListener = new MediaPlayer.OnBufferingUpdateListener() {
private final android.media.MediaPlayer.OnErrorListener videoErrorListener = new android.media.MediaPlayer.OnErrorListener() {
@Override
public boolean onError(android.media.MediaPlayer mp, int what, int extra) {
return genericOnError(mp, what, extra);
}
};
@Override
public void onBufferingUpdate(MediaPlayer mp, int percent) {
sendNotificationBroadcast(NOTIFICATION_TYPE_BUFFER_UPDATE, percent);
private boolean genericOnError(Object inObj, int what, int extra) {
final String TAG = "PlaybackService.onErrorListener";
Log.w(TAG, "An error has occured: " + what + " " + extra);
IPlayer mp = DuckType.coerce(inObj).to(IPlayer.class);
if (mp.isPlaying()) {
pause(true, true);
}
sendNotificationBroadcast(NOTIFICATION_TYPE_ERROR, what);
setCurrentlyPlayingMedia(PlaybackPreferences.NO_MEDIA_PLAYING);
stopSelf();
return true;
}
}
};
private final com.aocate.media.MediaPlayer.OnCompletionListener audioCompletionListener = new com.aocate.media.MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(com.aocate.media.MediaPlayer mp) {
genericOnCompletion();
}
};
private final android.media.MediaPlayer.OnCompletionListener videoCompletionListener = new android.media.MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(android.media.MediaPlayer mp) {
genericOnCompletion();
}
};
private void genericOnCompletion() {
endPlayback(true);
}
private final com.aocate.media.MediaPlayer.OnBufferingUpdateListener audioBufferingUpdateListener = new com.aocate.media.MediaPlayer.OnBufferingUpdateListener() {
@Override
public void onBufferingUpdate(com.aocate.media.MediaPlayer mp,
int percent) {
genericOnBufferingUpdate(percent);
}
};
private final android.media.MediaPlayer.OnBufferingUpdateListener videoBufferingUpdateListener = new android.media.MediaPlayer.OnBufferingUpdateListener() {
@Override
public void onBufferingUpdate(android.media.MediaPlayer mp, int percent) {
genericOnBufferingUpdate(percent);
}
};
private void genericOnBufferingUpdate(int percent) {
sendNotificationBroadcast(NOTIFICATION_TYPE_BUFFER_UPDATE, percent);
}
private void endPlayback(boolean playNextEpisode) {
if (AppConfig.DEBUG)
@ -790,7 +883,6 @@ public class PlaybackService extends Service {
DBWriter.removeQueueItem(PlaybackService.this, item.getId(), true);
}
DBWriter.addItemToPlaybackHistory(PlaybackService.this, (FeedMedia) media);
DBWriter.setFeedMedia(PlaybackService.this, (FeedMedia) media);
long autoDeleteMediaId = ((FeedComponent) media).getId();
if (shouldStream) {
autoDeleteMediaId = -1;
@ -870,7 +962,7 @@ public class PlaybackService extends Service {
/**
* Saves the current position and pauses playback. Note that, if audiofocus
* is abandoned, the lockscreen controls will also disapear.
*
*
* @param abandonFocus
* is true if the service should release audio focus
* @param reinit
@ -946,6 +1038,7 @@ public class PlaybackService extends Service {
Log.d(TAG, "Resuming/Starting playback");
writePlaybackPreferences();
setSpeed(Float.parseFloat(UserPreferences.getPlaybackSpeed()));
player.start();
if (status != PlayerStatus.PAUSED) {
player.seekTo((int) media.getPosition());
@ -1131,7 +1224,7 @@ public class PlaybackService extends Service {
/**
* Seek a specific position from the current position
*
*
* @param delta
* offset from current position (positive or negative)
* */
@ -1289,18 +1382,20 @@ public class PlaybackService extends Service {
isPlaying = true;
}
Intent i = new Intent(AVRCP_ACTION_PLAYER_STATUS_CHANGED);
i.putExtra("id", 1);
i.putExtra("artist", "");
i.putExtra("album", media.getFeedTitle());
i.putExtra("track", media.getEpisodeTitle());
i.putExtra("playing", isPlaying);
if (queue != null) {
i.putExtra("ListSize", queue.size());
if (media != null) {
Intent i = new Intent(AVRCP_ACTION_PLAYER_STATUS_CHANGED);
i.putExtra("id", 1);
i.putExtra("artist", "");
i.putExtra("album", media.getFeedTitle());
i.putExtra("track", media.getEpisodeTitle());
i.putExtra("playing", isPlaying);
if (queue != null) {
i.putExtra("ListSize", queue.size());
}
i.putExtra("duration", media.getDuration());
i.putExtra("position", media.getPosition());
sendBroadcast(i);
}
i.putExtra("duration", media.getDuration());
i.putExtra("position", media.getPosition());
sendBroadcast(i);
}
/**
@ -1377,7 +1472,7 @@ public class PlaybackService extends Service {
}
}
}
};
};
/** Periodically saves the position of the media file */
class PositionSaver implements Runnable {
@ -1479,7 +1574,7 @@ public class PlaybackService extends Service {
return media;
}
public MediaPlayer getPlayer() {
public IPlayer getPlayer() {
return player;
}
@ -1492,6 +1587,53 @@ public class PlaybackService extends Service {
postStatusUpdateIntent();
}
public boolean canSetSpeed() {
if (player != null && media != null && media.getMediaType() == MediaType.AUDIO) {
return ((AudioPlayer) player).canSetSpeed();
}
return false;
}
public boolean canSetPitch() {
if (player != null && media != null && media.getMediaType() == MediaType.AUDIO) {
return ((AudioPlayer) player).canSetPitch();
}
return false;
}
public void setSpeed(float speed) {
if (media != null && media.getMediaType() == MediaType.AUDIO) {
AudioPlayer audioPlayer = (AudioPlayer) player;
if (audioPlayer.canSetSpeed()) {
audioPlayer.setPlaybackSpeed((float) speed);
if (AppConfig.DEBUG)
Log.d(TAG, "Playback speed was set to " + speed);
sendNotificationBroadcast(
NOTIFICATION_TYPE_PLAYBACK_SPEED_CHANGE, 0);
}
}
}
public void setPitch(float pitch) {
if (media != null && media.getMediaType() == MediaType.AUDIO) {
AudioPlayer audioPlayer = (AudioPlayer) player;
if (audioPlayer.canSetPitch()) {
audioPlayer.setPlaybackPitch((float) pitch);
}
}
}
public float getCurrentPlaybackSpeed() {
if (media.getMediaType() == MediaType.AUDIO
&& player instanceof AudioPlayer) {
AudioPlayer audioPlayer = (AudioPlayer) player;
if (audioPlayer.canSetSpeed()) {
return audioPlayer.getCurrentSpeedMultiplier();
}
}
return -1;
}
/**
* call getDuration() on mediaplayer or return INVALID_TIME if player is in
* an invalid state. This method should be used instead of calling

View File

@ -184,7 +184,7 @@ public class DownloadService extends Service {
public int onStartCommand(Intent intent, int flags, int startId) {
if (intent.getParcelableExtra(EXTRA_REQUEST) != null) {
onDownloadQueued(intent);
} else if (numberOfDownloads.equals(0)) {
} else if (numberOfDownloads.get() == 0) {
stopSelf();
}
return Service.START_NOT_STICKY;
@ -421,52 +421,24 @@ public class DownloadService extends Service {
return null;
}
@SuppressLint("NewApi")
public void onDownloadCompleted(final Downloader downloader) {
final AsyncTask<Void, Void, Void> handlerTask = new AsyncTask<Void, Void, Void>() {
boolean successful;
@Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
if (!successful) {
queryDownloads();
}
}
@Override
protected void onPreExecute() {
super.onPreExecute();
removeDownload(downloader);
}
@Override
protected Void doInBackground(Void... params) {
return null;
}
};
if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.GINGERBREAD_MR1) {
handlerTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
} else {
handlerTask.execute();
}
}
/**
* Remove download from the DownloadRequester list and from the
* DownloadService list.
*/
private void removeDownload(final Downloader d) {
if (AppConfig.DEBUG)
Log.d(TAG, "Removing downloader: "
+ d.getDownloadRequest().getSource());
boolean rc = downloads.remove(d);
if (AppConfig.DEBUG)
Log.d(TAG, "Result of downloads.remove: " + rc);
DownloadRequester.getInstance().removeDownload(d.getDownloadRequest());
sendBroadcast(new Intent(ACTION_DOWNLOADS_CONTENT_CHANGED));
handler.post(new Runnable() {
@Override
public void run() {
if (AppConfig.DEBUG)
Log.d(TAG, "Removing downloader: "
+ d.getDownloadRequest().getSource());
boolean rc = downloads.remove(d);
if (AppConfig.DEBUG)
Log.d(TAG, "Result of downloads.remove: " + rc);
DownloadRequester.getInstance().removeDownload(d.getDownloadRequest());
sendBroadcast(new Intent(ACTION_DOWNLOADS_CONTENT_CHANGED));
}
});
}
/**
@ -561,7 +533,7 @@ public class DownloadService extends Service {
Log.d(TAG, numberOfDownloads.get() + " downloads left");
}
if (numberOfDownloads.get() <= 0) {
if (numberOfDownloads.get() <= 0 && DownloadRequester.getInstance().hasNoDownloads()) {
if (AppConfig.DEBUG)
Log.d(TAG, "Number of downloads is " + numberOfDownloads.get() + ", attempting shutdown");
stopSelf();
@ -647,28 +619,21 @@ public class DownloadService extends Service {
Log.d(TAG, "Feed has image; Downloading....");
savedFeed.getImage().setFeed(savedFeed);
final Feed savedFeedRef = savedFeed;
handler.post(new Runnable() {
@Override
public void run() {
try {
requester.downloadImage(DownloadService.this,
savedFeedRef.getImage());
} catch (DownloadRequestException e) {
e.printStackTrace();
DBWriter.addDownloadStatus(
DownloadService.this,
new DownloadStatus(
savedFeedRef.getImage(),
savedFeedRef
.getImage()
.getHumanReadableIdentifier(),
DownloadError.ERROR_REQUEST_ERROR,
false, e.getMessage()));
}
}
});
try {
requester.downloadImage(DownloadService.this,
savedFeedRef.getImage());
} catch (DownloadRequestException e) {
e.printStackTrace();
DBWriter.addDownloadStatus(
DownloadService.this,
new DownloadStatus(
savedFeedRef.getImage(),
savedFeedRef
.getImage()
.getHumanReadableIdentifier(),
DownloadError.ERROR_REQUEST_ERROR,
false, e.getMessage()));
}
}
} catch (SAXException e) {
@ -730,7 +695,7 @@ public class DownloadService extends Service {
}
private boolean hasValidFeedItems(Feed feed) {
for (FeedItem item : feed.getItemsArray()) {
for (FeedItem item : feed.getItems()) {
if (item.getTitle() == null) {
Log.e(TAG, "Item has no title");
return false;
@ -835,8 +800,9 @@ public class DownloadService extends Service {
media.setFile_url(request.getDestination());
// Get duration
MediaPlayer mediaplayer = new MediaPlayer();
MediaPlayer mediaplayer = null;
try {
mediaplayer = new MediaPlayer();
mediaplayer.setDataSource(media.getFile_url());
mediaplayer.prepare();
media.setDuration(mediaplayer.getDuration());
@ -845,8 +811,13 @@ public class DownloadService extends Service {
mediaplayer.reset();
} catch (IOException e) {
e.printStackTrace();
} catch (RuntimeException e) {
// Thrown by MediaPlayer initialization on some devices
e.printStackTrace();
} finally {
mediaplayer.release();
if (mediaplayer != null) {
mediaplayer.release();
}
}
if (media.getItem().getChapters() == null) {

View File

@ -52,7 +52,7 @@ public class DownloadStatus {
this.feedfileId = feedfileId;
this.reason = reason;
this.successful = successful;
this.completionDate = completionDate;
this.completionDate = (Date) completionDate.clone();
this.reasonDetailed = reasonDetailed;
this.feedfileType = feedfileType;
}
@ -133,7 +133,7 @@ public class DownloadStatus {
}
public Date getCompletionDate() {
return completionDate;
return (Date) completionDate.clone();
}
public long getFeedfileId() {
@ -162,6 +162,7 @@ public class DownloadStatus {
this.successful = false;
this.reason = reason;
this.reasonDetailed = reasonDetailed;
this.done = true;
}
public void setCancelled() {
@ -172,7 +173,7 @@ public class DownloadStatus {
}
public void setCompletionDate(Date completionDate) {
this.completionDate = completionDate;
this.completionDate = (Date) completionDate.clone();
}
public void setId(long id) {

View File

@ -6,12 +6,12 @@ import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;
import org.apache.commons.io.IOUtils;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
@ -30,161 +30,186 @@ import de.danoeh.antennapod.util.DownloadError;
import de.danoeh.antennapod.util.StorageUtils;
public class HttpDownloader extends Downloader {
private static final String TAG = "HttpDownloader";
private static final String TAG = "HttpDownloader";
private static final int MAX_REDIRECTS = 5;
private static final int MAX_REDIRECTS = 5;
private static final int BUFFER_SIZE = 8 * 1024;
private static final int CONNECTION_TIMEOUT = 30000;
private static final int SOCKET_TIMEOUT = 30000;
private static final int BUFFER_SIZE = 8 * 1024;
private static final int CONNECTION_TIMEOUT = 30000;
private static final int SOCKET_TIMEOUT = 30000;
public HttpDownloader(DownloadRequest request) {
super(request);
}
public HttpDownloader(DownloadRequest request) {
super(request);
}
private DefaultHttpClient createHttpClient() {
DefaultHttpClient httpClient = new DefaultHttpClient();
HttpParams params = httpClient.getParams();
params.setIntParameter("http.protocol.max-redirects", MAX_REDIRECTS);
params.setBooleanParameter("http.protocol.reject-relative-redirect",
false);
HttpConnectionParams.setSoTimeout(params, SOCKET_TIMEOUT);
HttpConnectionParams.setConnectionTimeout(params, CONNECTION_TIMEOUT);
HttpClientParams.setRedirecting(params, true);
private DefaultHttpClient createHttpClient() {
DefaultHttpClient httpClient = new DefaultHttpClient();
HttpParams params = httpClient.getParams();
params.setIntParameter("http.protocol.max-redirects", MAX_REDIRECTS);
params.setBooleanParameter("http.protocol.reject-relative-redirect",
false);
HttpConnectionParams.setSoTimeout(params, SOCKET_TIMEOUT);
HttpConnectionParams.setConnectionTimeout(params, CONNECTION_TIMEOUT);
HttpClientParams.setRedirecting(params, true);
// Workaround for broken URLs in redirection
((AbstractHttpClient) httpClient)
.setRedirectHandler(new APRedirectHandler());
return httpClient;
}
// Workaround for broken URLs in redirection
((AbstractHttpClient) httpClient)
.setRedirectHandler(new APRedirectHandler());
return httpClient;
}
@Override
protected void download() {
DefaultHttpClient httpClient = null;
OutputStream out = null;
InputStream connection = null;
try {
HttpGet httpGet = new HttpGet(request.getSource());
httpClient = createHttpClient();
HttpResponse response = httpClient.execute(httpGet);
HttpEntity httpEntity = response.getEntity();
int responseCode = response.getStatusLine().getStatusCode();
if (AppConfig.DEBUG)
Log.d(TAG, "Response code is " + responseCode);
if (responseCode == HttpURLConnection.HTTP_OK && httpEntity != null) {
if (StorageUtils.storageAvailable(PodcastApp.getInstance())) {
File destination = new File(request.getDestination());
if (!destination.exists()) {
connection = AndroidHttpClient
.getUngzippedContent(httpEntity);
InputStream in = new BufferedInputStream(connection);
out = new BufferedOutputStream(new FileOutputStream(
destination));
byte[] buffer = new byte[BUFFER_SIZE];
int count = 0;
request.setStatusMsg(R.string.download_running);
if (AppConfig.DEBUG)
Log.d(TAG, "Getting size of download");
request.setSize(httpEntity.getContentLength());
if (AppConfig.DEBUG)
Log.d(TAG, "Size is " + request.getSize());
if (request.getSize() < 0) {
request.setSize(DownloadStatus.SIZE_UNKNOWN);
}
@Override
protected void download() {
DefaultHttpClient httpClient = null;
BufferedOutputStream out = null;
InputStream connection = null;
try {
HttpGet httpGet = new HttpGet(request.getSource());
httpClient = createHttpClient();
HttpResponse response = httpClient.execute(httpGet);
HttpEntity httpEntity = response.getEntity();
int responseCode = response.getStatusLine().getStatusCode();
Header contentEncodingHeader = response.getFirstHeader("Content-Encoding");
long freeSpace = StorageUtils.getFreeSpaceAvailable();
if (AppConfig.DEBUG)
Log.d(TAG, "Free space is " + freeSpace);
if (request.getSize() == DownloadStatus.SIZE_UNKNOWN
|| request.getSize() <= freeSpace) {
if (AppConfig.DEBUG)
Log.d(TAG, "Starting download");
while (!cancelled
&& (count = in.read(buffer)) != -1) {
out.write(buffer, 0, count);
request.setSoFar(request.getSoFar() + count);
request.setProgressPercent((int) (((double) request
.getSoFar() / (double) request
.getSize()) * 100));
}
if (cancelled) {
onCancelled();
} else {
onSuccess();
}
} else {
onFail(DownloadError.ERROR_NOT_ENOUGH_SPACE, null);
}
} else {
Log.w(TAG, "File already exists");
onFail(DownloadError.ERROR_FILE_EXISTS, null);
}
} else {
onFail(DownloadError.ERROR_DEVICE_NOT_FOUND, null);
}
} else {
onFail(DownloadError.ERROR_HTTP_DATA_ERROR,
String.valueOf(responseCode));
}
} catch (IllegalArgumentException e) {
e.printStackTrace();
onFail(DownloadError.ERROR_MALFORMED_URL, e.getMessage());
} catch (SocketTimeoutException e) {
e.printStackTrace();
onFail(DownloadError.ERROR_CONNECTION_ERROR, e.getMessage());
} catch (UnknownHostException e) {
e.printStackTrace();
onFail(DownloadError.ERROR_UNKNOWN_HOST, e.getMessage());
} catch (IOException e) {
e.printStackTrace();
onFail(DownloadError.ERROR_IO_ERROR, e.getMessage());
} catch (NullPointerException e) {
// might be thrown by connection.getInputStream()
e.printStackTrace();
onFail(DownloadError.ERROR_CONNECTION_ERROR, request.getSource());
} finally {
IOUtils.closeQuietly(out);
if (httpClient != null) {
httpClient.getConnectionManager().shutdown();
}
}
}
final boolean isGzip = contentEncodingHeader != null &&
contentEncodingHeader.getValue().equalsIgnoreCase("gzip");
private void onSuccess() {
if (AppConfig.DEBUG)
Log.d(TAG, "Download was successful");
result.setSuccessful();
}
if (AppConfig.DEBUG)
Log.d(TAG, "Response code is " + responseCode);
private void onFail(DownloadError reason, String reasonDetailed) {
if (AppConfig.DEBUG) {
Log.d(TAG, "Download failed");
}
if (responseCode != HttpURLConnection.HTTP_OK || httpEntity == null) {
onFail(DownloadError.ERROR_HTTP_DATA_ERROR,
String.valueOf(responseCode));
return;
}
if (!StorageUtils.storageAvailable(PodcastApp.getInstance())) {
onFail(DownloadError.ERROR_DEVICE_NOT_FOUND, null);
return;
}
File destination = new File(request.getDestination());
if (destination.exists()) {
Log.w(TAG, "File already exists");
onFail(DownloadError.ERROR_FILE_EXISTS, null);
return;
}
connection = new BufferedInputStream(AndroidHttpClient
.getUngzippedContent(httpEntity));
out = new BufferedOutputStream(new FileOutputStream(
destination));
byte[] buffer = new byte[BUFFER_SIZE];
int count = 0;
request.setStatusMsg(R.string.download_running);
if (AppConfig.DEBUG)
Log.d(TAG, "Getting size of download");
request.setSize(httpEntity.getContentLength());
if (AppConfig.DEBUG)
Log.d(TAG, "Size is " + request.getSize());
if (request.getSize() < 0) {
request.setSize(DownloadStatus.SIZE_UNKNOWN);
}
long freeSpace = StorageUtils.getFreeSpaceAvailable();
if (AppConfig.DEBUG)
Log.d(TAG, "Free space is " + freeSpace);
if (request.getSize() != DownloadStatus.SIZE_UNKNOWN
&& request.getSize() > freeSpace) {
onFail(DownloadError.ERROR_NOT_ENOUGH_SPACE, null);
return;
}
if (AppConfig.DEBUG)
Log.d(TAG, "Starting download");
while (!cancelled
&& (count = connection.read(buffer)) != -1) {
out.write(buffer, 0, count);
request.setSoFar(request.getSoFar() + count);
request.setProgressPercent((int) (((double) request
.getSoFar() / (double) request
.getSize()) * 100));
}
if (cancelled) {
onCancelled();
} else {
out.flush();
// check if size specified in the response header is the same as the size of the
// written file. This check cannot be made if compression was used
if (!isGzip && request.getSize() != DownloadStatus.SIZE_UNKNOWN &&
request.getSoFar() != request.getSize()) {
onFail(DownloadError.ERROR_IO_ERROR,
"Download completed but size: " +
request.getSoFar() +
" does not equal expected size " +
request.getSize());
return;
}
onSuccess();
}
} catch (IllegalArgumentException e) {
e.printStackTrace();
onFail(DownloadError.ERROR_MALFORMED_URL, e.getMessage());
} catch (SocketTimeoutException e) {
e.printStackTrace();
onFail(DownloadError.ERROR_CONNECTION_ERROR, e.getMessage());
} catch (UnknownHostException e) {
e.printStackTrace();
onFail(DownloadError.ERROR_UNKNOWN_HOST, e.getMessage());
} catch (IOException e) {
e.printStackTrace();
onFail(DownloadError.ERROR_IO_ERROR, e.getMessage());
} catch (NullPointerException e) {
// might be thrown by connection.getInputStream()
e.printStackTrace();
onFail(DownloadError.ERROR_CONNECTION_ERROR, request.getSource());
} finally {
IOUtils.closeQuietly(out);
if (httpClient != null) {
httpClient.getConnectionManager().shutdown();
}
}
}
private void onSuccess() {
if (AppConfig.DEBUG)
Log.d(TAG, "Download was successful");
result.setSuccessful();
}
private void onFail(DownloadError reason, String reasonDetailed) {
if (AppConfig.DEBUG) {
Log.d(TAG, "Download failed");
}
result.setFailed(reason, reasonDetailed);
cleanup();
}
cleanup();
}
private void onCancelled() {
if (AppConfig.DEBUG)
Log.d(TAG, "Download was cancelled");
private void onCancelled() {
if (AppConfig.DEBUG)
Log.d(TAG, "Download was cancelled");
result.setCancelled();
cleanup();
}
cleanup();
}
/** Deletes unfinished downloads. */
private void cleanup() {
if (request.getDestination() != null) {
File dest = new File(request.getDestination());
if (dest.exists()) {
boolean rc = dest.delete();
if (AppConfig.DEBUG)
Log.d(TAG, "Deleted file " + dest.getName() + "; Result: "
+ rc);
} else {
if (AppConfig.DEBUG)
Log.d(TAG, "cleanup() didn't delete file: does not exist.");
}
}
}
/**
* Deletes unfinished downloads.
*/
private void cleanup() {
if (request.getDestination() != null) {
File dest = new File(request.getDestination());
if (dest.exists()) {
boolean rc = dest.delete();
if (AppConfig.DEBUG)
Log.d(TAG, "Deleted file " + dest.getName() + "; Result: "
+ rc);
} else {
if (AppConfig.DEBUG)
Log.d(TAG, "cleanup() didn't delete file: does not exist.");
}
}
}
}

View File

@ -229,9 +229,11 @@ public final class DBReader {
title, item, link);
break;
}
chapter.setId(chapterCursor
.getLong(PodDBAdapter.KEY_ID_INDEX));
item.getChapters().add(chapter);
if (chapter != null) {
chapter.setId(chapterCursor
.getLong(PodDBAdapter.KEY_ID_INDEX));
item.getChapters().add(chapter);
}
} while (chapterCursor.moveToNext());
}
chapterCursor.close();

View File

@ -28,6 +28,7 @@ import de.danoeh.antennapod.service.download.DownloadStatus;
import de.danoeh.antennapod.util.DownloadError;
import de.danoeh.antennapod.util.NetworkUtils;
import de.danoeh.antennapod.util.QueueAccess;
import de.danoeh.antennapod.util.comparator.FeedItemPubdateComparator;
import de.danoeh.antennapod.util.exception.MediaFileNotFoundException;
/**
@ -406,12 +407,13 @@ public final class DBTasks {
private static int performAutoCleanup(final Context context,
final int episodeNumber) {
List<FeedItem> candidates = DBReader.getDownloadedItems(context);
List<FeedItem> queue = DBReader.getQueue(context);
List<FeedItem> candidates = new ArrayList<FeedItem>();
List<FeedItem> downloadedItems = DBReader.getDownloadedItems(context);
QueueAccess queue = QueueAccess.IDListAccess(DBReader.getQueueIDList(context));
List<FeedItem> delete;
for (FeedItem item : candidates) {
for (FeedItem item : downloadedItems) {
if (item.hasMedia() && item.getMedia().isDownloaded()
&& !queue.contains(item) && item.isRead()) {
&& !queue.contains(item.getId()) && item.isRead()) {
candidates.add(item);
}
@ -440,7 +442,13 @@ public final class DBTasks {
}
for (FeedItem item : delete) {
DBWriter.deleteFeedMediaOfItem(context, item.getId());
try {
DBWriter.deleteFeedMediaOfItem(context, item.getId()).get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
int counter = delete.size();
@ -561,6 +569,7 @@ public final class DBTasks {
Log.d(TAG, "Feed with title " + newFeed.getTitle()
+ " already exists. Syncing new with existing one.");
Collections.sort(newFeed.getItems(), new FeedItemPubdateComparator());
savedFeed.setItems(DBReader.getFeedItemList(context, savedFeed));
if (savedFeed.compareWithOther(newFeed)) {
if (AppConfig.DEBUG)
@ -578,7 +587,7 @@ public final class DBTasks {
final int i = idx;
item.setFeed(savedFeed);
savedFeed.getItems().add(i, item);
DBWriter.markItemRead(context, item.getId(), false);
item.setRead(false);
} else {
oldItem.updateFromOther(item);
}

View File

@ -73,7 +73,10 @@ public class DBWriter {
}
media.setDownloaded(false);
media.setFile_url(null);
setFeedMedia(context, media);
PodDBAdapter adapter = new PodDBAdapter(context);
adapter.open();
adapter.setMedia(media);
adapter.close();
// If media is currently being played, change playback
// type to 'stream' and shutdown playback service
@ -212,7 +215,7 @@ public class DBWriter {
media.setPlaybackCompletionDate(new Date());
PodDBAdapter adapter = new PodDBAdapter(context);
adapter.open();
adapter.setMedia(media);
adapter.setFeedMediaPlaybackCompletionDate(media);
adapter.close();
EventDistributor.getInstance().sendPlaybackHistoryUpdateBroadcast();
@ -654,18 +657,18 @@ public class DBWriter {
}
/**
* Saves only value of the 'position'-attribute of a FeedMedia object.
* Saves the 'position' and 'duration' attributes of a FeedMedia object
*
* @param context A context that is used for opening a database connection.
* @param media The FeedMedia object.
*/
public static Future<?> setFeedMediaPosition(final Context context, final FeedMedia media) {
public static Future<?> setFeedMediaPlaybackInformation(final Context context, final FeedMedia media) {
return dbExec.submit(new Runnable() {
@Override
public void run() {
PodDBAdapter adapter = new PodDBAdapter(context);
adapter.open();
adapter.setFeedMediaPosition(media);
adapter.setFeedMediaPlaybackInformation(media);
adapter.close();
}
});

View File

@ -26,9 +26,9 @@ import de.danoeh.antennapod.util.URLChecker;
public class DownloadRequester {
private static final String TAG = "DownloadRequester";
public static String IMAGE_DOWNLOADPATH = "images/";
public static String FEED_DOWNLOADPATH = "cache/";
public static String MEDIA_DOWNLOADPATH = "media/";
public static final String IMAGE_DOWNLOADPATH = "images/";
public static final String FEED_DOWNLOADPATH = "cache/";
public static final String MEDIA_DOWNLOADPATH = "media/";
private static DownloadRequester downloader;
@ -38,7 +38,7 @@ public class DownloadRequester {
downloads = new ConcurrentHashMap<String, DownloadRequest>();
}
public static DownloadRequester getInstance() {
public static synchronized DownloadRequester getInstance() {
if (downloader == null) {
downloader = new DownloadRequester();
}

View File

@ -17,7 +17,7 @@ public class FeedItemStatistics {
this.numberOfItems = numberOfItems;
this.numberOfNewItems = numberOfNewItems;
this.numberOfInProgressItems = numberOfInProgressItems;
this.lastUpdate = lastUpdate;
this.lastUpdate = (lastUpdate != null) ? (Date) lastUpdate.clone() : null;
}
public long getFeedID() {
@ -37,6 +37,6 @@ public class FeedItemStatistics {
}
public Date getLastUpdate() {
return lastUpdate;
return (lastUpdate != null) ? (Date) lastUpdate.clone() : null;
}
}

View File

@ -10,7 +10,6 @@ import android.database.DatabaseUtils;
import android.database.MergeCursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteStatement;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
@ -235,7 +234,7 @@ public class PodDBAdapter {
/**
* Select id, description and content-encoded column from feeditems.
*/
public static final String[] SEL_FI_EXTRA = {KEY_ID, KEY_DESCRIPTION,
private static final String[] SEL_FI_EXTRA = {KEY_ID, KEY_DESCRIPTION,
KEY_CONTENT_ENCODED, KEY_FEED};
// column indices for SEL_FI_EXTRA
@ -279,6 +278,13 @@ public class PodDBAdapter {
//db.close();
}
public static boolean deleteDatabase(Context context) {
Log.w(TAG, "Deleting database");
dbHelperSingleton.close();
dbHelperSingleton = null;
return context.deleteDatabase(DATABASE_NAME);
}
/**
* Inserts or updates a feed entry
*
@ -361,6 +367,7 @@ public class PodDBAdapter {
values.put(KEY_DOWNLOAD_URL, media.getDownload_url());
values.put(KEY_DOWNLOADED, media.isDownloaded());
values.put(KEY_FILE_URL, media.getFile_url());
if (media.getPlaybackCompletionDate() != null) {
values.put(KEY_PLAYBACK_COMPLETION_DATE, media
.getPlaybackCompletionDate().getTime());
@ -379,14 +386,26 @@ public class PodDBAdapter {
return media.getId();
}
public void setFeedMediaPosition(FeedMedia media) {
public void setFeedMediaPlaybackInformation(FeedMedia media) {
if (media.getId() != 0) {
ContentValues values = new ContentValues();
values.put(KEY_POSITION, media.getPosition());
values.put(KEY_DURATION, media.getDuration());
db.update(TABLE_NAME_FEED_MEDIA, values, KEY_ID + "=?",
new String[]{String.valueOf(media.getId())});
} else {
Log.e(TAG, "setFeedMediaPosition: ID of media was 0");
Log.e(TAG, "setFeedMediaPlaybackInformation: ID of media was 0");
}
}
public void setFeedMediaPlaybackCompletionDate(FeedMedia media) {
if (media.getId() != 0) {
ContentValues values = new ContentValues();
values.put(KEY_PLAYBACK_COMPLETION_DATE, media.getPlaybackCompletionDate().getTime());
db.update(TABLE_NAME_FEED_MEDIA, values, KEY_ID + "=?",
new String[]{String.valueOf(media.getId())});
} else {
Log.e(TAG, "setFeedMediaPlaybackCompletionDate: ID of media was 0");
}
}
@ -397,8 +416,10 @@ public class PodDBAdapter {
public void setCompleteFeed(Feed feed) {
db.beginTransaction();
setFeed(feed);
for (FeedItem item : feed.getItemsArray()) {
setFeedItem(item);
if (feed.getItems() != null) {
for (FeedItem item : feed.getItems()) {
setFeedItem(item, false);
}
}
db.setTransactionSuccessful();
db.endTransaction();
@ -407,7 +428,7 @@ public class PodDBAdapter {
public void setFeedItemlist(List<FeedItem> items) {
db.beginTransaction();
for (FeedItem item : items) {
setFeedItem(item);
setFeedItem(item, true);
}
db.setTransactionSuccessful();
db.endTransaction();
@ -415,7 +436,7 @@ public class PodDBAdapter {
public long setSingleFeedItem(FeedItem item) {
db.beginTransaction();
long result = setFeedItem(item);
long result = setFeedItem(item, true);
db.setTransactionSuccessful();
db.endTransaction();
return result;
@ -423,10 +444,12 @@ public class PodDBAdapter {
/**
* Inserts or updates a feeditem entry
*
* @param item The FeedItem
* @param saveFeed true if the Feed of the item should also be saved. This should be set to
* false if the method is executed on a list of FeedItems of the same Feed.
* @return the id of the entry
*/
private long setFeedItem(FeedItem item) {
private long setFeedItem(FeedItem item, boolean saveFeed) {
ContentValues values = new ContentValues();
values.put(KEY_TITLE, item.getTitle());
values.put(KEY_LINK, item.getLink());
@ -438,7 +461,7 @@ public class PodDBAdapter {
}
values.put(KEY_PUBDATE, item.getPubDate().getTime());
values.put(KEY_PAYMENT_LINK, item.getPaymentLink());
if (item.getFeed() != null) {
if (saveFeed && item.getFeed() != null) {
setFeed(item.getFeed());
}
values.put(KEY_FEED, item.getFeed().getId());
@ -603,8 +626,10 @@ public class PodDBAdapter {
if (feed.getImage() != null) {
removeFeedImage(feed.getImage());
}
for (FeedItem item : feed.getItemsArray()) {
removeFeedItem(item);
if (feed.getItems() != null) {
for (FeedItem item : feed.getItems()) {
removeFeedItem(item);
}
}
db.delete(TABLE_NAME_FEEDS, KEY_ID + "=?",
new String[]{String.valueOf(feed.getId())});
@ -756,7 +781,7 @@ public class PodDBAdapter {
final String query = "SELECT " + SEL_FI_SMALL_STR + " FROM " + TABLE_NAME_FEED_ITEMS
+ " INNER JOIN " + TABLE_NAME_FEED_MEDIA + " ON "
+ TABLE_NAME_FEED_ITEMS + "." + KEY_ID + "="
+ TABLE_NAME_FEED_MEDIA + "." + KEY_ID + " WHERE "
+ TABLE_NAME_FEED_MEDIA + "." + KEY_FEEDITEM + " WHERE "
+ TABLE_NAME_FEED_MEDIA + "." + KEY_DOWNLOADED + ">0";
Cursor c = db.rawQuery(query, null);
return c;
@ -985,7 +1010,7 @@ public class PodDBAdapter {
" MAX(pubDate) AS latest_episode," +
" COUNT(CASE WHEN position>0 THEN 1 END) AS in_progress," +
" COUNT(CASE WHEN downloaded=1 THEN 1 END) AS episodes_downloaded " +
" FROM FeedItems INNER JOIN FeedMedia ON FeedItems.id=FeedMedia.feeditem GROUP BY FeedItems.feed)" +
" FROM FeedItems LEFT JOIN FeedMedia ON FeedItems.id=FeedMedia.feeditem GROUP BY FeedItems.feed)" +
" INNER JOIN Feeds ON Feeds.id = feed ORDER BY Feeds.title;";
public Cursor getFeedStatisticsCursor() {

View File

@ -121,7 +121,7 @@ public class NSAtom extends Namespace {
if (state.getContentBuf() != null) {
content = state.getContentBuf().toString();
} else {
content = new String();
content = "";
}
SyndElement topElement = state.getTagstack().peek();
String top = topElement.getName();

View File

@ -11,7 +11,7 @@ import android.util.Log;
public class SyndDateUtils {
private static final String TAG = "DateUtils";
public static final String[] RFC822DATES = { "dd MMM yy HH:mm:ss Z", };
private static final String[] RFC822DATES = { "dd MMM yy HH:mm:ss Z", };
/** RFC 3339 date format for UTC dates. */
public static final String RFC3339UTC = "yyyy-MM-dd'T'HH:mm:ss'Z'";
@ -123,12 +123,12 @@ public class SyndDateUtils {
int idx = 0;
if (parts.length == 3) {
// string has hours
result += Integer.valueOf(parts[idx]) * 3600000;
result += Integer.valueOf(parts[idx]) * 3600000L;
idx++;
}
result += Integer.valueOf(parts[idx]) * 60000;
result += Integer.valueOf(parts[idx]) * 60000L;
idx++;
result += (Float.valueOf(parts[idx])) * 1000;
result += (Float.valueOf(parts[idx])) * 1000L;
return result;
}
}

View File

@ -35,9 +35,9 @@ public class ChapterUtils {
* chapters.
*/
public static void readID3ChaptersFromPlayableStreamUrl(Playable p) {
if (AppConfig.DEBUG)
Log.d(TAG, "Reading id3 chapters from item " + p.getEpisodeTitle());
if (p != null && p.getStreamUrl() != null) {
if (AppConfig.DEBUG)
Log.d(TAG, "Reading id3 chapters from item " + p.getEpisodeTitle());
InputStream in = null;
try {
URL url = new URL(p.getStreamUrl());
@ -86,9 +86,9 @@ public class ChapterUtils {
* chapters.
*/
public static void readID3ChaptersFromPlayableFileUrl(Playable p) {
if (AppConfig.DEBUG)
Log.d(TAG, "Reading id3 chapters from item " + p.getEpisodeTitle());
if (p != null && p.localFileAvailable() && p.getLocalMediaUrl() != null) {
if (AppConfig.DEBUG)
Log.d(TAG, "Reading id3 chapters from item " + p.getEpisodeTitle());
File source = new File(p.getLocalMediaUrl());
if (source.exists()) {
ChapterReader reader = new ChapterReader();

View File

@ -0,0 +1,115 @@
/* Adapted from: http://thinking-in-code.blogspot.com/2008/11/duck-typing-in-java-using-dynamic.html */
package de.danoeh.antennapod.util;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* Allows "duck typing" or dynamic invocation based on method signature rather
* than type hierarchy. In other words, rather than checking whether something
* IS-a duck, check whether it WALKS-like-a duck or QUACKS-like a duck.
*
* To use first use the coerce static method to indicate the object you want to
* do Duck Typing for, then specify an interface to the to method which you want
* to coerce the type to, e.g:
*
* public interface Foo { void aMethod(); } class Bar { ... public void
* aMethod() { ... } ... } Bar bar = ...; Foo foo =
* DuckType.coerce(bar).to(Foo.class); foo.aMethod();
*
*
*/
public class DuckType {
private final Object objectToCoerce;
private DuckType(Object objectToCoerce) {
this.objectToCoerce = objectToCoerce;
}
private class CoercedProxy implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Method delegateMethod = findMethodBySignature(method);
assert delegateMethod != null;
return delegateMethod.invoke(DuckType.this.objectToCoerce, args);
}
}
/**
* Specify the duck typed object to coerce.
*
* @param object
* the object to coerce
* @return
*/
public static DuckType coerce(Object object) {
return new DuckType(object);
}
/**
* Coerce the Duck Typed object to the given interface providing it
* implements all the necessary methods.
*
* @param
* @param iface
* @return an instance of the given interface that wraps the duck typed
* class
* @throws ClassCastException
* if the object being coerced does not implement all the
* methods in the given interface.
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
public <T> T to(Class iface) {
assert iface.isInterface() : "cannot coerce object to a class, must be an interface";
if (isA(iface)) {
return (T) iface.cast(objectToCoerce);
}
if (quacksLikeA(iface)) {
return generateProxy(iface);
}
throw new ClassCastException("Could not coerce object of type " + objectToCoerce.getClass() + " to " + iface);
}
@SuppressWarnings("rawtypes")
private boolean isA(Class iface) {
return objectToCoerce.getClass().isInstance(iface);
}
/**
* Determine whether the duck typed object can be used with the given
* interface.
*
* @param Type
* of the interface to check.
* @param iface
* Interface class to check
* @return true if the object will support all the methods in the interface,
* false otherwise.
*/
@SuppressWarnings("rawtypes")
public boolean quacksLikeA(Class iface) {
for (Method method : iface.getMethods()) {
if (findMethodBySignature(method) == null) {
return false;
}
}
return true;
}
@SuppressWarnings({ "unchecked", "rawtypes" })
private <T> T generateProxy(Class iface) {
return (T) Proxy.newProxyInstance(iface.getClassLoader(), new Class[] { iface }, new CoercedProxy());
}
private Method findMethodBySignature(Method method) {
try {
return objectToCoerce.getClass().getMethod(method.getName(), method.getParameterTypes());
} catch (NoSuchMethodException e) {
return null;
}
}
}

View File

@ -1,8 +1,11 @@
package de.danoeh.antennapod.util;
import java.nio.charset.Charset;
import java.util.HashMap;
public class LangUtils {
public static final Charset UTF_8 = Charset.forName("UTF-8");
private static HashMap<String, String> languages;
static {
languages = new HashMap<String, String>();

View File

@ -51,9 +51,8 @@ public abstract class QueueAccess {
if (items == null) {
return false;
}
Iterator<FeedItem> it = items.iterator();
for (FeedItem i = it.next(); it.hasNext(); i = it.next()) {
if (i.getId() == id) {
for (FeedItem item : items) {
if (item.getId() == id) {
return true;
}
}
@ -63,8 +62,10 @@ public abstract class QueueAccess {
@Override
public boolean remove(long id) {
Iterator<FeedItem> it = items.iterator();
for (FeedItem i = it.next(); it.hasNext(); i = it.next()) {
if (i.getId() == id) {
FeedItem item;
while (it.hasNext()) {
item = it.next();
if (item.getId() == id) {
it.remove();
return true;
}

View File

@ -19,13 +19,12 @@ public final class URLChecker {
* */
public static String prepareURL(String url) {
StringBuilder builder = new StringBuilder();
url = url.trim();
if (!url.startsWith("http")) {
if (url.startsWith("feed://")) {
if (AppConfig.DEBUG) Log.d(TAG, "Replacing feed:// with http://");
url = url.replace("feed://", "http://");
} else if (!(url.startsWith("http://") || url.startsWith("https://"))) {
if (AppConfig.DEBUG) Log.d(TAG, "Adding http:// at the beginning of the URL");
builder.append("http://");
if (AppConfig.DEBUG) Log.d(TAG, "Missing http; appending");
} else if (url.startsWith("https")) {
if (AppConfig.DEBUG) Log.d(TAG, "Replacing https with http");
url = url.replaceFirst("https", "http");
}
builder.append(url);

View File

@ -9,8 +9,7 @@ public class DownloadStatusComparator implements Comparator<DownloadStatus> {
@Override
public int compare(DownloadStatus lhs, DownloadStatus rhs) {
return -lhs.getCompletionDate().compareTo(rhs.getCompletionDate());
return rhs.getCompletionDate().compareTo(lhs.getCompletionDate());
}
}

View File

@ -13,7 +13,7 @@ public class FeedItemPubdateComparator implements Comparator<FeedItem> {
}*/
@Override
public int compare(FeedItem lhs, FeedItem rhs) {
return -lhs.getPubDate().compareTo(rhs.getPubDate());
return rhs.getPubDate().compareTo(lhs.getPubDate());
}
}

View File

@ -11,8 +11,8 @@ public class PlaybackCompletionDateComparator implements Comparator<FeedItem> {
&& lhs.getMedia().getPlaybackCompletionDate() != null
&& rhs.getMedia() != null
&& rhs.getMedia().getPlaybackCompletionDate() != null) {
return -lhs.getMedia().getPlaybackCompletionDate()
.compareTo(rhs.getMedia().getPlaybackCompletionDate());
return rhs.getMedia().getPlaybackCompletionDate()
.compareTo(lhs.getMedia().getPlaybackCompletionDate());
}
return 0;
}

View File

@ -51,10 +51,13 @@ public class FeedItemMenuHandler {
* parameter should be set to false if the menu space is limited.
* @param queueAccess
* Used for testing if the queue contains the selected item
* @return Always returns true
* @return Returns true if selectedItem is not null.
* */
public static boolean onPrepareMenu(MenuInterface mi,
FeedItem selectedItem, boolean showExtendedMenu, QueueAccess queueAccess) {
if (selectedItem == null) {
return false;
}
DownloadRequester requester = DownloadRequester.getInstance();
boolean hasMedia = selectedItem.getMedia() != null;
boolean downloaded = hasMedia && selectedItem.getMedia().isDownloaded();

View File

@ -30,6 +30,10 @@ public class FeedMenuHandler {
}
public static boolean onPrepareOptionsMenu(Menu menu, Feed selectedFeed) {
if (selectedFeed == null) {
return false;
}
if (AppConfig.DEBUG)
Log.d(TAG, "Preparing options menu");
menu.findItem(R.id.mark_all_read_item).setVisible(

View File

@ -0,0 +1,30 @@
package de.danoeh.antennapod.util.playback;
import android.content.Context;
import android.util.Log;
import android.view.SurfaceHolder;
import com.aocate.media.MediaPlayer;
public class AudioPlayer extends MediaPlayer implements IPlayer {
private static final String TAG = "AudioPlayer";
public AudioPlayer(Context context) {
super(context);
}
@Override
public void setScreenOnWhilePlaying(boolean screenOn) {
Log.e(TAG, "Setting screen on while playing not supported in Audio Player");
throw new UnsupportedOperationException("Setting screen on while playing not supported in Audio Player");
}
@Override
public void setDisplay(SurfaceHolder sh) {
if (sh != null) {
Log.e(TAG, "Setting display not supported in Audio Player");
throw new UnsupportedOperationException("Setting display not supported in Audio Player");
}
}
}

View File

@ -25,7 +25,6 @@ public class ExternalMedia implements Playable {
private String episodeTitle;
private String feedTitle;
private String shownotes;
private MediaType mediaType = MediaType.AUDIO;
private List<Chapter> chapters;
private int duration;
@ -80,8 +79,13 @@ public class ExternalMedia implements Playable {
.extractMetadata(MediaMetadataRetriever.METADATA_KEY_TITLE);
feedTitle = mmr
.extractMetadata(MediaMetadataRetriever.METADATA_KEY_ALBUM);
duration = Integer.parseInt(mmr
try {
duration = Integer.parseInt(mmr
.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION));
} catch (NumberFormatException e) {
e.printStackTrace();
throw new PlayableException("NumberFormatException when reading duration of media file");
}
ChapterUtils.loadChaptersFromFileUrl(this);
}

View File

@ -0,0 +1,64 @@
package de.danoeh.antennapod.util.playback;
import java.io.IOException;
import android.view.SurfaceHolder;
public interface IPlayer {
boolean canSetPitch();
boolean canSetSpeed();
float getCurrentPitchStepsAdjustment();
int getCurrentPosition();
float getCurrentSpeedMultiplier();
int getDuration();
float getMaxSpeedMultiplier();
float getMinSpeedMultiplier();
boolean isLooping();
boolean isPlaying();
void pause();
void prepare() throws IllegalStateException, IOException;
void prepareAsync();
void release();
void reset();
void seekTo(int msec);
void setAudioStreamType(int streamtype);
void setScreenOnWhilePlaying(boolean screenOn);
void setDataSource(String path) throws IllegalStateException, IOException,
IllegalArgumentException, SecurityException;
void setDisplay(SurfaceHolder sh);
void setEnableSpeedAdjustment(boolean enableSpeedAdjustment);
void setLooping(boolean looping);
void setPitchStepsAdjustment(float pitchSteps);
void setPlaybackPitch(float f);
void setPlaybackSpeed(float f);
void setVolume(float left, float right);
void start();
void stop();
}

View File

@ -342,6 +342,9 @@ public abstract class PlaybackController {
case PlaybackService.NOTIFICATION_TYPE_PLAYBACK_END:
onPlaybackEnd();
break;
case PlaybackService.NOTIFICATION_TYPE_PLAYBACK_SPEED_CHANGE:
onPlaybackSpeedChange();
break;
}
} else {
@ -369,6 +372,8 @@ public abstract class PlaybackController {
}
};
public abstract void onPlaybackSpeedChange();
public abstract void onShutdownNotification();
/**
@ -663,6 +668,24 @@ public abstract class PlaybackController {
return status;
}
public boolean canSetPlaybackSpeed() {
return playbackService != null && playbackService.canSetSpeed();
}
public void setPlaybackSpeed(float speed) {
if (playbackService != null) {
playbackService.setSpeed(speed);
}
}
public float getCurrentPlaybackSpeedMultiplier() {
if (canSetPlaybackSpeed()) {
return playbackService.getCurrentPlaybackSpeed();
} else {
return -1;
}
}
public boolean isPlayingVideo() {
if (playbackService != null) {
return PlaybackService.isPlayingVideo();
@ -670,6 +693,7 @@ public abstract class PlaybackController {
return false;
}
/**
* Returns true if PlaybackController can communicate with the playback
* service.

View File

@ -0,0 +1,62 @@
package de.danoeh.antennapod.util.playback;
import android.media.MediaPlayer;
import android.util.Log;
public class VideoPlayer extends MediaPlayer implements IPlayer {
private static final String TAG = "VideoPlayer";
@Override
public boolean canSetPitch() {
return false;
}
@Override
public boolean canSetSpeed() {
return false;
}
@Override
public float getCurrentPitchStepsAdjustment() {
return 1;
}
@Override
public float getCurrentSpeedMultiplier() {
return 1;
}
@Override
public float getMaxSpeedMultiplier() {
return 1;
}
@Override
public float getMinSpeedMultiplier() {
return 1;
}
@Override
public void setEnableSpeedAdjustment(boolean enableSpeedAdjustment) throws UnsupportedOperationException {
Log.e(TAG, "Setting enable speed adjustment unsupported in video player");
throw new UnsupportedOperationException("Setting enable speed adjustment unsupported in video player");
}
@Override
public void setPitchStepsAdjustment(float pitchSteps) {
Log.e(TAG, "Setting pitch steps adjustment unsupported in video player");
throw new UnsupportedOperationException("Setting pitch steps adjustment unsupported in video player");
}
@Override
public void setPlaybackPitch(float f) {
Log.e(TAG, "Setting playback pitch unsupported in video player");
throw new UnsupportedOperationException("Setting playback pitch unsupported in video player");
}
@Override
public void setPlaybackSpeed(float f) {
Log.e(TAG, "Setting playback speed unsupported in video player");
throw new UnsupportedOperationException("Setting playback speed unsupported in video player");
}
}

View File

@ -2,18 +2,21 @@ package instrumentationTest.de.test.antennapod.service.download;
import java.io.File;
import android.test.InstrumentationTestCase;
import de.danoeh.antennapod.feed.FeedFile;
import de.danoeh.antennapod.service.download.*;
import android.test.AndroidTestCase;
import android.util.Log;
public class HttpDownloaderTest extends AndroidTestCase {
public class HttpDownloaderTest extends InstrumentationTestCase {
private static final String TAG = "HttpDownloaderTest";
private static final String DOWNLOAD_DIR = "testdownloads";
private static boolean successful = true;
private File destDir;
public HttpDownloaderTest() {
super();
}
@ -21,17 +24,23 @@ public class HttpDownloaderTest extends AndroidTestCase {
@Override
protected void tearDown() throws Exception {
super.tearDown();
File externalDir = getContext().getExternalFilesDir(DOWNLOAD_DIR);
assertNotNull(externalDir);
File[] contents = externalDir.listFiles();
File[] contents = destDir.listFiles();
for (File f : contents) {
assertTrue(f.delete());
}
}
@Override
protected void setUp() throws Exception {
super.setUp();
destDir = getInstrumentation().getTargetContext().getExternalFilesDir(DOWNLOAD_DIR);
assertNotNull(destDir);
assertTrue(destDir.exists());
}
private FeedFileImpl setupFeedFile(String downloadUrl, String title) {
FeedFileImpl feedfile = new FeedFileImpl(downloadUrl);
String fileUrl = new File(getContext().getExternalFilesDir(DOWNLOAD_DIR).getAbsolutePath(), title).getAbsolutePath();
String fileUrl = new File(destDir, title).getAbsolutePath();
File file = new File(fileUrl);
Log.d(TAG, "Deleting file: " + file.delete());
feedfile.setFile_url(fileUrl);
@ -47,42 +56,16 @@ public class HttpDownloaderTest extends AndroidTestCase {
assertNotNull(status);
assertTrue(status.isSuccessful() == expectedResult);
assertTrue(status.isDone());
assertTrue(new File(feedFile.getFile_url()).exists());
// the file should not exist if the download has failed
assertTrue(new File(feedFile.getFile_url()).exists() == expectedResult);
}
public void testRandomUrls() {
final String[] urls = {
"http://radiobox.omroep.nl/programme/read_programme_podcast/9168/read.rss",
"http://content.zdf.de/podcast/zdf_heute/heute_a.xml",
"http://rss.sciam.com/sciam/60secsciencepodcast",
"http://rss.sciam.com/sciam/60-second-mind",
"http://rss.sciam.com/sciam/60-second-space",
"http://rss.sciam.com/sciam/60-second-health",
"http://rss.sciam.com/sciam/60-second-tech",
"http://risky.biz/feeds/risky-business",
"http://risky.biz/feeds/rb2",
"http://podcast.hr-online.de/lateline/podcast.xml",
"http://bitlove.org/nsemak/mikrodilettanten/feed",
"http://bitlove.org/moepmoeporg/riotburnz/feed",
"http://bitlove.org/moepmoeporg/schachcast/feed",
"http://bitlove.org/moepmoeporg/sundaymoaning/feed",
"http://bitlove.org/motofunk/anekdotkast/feed",
"http://bitlove.org/motofunk/motofunk/feed",
"http://bitlove.org/nerdinand/zch/feed",
"http://podcast.homerj.de/podcasts.xml",
"http://www.dradio.de/rss/podcast/sendungen/wissenschaftundbildung/",
"http://www.dradio.de/rss/podcast/sendungen/wirtschaftundverbraucher/",
"http://www.dradio.de/rss/podcast/sendungen/literatur/",
"http://www.dradio.de/rss/podcast/sendungen/sport/",
"http://www.dradio.de/rss/podcast/sendungen/wirtschaftundgesellschaft/",
"http://www.dradio.de/rss/podcast/sendungen/filmederwoche/",
"http://www.blacksweetstories.com/feed/podcast/",
"http://feeds.5by5.tv/buildanalyze",
"http://bitlove.org/ranzzeit/ranz/feed"
};
for (int i = 0; i < urls.length; i++) {
download(urls[i], Integer.toString(i), true);
}
public void testPassingHttp() {
download("http://httpbin.org/status/200", "test200", true);
}
public void testPassingHttps() {
download("https://httpbin.org/status/200", "test200", true);
}
public void testRedirect() {
@ -111,6 +94,7 @@ public class HttpDownloaderTest extends AndroidTestCase {
downloader.call();
}
};
t.start();
downloader.cancel();
try {
t.join();

View File

@ -1,9 +1,252 @@
package instrumentationTest.de.test.antennapod.storage;
import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.test.InstrumentationTestCase;
import de.danoeh.antennapod.feed.Feed;
import de.danoeh.antennapod.feed.FeedItem;
import de.danoeh.antennapod.feed.FeedMedia;
import de.danoeh.antennapod.preferences.UserPreferences;
import de.danoeh.antennapod.storage.DBReader;
import de.danoeh.antennapod.storage.DBTasks;
import de.danoeh.antennapod.storage.PodDBAdapter;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
/**
* Test class for DBTasks
*/
public class DBTasksTest extends InstrumentationTestCase {
private static final String TEST_FOLDER = "testDBTasks";
private static final int EPISODE_CACHE_SIZE = 5;
private File destFolder;
@Override
protected void tearDown() throws Exception {
super.tearDown();
final Context context = getInstrumentation().getTargetContext();
assertTrue(PodDBAdapter.deleteDatabase(context));
for (File f : destFolder.listFiles()) {
assertTrue(f.delete());
}
assertTrue(destFolder.delete());
}
@Override
protected void setUp() throws Exception {
super.setUp();
destFolder = getInstrumentation().getTargetContext().getExternalFilesDir(TEST_FOLDER);
assertNotNull(destFolder);
assertTrue(destFolder.exists());
assertTrue(destFolder.canWrite());
final Context context = getInstrumentation().getTargetContext();
context.deleteDatabase(PodDBAdapter.DATABASE_NAME);
// make sure database is created
PodDBAdapter adapter = new PodDBAdapter(context);
adapter.open();
adapter.close();
SharedPreferences.Editor prefEdit = PreferenceManager.getDefaultSharedPreferences(getInstrumentation().getTargetContext().getApplicationContext()).edit();
prefEdit.putString(UserPreferences.PREF_EPISODE_CACHE_SIZE, Integer.toString(EPISODE_CACHE_SIZE));
prefEdit.commit();
}
public void testPerformAutoCleanupShouldDelete() throws IOException {
final int NUM_ITEMS = EPISODE_CACHE_SIZE * 2;
Feed feed = new Feed("url", new Date(), "title");
List<FeedItem> items = new ArrayList<FeedItem>();
feed.setItems(items);
List<File> files = new ArrayList<File>();
for (int i = 0; i < NUM_ITEMS; i++) {
FeedItem item = new FeedItem(0, "title", "id", "link", new Date(), true, feed);
File f = new File(destFolder, "file " + i);
assertTrue(f.createNewFile());
files.add(f);
item.setMedia(new FeedMedia(0, item, 1, 0, 1L, "m", f.getAbsolutePath(), "url", true, new Date(NUM_ITEMS - i)));
items.add(item);
}
PodDBAdapter adapter = new PodDBAdapter(getInstrumentation().getTargetContext());
adapter.open();
adapter.setCompleteFeed(feed);
adapter.close();
assertTrue(feed.getId() != 0);
for (FeedItem item : items) {
assertTrue(item.getId() != 0);
assertTrue(item.getMedia().getId() != 0);
}
DBTasks.performAutoCleanup(getInstrumentation().getTargetContext());
for (int i = 0; i < files.size(); i++) {
if (i < EPISODE_CACHE_SIZE) {
assertTrue(files.get(i).exists());
} else {
assertFalse(files.get(i).exists());
}
}
}
public void testPerformAutoCleanupShouldNotDeleteBecauseUnread() throws IOException {
final int NUM_ITEMS = EPISODE_CACHE_SIZE * 2;
Feed feed = new Feed("url", new Date(), "title");
List<FeedItem> items = new ArrayList<FeedItem>();
feed.setItems(items);
List<File> files = new ArrayList<File>();
for (int i = 0; i < NUM_ITEMS; i++) {
FeedItem item = new FeedItem(0, "title", "id", "link", new Date(), false, feed);
File f = new File(destFolder, "file " + i);
assertTrue(f.createNewFile());
assertTrue(f.exists());
files.add(f);
item.setMedia(new FeedMedia(0, item, 1, 0, 1L, "m", f.getAbsolutePath(), "url", true, new Date(NUM_ITEMS - i)));
items.add(item);
}
PodDBAdapter adapter = new PodDBAdapter(getInstrumentation().getTargetContext());
adapter.open();
adapter.setCompleteFeed(feed);
adapter.close();
assertTrue(feed.getId() != 0);
for (FeedItem item : items) {
assertTrue(item.getId() != 0);
assertTrue(item.getMedia().getId() != 0);
}
DBTasks.performAutoCleanup(getInstrumentation().getTargetContext());
for (File file : files) {
assertTrue(file.exists());
}
}
public void testPerformAutoCleanupShouldNotDeleteBecauseInQueue() throws IOException {
final int NUM_ITEMS = EPISODE_CACHE_SIZE * 2;
Feed feed = new Feed("url", new Date(), "title");
List<FeedItem> items = new ArrayList<FeedItem>();
feed.setItems(items);
List<File> files = new ArrayList<File>();
for (int i = 0; i < NUM_ITEMS; i++) {
FeedItem item = new FeedItem(0, "title", "id", "link", new Date(), true, feed);
File f = new File(destFolder, "file " + i);
assertTrue(f.createNewFile());
assertTrue(f.exists());
files.add(f);
item.setMedia(new FeedMedia(0, item, 1, 0, 1L, "m", f.getAbsolutePath(), "url", true, new Date(NUM_ITEMS - i)));
items.add(item);
}
PodDBAdapter adapter = new PodDBAdapter(getInstrumentation().getTargetContext());
adapter.open();
adapter.setCompleteFeed(feed);
adapter.setQueue(items);
adapter.close();
assertTrue(feed.getId() != 0);
for (FeedItem item : items) {
assertTrue(item.getId() != 0);
assertTrue(item.getMedia().getId() != 0);
}
DBTasks.performAutoCleanup(getInstrumentation().getTargetContext());
for (File file : files) {
assertTrue(file.exists());
}
}
public void testUpdateFeedNewFeed() {
final Context context = getInstrumentation().getTargetContext();
final int NUM_ITEMS = 10;
Feed feed = new Feed("url", new Date(), "title");
feed.setItems(new ArrayList<FeedItem>());
for (int i = 0; i < NUM_ITEMS; i++) {
feed.getItems().add(new FeedItem(0, "item " + i, "id " + i, "link " + i, new Date(), false, feed));
}
Feed newFeed = DBTasks.updateFeed(context, feed);
assertTrue(newFeed == feed);
assertTrue(feed.getId() != 0);
for (FeedItem item : feed.getItems()) {
assertFalse(item.isRead());
assertTrue(item.getId() != 0);
}
}
public void testUpdateFeedUpdatedFeed() {
final Context context = getInstrumentation().getTargetContext();
final int NUM_ITEMS_OLD = 10;
final int NUM_ITEMS_NEW = 10;
final Feed feed = new Feed("url", new Date(), "title");
feed.setItems(new ArrayList<FeedItem>());
for (int i = 0; i < NUM_ITEMS_OLD; i++) {
feed.getItems().add(new FeedItem(0, "item " + i, "id " + i, "link " + i, new Date(i), true, feed));
}
PodDBAdapter adapter = new PodDBAdapter(context);
adapter.open();
adapter.setCompleteFeed(feed);
adapter.close();
// ensure that objects have been saved in db, then reset
assertTrue(feed.getId() != 0);
final long feedID = feed.getId();
feed.setId(0);
List<Long> itemIDs = new ArrayList<Long>();
for (FeedItem item : feed.getItems()) {
assertTrue(item.getId() != 0);
itemIDs.add(item.getId());
item.setId(0);
}
for (int i = NUM_ITEMS_OLD; i < NUM_ITEMS_NEW + NUM_ITEMS_OLD; i++) {
feed.getItems().add(0, new FeedItem(0, "item " + i, "id " + i, "link " + i, new Date(i), true, feed));
}
final Feed newFeed = DBTasks.updateFeed(context, feed);
assertTrue(feed != newFeed);
updatedFeedTest(newFeed, feedID, itemIDs, NUM_ITEMS_OLD, NUM_ITEMS_NEW);
final Feed feedFromDB = DBReader.getFeed(context, newFeed.getId());
assertNotNull(feedFromDB);
assertTrue(feedFromDB.getId() == newFeed.getId());
updatedFeedTest(feedFromDB, feedID, itemIDs, NUM_ITEMS_OLD, NUM_ITEMS_NEW);
}
private void updatedFeedTest(final Feed newFeed, long feedID, List<Long> itemIDs, final int NUM_ITEMS_OLD, final int NUM_ITEMS_NEW) {
assertTrue(newFeed.getId() == feedID);
assertTrue(newFeed.getItems().size() == NUM_ITEMS_NEW + NUM_ITEMS_OLD);
Collections.reverse(newFeed.getItems());
Date lastDate = new Date(0);
for (int i = 0; i < NUM_ITEMS_OLD; i++) {
FeedItem item = newFeed.getItems().get(i);
assertTrue(item.getFeed() == newFeed);
assertTrue(item.getId() == itemIDs.get(i));
assertTrue(item.isRead());
assertTrue(item.getPubDate().getTime() >= lastDate.getTime());
lastDate = item.getPubDate();
}
for (int i = NUM_ITEMS_OLD; i < NUM_ITEMS_NEW + NUM_ITEMS_OLD; i++) {
FeedItem item = newFeed.getItems().get(i);
assertTrue(item.getFeed() == newFeed);
assertTrue(item.getId() != 0);
assertFalse(item.isRead());
assertTrue(item.getPubDate().getTime() >= lastDate.getTime());
lastDate = item.getPubDate();
}
}
}

View File

@ -3,6 +3,7 @@ package instrumentationTest.de.test.antennapod.storage;
import android.content.Context;
import android.database.Cursor;
import android.test.InstrumentationTestCase;
import android.util.Log;
import de.danoeh.antennapod.feed.Feed;
import de.danoeh.antennapod.feed.FeedImage;
import de.danoeh.antennapod.feed.FeedItem;
@ -14,10 +15,10 @@ import de.danoeh.antennapod.storage.PodDBAdapter;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
@ -25,13 +26,15 @@ import java.util.concurrent.TimeoutException;
* Test class for DBWriter
*/
public class DBWriterTest extends InstrumentationTestCase {
private static final String TAG = "DBWriterTest";
private static final String TEST_FOLDER = "testDBWriter";
private static final long TIMEOUT = 5L;
@Override
protected void tearDown() throws Exception {
super.tearDown();
final Context context = getInstrumentation().getTargetContext();
context.deleteDatabase(PodDBAdapter.DATABASE_NAME);
assertTrue(PodDBAdapter.deleteDatabase(getInstrumentation().getTargetContext()));
File testDir = context.getExternalFilesDir(TEST_FOLDER);
assertNotNull(testDir);
@ -59,10 +62,7 @@ public class DBWriterTest extends InstrumentationTestCase {
Feed feed = new Feed("url", new Date(), "title");
List<FeedItem> items = new ArrayList<FeedItem>();
feed.setItems(items);
FeedItem item = new FeedItem();
item.setTitle("title");
item.setPubDate(new Date());
item.setFeed(feed);
FeedItem item = new FeedItem(0, "Item", "Item", "url", new Date(), true, feed);
FeedMedia media = new FeedMedia(0, item, 1, 1, 1, "mime_type", dest.getAbsolutePath(), "download_url", true, null);
item.setMedia(media);
@ -84,42 +84,11 @@ public class DBWriterTest extends InstrumentationTestCase {
assertNull(media.getFile_url());
}
public void testDeleteFeedMediaOfItemFileDoesNotExists() throws IOException, ExecutionException, InterruptedException {
File dest = new File(getInstrumentation().getTargetContext().getExternalFilesDir(TEST_FOLDER), "testFile");
Feed feed = new Feed("url", new Date(), "title");
List<FeedItem> items = new ArrayList<FeedItem>();
feed.setItems(items);
FeedItem item = new FeedItem();
item.setTitle("title");
item.setPubDate(new Date());
item.setFeed(feed);
FeedMedia media = new FeedMedia(0, item, 1, 1, 1, "mime_type", dest.getAbsolutePath(), "download_url", false, null);
item.setMedia(media);
items.add(item);
PodDBAdapter adapter = new PodDBAdapter(getInstrumentation().getTargetContext());
adapter.open();
adapter.setCompleteFeed(feed);
adapter.close();
assertTrue(media.getId() != 0);
assertTrue(item.getId() != 0);
DBWriter.deleteFeedMediaOfItem(getInstrumentation().getTargetContext(), media.getId()).get();
media = DBReader.getFeedMedia(getInstrumentation().getTargetContext(), media.getId());
assertNotNull(media);
assertFalse(dest.exists());
assertFalse(media.isDownloaded());
assertNull(media.getFile_url());
}
public void testDeleteFeed() throws IOException, ExecutionException, InterruptedException, TimeoutException {
File destFolder = getInstrumentation().getTargetContext().getExternalFilesDir(TEST_FOLDER);
assertNotNull(destFolder);
Feed feed = new Feed ("url", new Date(), "title");
Feed feed = new Feed("url", new Date(), "title");
feed.setItems(new ArrayList<FeedItem>());
// create Feed image
@ -132,10 +101,7 @@ public class DBWriterTest extends InstrumentationTestCase {
List<File> itemFiles = new ArrayList<File>();
// create items with downloaded media files
for (int i = 0; i < 10; i++) {
FeedItem item = new FeedItem();
item.setTitle("Item " + i);
item.setPubDate(new Date(System.currentTimeMillis()));
item.setFeed(feed);
FeedItem item = new FeedItem(0, "Item " + i, "Item" + i, "url", new Date(), true, feed);
feed.getItems().add(item);
File enc = new File(destFolder, "file " + i);
@ -158,7 +124,7 @@ public class DBWriterTest extends InstrumentationTestCase {
assertTrue(item.getMedia().getId() != 0);
}
DBWriter.deleteFeed(getInstrumentation().getTargetContext(), feed.getId()).get(5, TimeUnit.SECONDS);
DBWriter.deleteFeed(getInstrumentation().getTargetContext(), feed.getId()).get(TIMEOUT, TimeUnit.SECONDS);
// check if files still exist
assertFalse(imgFile.exists());
@ -188,7 +154,7 @@ public class DBWriterTest extends InstrumentationTestCase {
File destFolder = getInstrumentation().getTargetContext().getExternalFilesDir(TEST_FOLDER);
assertNotNull(destFolder);
Feed feed = new Feed ("url", new Date(), "title");
Feed feed = new Feed("url", new Date(), "title");
feed.setItems(new ArrayList<FeedItem>());
feed.setImage(null);
@ -196,16 +162,13 @@ public class DBWriterTest extends InstrumentationTestCase {
List<File> itemFiles = new ArrayList<File>();
// create items with downloaded media files
for (int i = 0; i < 10; i++) {
FeedItem item = new FeedItem();
item.setTitle("Item " + i);
item.setPubDate(new Date(System.currentTimeMillis()));
item.setFeed(feed);
FeedItem item = new FeedItem(0, "Item " + i, "Item" + i, "url", new Date(), true, feed);
feed.getItems().add(item);
File enc = new File(destFolder, "file " + i);
assertTrue(enc.createNewFile());
itemFiles.add(enc);
itemFiles.add(enc);
FeedMedia media = new FeedMedia(0, item, 1, 1, 1, "mime_type", enc.getAbsolutePath(), "download_url", true, null);
item.setMedia(media);
}
@ -221,7 +184,7 @@ public class DBWriterTest extends InstrumentationTestCase {
assertTrue(item.getMedia().getId() != 0);
}
DBWriter.deleteFeed(getInstrumentation().getTargetContext(), feed.getId()).get(5, TimeUnit.SECONDS);
DBWriter.deleteFeed(getInstrumentation().getTargetContext(), feed.getId()).get(TIMEOUT, TimeUnit.SECONDS);
// check if files still exist
for (File f : itemFiles) {
@ -247,7 +210,7 @@ public class DBWriterTest extends InstrumentationTestCase {
File destFolder = getInstrumentation().getTargetContext().getExternalFilesDir(TEST_FOLDER);
assertNotNull(destFolder);
Feed feed = new Feed ("url", new Date(), "title");
Feed feed = new Feed("url", new Date(), "title");
feed.setItems(null);
// create Feed image
@ -265,7 +228,7 @@ public class DBWriterTest extends InstrumentationTestCase {
assertTrue(feed.getId() != 0);
assertTrue(feed.getImage().getId() != 0);
DBWriter.deleteFeed(getInstrumentation().getTargetContext(), feed.getId()).get(5, TimeUnit.SECONDS);
DBWriter.deleteFeed(getInstrumentation().getTargetContext(), feed.getId()).get(TIMEOUT, TimeUnit.SECONDS);
// check if files still exist
assertFalse(imgFile.exists());
@ -284,7 +247,7 @@ public class DBWriterTest extends InstrumentationTestCase {
File destFolder = getInstrumentation().getTargetContext().getExternalFilesDir(TEST_FOLDER);
assertNotNull(destFolder);
Feed feed = new Feed ("url", new Date(), "title");
Feed feed = new Feed("url", new Date(), "title");
feed.setItems(new ArrayList<FeedItem>());
// create Feed image
@ -296,10 +259,7 @@ public class DBWriterTest extends InstrumentationTestCase {
// create items
for (int i = 0; i < 10; i++) {
FeedItem item = new FeedItem();
item.setTitle("Item " + i);
item.setPubDate(new Date(System.currentTimeMillis()));
item.setFeed(feed);
FeedItem item = new FeedItem(0, "Item " + i, "Item" + i, "url", new Date(), true, feed);
feed.getItems().add(item);
}
@ -315,7 +275,7 @@ public class DBWriterTest extends InstrumentationTestCase {
assertTrue(item.getId() != 0);
}
DBWriter.deleteFeed(getInstrumentation().getTargetContext(), feed.getId()).get(5, TimeUnit.SECONDS);
DBWriter.deleteFeed(getInstrumentation().getTargetContext(), feed.getId()).get(TIMEOUT, TimeUnit.SECONDS);
// check if files still exist
assertFalse(imgFile.exists());
@ -339,7 +299,7 @@ public class DBWriterTest extends InstrumentationTestCase {
File destFolder = getInstrumentation().getTargetContext().getExternalFilesDir(TEST_FOLDER);
assertNotNull(destFolder);
Feed feed = new Feed ("url", new Date(), "title");
Feed feed = new Feed("url", new Date(), "title");
feed.setItems(new ArrayList<FeedItem>());
// create Feed image
@ -351,10 +311,7 @@ public class DBWriterTest extends InstrumentationTestCase {
List<File> itemFiles = new ArrayList<File>();
// create items with downloaded media files
for (int i = 0; i < 10; i++) {
FeedItem item = new FeedItem();
item.setTitle("Item " + i);
item.setPubDate(new Date(System.currentTimeMillis()));
item.setFeed(feed);
FeedItem item = new FeedItem(0, "Item " + i, "Item" + i, "url", new Date(), true, feed);
feed.getItems().add(item);
File enc = new File(destFolder, "file " + i);
@ -387,7 +344,7 @@ public class DBWriterTest extends InstrumentationTestCase {
queueCursor.close();
adapter.close();
DBWriter.deleteFeed(getInstrumentation().getTargetContext(), feed.getId()).get(5, TimeUnit.SECONDS);
DBWriter.deleteFeed(getInstrumentation().getTargetContext(), feed.getId()).get(TIMEOUT, TimeUnit.SECONDS);
adapter.open();
Cursor c = adapter.getFeedCursor(feed.getId());
@ -414,7 +371,7 @@ public class DBWriterTest extends InstrumentationTestCase {
File destFolder = getInstrumentation().getTargetContext().getExternalFilesDir(TEST_FOLDER);
assertNotNull(destFolder);
Feed feed = new Feed ("url", new Date(), "title");
Feed feed = new Feed("url", new Date(), "title");
feed.setItems(new ArrayList<FeedItem>());
// create Feed image
@ -426,10 +383,7 @@ public class DBWriterTest extends InstrumentationTestCase {
List<File> itemFiles = new ArrayList<File>();
// create items with downloaded media files
for (int i = 0; i < 10; i++) {
FeedItem item = new FeedItem();
item.setTitle("Item " + i);
item.setPubDate(new Date(System.currentTimeMillis()));
item.setFeed(feed);
FeedItem item = new FeedItem(0, "Item " + i, "Item" + i, "url", new Date(), true, feed);
feed.getItems().add(item);
File enc = new File(destFolder, "file " + i);
@ -451,7 +405,7 @@ public class DBWriterTest extends InstrumentationTestCase {
assertTrue(item.getMedia().getId() != 0);
}
DBWriter.deleteFeed(getInstrumentation().getTargetContext(), feed.getId()).get(5, TimeUnit.SECONDS);
DBWriter.deleteFeed(getInstrumentation().getTargetContext(), feed.getId()).get(TIMEOUT, TimeUnit.SECONDS);
adapter = new PodDBAdapter(getInstrumentation().getContext());
adapter.open();
@ -470,4 +424,261 @@ public class DBWriterTest extends InstrumentationTestCase {
c.close();
}
}
private FeedMedia playbackHistorySetup(Date playbackCompletionDate) {
final Context context = getInstrumentation().getTargetContext();
Feed feed = new Feed("url", new Date(), "title");
feed.setItems(new ArrayList<FeedItem>());
FeedItem item = new FeedItem(0, "title", "id", "link", new Date(), true, feed);
FeedMedia media = new FeedMedia(0, item, 10, 0, 1, "mime", null, "url", false, playbackCompletionDate);
feed.getItems().add(item);
item.setMedia(media);
PodDBAdapter adapter = new PodDBAdapter(context);
adapter.open();
adapter.setCompleteFeed(feed);
adapter.close();
assertTrue(media.getId() != 0);
return media;
}
public void testAddItemToPlaybackHistoryNotPlayedYet() throws ExecutionException, InterruptedException {
final Context context = getInstrumentation().getTargetContext();
FeedMedia media = playbackHistorySetup(null);
DBWriter.addItemToPlaybackHistory(context, media).get();
PodDBAdapter adapter = new PodDBAdapter(context);
adapter.open();
media = DBReader.getFeedMedia(context, media.getId());
adapter.close();
assertNotNull(media);
assertNotNull(media.getPlaybackCompletionDate());
}
public void testAddItemToPlaybackHistoryAlreadyPlayed() throws ExecutionException, InterruptedException {
final long OLD_DATE = 0;
final Context context = getInstrumentation().getTargetContext();
FeedMedia media = playbackHistorySetup(new Date(OLD_DATE));
DBWriter.addItemToPlaybackHistory(getInstrumentation().getTargetContext(), media).get();
PodDBAdapter adapter = new PodDBAdapter(context);
adapter.open();
media = DBReader.getFeedMedia(context, media.getId());
adapter.close();
assertNotNull(media);
assertNotNull(media.getPlaybackCompletionDate());
assertFalse(OLD_DATE == media.getPlaybackCompletionDate().getTime());
}
private Feed queueTestSetupMultipleItems(final int NUM_ITEMS) throws InterruptedException, ExecutionException, TimeoutException {
final Context context = getInstrumentation().getTargetContext();
Feed feed = new Feed("url", new Date(), "title");
feed.setItems(new ArrayList<FeedItem>());
for (int i = 0; i < NUM_ITEMS; i++) {
FeedItem item = new FeedItem(0, "title " + i, "id " + i, "link " + i, new Date(), true, feed);
feed.getItems().add(item);
}
PodDBAdapter adapter = new PodDBAdapter(context);
adapter.open();
adapter.setCompleteFeed(feed);
adapter.close();
for (FeedItem item : feed.getItems()) {
assertTrue(item.getId() != 0);
}
List<Future<?>> futures = new ArrayList<Future<?>>();
for (FeedItem item : feed.getItems()) {
futures.add(DBWriter.addQueueItem(context, item.getId()));
}
for (Future<?> f : futures) {
f.get(TIMEOUT, TimeUnit.SECONDS);
}
return feed;
}
public void testAddQueueItemSingleItem() throws InterruptedException, ExecutionException, TimeoutException {
final Context context = getInstrumentation().getTargetContext();
Feed feed = new Feed("url", new Date(), "title");
feed.setItems(new ArrayList<FeedItem>());
FeedItem item = new FeedItem(0, "title", "id", "link", new Date(), true, feed);
feed.getItems().add(item);
PodDBAdapter adapter = new PodDBAdapter(context);
adapter.open();
adapter.setCompleteFeed(feed);
adapter.close();
assertTrue(item.getId() != 0);
DBWriter.addQueueItem(context, item.getId()).get(TIMEOUT, TimeUnit.SECONDS);
adapter = new PodDBAdapter(context);
adapter.open();
Cursor cursor = adapter.getQueueIDCursor();
assertTrue(cursor.moveToFirst());
assertTrue(cursor.getLong(0) == item.getId());
cursor.close();
adapter.close();
}
public void testAddQueueItemSingleItemAlreadyInQueue() throws InterruptedException, ExecutionException, TimeoutException {
final Context context = getInstrumentation().getTargetContext();
Feed feed = new Feed("url", new Date(), "title");
feed.setItems(new ArrayList<FeedItem>());
FeedItem item = new FeedItem(0, "title", "id", "link", new Date(), true, feed);
feed.getItems().add(item);
PodDBAdapter adapter = new PodDBAdapter(context);
adapter.open();
adapter.setCompleteFeed(feed);
adapter.close();
assertTrue(item.getId() != 0);
DBWriter.addQueueItem(context, item.getId()).get(TIMEOUT, TimeUnit.SECONDS);
adapter = new PodDBAdapter(context);
adapter.open();
Cursor cursor = adapter.getQueueIDCursor();
assertTrue(cursor.moveToFirst());
assertTrue(cursor.getLong(0) == item.getId());
cursor.close();
adapter.close();
DBWriter.addQueueItem(context, item.getId()).get(TIMEOUT, TimeUnit.SECONDS);
adapter = new PodDBAdapter(context);
adapter.open();
cursor = adapter.getQueueIDCursor();
assertTrue(cursor.moveToFirst());
assertTrue(cursor.getLong(0) == item.getId());
assertTrue(cursor.getCount() == 1);
cursor.close();
adapter.close();
}
public void testAddQueueItemMultipleItems() throws InterruptedException, ExecutionException, TimeoutException {
final Context context = getInstrumentation().getTargetContext();
final int NUM_ITEMS = 10;
Feed feed = queueTestSetupMultipleItems(NUM_ITEMS);
PodDBAdapter adapter = new PodDBAdapter(context);
adapter.open();
Cursor cursor = adapter.getQueueIDCursor();
assertTrue(cursor.moveToFirst());
assertTrue(cursor.getCount() == NUM_ITEMS);
for (int i = 0; i < NUM_ITEMS; i++) {
assertTrue(cursor.moveToPosition(i));
assertTrue(cursor.getLong(0) == feed.getItems().get(i).getId());
}
cursor.close();
adapter.close();
}
public void testClearQueue() throws InterruptedException, ExecutionException, TimeoutException {
final Context context = getInstrumentation().getTargetContext();
final int NUM_ITEMS = 10;
Feed feed = queueTestSetupMultipleItems(NUM_ITEMS);
DBWriter.clearQueue(context).get(TIMEOUT, TimeUnit.SECONDS);
PodDBAdapter adapter = new PodDBAdapter(context);
adapter.open();
Cursor cursor = adapter.getQueueIDCursor();
assertFalse(cursor.moveToFirst());
cursor.close();
adapter.close();
}
public void testRemoveQueueItem() throws InterruptedException, ExecutionException, TimeoutException {
final int NUM_ITEMS = 10;
final Context context = getInstrumentation().getTargetContext();
Feed feed = new Feed("url", new Date(), "title");
feed.setItems(new ArrayList<FeedItem>());
for (int i = 0; i < NUM_ITEMS; i++) {
FeedItem item = new FeedItem(0, "title " + i, "id " + i, "link " + i, new Date(), true, feed);
feed.getItems().add(item);
}
PodDBAdapter adapter = new PodDBAdapter(context);
adapter.open();
adapter.setCompleteFeed(feed);
adapter.close();
for (FeedItem item : feed.getItems()) {
assertTrue(item.getId() != 0);
}
for (int removeIndex = 0; removeIndex < NUM_ITEMS; removeIndex++) {
final long id = feed.getItems().get(removeIndex).getId();
adapter = new PodDBAdapter(context);
adapter.open();
adapter.setQueue(feed.getItems());
adapter.close();
DBWriter.removeQueueItem(context, id, false).get(TIMEOUT, TimeUnit.SECONDS);
adapter = new PodDBAdapter(context);
adapter.open();
Cursor queue = adapter.getQueueIDCursor();
assertTrue(queue.getCount() == NUM_ITEMS - 1);
for (int i = 0; i < queue.getCount(); i++) {
assertTrue(queue.moveToPosition(i));
final long queueID = queue.getLong(0);
assertTrue(queueID != id); // removed item is no longer in queue
boolean idFound = false;
for (FeedItem item : feed.getItems()) { // items that were not removed are still in the queue
idFound = idFound | (item.getId() == queueID);
}
assertTrue(idFound);
}
queue.close();
adapter.close();
}
}
public void testMoveQueueItem() throws InterruptedException, ExecutionException, TimeoutException {
final int NUM_ITEMS = 10;
final Context context = getInstrumentation().getTargetContext();
Feed feed = new Feed("url", new Date(), "title");
feed.setItems(new ArrayList<FeedItem>());
for (int i = 0; i < NUM_ITEMS; i++) {
FeedItem item = new FeedItem(0, "title " + i, "id " + i, "link " + i, new Date(), true, feed);
feed.getItems().add(item);
}
PodDBAdapter adapter = new PodDBAdapter(context);
adapter.open();
adapter.setCompleteFeed(feed);
adapter.close();
for (FeedItem item : feed.getItems()) {
assertTrue(item.getId() != 0);
}
for (int from = 0; from < NUM_ITEMS; from++) {
for (int to = 0; to < NUM_ITEMS; to++) {
if (from == to) {
continue;
}
Log.d(TAG, String.format("testMoveQueueItem: From=%d, To=%d", from, to));
final long fromID = feed.getItems().get(from).getId();
adapter = new PodDBAdapter(context);
adapter.open();
adapter.setQueue(feed.getItems());
adapter.close();
DBWriter.moveQueueItem(context, from, to, false).get(TIMEOUT, TimeUnit.SECONDS);
adapter = new PodDBAdapter(context);
adapter.open();
Cursor queue = adapter.getQueueIDCursor();
assertTrue(queue.getCount() == NUM_ITEMS);
assertTrue(queue.moveToPosition(from));
assertFalse(queue.getLong(0) == fromID);
assertTrue(queue.moveToPosition(to));
assertTrue(queue.getLong(0) == fromID);
queue.close();
adapter.close();
}
}
}
}

View File

@ -124,7 +124,7 @@ public class FeedHandlerTest extends AndroidTestCase {
}
private boolean hasValidFeedItems(Feed feed) {
for (FeedItem item : feed.getItemsArray()) {
for (FeedItem item : feed.getItems()) {
if (item.getTitle() == null) {
Log.e(TAG, "Item has no title");
return false;