From 6407e680ddf503e9e1dad9ca94ad081febb1a4c7 Mon Sep 17 00:00:00 2001 From: daniel oeh Date: Fri, 30 May 2014 21:11:44 +0200 Subject: [PATCH 01/24] Added UI test classes --- build.gradle | 2 + .../test/antennapod/ui/MainActivityTest.java | 24 +++++ .../de/test/antennapod/ui/UITestUtils.java | 93 +++++++++++++++++++ 3 files changed, 119 insertions(+) create mode 100644 src/instrumentationTest/de/test/antennapod/ui/MainActivityTest.java create mode 100644 src/instrumentationTest/de/test/antennapod/ui/UITestUtils.java diff --git a/build.gradle b/build.gradle index 9046f2648..b1a2a46ca 100644 --- a/build.gradle +++ b/build.gradle @@ -28,6 +28,8 @@ dependencies { compile 'commons-io:commons-io:2.4' compile 'com.nineoldandroids:library:2.4.0' compile project(':submodules:dslv:library') + + compile 'com.jayway.android.robotium:robotium-solo:5.0.1' } android { diff --git a/src/instrumentationTest/de/test/antennapod/ui/MainActivityTest.java b/src/instrumentationTest/de/test/antennapod/ui/MainActivityTest.java new file mode 100644 index 000000000..5dc119a88 --- /dev/null +++ b/src/instrumentationTest/de/test/antennapod/ui/MainActivityTest.java @@ -0,0 +1,24 @@ +package instrumentationTest.de.test.antennapod.ui; + +import android.test.ActivityInstrumentationTestCase2; +import com.robotium.solo.Solo; +import de.danoeh.antennapod.activity.MainActivity; + +/** + * User interface tests for MainActivity + */ +public class MainActivityTest extends ActivityInstrumentationTestCase2 { + + private Solo solo; + + public MainActivityTest(Class activityClass) { + super(activityClass); + } + + @Override + protected void setUp() throws Exception { + solo = new Solo(getInstrumentation(), getActivity()); + } + + +} diff --git a/src/instrumentationTest/de/test/antennapod/ui/UITestUtils.java b/src/instrumentationTest/de/test/antennapod/ui/UITestUtils.java new file mode 100644 index 000000000..9fd5c4d77 --- /dev/null +++ b/src/instrumentationTest/de/test/antennapod/ui/UITestUtils.java @@ -0,0 +1,93 @@ +package instrumentationTest.de.test.antennapod.ui; + +import android.content.Context; +import android.graphics.Bitmap; +import de.danoeh.antennapod.feed.Feed; +import de.danoeh.antennapod.feed.FeedImage; +import de.danoeh.antennapod.feed.FeedItem; +import de.danoeh.antennapod.feed.FeedMedia; +import instrumentationTest.de.test.antennapod.util.service.download.HTTPBin; +import instrumentationTest.de.test.antennapod.util.syndication.feedgenerator.RSS2Generator; +import junit.framework.Assert; +import org.apache.commons.io.FileUtils; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +/** + * Utility methods for UI tests + */ +public class UITestUtils { + + private static final String DATA_FOLDER = "test/UITestUtils"; + + public static final int NUM_FEEDS = 10; + public static final int NUM_ITEMS_PER_FEED = 20; + + + private Context context; + private HTTPBin server; + private File destDir; + private File hostedFeedDir; + + public UITestUtils(Context context) { + this.context = context; + } + + + public void setup() throws IOException { + destDir = context.getExternalFilesDir(DATA_FOLDER); + destDir.mkdir(); + hostedFeedDir = new File(destDir, "hostedFeeds"); + hostedFeedDir.mkdir(); + Assert.assertTrue(destDir.exists()); + Assert.assertTrue(hostedFeedDir.exists()); + server.start(); + } + + public void tearDown() throws IOException { + FileUtils.deleteDirectory(destDir); + server.stop(); + } + + private String hostFeed(Feed feed) throws IOException { + File feedFile = new File(hostedFeedDir, feed.getTitle()); + FileOutputStream out = new FileOutputStream(feedFile); + RSS2Generator generator = new RSS2Generator(); + generator.writeFeed(feed, out, "UTF-8", 0); + out.close(); + int id = server.serveFile(feedFile); + Assert.assertTrue(id != -1); + return String.format("http://127.0.0.1/files/%d", id); + } + + private File newBitmapFile(String name) throws IOException { + File imgFile = new File(destDir, name); + Bitmap bitmap = Bitmap.createBitmap(128, 128, Bitmap.Config.ARGB_8888); + FileOutputStream out = new FileOutputStream(imgFile); + bitmap.compress(Bitmap.CompressFormat.PNG, 1, out); + out.close(); + return imgFile; + } + + public void addFeedData() throws IOException { + for (int i = 0; i < NUM_FEEDS; i++) { + FeedImage image = new FeedImage(0, "image " + i, newBitmapFile("image" + i).getAbsolutePath(), "http://example.com/feed" + i + "/image", true); + Feed feed = new Feed(0, new Date(), "Title " + i, "http://example.com/" + i, "Description of feed " + i, + "http://example.com/pay/feed" + i, "author " + i, "en", Feed.TYPE_RSS2, "feed" + i, image, null, + "http://example.com/feed/src/" + i, false); + feed.setDownload_url(hostFeed(feed)); + + // create items + List items = new ArrayList(); + for (int j = 0; j < NUM_ITEMS_PER_FEED; j++) { + FeedItem item = new FeedItem(0, "item" + j, "item" + j, "http://example.com/feed" + i + "/item/" + j, new Date(), true, feed); + items.add(item); + } + } + } +} From 662f8a46794f33276582783ccc2f24486651eb18 Mon Sep 17 00:00:00 2001 From: daniel oeh Date: Sun, 1 Jun 2014 13:13:17 +0200 Subject: [PATCH 02/24] Finished UITestUtils implementation --- build.gradle | 2 +- .../service/download/HttpDownloaderTest.java | 11 +- .../test/antennapod/ui/MainActivityTest.java | 14 ++- .../de/test/antennapod/ui/UITestUtils.java | 116 ++++++++++++++++-- .../test/antennapod/ui/UITestUtilsTest.java | 91 ++++++++++++++ .../util/service/download/HTTPBin.java | 2 + 6 files changed, 219 insertions(+), 17 deletions(-) create mode 100644 src/instrumentationTest/de/test/antennapod/ui/UITestUtilsTest.java diff --git a/build.gradle b/build.gradle index b1a2a46ca..c350673c6 100644 --- a/build.gradle +++ b/build.gradle @@ -29,7 +29,7 @@ dependencies { compile 'com.nineoldandroids:library:2.4.0' compile project(':submodules:dslv:library') - compile 'com.jayway.android.robotium:robotium-solo:5.0.1' + compile 'com.jayway.android.robotium:robotium-solo:5.1' } android { diff --git a/src/instrumentationTest/de/test/antennapod/service/download/HttpDownloaderTest.java b/src/instrumentationTest/de/test/antennapod/service/download/HttpDownloaderTest.java index f4726504e..6e44c8a0f 100644 --- a/src/instrumentationTest/de/test/antennapod/service/download/HttpDownloaderTest.java +++ b/src/instrumentationTest/de/test/antennapod/service/download/HttpDownloaderTest.java @@ -22,7 +22,6 @@ public class HttpDownloaderTest extends InstrumentationTestCase { private File destDir; private HTTPBin httpServer; - private static final String BASE_URL = "http://127.0.0.1:" + HTTPBin.PORT; public HttpDownloaderTest() { super(); @@ -79,15 +78,15 @@ public class HttpDownloaderTest extends InstrumentationTestCase { } - private static final String URL_404 = BASE_URL + "/status/404"; - private static final String URL_AUTH = BASE_URL + "/basic-auth/user/passwd"; + private static final String URL_404 = HTTPBin.BASE_URL + "/status/404"; + private static final String URL_AUTH = HTTPBin.BASE_URL + "/basic-auth/user/passwd"; public void testPassingHttp() { - download(BASE_URL + "/status/200", "test200", true); + download(HTTPBin.BASE_URL + "/status/200", "test200", true); } public void testRedirect() { - download(BASE_URL + "/redirect/4", "testRedirect", true); + download(HTTPBin.BASE_URL + "/redirect/4", "testRedirect", true); } public void testGzip() { @@ -99,7 +98,7 @@ public class HttpDownloaderTest extends InstrumentationTestCase { } public void testCancel() { - final String url = BASE_URL + "/delay/3"; + final String url = HTTPBin.BASE_URL + "/delay/3"; FeedFileImpl feedFile = setupFeedFile(url, "delay", true); final Downloader downloader = new HttpDownloader(new DownloadRequest(feedFile.getFile_url(), url, "delay", 0, feedFile.getTypeAsInt())); Thread t = new Thread() { diff --git a/src/instrumentationTest/de/test/antennapod/ui/MainActivityTest.java b/src/instrumentationTest/de/test/antennapod/ui/MainActivityTest.java index 5dc119a88..319664806 100644 --- a/src/instrumentationTest/de/test/antennapod/ui/MainActivityTest.java +++ b/src/instrumentationTest/de/test/antennapod/ui/MainActivityTest.java @@ -10,14 +10,24 @@ import de.danoeh.antennapod.activity.MainActivity; public class MainActivityTest extends ActivityInstrumentationTestCase2 { private Solo solo; + private UITestUtils uiTestUtils; - public MainActivityTest(Class activityClass) { - super(activityClass); + public MainActivityTest() { + super(MainActivity.class); } @Override protected void setUp() throws Exception { + super.setUp(); solo = new Solo(getInstrumentation(), getActivity()); + uiTestUtils = new UITestUtils(getInstrumentation().getTargetContext()); + uiTestUtils.setup(); + } + + @Override + protected void tearDown() throws Exception { + uiTestUtils.tearDown(); + super.tearDown(); } diff --git a/src/instrumentationTest/de/test/antennapod/ui/UITestUtils.java b/src/instrumentationTest/de/test/antennapod/ui/UITestUtils.java index 9fd5c4d77..4d1ca093a 100644 --- a/src/instrumentationTest/de/test/antennapod/ui/UITestUtils.java +++ b/src/instrumentationTest/de/test/antennapod/ui/UITestUtils.java @@ -6,33 +6,41 @@ import de.danoeh.antennapod.feed.Feed; import de.danoeh.antennapod.feed.FeedImage; import de.danoeh.antennapod.feed.FeedItem; import de.danoeh.antennapod.feed.FeedMedia; +import de.danoeh.antennapod.storage.DBWriter; +import de.danoeh.antennapod.storage.PodDBAdapter; import instrumentationTest.de.test.antennapod.util.service.download.HTTPBin; import instrumentationTest.de.test.antennapod.util.syndication.feedgenerator.RSS2Generator; import junit.framework.Assert; import org.apache.commons.io.FileUtils; +import org.apache.commons.io.IOUtils; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; +import java.io.InputStream; import java.util.ArrayList; import java.util.Date; import java.util.List; /** - * Utility methods for UI tests + * Utility methods for UI tests. + * Starts a web server that hosts feeds, episodes and images. */ public class UITestUtils { private static final String DATA_FOLDER = "test/UITestUtils"; - public static final int NUM_FEEDS = 10; - public static final int NUM_ITEMS_PER_FEED = 20; + public static final int NUM_FEEDS = 5; + public static final int NUM_ITEMS_PER_FEED = 10; private Context context; - private HTTPBin server; + private HTTPBin server = new HTTPBin(); private File destDir; private File hostedFeedDir; + private File hostedMediaDir; + + public List hostedFeeds = new ArrayList(); public UITestUtils(Context context) { this.context = context; @@ -44,14 +52,23 @@ public class UITestUtils { destDir.mkdir(); hostedFeedDir = new File(destDir, "hostedFeeds"); hostedFeedDir.mkdir(); + hostedMediaDir = new File(destDir, "hostedMediaDir"); + hostedMediaDir.mkdir(); Assert.assertTrue(destDir.exists()); Assert.assertTrue(hostedFeedDir.exists()); + Assert.assertTrue(hostedMediaDir.exists()); server.start(); } public void tearDown() throws IOException { FileUtils.deleteDirectory(destDir); + FileUtils.deleteDirectory(hostedMediaDir); + FileUtils.deleteDirectory(hostedFeedDir); server.stop(); + + if (localFeedDataAdded) { + PodDBAdapter.deleteDatabase(context); + } } private String hostFeed(Feed feed) throws IOException { @@ -62,7 +79,13 @@ public class UITestUtils { out.close(); int id = server.serveFile(feedFile); Assert.assertTrue(id != -1); - return String.format("http://127.0.0.1/files/%d", id); + return String.format("%s/files/%d", HTTPBin.BASE_URL, id); + } + + private String hostFile(File file) { + int id = server.serveFile(file); + Assert.assertTrue(id != -1); + return String.format("%s/files/%d", HTTPBin.BASE_URL, id); } private File newBitmapFile(String name) throws IOException { @@ -74,20 +97,97 @@ public class UITestUtils { return imgFile; } - public void addFeedData() throws IOException { + private File newMediaFile(String name) throws IOException { + File mediaFile = new File(hostedMediaDir, name); + Assert.assertFalse(mediaFile.exists()); + + InputStream in = context.getAssets().open("testfile.mp3"); + Assert.assertNotNull(in); + + FileOutputStream out = new FileOutputStream(mediaFile); + IOUtils.copy(in, out); + out.close(); + + return mediaFile; + } + + private boolean feedDataHosted = false; + + /** + * Adds feeds, images and episodes to the webserver for testing purposes. + */ + public void addHostedFeedData() throws IOException { + if (feedDataHosted) throw new IllegalStateException("addHostedFeedData was called twice on the same instance"); for (int i = 0; i < NUM_FEEDS; i++) { - FeedImage image = new FeedImage(0, "image " + i, newBitmapFile("image" + i).getAbsolutePath(), "http://example.com/feed" + i + "/image", true); + File bitmapFile = newBitmapFile("image" + i); + FeedImage image = new FeedImage(0, "image " + i, null, hostFile(bitmapFile), false); Feed feed = new Feed(0, new Date(), "Title " + i, "http://example.com/" + i, "Description of feed " + i, "http://example.com/pay/feed" + i, "author " + i, "en", Feed.TYPE_RSS2, "feed" + i, image, null, "http://example.com/feed/src/" + i, false); - feed.setDownload_url(hostFeed(feed)); + image.setOwner(feed); // create items List items = new ArrayList(); for (int j = 0; j < NUM_ITEMS_PER_FEED; j++) { FeedItem item = new FeedItem(0, "item" + j, "item" + j, "http://example.com/feed" + i + "/item/" + j, new Date(), true, feed); items.add(item); + + File mediaFile = newMediaFile("feed-" + i + "-episode-" + j + ".mp3"); + item.setMedia(new FeedMedia(0, item, 0, 0, mediaFile.length(), "audio/mp3", null, hostFile(mediaFile), false, null, 0)); + } + feed.setItems(items); + feed.setDownload_url(hostFeed(feed)); + hostedFeeds.add(feed); } + feedDataHosted = true; + } + + + private boolean localFeedDataAdded = false; + + /** + * Adds feeds, images and episodes to the local database. This method will also call addHostedFeedData if it has not + * been called yet. + * + * Adds one item of each feed to the queue and to the playback history. + * + * This method should NOT be called if the testing class wants to download the hosted feed data. + * + * @param downloadEpisodes true if episodes should also be marked as downloaded. + */ + public void addLocalFeedData(boolean downloadEpisodes) throws Exception { + if (localFeedDataAdded) throw new IllegalStateException("addLocalFeedData was called twice on the same instance"); + if (!feedDataHosted) { + addHostedFeedData(); + } + + List queue = new ArrayList(); + + PodDBAdapter adapter = new PodDBAdapter(context); + adapter.open(); + for (Feed feed : hostedFeeds) { + feed.setDownloaded(true); + if (feed.getImage() != null) { + FeedImage image = feed.getImage(); + image.setFile_url(image.getDownload_url()); + image.setDownloaded(true); + } + if (downloadEpisodes) { + for (FeedItem item : feed.getItems()) { + if (item.hasMedia()) { + FeedMedia media = item.getMedia(); + media.setFile_url(media.getDownload_url()); + media.setDownloaded(true); + } + } + } + + queue.add(feed.getItems().get(0)); + feed.getItems().get(1).getMedia().setPlaybackCompletionDate(new Date()); + } + adapter.setCompleteFeed(hostedFeeds.toArray(new Feed[hostedFeeds.size()])); + adapter.setQueue(queue); + adapter.close(); } } diff --git a/src/instrumentationTest/de/test/antennapod/ui/UITestUtilsTest.java b/src/instrumentationTest/de/test/antennapod/ui/UITestUtilsTest.java new file mode 100644 index 000000000..9628c5522 --- /dev/null +++ b/src/instrumentationTest/de/test/antennapod/ui/UITestUtilsTest.java @@ -0,0 +1,91 @@ +package instrumentationTest.de.test.antennapod.ui; + +import android.test.InstrumentationTestCase; +import de.danoeh.antennapod.feed.Feed; +import de.danoeh.antennapod.feed.FeedItem; +import org.apache.http.HttpStatus; + +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.List; + +/** + * Test for the UITestUtils. Makes sure that all URLs are reachable and that the class does not cause any crashes. + */ +public class UITestUtilsTest extends InstrumentationTestCase { + + private UITestUtils uiTestUtils; + + @Override + protected void setUp() throws Exception { + super.setUp(); + uiTestUtils = new UITestUtils(getInstrumentation().getTargetContext()); + uiTestUtils.setup(); + } + + @Override + public void tearDown() throws Exception { + super.tearDown(); + uiTestUtils.tearDown(); + } + + public void testAddHostedFeeds() throws Exception { + uiTestUtils.addHostedFeedData(); + final List feeds = uiTestUtils.hostedFeeds; + assertNotNull(feeds); + assertFalse(feeds.isEmpty()); + + for (Feed feed : feeds) { + testUrlReachable(feed.getDownload_url()); + if (feed.getImage() != null) { + testUrlReachable(feed.getImage().getDownload_url()); + } + for (FeedItem item : feed.getItems()) { + if (item.hasMedia()) { + testUrlReachable(item.getMedia().getDownload_url()); + } + } + } + } + + private void testUrlReachable(String strUtl) throws Exception { + URL url = new URL(strUtl); + HttpURLConnection conn = (HttpURLConnection) url.openConnection(); + conn.setRequestMethod("GET"); + conn.connect(); + int rc = conn.getResponseCode(); + assertEquals(HttpStatus.SC_OK, rc); + conn.disconnect(); + } + + private void addLocalFeedDataCheck(boolean downloadEpisodes) throws Exception { + uiTestUtils.addLocalFeedData(downloadEpisodes); + assertNotNull(uiTestUtils.hostedFeeds); + assertFalse(uiTestUtils.hostedFeeds.isEmpty()); + + for (Feed feed : uiTestUtils.hostedFeeds) { + assertTrue(feed.getId() != 0); + if (feed.getImage() != null) { + assertTrue(feed.getImage().getId() != 0); + } + for (FeedItem item : feed.getItems()) { + assertTrue(item.getId() != 0); + if (item.hasMedia()) { + assertTrue(item.getMedia().getId() != 0); + if (downloadEpisodes) { + assertTrue(item.getMedia().isDownloaded()); + assertNotNull(item.getMedia().getFile_url()); + } + } + } + } + } + + public void testAddLocalFeedDataNoDownload() throws Exception { + addLocalFeedDataCheck(false); + } + + public void testAddLocalFeedDataDownload() throws Exception { + addLocalFeedDataCheck(true); + } +} diff --git a/src/instrumentationTest/de/test/antennapod/util/service/download/HTTPBin.java b/src/instrumentationTest/de/test/antennapod/util/service/download/HTTPBin.java index 272bcda5e..adde08ef0 100644 --- a/src/instrumentationTest/de/test/antennapod/util/service/download/HTTPBin.java +++ b/src/instrumentationTest/de/test/antennapod/util/service/download/HTTPBin.java @@ -26,6 +26,8 @@ import java.util.zip.GZIPOutputStream; public class HTTPBin extends NanoHTTPD { private static final String TAG = "HTTPBin"; public static final int PORT = 8124; + public static final String BASE_URL = "http://127.0.0.1:" + HTTPBin.PORT; + private static final String MIME_HTML = "text/html"; private static final String MIME_PLAIN = "text/plain"; From 78f725d50d9ae374c356fc9458ee79ccc752454a Mon Sep 17 00:00:00 2001 From: daniel oeh Date: Sun, 1 Jun 2014 14:32:48 +0200 Subject: [PATCH 03/24] Added basic MainActivity tests --- .../antennapod/activity/MainActivity.java | 4 +- .../test/antennapod/ui/MainActivityTest.java | 86 +++++++++++++++++++ 2 files changed, 88 insertions(+), 2 deletions(-) diff --git a/src/de/danoeh/antennapod/activity/MainActivity.java b/src/de/danoeh/antennapod/activity/MainActivity.java index 92afea77c..898897bd8 100644 --- a/src/de/danoeh/antennapod/activity/MainActivity.java +++ b/src/de/danoeh/antennapod/activity/MainActivity.java @@ -41,8 +41,8 @@ public class MainActivity extends ActionBarActivity { | EventDistributor.FEED_LIST_UPDATE | EventDistributor.UNREAD_ITEMS_UPDATE; - private static final String PREF_NAME = "MainActivityPrefs"; - private static final String PREF_IS_FIRST_LAUNCH = "prefMainActivityIsFirstLaunch"; + public static final String PREF_NAME = "MainActivityPrefs"; + public static final String PREF_IS_FIRST_LAUNCH = "prefMainActivityIsFirstLaunch"; public static final String EXTRA_NAV_INDEX = "nav_index"; public static final String EXTRA_NAV_TYPE = "nav_type"; diff --git a/src/instrumentationTest/de/test/antennapod/ui/MainActivityTest.java b/src/instrumentationTest/de/test/antennapod/ui/MainActivityTest.java index 319664806..26f38da98 100644 --- a/src/instrumentationTest/de/test/antennapod/ui/MainActivityTest.java +++ b/src/instrumentationTest/de/test/antennapod/ui/MainActivityTest.java @@ -1,8 +1,18 @@ package instrumentationTest.de.test.antennapod.ui; +import android.content.Context; +import android.content.SharedPreferences; +import android.content.res.Resources; import android.test.ActivityInstrumentationTestCase2; +import android.view.View; import com.robotium.solo.Solo; +import de.danoeh.antennapod.R; +import de.danoeh.antennapod.activity.DefaultOnlineFeedViewActivity; import de.danoeh.antennapod.activity.MainActivity; +import de.danoeh.antennapod.activity.PreferenceActivity; +import de.danoeh.antennapod.feed.EventDistributor; +import de.danoeh.antennapod.feed.Feed; +import de.danoeh.antennapod.storage.PodDBAdapter; /** * User interface tests for MainActivity @@ -22,13 +32,89 @@ public class MainActivityTest extends ActivityInstrumentationTestCase2 Date: Sun, 1 Jun 2014 17:56:35 +0200 Subject: [PATCH 04/24] Fixed up-/back-button behavior in PreferenceActivity. fixes #412, fixes #107 --- .../activity/PreferenceActivity.java | 74 ++++++++++++------- 1 file changed, 49 insertions(+), 25 deletions(-) diff --git a/src/de/danoeh/antennapod/activity/PreferenceActivity.java b/src/de/danoeh/antennapod/activity/PreferenceActivity.java index 1070c71f8..77ec579ed 100644 --- a/src/de/danoeh/antennapod/activity/PreferenceActivity.java +++ b/src/de/danoeh/antennapod/activity/PreferenceActivity.java @@ -15,6 +15,7 @@ import android.preference.Preference; import android.preference.Preference.OnPreferenceChangeListener; import android.preference.Preference.OnPreferenceClickListener; import android.preference.PreferenceScreen; +import android.support.v4.app.NavUtils; import android.util.Log; import android.view.Menu; import android.view.MenuItem; @@ -46,7 +47,7 @@ public class PreferenceActivity extends android.preference.PreferenceActivity { private static final String PREF_FLATTR_THIS_APP = "prefFlattrThisApp"; private static final String PREF_FLATTR_AUTH = "pref_flattr_authenticate"; private static final String PREF_FLATTR_REVOKE = "prefRevokeAccess"; - private static final String PREF_AUTO_FLATTR = "pref_auto_flattr"; + private static final String PREF_AUTO_FLATTR = "pref_auto_flattr"; private static final String PREF_OPML_EXPORT = "prefOpmlExport"; private static final String PREF_ABOUT = "prefAbout"; private static final String PREF_CHOOSE_DATA_DIR = "prefChooseDataDir"; @@ -89,7 +90,8 @@ public class PreferenceActivity extends android.preference.PreferenceActivity { return true; } - }); + } + ); findPreference(PREF_FLATTR_REVOKE).setOnPreferenceClickListener( new OnPreferenceClickListener() { @@ -101,7 +103,8 @@ public class PreferenceActivity extends android.preference.PreferenceActivity { return true; } - }); + } + ); findPreference(PREF_ABOUT).setOnPreferenceClickListener( new OnPreferenceClickListener() { @@ -113,7 +116,8 @@ public class PreferenceActivity extends android.preference.PreferenceActivity { return true; } - }); + } + ); findPreference(PREF_OPML_EXPORT).setOnPreferenceClickListener( new OnPreferenceClickListener() { @@ -125,7 +129,8 @@ public class PreferenceActivity extends android.preference.PreferenceActivity { return true; } - }); + } + ); findPreference(PREF_CHOOSE_DATA_DIR).setOnPreferenceClickListener( new OnPreferenceClickListener() { @@ -135,10 +140,12 @@ public class PreferenceActivity extends android.preference.PreferenceActivity { startActivityForResult( new Intent(PreferenceActivity.this, DirectoryChooserActivity.class), - DirectoryChooserActivity.RESULT_CODE_DIR_SELECTED); + DirectoryChooserActivity.RESULT_CODE_DIR_SELECTED + ); return true; } - }); + } + ); findPreference(UserPreferences.PREF_THEME) .setOnPreferenceChangeListener( new OnPreferenceChangeListener() { @@ -153,18 +160,19 @@ public class PreferenceActivity extends android.preference.PreferenceActivity { startActivity(i); return true; } - }); + } + ); findPreference(UserPreferences.PREF_ENABLE_AUTODL) .setOnPreferenceChangeListener(new OnPreferenceChangeListener() { - @Override - public boolean onPreferenceChange(Preference preference, Object newValue) { - if (newValue instanceof Boolean) { - findPreference(UserPreferences.PREF_ENABLE_AUTODL_WIFI_FILTER).setEnabled((Boolean) newValue); - setSelectedNetworksEnabled((Boolean) newValue && UserPreferences.isEnableAutodownloadWifiFilter()); - } - return true; - } - }); + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + if (newValue instanceof Boolean) { + findPreference(UserPreferences.PREF_ENABLE_AUTODL_WIFI_FILTER).setEnabled((Boolean) newValue); + setSelectedNetworksEnabled((Boolean) newValue && UserPreferences.isEnableAutodownloadWifiFilter()); + } + return true; + } + }); findPreference(UserPreferences.PREF_ENABLE_AUTODL_WIFI_FILTER) .setOnPreferenceChangeListener( new OnPreferenceChangeListener() { @@ -179,7 +187,8 @@ public class PreferenceActivity extends android.preference.PreferenceActivity { return false; } } - }); + } + ); findPreference(UserPreferences.PREF_EPISODE_CACHE_SIZE) .setOnPreferenceChangeListener( new OnPreferenceChangeListener() { @@ -190,7 +199,8 @@ public class PreferenceActivity extends android.preference.PreferenceActivity { } return true; } - }); + } + ); findPreference(PREF_PLAYBACK_SPEED_LAUNCHER) .setOnPreferenceClickListener(new OnPreferenceClickListener() { @Override @@ -343,12 +353,14 @@ public class PreferenceActivity extends android.preference.PreferenceActivity { public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case android.R.id.home: + Intent destIntent = new Intent(this, MainActivity.class); + destIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + startActivity(destIntent); finish(); - break; + return true; default: return false; } - return true; } @Override @@ -389,7 +401,8 @@ public class PreferenceActivity extends android.preference.PreferenceActivity { String key = preference.getKey(); ArrayList prefValuesList = new ArrayList( Arrays.asList(UserPreferences - .getAutodownloadSelectedNetworks())); + .getAutodownloadSelectedNetworks()) + ); boolean newValue = ((CheckBoxPreference) preference) .isChecked(); if (BuildConfig.DEBUG) @@ -406,8 +419,9 @@ public class PreferenceActivity extends android.preference.PreferenceActivity { UserPreferences.setAutodownloadSelectedNetworks( PreferenceActivity.this, prefValuesList - .toArray(new String[prefValuesList - .size()])); + .toArray(new String[prefValuesList + .size()]) + ); return true; } else { return false; @@ -461,7 +475,17 @@ public class PreferenceActivity extends android.preference.PreferenceActivity { .setBackgroundDrawable( this.getWindow().getDecorView() .getBackground().getConstantState() - .newDrawable()); + .newDrawable() + ); return false; } + + @Override + public void onBackPressed() { + // The default back button behavior has to be overwritten because changing the theme clears the back stack + Intent destIntent = new Intent(this, MainActivity.class); + destIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + startActivity(destIntent); + finish(); + } } From e4737bcc34b6b607157aaaf1d21092481ebbb5c8 Mon Sep 17 00:00:00 2001 From: axq Date: Sun, 1 Jun 2014 08:59:41 -0700 Subject: [PATCH 05/24] Update copyright year to 2014 I wondered if the app was stale the first time I opened the about page. Users should feel great about an app with this much development activity. --- assets/about.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/about.html b/assets/about.html index e6ffdc2ed..196f8b94f 100644 --- a/assets/about.html +++ b/assets/about.html @@ -43,7 +43,7 @@

AntennaPod, Version 0.9.9.0

-

Copyright © 2012 Daniel Oeh

+

Copyright © 2014 Daniel Oeh

Licensed under the MIT License (View)

From 9e1eba29ab59f7218c9910eeb841c8e4dc8e1269 Mon Sep 17 00:00:00 2001 From: daniel oeh Date: Sat, 7 Jun 2014 11:32:11 +0200 Subject: [PATCH 06/24] Updated version numbers in build.gradle requires submodule update --- build.gradle | 4 ++-- submodules/dslv | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build.gradle b/build.gradle index c350673c6..7ae35ae61 100644 --- a/build.gradle +++ b/build.gradle @@ -3,7 +3,7 @@ buildscript { mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:0.10.+' + classpath 'com.android.tools.build:gradle:0.11.+' } } apply plugin: 'android' @@ -34,7 +34,7 @@ dependencies { android { compileSdkVersion 19 - buildToolsVersion "19.0.3" + buildToolsVersion "19.1.0" defaultConfig { minSdkVersion 10 diff --git a/submodules/dslv b/submodules/dslv index 5f58dff34..f659a75b0 160000 --- a/submodules/dslv +++ b/submodules/dslv @@ -1 +1 @@ -Subproject commit 5f58dff340f705b4dc7f920f81c33d382919c3ad +Subproject commit f659a75b015932565d230f72829baa4bbf403463 From 9bdc003a8c6bcab04bd1a9e5cc422df435def1df Mon Sep 17 00:00:00 2001 From: daniel oeh Date: Sat, 7 Jun 2014 12:35:55 +0200 Subject: [PATCH 07/24] Removed pull-to-refresh Causes too much accidental updates --- res/layout/new_episodes_fragment.xml | 7 ----- .../fragment/NewEpisodesFragment.java | 28 ++----------------- 2 files changed, 3 insertions(+), 32 deletions(-) diff --git a/res/layout/new_episodes_fragment.xml b/res/layout/new_episodes_fragment.xml index b091659fa..63c712f57 100644 --- a/res/layout/new_episodes_fragment.xml +++ b/res/layout/new_episodes_fragment.xml @@ -6,11 +6,6 @@ android:layout_width="match_parent" android:layout_height="match_parent"> - - - - feeds = ((MainActivity) getActivity()).getFeeds(); - if (feeds != null) { - DBTasks.refreshAllFeeds(getActivity(), feeds); - } - } - }); if (!itemsLoaded) { progLoading.setVisibility(View.VISIBLE); @@ -331,18 +318,9 @@ public class NewEpisodesFragment extends Fragment { if (!viewsCreated) { return; } - - if (DownloadService.isRunning - && DownloadRequester.getInstance().isDownloadingFeeds()) { - swipeRefreshLayout.setRefreshing(true); - - } else { - swipeRefreshLayout.setRefreshing(false); - - // if case other fragments have set this to true, this fragment should remove the progress indicator - ((ActionBarActivity) getActivity()) - .setSupportProgressBarIndeterminateVisibility(false); - } + ((ActionBarActivity) getActivity()) + .setSupportProgressBarIndeterminateVisibility(DownloadService.isRunning + && DownloadRequester.getInstance().isDownloadingFeeds()); } private EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() { From 7220c65ef424116b322b85e92a90bdfd419c59bf Mon Sep 17 00:00:00 2001 From: daniel oeh Date: Sat, 7 Jun 2014 12:36:47 +0200 Subject: [PATCH 08/24] Improved menu behavior in all episodes view fixes #451 --- res/menu/new_episodes.xml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/res/menu/new_episodes.xml b/res/menu/new_episodes.xml index 3648c5fce..4cf3b5fec 100644 --- a/res/menu/new_episodes.xml +++ b/res/menu/new_episodes.xml @@ -3,13 +3,6 @@ - - + + Date: Sat, 7 Jun 2014 13:16:52 +0200 Subject: [PATCH 09/24] Removed unused files --- res/layout/feedlist_item.xml | 129 ------------- res/layout/feedlist_item_grid.xml | 138 -------------- res/values-large/refs.xml | 5 - .../antennapod/adapter/FeedlistAdapter.java | 174 ------------------ 4 files changed, 446 deletions(-) delete mode 100644 res/layout/feedlist_item.xml delete mode 100644 res/layout/feedlist_item_grid.xml delete mode 100644 res/values-large/refs.xml delete mode 100644 src/de/danoeh/antennapod/adapter/FeedlistAdapter.java diff --git a/res/layout/feedlist_item.xml b/res/layout/feedlist_item.xml deleted file mode 100644 index f6c43675d..000000000 --- a/res/layout/feedlist_item.xml +++ /dev/null @@ -1,129 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/res/layout/feedlist_item_grid.xml b/res/layout/feedlist_item_grid.xml deleted file mode 100644 index 934904374..000000000 --- a/res/layout/feedlist_item_grid.xml +++ /dev/null @@ -1,138 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/res/values-large/refs.xml b/res/values-large/refs.xml deleted file mode 100644 index 7caac382f..000000000 --- a/res/values-large/refs.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - @layout/feedlist_item_grid - - \ No newline at end of file diff --git a/src/de/danoeh/antennapod/adapter/FeedlistAdapter.java b/src/de/danoeh/antennapod/adapter/FeedlistAdapter.java deleted file mode 100644 index 30c1ff880..000000000 --- a/src/de/danoeh/antennapod/adapter/FeedlistAdapter.java +++ /dev/null @@ -1,174 +0,0 @@ -package de.danoeh.antennapod.adapter; - -import android.content.Context; -import android.text.format.DateUtils; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.BaseAdapter; -import android.widget.ImageView; -import android.widget.TextView; -import de.danoeh.antennapod.R; -import de.danoeh.antennapod.asynctask.ImageLoader; -import de.danoeh.antennapod.feed.Feed; -import de.danoeh.antennapod.storage.DownloadRequester; -import de.danoeh.antennapod.storage.FeedItemStatistics; -import de.danoeh.antennapod.util.ThemeUtils; - -public class FeedlistAdapter extends BaseAdapter { - private static final String TAG = "FeedlistAdapter"; - - private Context context; - protected ItemAccess itemAccess; - - private int selectedItemIndex; - private ImageLoader imageLoader; - public static final int SELECTION_NONE = -1; - - public FeedlistAdapter(Context context, ItemAccess itemAccess) { - super(); - if (context == null) { - throw new IllegalArgumentException("context must not be null"); - } - if (itemAccess == null) { - throw new IllegalArgumentException("itemAccess must not be null"); - } - - this.context = context; - this.itemAccess = itemAccess; - selectedItemIndex = SELECTION_NONE; - imageLoader = ImageLoader.getInstance(); - } - - @Override - public View getView(int position, View convertView, ViewGroup parent) { - final Holder holder; - final Feed feed = getItem(position); - final FeedItemStatistics feedItemStatistics = itemAccess.getFeedItemStatistics(position); - - // Inflate Layout - if (convertView == null) { - holder = new Holder(); - LayoutInflater inflater = (LayoutInflater) context - .getSystemService(Context.LAYOUT_INFLATER_SERVICE); - - convertView = inflater.inflate(R.layout.feedlist_item, null); - holder.title = (TextView) convertView - .findViewById(R.id.txtvFeedname); - - holder.newEpisodes = (TextView) convertView - .findViewById(R.id.txtvNewEps); - holder.inProgressEpisodes = (TextView) convertView - .findViewById(R.id.txtvProgressEps); - holder.newEpisodesLabel = (View) convertView - .findViewById(R.id.lNewStatusLabel); - holder.inProgressEpisodesLabel = (View) convertView - .findViewById(R.id.lProgressStatusLabel); - holder.image = (ImageView) convertView - .findViewById(R.id.imgvFeedimage); - holder.lastUpdate = (TextView) convertView - .findViewById(R.id.txtvLastUpdate); - holder.numberOfEpisodes = (TextView) convertView - .findViewById(R.id.txtvNumEpisodes); - convertView.setTag(holder); - } else { - holder = (Holder) convertView.getTag(); - - } - - if (position == selectedItemIndex) { - convertView.setBackgroundColor(convertView.getResources().getColor( - ThemeUtils.getSelectionBackgroundColor())); - } else { - convertView.setBackgroundResource(0); - } - - holder.title.setText(feed.getTitle()); - - if (feedItemStatistics != null) { - if (DownloadRequester.getInstance().isDownloadingFile(feed)) { - holder.lastUpdate.setText(R.string.refreshing_label); - } else { - if (feedItemStatistics.lastUpdateKnown()) { - holder.lastUpdate.setText(convertView.getResources().getString( - R.string.most_recent_prefix) - + DateUtils.getRelativeTimeSpanString( - feedItemStatistics.getLastUpdate().getTime(), - System.currentTimeMillis(), 0, 0)); - } else { - holder.lastUpdate.setText(""); - } - } - holder.numberOfEpisodes.setText(feedItemStatistics.getNumberOfItems() - + convertView.getResources() - .getString(R.string.episodes_suffix)); - - if (feedItemStatistics.getNumberOfNewItems() > 0) { - holder.newEpisodes.setText(Integer.toString(feedItemStatistics.getNumberOfNewItems())); - holder.newEpisodesLabel.setVisibility(View.VISIBLE); - } else { - holder.newEpisodesLabel.setVisibility(View.INVISIBLE); - } - - if (feedItemStatistics.getNumberOfInProgressItems() > 0) { - holder.inProgressEpisodes - .setText(Integer.toString(feedItemStatistics.getNumberOfInProgressItems())); - holder.inProgressEpisodesLabel.setVisibility(View.VISIBLE); - } else { - holder.inProgressEpisodesLabel.setVisibility(View.INVISIBLE); - } - } - final String imageUrl = (feed.getImage() != null && feed.getImage().isDownloaded()) ? feed.getImage() - .getFile_url() : null; - imageLoader.loadThumbnailBitmap( - feed.getImage(), - holder.image, - (int) convertView.getResources().getDimension( - R.dimen.thumbnail_length)); - - return convertView; - } - - static class Holder { - TextView title; - TextView lastUpdate; - TextView numberOfEpisodes; - TextView newEpisodes; - TextView inProgressEpisodes; - ImageView image; - View newEpisodesLabel; - View inProgressEpisodesLabel; - } - - public int getSelectedItemIndex() { - return selectedItemIndex; - } - - public void setSelectedItemIndex(int selectedItemIndex) { - this.selectedItemIndex = selectedItemIndex; - notifyDataSetChanged(); - } - - @Override - public int getCount() { - return itemAccess.getCount(); - } - - @Override - public Feed getItem(int position) { - return itemAccess.getItem(position); - } - - @Override - public long getItemId(int position) { - return position; - } - - public interface ItemAccess { - int getCount(); - - Feed getItem(int position); - - FeedItemStatistics getFeedItemStatistics(int position); - } -} From d058b474601f373d2e6682e5d4a79f019f993923 Mon Sep 17 00:00:00 2001 From: daniel oeh Date: Sat, 7 Jun 2014 13:17:25 +0200 Subject: [PATCH 10/24] Added 'new' indicator to all episodes view + layout improvements --- res/layout/feeditemlist_item.xml | 20 ++-------- res/layout/new_episodes_listitem.xml | 39 +++++++++---------- res/values-v14/styles.xml | 9 +++++ res/values/colors.xml | 3 +- res/values/dimens.xml | 2 +- res/values/styles.xml | 8 +++- .../adapter/NewEpisodesListAdapter.java | 7 ++++ 7 files changed, 47 insertions(+), 41 deletions(-) create mode 100644 res/values-v14/styles.xml diff --git a/res/layout/feeditemlist_item.xml b/res/layout/feeditemlist_item.xml index 147157c5a..f3701de2c 100644 --- a/res/layout/feeditemlist_item.xml +++ b/res/layout/feeditemlist_item.xml @@ -18,7 +18,6 @@ android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_alignParentTop="true" - android:layout_marginBottom="4dp" android:textColor="?android:attr/textColorTertiary" android:textSize="@dimen/text_size_micro"/> @@ -28,7 +27,6 @@ android:layout_height="wrap_content" android:layout_below="@id/txtvPublished" android:layout_marginBottom="8dp" - android:layout_marginRight="4dp" style="@style/AntennaPod.TextView.ListItemPrimaryTitle"/> @@ -59,7 +57,6 @@ android:layout_alignParentLeft="true" android:layout_below="@id/txtvItemname" android:maxLines="2" - android:layout_marginBottom="2dp" android:textColor="?android:attr/textColorTertiary" android:textSize="@dimen/text_size_micro"/> @@ -77,22 +74,11 @@ - + android:layout_alignParentTop="true"/> + + - - + android:layout_margin="4dp"> + + @@ -83,8 +84,6 @@ android:layout_width="0dp" android:layout_height="wrap_content" android:layout_toLeftOf="@id/imgvInPlaylist" - android:layout_marginLeft="8dp" - android:layout_marginRight="8dp" android:layout_alignParentLeft="true" android:textColor="?android:attr/textColorTertiary" android:textSize="@dimen/text_size_micro"/> diff --git a/res/values-v14/styles.xml b/res/values-v14/styles.xml new file mode 100644 index 000000000..6a39d6175 --- /dev/null +++ b/res/values-v14/styles.xml @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/res/values/colors.xml b/res/values/colors.xml index fe028ad20..6b535079d 100644 --- a/res/values/colors.xml +++ b/res/values/colors.xml @@ -9,14 +9,13 @@ #DDDDDD #669900 #CC0000 - #E099CC00 #E033B5E5 #E0EE5F52 #262C31 #DDDDDD #EDEDED #060708 - + #669900 #FEBB20 diff --git a/res/values/dimens.xml b/res/values/dimens.xml index 10294ca61..1ebcdb76d 100644 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -11,7 +11,7 @@ 18sp 22sp 32dp - 80dp + 85dp 70dp 70dp 110dp diff --git a/res/values/styles.xml b/res/values/styles.xml index 94edd7413..7477712b2 100644 --- a/res/values/styles.xml +++ b/res/values/styles.xml @@ -127,7 +127,7 @@ + + diff --git a/src/de/danoeh/antennapod/adapter/NewEpisodesListAdapter.java b/src/de/danoeh/antennapod/adapter/NewEpisodesListAdapter.java index 4a959dfd2..555a334f6 100644 --- a/src/de/danoeh/antennapod/adapter/NewEpisodesListAdapter.java +++ b/src/de/danoeh/antennapod/adapter/NewEpisodesListAdapter.java @@ -66,6 +66,7 @@ public class NewEpisodesListAdapter extends BaseAdapter { holder.title = (TextView) convertView.findViewById(R.id.txtvTitle); holder.pubDate = (TextView) convertView .findViewById(R.id.txtvPublished); + holder.statusUnread = convertView.findViewById(R.id.statusUnread); holder.butSecondary = (ImageButton) convertView .findViewById(R.id.butSecondaryAction); holder.queueStatus = (ImageView) convertView @@ -81,6 +82,11 @@ public class NewEpisodesListAdapter extends BaseAdapter { holder.title.setText(item.getTitle()); holder.pubDate.setText(DateUtils.formatDateTime(context, item.getPubDate().getTime(), DateUtils.FORMAT_SHOW_DATE)); + if (item.isRead()) { + holder.statusUnread.setVisibility(View.GONE); + } else { + holder.statusUnread.setVisibility(View.VISIBLE); + } FeedMedia media = item.getMedia(); if (media != null) { @@ -140,6 +146,7 @@ public class NewEpisodesListAdapter extends BaseAdapter { static class Holder { TextView title; TextView pubDate; + View statusUnread; ImageView queueStatus; ImageView imageView; ProgressBar downloadProgress; From 998d3f16280c6a84dc10a138e5eb4fd4f4355f7e Mon Sep 17 00:00:00 2001 From: daniel oeh Date: Sat, 7 Jun 2014 13:51:01 +0200 Subject: [PATCH 11/24] Added progress bar to queue items --- res/layout/queue_listitem.xml | 36 ++++++------ .../antennapod/adapter/AdapterUtils.java | 57 +++++++++++++++++++ .../adapter/FeedItemlistAdapter.java | 25 +------- .../antennapod/adapter/QueueListAdapter.java | 17 +++--- 4 files changed, 86 insertions(+), 49 deletions(-) create mode 100644 src/de/danoeh/antennapod/adapter/AdapterUtils.java diff --git a/res/layout/queue_listitem.xml b/res/layout/queue_listitem.xml index a2545097f..4a55cd466 100644 --- a/res/layout/queue_listitem.xml +++ b/res/layout/queue_listitem.xml @@ -15,30 +15,27 @@ android:layout_margin="8dp" android:contentDescription="@string/drag_handle_content_description"/> + + - - + android:layout_margin="8dp"> + + + android:layout_toRightOf="@id/txtvPosition"/> diff --git a/src/de/danoeh/antennapod/adapter/AdapterUtils.java b/src/de/danoeh/antennapod/adapter/AdapterUtils.java new file mode 100644 index 000000000..f393fb7d7 --- /dev/null +++ b/src/de/danoeh/antennapod/adapter/AdapterUtils.java @@ -0,0 +1,57 @@ +package de.danoeh.antennapod.adapter; + +import android.content.res.Resources; +import android.view.View; +import android.widget.ProgressBar; +import android.widget.TextView; +import de.danoeh.antennapod.R; +import de.danoeh.antennapod.feed.FeedItem; +import de.danoeh.antennapod.feed.FeedMedia; +import de.danoeh.antennapod.util.Converter; + +/** + * Utility methods for adapters + */ +public class AdapterUtils { + + private AdapterUtils() { + + } + + /** + * Updates the contents of the TextView that shows the current playback position and the ProgressBar. + */ + public static void updateEpisodePlaybackProgress(FeedItem item, Resources res, TextView txtvPos, ProgressBar episodeProgress) { + FeedMedia media = item.getMedia(); + episodeProgress.setVisibility(View.GONE); + if (media == null) { + txtvPos.setVisibility(View.GONE); + return; + } else { + txtvPos.setVisibility(View.VISIBLE); + } + + FeedItem.State state = item.getState(); + if (state == FeedItem.State.PLAYING + || state == FeedItem.State.IN_PROGRESS) { + if (media.getDuration() > 0) { + episodeProgress.setVisibility(View.VISIBLE); + episodeProgress + .setProgress((int) (((double) media + .getPosition()) / media.getDuration() * 100)); + txtvPos.setText(Converter + .getDurationStringLong(media.getDuration() + - media.getPosition())); + } + } else if (!media.isDownloaded()) { + txtvPos.setText(res.getString( + R.string.size_prefix) + + Converter.byteToString(media.getSize())); + } else { + txtvPos.setText(res.getString( + R.string.length_prefix) + + Converter.getDurationStringLong(media + .getDuration())); + } + } +} diff --git a/src/de/danoeh/antennapod/adapter/FeedItemlistAdapter.java b/src/de/danoeh/antennapod/adapter/FeedItemlistAdapter.java index 5475f122f..c4a16d4db 100644 --- a/src/de/danoeh/antennapod/adapter/FeedItemlistAdapter.java +++ b/src/de/danoeh/antennapod/adapter/FeedItemlistAdapter.java @@ -134,28 +134,8 @@ public class FeedItemlistAdapter extends BaseAdapter { holder.lenSize.setVisibility(View.INVISIBLE); } else { - if (state == FeedItem.State.PLAYING - || state == FeedItem.State.IN_PROGRESS) { - if (media.getDuration() > 0) { - holder.episodeProgress - .setProgress((int) (((double) media - .getPosition()) / media.getDuration() * 100)); - holder.lenSize.setText(Converter - .getDurationStringLong(media.getDuration() - - media.getPosition())); - } - } else if (!media.isDownloaded()) { - holder.lenSize.setText(context.getString( - R.string.size_prefix) - + Converter.byteToString(media.getSize())); - } else { - holder.lenSize.setText(context.getString( - R.string.length_prefix) - + Converter.getDurationStringLong(media - .getDuration())); - } + AdapterUtils.updateEpisodePlaybackProgress(item, context.getResources(), holder.lenSize, holder.episodeProgress); - holder.lenSize.setVisibility(View.VISIBLE); if (((ItemAccess) itemAccess).isInQueue(item)) { holder.inPlaylist.setVisibility(View.VISIBLE); } else { @@ -166,9 +146,6 @@ public class FeedItemlistAdapter extends BaseAdapter { item.getMedia())) { holder.episodeProgress.setVisibility(View.VISIBLE); holder.episodeProgress.setProgress(((ItemAccess) itemAccess).getItemDownloadProgressPercent(item)); - } else if (!(state == FeedItem.State.IN_PROGRESS - || state == FeedItem.State.PLAYING)) { - holder.episodeProgress.setVisibility(View.GONE); } TypedArray typeDrawables = context.obtainStyledAttributes( diff --git a/src/de/danoeh/antennapod/adapter/QueueListAdapter.java b/src/de/danoeh/antennapod/adapter/QueueListAdapter.java index fb6848a1e..ecce1b473 100644 --- a/src/de/danoeh/antennapod/adapter/QueueListAdapter.java +++ b/src/de/danoeh/antennapod/adapter/QueueListAdapter.java @@ -61,7 +61,8 @@ public class QueueListAdapter extends BaseAdapter { holder.title = (TextView) convertView.findViewById(R.id.txtvTitle); holder.butSecondary = (ImageButton) convertView .findViewById(R.id.butSecondaryAction); - holder.downloadProgress = (ProgressBar) convertView + holder.position = (TextView) convertView.findViewById(R.id.txtvPosition); + holder.progress = (ProgressBar) convertView .findViewById(R.id.pbar_download_progress); holder.imageView = (ImageView) convertView.findViewById(R.id.imgvImage); convertView.setTag(holder); @@ -71,20 +72,17 @@ public class QueueListAdapter extends BaseAdapter { holder.title.setText(item.getTitle()); + AdapterUtils.updateEpisodePlaybackProgress(item, context.getResources(), holder.position, holder.progress); + FeedMedia media = item.getMedia(); if (media != null) { final boolean isDownloadingMedia = DownloadRequester.getInstance().isDownloadingFile(media); - - if (isDownloadingMedia) { - holder.downloadProgress.setVisibility(View.VISIBLE); - } else { - holder.downloadProgress.setVisibility(View.GONE); - } if (!media.isDownloaded()) { if (isDownloadingMedia) { // item is being downloaded - holder.downloadProgress.setProgress(itemAccess.getItemDownloadProgressPercent(item)); + holder.progress.setVisibility(View.VISIBLE); + holder.progress.setProgress(itemAccess.getItemDownloadProgressPercent(item)); } } } @@ -116,7 +114,8 @@ public class QueueListAdapter extends BaseAdapter { static class Holder { TextView title; ImageView imageView; - ProgressBar downloadProgress; + TextView position; + ProgressBar progress; ImageButton butSecondary; } From a174b4db75d00f0391e493e1ba849a1bb996fbf5 Mon Sep 17 00:00:00 2001 From: daniel oeh Date: Sat, 7 Jun 2014 14:10:44 +0200 Subject: [PATCH 12/24] Bugfix: Parser failures were not reported properly. fixes #450 --- src/de/danoeh/antennapod/service/download/DownloadService.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/de/danoeh/antennapod/service/download/DownloadService.java b/src/de/danoeh/antennapod/service/download/DownloadService.java index ed2f5d532..4f60ef8d6 100644 --- a/src/de/danoeh/antennapod/service/download/DownloadService.java +++ b/src/de/danoeh/antennapod/service/download/DownloadService.java @@ -890,6 +890,7 @@ public class DownloadService extends Service { if (successful) { return savedFeed; } else { + numberOfDownloads.decrementAndGet(); saveDownloadStatus(new DownloadStatus(savedFeed, savedFeed.getHumanReadableIdentifier(), reason, successful, reasonDetailed)); From c4e0078382c6ea048456e127cc887476d48a6686 Mon Sep 17 00:00:00 2001 From: daniel oeh Date: Sat, 7 Jun 2014 16:38:04 +0200 Subject: [PATCH 13/24] Bugfix: Playback could not be restarted in some cases. fixes #387 --- .../service/playback/PlaybackService.java | 1 + .../playback/PlaybackServiceMediaPlayer.java | 36 ++++++++++++++++--- 2 files changed, 33 insertions(+), 4 deletions(-) diff --git a/src/de/danoeh/antennapod/service/playback/PlaybackService.java b/src/de/danoeh/antennapod/service/playback/PlaybackService.java index fb2569bfd..b7ff62129 100644 --- a/src/de/danoeh/antennapod/service/playback/PlaybackService.java +++ b/src/de/danoeh/antennapod/service/playback/PlaybackService.java @@ -541,6 +541,7 @@ public class PlaybackService extends Service { (nextMedia.getMediaType() == MediaType.VIDEO) ? EXTRA_CODE_VIDEO : EXTRA_CODE_AUDIO); } else { sendNotificationBroadcast(NOTIFICATION_TYPE_PLAYBACK_END, 0); + mediaPlayer.stop(); //stopSelf(); } } diff --git a/src/de/danoeh/antennapod/service/playback/PlaybackServiceMediaPlayer.java b/src/de/danoeh/antennapod/service/playback/PlaybackServiceMediaPlayer.java index 24ff9b3fa..2915da5a1 100644 --- a/src/de/danoeh/antennapod/service/playback/PlaybackServiceMediaPlayer.java +++ b/src/de/danoeh/antennapod/service/playback/PlaybackServiceMediaPlayer.java @@ -77,7 +77,8 @@ public class PlaybackServiceMediaPlayer { public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) { if (BuildConfig.DEBUG) Log.d(TAG, "Rejected execution of runnable"); } - }); + } + ); mediaPlayer = null; statusBeforeSeeking = null; @@ -150,6 +151,8 @@ public class PlaybackServiceMediaPlayer { if (media != null) { if (!forceReset && media.getIdentifier().equals(playable.getIdentifier())) { // episode is already playing -> ignore method call + if (BuildConfig.DEBUG) + Log.d(TAG, "Method call to playMediaObject was ignored: media file already playing."); return; } else { // stop playback of this episode @@ -284,7 +287,8 @@ public class PlaybackServiceMediaPlayer { reinit(); } } else { - if (BuildConfig.DEBUG) Log.d(TAG, "Ignoring call to pause: Player is in " + playerStatus + " state"); + if (BuildConfig.DEBUG) + Log.d(TAG, "Ignoring call to pause: Player is in " + playerStatus + " state"); } playerLock.unlock(); @@ -385,9 +389,10 @@ public class PlaybackServiceMediaPlayer { /** * Seeks to the specified position. If the PSMP object is in an invalid state, this method will do nothing. + * * @param t The position to seek to in milliseconds. t < 0 will be interpreted as t = 0 - *

- * This method is executed on the caller's thread. + *

+ * This method is executed on the caller's thread. */ private void seekToSync(int t) { if (t < 0) { @@ -757,6 +762,29 @@ public class PlaybackServiceMediaPlayer { }); } + /** + * Moves the PlaybackServiceMediaPlayer into STOPPED state. This call is only valid if the player is currently in + * INDETERMINATE state, for example after a call to endPlayback. + * This method will only take care of changing the PlayerStatus of this object! Other tasks like + * abandoning audio focus have to be done with other methods. + */ + public void stop() { + executor.submit(new Runnable() { + @Override + public void run() { + playerLock.lock(); + + if (playerStatus == PlayerStatus.INDETERMINATE) { + setPlayerStatus(PlayerStatus.STOPPED, null); + } else { + if (BuildConfig.DEBUG) Log.d(TAG, "Ignored call to stop: Current player state is: " + playerStatus); + } + playerLock.unlock(); + + } + }); + } + /** * Holds information about a PSMP object. */ From c3e0ac9e74ef012c08eef0348604c2fdefca8074 Mon Sep 17 00:00:00 2001 From: daniel oeh Date: Sat, 7 Jun 2014 16:38:29 +0200 Subject: [PATCH 14/24] Added test cases for starting playback --- .../test/antennapod/ui/MainActivityTest.java | 2 - .../de/test/antennapod/ui/PlaybackTest.java | 149 ++++++++++++++++++ .../de/test/antennapod/ui/UITestUtils.java | 11 +- .../test/antennapod/ui/UITestUtilsTest.java | 3 + .../util/service/download/HTTPBin.java | 2 +- 5 files changed, 159 insertions(+), 8 deletions(-) create mode 100644 src/instrumentationTest/de/test/antennapod/ui/PlaybackTest.java diff --git a/src/instrumentationTest/de/test/antennapod/ui/MainActivityTest.java b/src/instrumentationTest/de/test/antennapod/ui/MainActivityTest.java index 26f38da98..2dfd6a544 100644 --- a/src/instrumentationTest/de/test/antennapod/ui/MainActivityTest.java +++ b/src/instrumentationTest/de/test/antennapod/ui/MainActivityTest.java @@ -66,8 +66,6 @@ public class MainActivityTest extends ActivityInstrumentationTestCase2 { + + private Solo solo; + private UITestUtils uiTestUtils; + + public PlaybackTest() { + super(MainActivity.class); + } + + @Override + public void setUp() throws Exception { + super.setUp(); + solo = new Solo(getInstrumentation(), getActivity()); + uiTestUtils = new UITestUtils(getInstrumentation().getTargetContext()); + uiTestUtils.setup(); + // create database + PodDBAdapter adapter = new PodDBAdapter(getInstrumentation().getTargetContext()); + adapter.open(); + adapter.close(); + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getInstrumentation().getTargetContext()); + prefs.edit().putBoolean(UserPreferences.PREF_PAUSE_ON_HEADSET_DISCONNECT, false).commit(); + } + + @Override + public void tearDown() throws Exception { + uiTestUtils.tearDown(); + solo.finishOpenedActivities(); + PodDBAdapter.deleteDatabase(getInstrumentation().getTargetContext()); + + // shut down playback service + skipEpisode(); + getInstrumentation().getTargetContext().sendBroadcast( + new Intent(PlaybackService.ACTION_SHUTDOWN_PLAYBACK_SERVICE)); + + super.tearDown(); + } + + private void setContinuousPlaybackPreference(boolean value) { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getInstrumentation().getTargetContext()); + prefs.edit().putBoolean(UserPreferences.PREF_FOLLOW_QUEUE, value).commit(); + } + + private void skipEpisode() { + Intent skipIntent = new Intent(PlaybackService.ACTION_SKIP_CURRENT_EPISODE); + getInstrumentation().getTargetContext().sendBroadcast(skipIntent); + } + + private void startLocalPlayback() { + assertTrue(solo.waitForActivity(MainActivity.class)); + solo.setNavigationDrawer(Solo.CLOSED); + solo.clickOnView(solo.getView(R.id.butSecondaryAction)); + assertTrue(solo.waitForActivity(AudioplayerActivity.class)); + assertTrue(solo.waitForView(solo.getView(R.id.butPlay))); + } + + private void startLocalPlaybackFromQueue() { + assertTrue(solo.waitForActivity(MainActivity.class)); + solo.clickOnView(solo.getView(android.R.id.home)); + solo.clickOnText(solo.getString(R.string.queue_label)); + assertTrue(solo.waitForView(solo.getView(R.id.butSecondaryAction))); + solo.clickOnImageButton(0); + assertTrue(solo.waitForActivity(AudioplayerActivity.class)); + assertTrue(solo.waitForView(solo.getView(R.id.butPlay))); + } + + public void testStartLocal() throws Exception { + uiTestUtils.addLocalFeedData(true); + DBWriter.clearQueue(getInstrumentation().getTargetContext()).get(); + startLocalPlayback(); + + solo.clickOnView(solo.getView(R.id.butPlay)); + } + + public void testContinousPlaybackOffSingleEpisode() throws Exception { + setContinuousPlaybackPreference(false); + uiTestUtils.addLocalFeedData(true); + DBWriter.clearQueue(getInstrumentation().getTargetContext()).get(); + startLocalPlayback(); + assertTrue(solo.waitForActivity(MainActivity.class)); + } + + + public void testContinousPlaybackOffMultipleEpisodes() throws Exception { + setContinuousPlaybackPreference(false); + uiTestUtils.addLocalFeedData(true); + List queue = DBReader.getQueue(getInstrumentation().getTargetContext()); + FeedItem second = queue.get(1); + + startLocalPlaybackFromQueue(); + assertTrue(solo.waitForText(second.getTitle())); + } + + public void testContinuousPlaybackOnMultipleEpisodes() throws Exception { + setContinuousPlaybackPreference(true); + uiTestUtils.addLocalFeedData(true); + List queue = DBReader.getQueue(getInstrumentation().getTargetContext()); + FeedItem second = queue.get(1); + + startLocalPlaybackFromQueue(); + assertTrue(solo.waitForText(second.getTitle())); + } + + /** + * Check if an episode can be played twice without problems. + */ + private void replayEpisodeCheck(boolean followQueue) throws Exception { + setContinuousPlaybackPreference(followQueue); + uiTestUtils.addLocalFeedData(true); + DBWriter.clearQueue(getInstrumentation().getTargetContext()).get(); + String title = ((TextView) solo.getView(R.id.txtvTitle)).getText().toString(); + startLocalPlayback(); + assertTrue(solo.waitForText(title)); + assertTrue(solo.waitForActivity(MainActivity.class)); + startLocalPlayback(); + assertTrue(solo.waitForText(title)); + assertTrue(solo.waitForActivity(MainActivity.class)); + } + + public void testReplayEpisodeContinuousPlaybackOn() throws Exception { + replayEpisodeCheck(true); + } + + public void testReplayEpisodeContinuousPlaybackOff() throws Exception { + replayEpisodeCheck(false); + } +} diff --git a/src/instrumentationTest/de/test/antennapod/ui/UITestUtils.java b/src/instrumentationTest/de/test/antennapod/ui/UITestUtils.java index 4d1ca093a..a02d4e55c 100644 --- a/src/instrumentationTest/de/test/antennapod/ui/UITestUtils.java +++ b/src/instrumentationTest/de/test/antennapod/ui/UITestUtils.java @@ -2,10 +2,7 @@ package instrumentationTest.de.test.antennapod.ui; import android.content.Context; import android.graphics.Bitmap; -import de.danoeh.antennapod.feed.Feed; -import de.danoeh.antennapod.feed.FeedImage; -import de.danoeh.antennapod.feed.FeedItem; -import de.danoeh.antennapod.feed.FeedMedia; +import de.danoeh.antennapod.feed.*; import de.danoeh.antennapod.storage.DBWriter; import de.danoeh.antennapod.storage.PodDBAdapter; import instrumentationTest.de.test.antennapod.util.service.download.HTTPBin; @@ -13,6 +10,7 @@ import instrumentationTest.de.test.antennapod.util.syndication.feedgenerator.RSS import junit.framework.Assert; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; +import org.apache.commons.lang3.StringUtils; import java.io.File; import java.io.FileOutputStream; @@ -177,7 +175,8 @@ public class UITestUtils { for (FeedItem item : feed.getItems()) { if (item.hasMedia()) { FeedMedia media = item.getMedia(); - media.setFile_url(media.getDownload_url()); + int fileId = Integer.parseInt(StringUtils.substringAfter(media.getDownload_url(), "files/")); + media.setFile_url(server.accessFile(fileId).getAbsolutePath()); media.setDownloaded(true); } } @@ -189,5 +188,7 @@ public class UITestUtils { adapter.setCompleteFeed(hostedFeeds.toArray(new Feed[hostedFeeds.size()])); adapter.setQueue(queue); adapter.close(); + EventDistributor.getInstance().sendFeedUpdateBroadcast(); + EventDistributor.getInstance().sendQueueUpdateBroadcast(); } } diff --git a/src/instrumentationTest/de/test/antennapod/ui/UITestUtilsTest.java b/src/instrumentationTest/de/test/antennapod/ui/UITestUtilsTest.java index 9628c5522..88180152c 100644 --- a/src/instrumentationTest/de/test/antennapod/ui/UITestUtilsTest.java +++ b/src/instrumentationTest/de/test/antennapod/ui/UITestUtilsTest.java @@ -5,6 +5,7 @@ import de.danoeh.antennapod.feed.Feed; import de.danoeh.antennapod.feed.FeedItem; import org.apache.http.HttpStatus; +import java.io.File; import java.net.HttpURLConnection; import java.net.URL; import java.util.List; @@ -75,6 +76,8 @@ public class UITestUtilsTest extends InstrumentationTestCase { if (downloadEpisodes) { assertTrue(item.getMedia().isDownloaded()); assertNotNull(item.getMedia().getFile_url()); + File file = new File(item.getMedia().getFile_url()); + assertTrue(file.exists()); } } } diff --git a/src/instrumentationTest/de/test/antennapod/util/service/download/HTTPBin.java b/src/instrumentationTest/de/test/antennapod/util/service/download/HTTPBin.java index adde08ef0..fc5025b14 100644 --- a/src/instrumentationTest/de/test/antennapod/util/service/download/HTTPBin.java +++ b/src/instrumentationTest/de/test/antennapod/util/service/download/HTTPBin.java @@ -72,7 +72,7 @@ public class HTTPBin extends NanoHTTPD { } } - private synchronized File accessFile(int id) { + public synchronized File accessFile(int id) { if (id < 0 || id >= servedFiles.size()) { return null; } else { From d9a610fb61028845349d98c4e1574778a7cfc05e Mon Sep 17 00:00:00 2001 From: daniel oeh Date: Sun, 8 Jun 2014 13:57:32 +0200 Subject: [PATCH 15/24] Show queue size and number of unread items in navdrawer. closes #453 --- res/layout/nav_listitem.xml | 20 ++++++-- .../activity/AudioplayerActivity.java | 34 ++++++++----- .../antennapod/activity/MainActivity.java | 49 ++++++++++++------- .../antennapod/adapter/NavListAdapter.java | 17 +++++++ .../danoeh/antennapod/storage/DBReader.java | 48 ++++++++++++++++-- .../antennapod/storage/PodDBAdapter.java | 12 ++++- .../test/antennapod/storage/DBReaderTest.java | 40 +++++++++++++++ 7 files changed, 180 insertions(+), 40 deletions(-) diff --git a/res/layout/nav_listitem.xml b/res/layout/nav_listitem.xml index 585ee7528..3691026a1 100644 --- a/res/layout/nav_listitem.xml +++ b/res/layout/nav_listitem.xml @@ -1,9 +1,9 @@ + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + \ No newline at end of file diff --git a/src/de/danoeh/antennapod/activity/AudioplayerActivity.java b/src/de/danoeh/antennapod/activity/AudioplayerActivity.java index 2ffaae967..090c3f1f5 100644 --- a/src/de/danoeh/antennapod/activity/AudioplayerActivity.java +++ b/src/de/danoeh/antennapod/activity/AudioplayerActivity.java @@ -34,8 +34,6 @@ import de.danoeh.antennapod.storage.DBReader; import de.danoeh.antennapod.util.playback.ExternalMedia; import de.danoeh.antennapod.util.playback.Playable; -import java.util.List; - /** * Activity for playing audio files. */ @@ -624,20 +622,20 @@ public class AudioplayerActivity extends MediaplayerActivity { } } - private List feeds; - private AsyncTask> loadTask; + private DBReader.NavDrawerData navDrawerData; + private AsyncTask loadTask; private void loadData() { - loadTask = new AsyncTask>() { + loadTask = new AsyncTask() { @Override - protected List doInBackground(Void... params) { - return DBReader.getFeedList(AudioplayerActivity.this); + protected DBReader.NavDrawerData doInBackground(Void... params) { + return DBReader.getNavDrawerData(AudioplayerActivity.this); } @Override - protected void onPostExecute(List result) { + protected void onPostExecute(DBReader.NavDrawerData result) { super.onPostExecute(result); - feeds = result; + navDrawerData = result; if (navAdapter != null) { navAdapter.notifyDataSetChanged(); } @@ -667,8 +665,8 @@ public class AudioplayerActivity extends MediaplayerActivity { private final NavListAdapter.ItemAccess itemAccess = new NavListAdapter.ItemAccess() { @Override public int getCount() { - if (feeds != null) { - return feeds.size(); + if (navDrawerData != null) { + return navDrawerData.feeds.size(); } else { return 0; } @@ -676,8 +674,8 @@ public class AudioplayerActivity extends MediaplayerActivity { @Override public Feed getItem(int position) { - if (feeds != null && position < feeds.size()) { - return feeds.get(position); + if (navDrawerData != null && position < navDrawerData.feeds.size()) { + return navDrawerData.feeds.get(position); } else { return null; } @@ -687,5 +685,15 @@ public class AudioplayerActivity extends MediaplayerActivity { public int getSelectedItemIndex() { return -1; } + + @Override + public int getQueueSize() { + return (navDrawerData != null) ? navDrawerData.queueSize : 0; + } + + @Override + public int getNumberOfUnreadItems() { + return (navDrawerData != null) ? navDrawerData.numUnreadItems : 0; + } }; } diff --git a/src/de/danoeh/antennapod/activity/MainActivity.java b/src/de/danoeh/antennapod/activity/MainActivity.java index 898897bd8..257bea82d 100644 --- a/src/de/danoeh/antennapod/activity/MainActivity.java +++ b/src/de/danoeh/antennapod/activity/MainActivity.java @@ -39,7 +39,8 @@ public class MainActivity extends ActionBarActivity { private static final int EVENTS = EventDistributor.DOWNLOAD_HANDLED | EventDistributor.DOWNLOAD_QUEUED | EventDistributor.FEED_LIST_UPDATE - | EventDistributor.UNREAD_ITEMS_UPDATE; + | EventDistributor.UNREAD_ITEMS_UPDATE + | EventDistributor.QUEUE_UPDATE; public static final String PREF_NAME = "MainActivityPrefs"; public static final String PREF_IS_FIRST_LAUNCH = "prefMainActivityIsFirstLaunch"; @@ -147,7 +148,7 @@ public class MainActivity extends ActionBarActivity { } public List getFeeds() { - return feeds; + return (navDrawerData != null) ? navDrawerData.feeds : null; } private void loadFragment(int viewType, int relPos, Bundle args) { @@ -207,9 +208,9 @@ public class MainActivity extends ActionBarActivity { } public void loadFeedFragment(long feedID) { - if (feeds != null) { - for (int i = 0; i < feeds.size(); i++) { - if (feeds.get(i).getId() == feedID) { + if (navDrawerData != null) { + for (int i = 0; i < navDrawerData.feeds.size(); i++) { + if (navDrawerData.feeds.get(i).getId() == feedID) { loadFragment(NavListAdapter.VIEW_TYPE_SUBSCRIPTION, i, null); break; } @@ -279,7 +280,7 @@ public class MainActivity extends ActionBarActivity { EventDistributor.getInstance().register(contentUpdate); Intent intent = getIntent(); - if (feeds != null && intent.hasExtra(EXTRA_NAV_INDEX) && intent.hasExtra(EXTRA_NAV_TYPE)) { + if (navDrawerData != null && intent.hasExtra(EXTRA_NAV_INDEX) && intent.hasExtra(EXTRA_NAV_TYPE)) { handleNavIntent(); } @@ -322,15 +323,15 @@ public class MainActivity extends ActionBarActivity { return true; } - private List feeds; - private AsyncTask> loadTask; + private DBReader.NavDrawerData navDrawerData; + private AsyncTask loadTask; private int selectedNavListIndex = 0; private NavListAdapter.ItemAccess itemAccess = new NavListAdapter.ItemAccess() { @Override public int getCount() { - if (feeds != null) { - return feeds.size(); + if (navDrawerData != null) { + return navDrawerData.feeds.size(); } else { return 0; } @@ -338,8 +339,8 @@ public class MainActivity extends ActionBarActivity { @Override public Feed getItem(int position) { - if (feeds != null && position < feeds.size()) { - return feeds.get(position); + if (navDrawerData != null && position < navDrawerData.feeds.size()) { + return navDrawerData.feeds.get(position); } else { return null; } @@ -350,23 +351,33 @@ public class MainActivity extends ActionBarActivity { return selectedNavListIndex; } + @Override + public int getQueueSize() { + return (navDrawerData != null) ? navDrawerData.queueSize : 0; + } + + @Override + public int getNumberOfUnreadItems() { + return (navDrawerData != null) ? navDrawerData.numUnreadItems : 0; + } + }; private void loadData() { cancelLoadTask(); - loadTask = new AsyncTask>() { + loadTask = new AsyncTask() { @Override - protected List doInBackground(Void... params) { - return DBReader.getFeedList(MainActivity.this); + protected DBReader.NavDrawerData doInBackground(Void... params) { + return DBReader.getNavDrawerData(MainActivity.this); } @Override - protected void onPostExecute(List result) { - super.onPostExecute(result); - boolean handleIntent = (feeds == null); + protected void onPostExecute(DBReader.NavDrawerData result) { + super.onPostExecute(navDrawerData); + boolean handleIntent = (navDrawerData == null); - feeds = result; + navDrawerData = result; navAdapter.notifyDataSetChanged(); if (handleIntent) { diff --git a/src/de/danoeh/antennapod/adapter/NavListAdapter.java b/src/de/danoeh/antennapod/adapter/NavListAdapter.java index 928ec5dde..2e95a176b 100644 --- a/src/de/danoeh/antennapod/adapter/NavListAdapter.java +++ b/src/de/danoeh/antennapod/adapter/NavListAdapter.java @@ -113,6 +113,7 @@ public class NavListAdapter extends BaseAdapter { convertView = inflater.inflate(R.layout.nav_listitem, null); holder.title = (TextView) convertView.findViewById(R.id.txtvTitle); + holder.count = (TextView) convertView.findViewById(R.id.txtvCount); holder.image = (ImageView) convertView.findViewById(R.id.imgvCover); convertView.setTag(holder); } else { @@ -120,6 +121,17 @@ public class NavListAdapter extends BaseAdapter { } holder.title.setText(title); + + if (NAV_TITLES[position] == R.string.queue_label) { + holder.count.setVisibility(View.VISIBLE); + holder.count.setText(String.valueOf(itemAccess.getQueueSize())); + } else if (NAV_TITLES[position] == R.string.all_episodes_label) { + holder.count.setVisibility(View.VISIBLE); + holder.count.setText(String.valueOf(itemAccess.getNumberOfUnreadItems())); + } else { + holder.count.setVisibility(View.GONE); + } + holder.image.setImageDrawable(drawables[position]); return convertView; @@ -174,6 +186,7 @@ public class NavListAdapter extends BaseAdapter { static class NavHolder { TextView title; + TextView count; ImageView image; } @@ -193,6 +206,10 @@ public class NavListAdapter extends BaseAdapter { public Feed getItem(int position); public int getSelectedItemIndex(); + + public int getQueueSize(); + + public int getNumberOfUnreadItems(); } } diff --git a/src/de/danoeh/antennapod/storage/DBReader.java b/src/de/danoeh/antennapod/storage/DBReader.java index 4aeca7cd6..e49ea4f83 100644 --- a/src/de/danoeh/antennapod/storage/DBReader.java +++ b/src/de/danoeh/antennapod/storage/DBReader.java @@ -56,6 +56,14 @@ public final class DBReader { PodDBAdapter adapter = new PodDBAdapter(context); adapter.open(); + List result = getFeedList(adapter); + adapter.close(); + return result; + } + + private static List getFeedList(PodDBAdapter adapter) { + if (BuildConfig.DEBUG) + Log.d(TAG, "Extracting Feedlist"); Cursor feedlistCursor = adapter.getAllFeedsCursor(); List feeds = new ArrayList(feedlistCursor.getCount()); @@ -509,8 +517,8 @@ public final class DBReader { * Loads a list of FeedItems sorted by pubDate in descending order. * * @param context A context that is used for opening a database connection. - * @param limit The maximum number of episodes that should be loaded. - * */ + * @param limit The maximum number of episodes that should be loaded. + */ public static List getRecentlyPublishedEpisodes(Context context, int limit) { if (BuildConfig.DEBUG) Log.d(TAG, "Extracting recently published items list"); @@ -596,7 +604,8 @@ public final class DBReader { .getString(PodDBAdapter.KEY_DOWNLOADSTATUS_TITLE_INDEX); Date completionDate = new Date( logCursor - .getLong(PodDBAdapter.KEY_COMPLETION_DATE_INDEX)); + .getLong(PodDBAdapter.KEY_COMPLETION_DATE_INDEX) + ); downloadLog.add(new DownloadStatus(id, title, feedfileId, feedfileType, successful, DownloadError.fromCode(reason), completionDate, reasonDetailed)); @@ -787,7 +796,8 @@ public final class DBReader { cursor.getString(cursor .getColumnIndex(PodDBAdapter.KEY_DOWNLOAD_URL)), cursor.getInt(cursor - .getColumnIndex(PodDBAdapter.KEY_DOWNLOADED)) > 0); + .getColumnIndex(PodDBAdapter.KEY_DOWNLOADED)) > 0 + ); cursor.close(); return image; } @@ -865,4 +875,34 @@ public final class DBReader { adapter.close(); return empty; } + + /** + * Returns data necessary for displaying the navigation drawer. This includes + * the list of subscriptions, the number of items in the queue and the number of unread + * items. + * + * @param context A context that is used for opening a database connection. + */ + public static NavDrawerData getNavDrawerData(Context context) { + PodDBAdapter adapter = new PodDBAdapter(context); + adapter.open(); + List feeds = getFeedList(adapter); + int queueSize = adapter.getQueueSize(); + int numUnreadItems = adapter.getNumberOfUnreadItems(); + NavDrawerData result = new NavDrawerData(feeds, queueSize, numUnreadItems); + adapter.close(); + return result; + } + + public static class NavDrawerData { + public List feeds; + public int queueSize; + public int numUnreadItems; + + public NavDrawerData(List feeds, int queueSize, int numUnreadItems) { + this.feeds = feeds; + this.queueSize = queueSize; + this.numUnreadItems = numUnreadItems; + } + } } diff --git a/src/de/danoeh/antennapod/storage/PodDBAdapter.java b/src/de/danoeh/antennapod/storage/PodDBAdapter.java index 285709537..06c8b1fc9 100644 --- a/src/de/danoeh/antennapod/storage/PodDBAdapter.java +++ b/src/de/danoeh/antennapod/storage/PodDBAdapter.java @@ -1,6 +1,5 @@ package de.danoeh.antennapod.storage; -import android.app.backup.BackupManager; import android.content.ContentValues; import android.content.Context; import android.database.Cursor; @@ -1107,6 +1106,17 @@ public class PodDBAdapter { } + public int getQueueSize() { + final String query = String.format("SELECT COUNT(%s) FROM %s", KEY_ID, TABLE_NAME_QUEUE); + Cursor c = db.rawQuery(query, null); + int result = 0; + if (c.moveToFirst()) { + result = c.getInt(0); + } + c.close(); + return result; + } + public final int getNumberOfUnreadItems() { final String query = "SELECT COUNT(DISTINCT " + KEY_ID + ") AS count FROM " + TABLE_NAME_FEED_ITEMS + " WHERE " + KEY_READ + " = 0"; diff --git a/src/instrumentationTest/de/test/antennapod/storage/DBReaderTest.java b/src/instrumentationTest/de/test/antennapod/storage/DBReaderTest.java index cb854b88d..c42c7a0cc 100644 --- a/src/instrumentationTest/de/test/antennapod/storage/DBReaderTest.java +++ b/src/instrumentationTest/de/test/antennapod/storage/DBReaderTest.java @@ -365,4 +365,44 @@ public class DBReaderTest extends InstrumentationTestCase { assertEquals("Wrong entry at index " + i, feeds.get(i).getId(), statistics.get(i).getFeedID()); } } + + public void testGetNavDrawerDataQueueEmptyNoUnreadItems() { + final Context context = getInstrumentation().getTargetContext(); + final int NUM_FEEDS = 10; + final int NUM_ITEMS = 10; + List feeds = DBTestUtils.saveFeedlist(context, NUM_FEEDS, NUM_ITEMS, true); + DBReader.NavDrawerData navDrawerData = DBReader.getNavDrawerData(context); + assertEquals(NUM_FEEDS, navDrawerData.feeds.size()); + assertEquals(0, navDrawerData.numUnreadItems); + assertEquals(0, navDrawerData.queueSize); + } + + public void testGetNavDrawerDataQueueNotEmptyWithUnreadItems() { + final Context context = getInstrumentation().getTargetContext(); + final int NUM_FEEDS = 10; + final int NUM_ITEMS = 10; + final int NUM_QUEUE = 1; + final int NUM_UNREAD = 2; + List feeds = DBTestUtils.saveFeedlist(context, NUM_FEEDS, NUM_ITEMS, true); + PodDBAdapter adapter = new PodDBAdapter(context); + adapter.open(); + for (int i = 0; i < NUM_UNREAD; i++) { + FeedItem item = feeds.get(0).getItems().get(i); + item.setRead(false); + adapter.setSingleFeedItem(item); + } + List queue = new ArrayList(); + for (int i = 0; i < NUM_QUEUE; i++) { + FeedItem item = feeds.get(1).getItems().get(i); + queue.add(item); + } + adapter.setQueue(queue); + + adapter.close(); + + DBReader.NavDrawerData navDrawerData = DBReader.getNavDrawerData(context); + assertEquals(NUM_FEEDS, navDrawerData.feeds.size()); + assertEquals(NUM_UNREAD, navDrawerData.numUnreadItems); + assertEquals(NUM_QUEUE, navDrawerData.queueSize); + } } From 80066bcf01754e253d47eb8cc7ba668236850bf6 Mon Sep 17 00:00:00 2001 From: daniel oeh Date: Sun, 8 Jun 2014 14:02:00 +0200 Subject: [PATCH 16/24] Replaced deprecated symbols in build.gradle --- build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index 7ae35ae61..7e1f7809b 100644 --- a/build.gradle +++ b/build.gradle @@ -39,7 +39,7 @@ android { defaultConfig { minSdkVersion 10 targetSdkVersion 19 - testPackageName "de.test.antennapod" + testApplicationId "de.test.antennapod" testInstrumentationRunner "instrumentationTest.de.test.antennapod.AntennaPodTestRunner" } @@ -82,7 +82,7 @@ android { buildTypes { debug { - packageNameSuffix ".debug" + applicationIdSuffix ".debug" } release { runProguard true From 60ded0e12e4dbe389e069e0958cf1d593e321864 Mon Sep 17 00:00:00 2001 From: daniel oeh Date: Mon, 9 Jun 2014 11:07:56 +0200 Subject: [PATCH 17/24] Crop by character in navdrawer. fixes #457 --- res/layout/nav_feedlistitem.xml | 1 + res/layout/nav_listitem.xml | 1 + 2 files changed, 2 insertions(+) diff --git a/res/layout/nav_feedlistitem.xml b/res/layout/nav_feedlistitem.xml index e01087077..d94c9ada1 100644 --- a/res/layout/nav_feedlistitem.xml +++ b/res/layout/nav_feedlistitem.xml @@ -24,6 +24,7 @@ android:id="@+id/txtvTitle" android:lines="1" android:ellipsize="end" + android:singleLine="true" android:layout_centerVertical="true" android:textColor="?android:attr/textColorPrimary" android:textSize="@dimen/text_size_navdrawer" diff --git a/res/layout/nav_listitem.xml b/res/layout/nav_listitem.xml index 3691026a1..9d70e7d7c 100644 --- a/res/layout/nav_listitem.xml +++ b/res/layout/nav_listitem.xml @@ -24,6 +24,7 @@ android:id="@+id/txtvTitle" android:lines="1" android:ellipsize="end" + android:singleLine="true" android:layout_centerVertical="true" android:textColor="?android:attr/textColorPrimary" android:textSize="@dimen/text_size_navdrawer" From b261de232fd64217f1d8032f171412c2e79392c4 Mon Sep 17 00:00:00 2001 From: daniel oeh Date: Mon, 9 Jun 2014 11:26:17 +0200 Subject: [PATCH 18/24] Prepared release of version 0.9.9.1 --- AndroidManifest.xml | 4 ++-- CHANGELOG.md | 4 ++++ assets/about.html | 2 +- pom.xml | 2 +- src/de/danoeh/antennapod/AppConfig.java | 3 ++- 5 files changed, 10 insertions(+), 5 deletions(-) diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 4d7e8896d..b06a9b5c7 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -1,8 +1,8 @@ + android:versionCode="38" + android:versionName="0.9.9.1"> diff --git a/CHANGELOG.md b/CHANGELOG.md index 709b95fb1..1cce0c57a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Change Log ========== +Version 0.9.9.1 +--------------- +* Several bugfixes and improvements + Version 0.9.9.0 --------------- * New user interface diff --git a/assets/about.html b/assets/about.html index e6ffdc2ed..6d3114aff 100644 --- a/assets/about.html +++ b/assets/about.html @@ -41,7 +41,7 @@