diff options
Diffstat (limited to 'app/src')
15 files changed, 880 insertions, 366 deletions
diff --git a/app/src/androidTest/java/de/test/antennapod/ui/PlaybackSonicTest.java b/app/src/androidTest/java/de/test/antennapod/ui/PlaybackSonicTest.java new file mode 100644 index 000000000..17ec87aa3 --- /dev/null +++ b/app/src/androidTest/java/de/test/antennapod/ui/PlaybackSonicTest.java @@ -0,0 +1,363 @@ +package de.test.antennapod.ui; + +import android.annotation.TargetApi; +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.os.Build; +import android.preference.PreferenceManager; +import android.test.ActivityInstrumentationTestCase2; +import android.test.FlakyTest; +import android.view.View; +import android.widget.ImageButton; +import android.widget.ListView; + +import com.robotium.solo.Solo; +import com.robotium.solo.Timeout; + +import java.util.List; + +import de.danoeh.antennapod.R; +import de.danoeh.antennapod.activity.MainActivity; +import de.danoeh.antennapod.core.feed.FeedItem; +import de.danoeh.antennapod.core.feed.FeedMedia; +import de.danoeh.antennapod.core.preferences.UserPreferences; +import de.danoeh.antennapod.core.service.playback.PlaybackService; +import de.danoeh.antennapod.core.service.playback.PlayerStatus; +import de.danoeh.antennapod.core.storage.DBReader; +import de.danoeh.antennapod.core.storage.DBWriter; +import de.danoeh.antennapod.core.storage.PodDBAdapter; +import de.danoeh.antennapod.core.util.playback.Playable; +import de.danoeh.antennapod.core.util.playback.PlaybackController; + +/** + * test cases for starting and ending playback from the MainActivity and AudioPlayerActivity + */ +@TargetApi(Build.VERSION_CODES.JELLY_BEAN) +public class PlaybackSonicTest extends ActivityInstrumentationTestCase2<MainActivity> { + + private static final String TAG = PlaybackTest.class.getSimpleName(); + public static final int EPISODES_DRAWER_LIST_INDEX = 1; + public static final int QUEUE_DRAWER_LIST_INDEX = 0; + + private Solo solo; + private UITestUtils uiTestUtils; + + private Context context; + + private PlaybackController controller; + protected FeedMedia currentMedia; + + private PlaybackController createController(Activity activity) { + return new PlaybackController(activity, false) { + + @Override + public void setupGUI() { + } + + @Override + public void onPositionObserverUpdate() { + } + + @Override + public void onBufferStart() { + } + + @Override + public void onBufferEnd() { + } + + @Override + public void onBufferUpdate(float progress) { + } + + @Override + public void handleError(int code) { + } + + @Override + public void onReloadNotification(int code) { + } + + @Override + public void onSleepTimerUpdate() { + } + + @Override + public ImageButton getPlayButton() { + return null; + } + + @Override + public void postStatusMsg(int msg) { + } + + @Override + public void clearStatusMsg() { + } + + @Override + public boolean loadMediaInfo() { + Playable playable = controller.getMedia(); + if(playable == null) { + currentMedia = null; + return true; + } else if(playable instanceof FeedMedia) { + currentMedia = (FeedMedia) playable; + return true; + } else { + return false; + } + } + + @Override + public void onAwaitingVideoSurface() { + } + + @Override + public void onServiceQueried() { + } + + @Override + public void onShutdownNotification() { + } + + @Override + public void onPlaybackEnd() { + currentMedia = null; + } + + @Override + public void onPlaybackSpeedChange() { + } + + @Override + protected void setScreenOn(boolean enable) { + } + }; + } + + public PlaybackSonicTest() { + super(MainActivity.class); + } + + @Override + public void setUp() throws Exception { + super.setUp(); + + PodDBAdapter.deleteDatabase(); + + context = getInstrumentation().getTargetContext(); + + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + prefs.edit() + .clear() + .putBoolean(UserPreferences.PREF_UNPAUSE_ON_HEADSET_RECONNECT, false) + .putBoolean(UserPreferences.PREF_PAUSE_ON_HEADSET_DISCONNECT, false) + .putBoolean(UserPreferences.PREF_SONIC, true) + .commit(); + + controller = createController(getActivity()); + controller.init(); + + solo = new Solo(getInstrumentation(), getActivity()); + + uiTestUtils = new UITestUtils(context); + uiTestUtils.setup(); + + // create database + PodDBAdapter adapter = PodDBAdapter.getInstance(); + adapter.open(); + adapter.close(); + } + + @Override + public void tearDown() throws Exception { + controller.release(); + solo.finishOpenedActivities(); + uiTestUtils.tearDown(); + + // shut down playback service + skipEpisode(); + context.sendBroadcast(new Intent(PlaybackService.ACTION_SHUTDOWN_PLAYBACK_SERVICE)); + + super.tearDown(); + } + + private void openNavDrawer() { + solo.clickOnScreen(50, 50); + } + + private void setContinuousPlaybackPreference(boolean value) { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + prefs.edit().putBoolean(UserPreferences.PREF_FOLLOW_QUEUE, value).commit(); + } + + private void skipEpisode() { + Intent skipIntent = new Intent(PlaybackService.ACTION_SKIP_CURRENT_EPISODE); + context.sendBroadcast(skipIntent); + } + + private void startLocalPlayback() { + openNavDrawer(); + // if we try to just click on plain old text then + // we might wind up clicking on the fragment title and not + // the drawer element like we want. + ListView drawerView = (ListView)solo.getView(R.id.nav_list); + // this should be 'Episodes' + View targetView = drawerView.getChildAt(EPISODES_DRAWER_LIST_INDEX); + solo.waitForView(targetView); + solo.clickOnView(targetView); + solo.waitForText(solo.getString(R.string.all_episodes_short_label)); + solo.clickOnText(solo.getString(R.string.all_episodes_short_label)); + + final List<FeedItem> episodes = DBReader.getRecentlyPublishedEpisodes(10); + assertTrue(solo.waitForView(solo.getView(R.id.butSecondaryAction))); + + solo.clickOnView(solo.getView(R.id.butSecondaryAction)); + long mediaId = episodes.get(0).getMedia().getId(); + boolean playing = solo.waitForCondition(() -> { + if (currentMedia != null) { + return currentMedia.getId() == mediaId; + } else { + return false; + } + }, Timeout.getSmallTimeout()); + assertTrue(playing); + } + + private void startLocalPlaybackFromQueue() { + openNavDrawer(); + + // if we try to just click on plain old text then + // we might wind up clicking on the fragment title and not + // the drawer element like we want. + ListView drawerView = (ListView)solo.getView(R.id.nav_list); + // this should be 'Queue' + View targetView = drawerView.getChildAt(QUEUE_DRAWER_LIST_INDEX); + solo.waitForView(targetView); + solo.clickOnView(targetView); + + assertTrue(solo.waitForView(solo.getView(R.id.butSecondaryAction))); + final List<FeedItem> queue = DBReader.getQueue(); + solo.clickOnImageButton(1); + assertTrue(solo.waitForView(solo.getView(R.id.butPlay))); + long mediaId = queue.get(0).getMedia().getId(); + boolean playing = solo.waitForCondition(() -> { + if(currentMedia != null) { + return currentMedia.getId() == mediaId; + } else { + return false; + } + }, Timeout.getSmallTimeout()); + assertTrue(playing); + } + + public void testStartLocal() throws Exception { + uiTestUtils.addLocalFeedData(true); + DBWriter.clearQueue().get(); + startLocalPlayback(); + } + + public void testContinousPlaybackOffSingleEpisode() throws Exception { + setContinuousPlaybackPreference(false); + uiTestUtils.addLocalFeedData(true); + DBWriter.clearQueue().get(); + startLocalPlayback(); + } + + @FlakyTest(tolerance = 3) + public void testContinousPlaybackOffMultipleEpisodes() throws Exception { + setContinuousPlaybackPreference(false); + uiTestUtils.addLocalFeedData(true); + List<FeedItem> queue = DBReader.getQueue(); + final FeedItem first = queue.get(0); + + startLocalPlaybackFromQueue(); + boolean stopped = solo.waitForCondition(() -> { + if (currentMedia != null) { + return currentMedia.getId() != first.getMedia().getId(); + } else { + return false; + } + }, Timeout.getSmallTimeout()); + assertTrue(stopped); + Thread.sleep(1000); + PlayerStatus status = controller.getStatus(); + assertFalse(status.equals(PlayerStatus.PLAYING)); + } + + @FlakyTest(tolerance = 3) + public void testContinuousPlaybackOnMultipleEpisodes() throws Exception { + setContinuousPlaybackPreference(true); + uiTestUtils.addLocalFeedData(true); + List<FeedItem> queue = DBReader.getQueue(); + final FeedItem first = queue.get(0); + final FeedItem second = queue.get(1); + + startLocalPlaybackFromQueue(); + boolean firstPlaying = solo.waitForCondition(() -> { + if (currentMedia != null) { + return currentMedia.getId() == first.getMedia().getId(); + } else { + return false; + } + }, Timeout.getSmallTimeout()); + assertTrue(firstPlaying); + boolean secondPlaying = solo.waitForCondition(() -> { + if (currentMedia != null) { + return currentMedia.getId() == second.getMedia().getId(); + } else { + return false; + } + }, Timeout.getLargeTimeout()); + assertTrue(secondPlaying); + } + + /** + * Check if an episode can be played twice without problems. + */ + private void replayEpisodeCheck(boolean followQueue) throws Exception { + setContinuousPlaybackPreference(followQueue); + uiTestUtils.addLocalFeedData(true); + DBWriter.clearQueue().get(); + final List<FeedItem> episodes = DBReader.getRecentlyPublishedEpisodes(10); + + startLocalPlayback(); + long mediaId = episodes.get(0).getMedia().getId(); + boolean startedPlaying = solo.waitForCondition(() -> { + if (currentMedia != null) { + return currentMedia.getId() == mediaId; + } else { + return false; + } + }, Timeout.getSmallTimeout()); + assertTrue(startedPlaying); + + boolean stoppedPlaying = solo.waitForCondition(() -> { + return currentMedia == null || currentMedia.getId() != mediaId; + }, Timeout.getLargeTimeout()); + assertTrue(stoppedPlaying); + + startLocalPlayback(); + boolean startedReplay = solo.waitForCondition(() -> { + if(currentMedia != null) { + return currentMedia.getId() == mediaId; + } else { + return false; + } + }, Timeout.getLargeTimeout()); + assertTrue(startedReplay); + } + + public void testReplayEpisodeContinuousPlaybackOn() throws Exception { + replayEpisodeCheck(true); + } + + public void testReplayEpisodeContinuousPlaybackOff() throws Exception { + replayEpisodeCheck(false); + } + + +} diff --git a/app/src/main/assets/LICENSE_ANTENNAPOD_AUDIOPLAYER.txt b/app/src/main/assets/LICENSE_ANTENNAPOD_AUDIOPLAYER.txt new file mode 100644 index 000000000..8dada3eda --- /dev/null +++ b/app/src/main/assets/LICENSE_ANTENNAPOD_AUDIOPLAYER.txt @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/app/src/main/java/de/danoeh/antennapod/activity/AboutActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/AboutActivity.java index df764e829..fe31ccda8 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/AboutActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/AboutActivity.java @@ -57,9 +57,13 @@ public class AboutActivity extends ActionBarActivity { @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { - url = url.replace("file:///android_asset/", ""); - loadAsset(url); - return true; + if(url.startsWith("http")) { + return false; + } else { + url = url.replace("file:///android_asset/", ""); + loadAsset(url); + return true; + } } }); @@ -126,8 +130,10 @@ public class AboutActivity extends ActionBarActivity { @Override public void onBackPressed() { - if(showingLicense) { + if(showingLicense || webview.canGoBack()) { loadAsset("about.html"); + } else if(webview.canGoBack()) { + webview.goBack(); } else { super.onBackPressed(); } diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/VariableSpeedDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/VariableSpeedDialog.java index 8eba51540..b01b13492 100644 --- a/app/src/main/java/de/danoeh/antennapod/dialog/VariableSpeedDialog.java +++ b/app/src/main/java/de/danoeh/antennapod/dialog/VariableSpeedDialog.java @@ -6,18 +6,19 @@ 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.core.preferences.UserPreferences; import java.util.Arrays; import java.util.List; +import de.danoeh.antennapod.R; +import de.danoeh.antennapod.core.preferences.UserPreferences; + public class VariableSpeedDialog { private VariableSpeedDialog() { } public static void showDialog(final Context context) { - if (com.aocate.media.MediaPlayer.isPrestoLibraryInstalled(context)) { + if (org.antennapod.audio.MediaPlayer.isPrestoLibraryInstalled(context)) { showSpeedSelectorDialog(context); } else { showGetPluginDialog(context); diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java index 5d0edb638..5aed66013 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java @@ -4,10 +4,10 @@ import android.app.Activity; import android.content.Context; import android.content.DialogInterface; import android.content.SharedPreferences; -import android.os.AsyncTask; import android.os.Bundle; import android.os.Handler; import android.support.v4.app.Fragment; +import android.support.v4.util.Pair; import android.support.v4.view.MenuItemCompat; import android.support.v7.widget.SearchView; import android.util.Log; @@ -38,7 +38,6 @@ import de.danoeh.antennapod.core.feed.EventDistributor; import de.danoeh.antennapod.core.feed.Feed; import de.danoeh.antennapod.core.feed.FeedItem; import de.danoeh.antennapod.core.feed.FeedMedia; -import de.danoeh.antennapod.core.feed.QueueEvent; import de.danoeh.antennapod.core.service.download.DownloadService; import de.danoeh.antennapod.core.service.download.Downloader; import de.danoeh.antennapod.core.storage.DBReader; @@ -49,6 +48,10 @@ import de.danoeh.antennapod.core.storage.DownloadRequester; import de.danoeh.antennapod.core.util.LongList; import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler; import de.danoeh.antennapod.menuhandler.MenuItemUtils; +import rx.Observable; +import rx.Subscription; +import rx.android.schedulers.AndroidSchedulers; +import rx.schedulers.Schedulers; /** * Shows unread or recently published episodes @@ -90,6 +93,8 @@ public class AllEpisodesFragment extends Fragment { private boolean isUpdatingFeeds; + protected Subscription subscription; + public AllEpisodesFragment() { // by default we show all the episodes this(false, DEFAULT_PREF_NAME); @@ -113,7 +118,7 @@ public class AllEpisodesFragment extends Fragment { @Override public void onResume() { super.onResume(); - startItemLoader(); + loadItems(); } @Override @@ -140,7 +145,9 @@ public class AllEpisodesFragment extends Fragment { public void onStop() { super.onStop(); EventDistributor.getInstance().unregister(contentUpdate); - stopItemLoader(); + if(subscription != null) { + subscription.unsubscribe(); + } } @Override @@ -436,7 +443,7 @@ public class AllEpisodesFragment extends Fragment { @Override public void update(EventDistributor eventDistributor, Integer arg) { if ((arg & EVENTS) != 0) { - startItemLoader(); + loadItems(); if (isUpdatingFeeds != updateRefreshMenuItemChecker.isRefreshing()) { getActivity().supportInvalidateOptionsMenu(); } @@ -453,69 +460,43 @@ public class AllEpisodesFragment extends Fragment { } } - private ItemLoader itemLoader; - - protected void startItemLoader() { - if (itemLoader != null) { - itemLoader.cancel(true); + protected void loadItems() { + if(subscription != null) { + subscription.unsubscribe(); } - itemLoader = new ItemLoader(); - itemLoader.execute(); - } - - protected void stopItemLoader() { - if (itemLoader != null) { - itemLoader.cancel(true); + if (viewsCreated && !itemsLoaded) { + listView.setVisibility(View.GONE); + txtvEmpty.setVisibility(View.GONE); + progLoading.setVisibility(View.VISIBLE); } + subscription = Observable.defer(() -> Observable.just(loadData())) + .subscribeOn(Schedulers.newThread()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(data -> { + listView.setVisibility(View.VISIBLE); + progLoading.setVisibility(View.GONE); + if (data != null) { + episodes = data.first; + queuedItemsIds = data.second; + itemsLoaded = true; + if (viewsCreated && activity.get() != null) { + onFragmentLoaded(); + } + } + }, error -> { + Log.e(TAG, Log.getStackTraceString(error)); + }); } - private class ItemLoader extends AsyncTask<Void, Void, Object[]> { - - @Override - protected void onPreExecute() { - super.onPreExecute(); - if (viewsCreated && !itemsLoaded) { - listView.setVisibility(View.GONE); - txtvEmpty.setVisibility(View.GONE); - progLoading.setVisibility(View.VISIBLE); - } - } - - @Override - protected Object[] doInBackground(Void... params) { - Context context = activity.get(); - if (context != null) { - if(showOnlyNewEpisodes) { - return new Object[] { - DBReader.getNewItemsList(), - DBReader.getQueueIDList(), - null // see ItemAccess.isNew - }; - } else { - return new Object[]{ - DBReader.getRecentlyPublishedEpisodes(RECENT_EPISODES_LIMIT), - DBReader.getQueueIDList() - }; - } - } else { - return null; - } - } - - @Override - protected void onPostExecute(Object[] lists) { - super.onPostExecute(lists); - listView.setVisibility(View.VISIBLE); - progLoading.setVisibility(View.GONE); - - if (lists != null) { - episodes = (List<FeedItem>) lists[0]; - queuedItemsIds = (LongList) lists[1]; - itemsLoaded = true; - if (viewsCreated && activity.get() != null) { - onFragmentLoaded(); - } - } + private Pair<List<FeedItem>,LongList> loadData() { + List<FeedItem> items; + if(showOnlyNewEpisodes) { + items = DBReader.getNewItemsList(); + } else { + items = DBReader.getRecentlyPublishedEpisodes(RECENT_EPISODES_LIMIT); } + LongList queuedIds = DBReader.getQueueIDList(); + return Pair.create(items, queuedIds); } + } diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java index 278928f3d..c5b582d3a 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java @@ -1,14 +1,12 @@ package de.danoeh.antennapod.fragment; import android.app.Activity; -import android.content.Context; -import android.os.AsyncTask; import android.os.Bundle; import android.support.v4.app.ListFragment; +import android.util.Log; import android.view.View; import android.widget.ListView; -import java.util.Collections; import java.util.List; import de.danoeh.antennapod.R; @@ -18,11 +16,18 @@ import de.danoeh.antennapod.core.feed.EventDistributor; import de.danoeh.antennapod.core.feed.FeedItem; import de.danoeh.antennapod.core.storage.DBReader; import de.danoeh.antennapod.core.storage.DBWriter; +import rx.Observable; +import rx.Subscription; +import rx.android.schedulers.AndroidSchedulers; +import rx.schedulers.Schedulers; /** * Displays all running downloads and provides a button to delete them */ public class CompletedDownloadsFragment extends ListFragment { + + private static final String TAG = CompletedDownloadsFragment.class.getSimpleName(); + private static final int EVENTS = EventDistributor.DOWNLOAD_HANDLED | EventDistributor.DOWNLOADLOG_UPDATE | @@ -34,11 +39,12 @@ public class CompletedDownloadsFragment extends ListFragment { private boolean viewCreated = false; private boolean itemsLoaded = false; + private Subscription subscription; + @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - - startItemLoader(); + loadItems(); } @Override @@ -51,13 +57,17 @@ public class CompletedDownloadsFragment extends ListFragment { public void onStop() { super.onStop(); EventDistributor.getInstance().unregister(contentUpdate); - stopItemLoader(); + if(subscription != null) { + subscription.unsubscribe(); + } } @Override public void onDetach() { super.onDetach(); - stopItemLoader(); + if(subscription != null) { + subscription.unsubscribe(); + } } @Override @@ -65,7 +75,9 @@ public class CompletedDownloadsFragment extends ListFragment { super.onDestroyView(); listAdapter = null; viewCreated = false; - stopItemLoader(); + if(subscription != null) { + subscription.unsubscribe(); + } } @Override @@ -132,56 +144,32 @@ public class CompletedDownloadsFragment extends ListFragment { @Override public void update(EventDistributor eventDistributor, Integer arg) { if ((arg & EVENTS) != 0) { - startItemLoader(); + loadItems(); } } }; - private ItemLoader itemLoader; - - private void startItemLoader() { - if (itemLoader != null) { - itemLoader.cancel(true); + private void loadItems() { + if(subscription != null) { + subscription.unsubscribe(); } - itemLoader = new ItemLoader(); - itemLoader.execute(); - } - - private void stopItemLoader() { - if (itemLoader != null) { - itemLoader.cancel(true); + if (!itemsLoaded && viewCreated) { + setListShown(false); } + subscription = Observable.defer(() -> Observable.just(DBReader.getDownloadedItems())) + .subscribeOn(Schedulers.newThread()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(result -> { + if (result != null) { + items = result; + itemsLoaded = true; + if (viewCreated && getActivity() != null) { + onFragmentLoaded(); + } + } + }, error -> { + Log.e(TAG, Log.getStackTraceString(error)); + }); } - private class ItemLoader extends AsyncTask<Void, Void, List<FeedItem>> { - - @Override - protected void onPreExecute() { - super.onPreExecute(); - if (!itemsLoaded && viewCreated) { - setListShown(false); - } - } - - @Override - protected void onPostExecute(List<FeedItem> results) { - super.onPostExecute(results); - if (results != null) { - items = results; - itemsLoaded = true; - if (viewCreated && getActivity() != null) { - onFragmentLoaded(); - } - } - } - - @Override - protected List<FeedItem> doInBackground(Void... params) { - Context context = getActivity(); - if (context != null) { - return DBReader.getDownloadedItems(); - } - return Collections.emptyList(); - } - } } diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/DownloadLogFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/DownloadLogFragment.java index da2c05a69..669c6ac49 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/DownloadLogFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/DownloadLogFragment.java @@ -1,11 +1,10 @@ package de.danoeh.antennapod.fragment; -import android.content.Context; import android.content.res.TypedArray; -import android.os.AsyncTask; import android.os.Bundle; import android.support.v4.app.ListFragment; import android.support.v4.view.MenuItemCompat; +import android.util.Log; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; @@ -20,6 +19,10 @@ import de.danoeh.antennapod.core.feed.EventDistributor; import de.danoeh.antennapod.core.service.download.DownloadStatus; import de.danoeh.antennapod.core.storage.DBReader; import de.danoeh.antennapod.core.storage.DBWriter; +import rx.Observable; +import rx.Subscription; +import rx.android.schedulers.AndroidSchedulers; +import rx.schedulers.Schedulers; /** * Shows the download log @@ -34,19 +37,23 @@ public class DownloadLogFragment extends ListFragment { private boolean viewsCreated = false; private boolean itemsLoaded = false; + private Subscription subscription; + @Override public void onStart() { super.onStart(); setHasOptionsMenu(true); EventDistributor.getInstance().register(contentUpdate); - startItemLoader(); + loadItems(); } @Override public void onStop() { super.onStop(); EventDistributor.getInstance().unregister(contentUpdate); - stopItemLoader(); + if(subscription != null) { + subscription.unsubscribe(); + } } @Override @@ -93,27 +100,11 @@ public class DownloadLogFragment extends ListFragment { @Override public void update(EventDistributor eventDistributor, Integer arg) { if ((arg & EventDistributor.DOWNLOADLOG_UPDATE) != 0) { - startItemLoader(); + loadItems(); } } }; - private ItemLoader itemLoader; - - private void startItemLoader() { - if (itemLoader != null) { - itemLoader.cancel(true); - } - itemLoader = new ItemLoader(); - itemLoader.execute(); - } - - private void stopItemLoader() { - if (itemLoader != null) { - itemLoader.cancel(true); - } - } - @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { super.onCreateOptionsMenu(menu, inflater); @@ -152,27 +143,24 @@ public class DownloadLogFragment extends ListFragment { } } - private class ItemLoader extends AsyncTask<Void, Void, List<DownloadStatus>> { - - @Override - protected void onPostExecute(List<DownloadStatus> downloadStatuses) { - super.onPostExecute(downloadStatuses); - if (downloadStatuses != null) { - downloadLog = downloadStatuses; - itemsLoaded = true; - if (viewsCreated) { - onFragmentLoaded(); - } - } - } - - @Override - protected List<DownloadStatus> doInBackground(Void... params) { - Context context = getActivity(); - if (context != null) { - return DBReader.getDownloadLog(); - } - return null; + private void loadItems() { + if(subscription != null) { + subscription.unsubscribe(); } + subscription = Observable.defer(() -> Observable.just(DBReader.getDownloadLog())) + .subscribeOn(Schedulers.newThread()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(result -> { + if (result != null) { + downloadLog = result; + itemsLoaded = true; + if (viewsCreated) { + onFragmentLoaded(); + } + } + }, error -> { + Log.e(TAG, Log.getStackTraceString(error)); + }); } + } diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ItemlistFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ItemlistFragment.java index 38c9b645a..3d0ff66f7 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/ItemlistFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItemlistFragment.java @@ -7,12 +7,12 @@ import android.content.Intent; import android.content.res.TypedArray; import android.graphics.Color; import android.graphics.LightingColorFilter; -import android.os.AsyncTask; import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.support.v4.app.Fragment; import android.support.v4.app.ListFragment; +import android.support.v4.util.Pair; import android.support.v4.view.MenuItemCompat; import android.support.v7.app.ActionBarActivity; import android.support.v7.widget.SearchView; @@ -73,6 +73,10 @@ import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler; import de.danoeh.antennapod.menuhandler.FeedMenuHandler; import de.danoeh.antennapod.menuhandler.MenuItemUtils; import de.greenrobot.event.EventBus; +import rx.Observable; +import rx.Subscription; +import rx.android.schedulers.AndroidSchedulers; +import rx.schedulers.Schedulers; /** * Displays a list of FeedItems. @@ -111,6 +115,8 @@ public class ItemlistFragment extends ListFragment { private TextView txtvInformation; + private Subscription subscription; + /** * Creates new ItemlistFragment which shows the Feeditems of a specific * feed. Sets 'showFeedtitle' to false @@ -156,7 +162,9 @@ public class ItemlistFragment extends ListFragment { super.onStop(); EventDistributor.getInstance().unregister(contentUpdate); EventBus.getDefault().unregister(this); - stopItemLoader(); + if(subscription != null) { + subscription.unsubscribe(); + } } @Override @@ -164,13 +172,15 @@ public class ItemlistFragment extends ListFragment { super.onResume(); Log.d(TAG, "onResume()"); updateProgressBarVisibility(); - startItemLoader(); + loadItems(); } @Override public void onDetach() { super.onDetach(); - stopItemLoader(); + if(subscription != null) { + subscription.unsubscribe(); + } } @Override @@ -385,13 +395,13 @@ public class ItemlistFragment extends ListFragment { public void onEvent(QueueEvent event) { Log.d(TAG, "onEvent(" + event + ")"); - startItemLoader(); + loadItems(); } public void onEvent(FeedEvent event) { Log.d(TAG, "onEvent(" + event + ")"); if(event.feedId == feedID) { - startItemLoader(); + loadItems(); } } @@ -404,7 +414,7 @@ public class ItemlistFragment extends ListFragment { if ((EventDistributor.DOWNLOAD_QUEUED & arg) != 0) { updateProgressBarVisibility(); } else { - startItemLoader(); + loadItems(); updateProgressBarVisibility(); } } @@ -608,51 +618,37 @@ public class ItemlistFragment extends ListFragment { } }; - private ItemLoader itemLoader; - private void startItemLoader() { - if (itemLoader != null) { - itemLoader.cancel(true); + private void loadItems() { + if(subscription != null) { + subscription.unsubscribe(); } - itemLoader = new ItemLoader(); - itemLoader.execute(feedID); - } - private void stopItemLoader() { - if (itemLoader != null) { - itemLoader.cancel(true); - } + subscription = Observable.defer(() -> Observable.just(loadData())) + .subscribeOn(Schedulers.newThread()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(result -> { + if (result != null) { + feed = result.first; + queuedItemsIds = result.second; + itemsLoaded = true; + if (viewsCreated) { + onFragmentLoaded(); + } + } + }, error -> { + Log.e(TAG, Log.getStackTraceString(error)); + }); } - private class ItemLoader extends AsyncTask<Long, Void, Object[]> { - @Override - protected Object[] doInBackground(Long... params) { - long feedID = params[0]; - Context context = getActivity(); - if (context != null) { - Feed feed = DBReader.getFeed(feedID); - if(feed != null && feed.getItemFilter() != null) { - FeedItemFilter filter = feed.getItemFilter(); - feed.setItems(filter.filter(context, feed.getItems())); - } - LongList queuedItemsIds = DBReader.getQueueIDList(); - return new Object[] { feed, queuedItemsIds }; - } else { - return null; - } - } - - @Override - protected void onPostExecute(Object[] res) { - super.onPostExecute(res); - if (res != null) { - feed = (Feed) res[0]; - queuedItemsIds = (LongList) res[1]; - itemsLoaded = true; - if (viewsCreated) { - onFragmentLoaded(); - } - } + private Pair<Feed, LongList> loadData() { + Feed feed = DBReader.getFeed(feedID); + if(feed != null && feed.getItemFilter() != null) { + FeedItemFilter filter = feed.getItemFilter(); + feed.setItems(filter.filter(feed.getItems())); } + LongList queuedItemsIds = DBReader.getQueueIDList(); + return Pair.create(feed, queuedItemsIds); } + } diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/NewEpisodesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/NewEpisodesFragment.java index 6177f2a50..d454208c1 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/NewEpisodesFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/NewEpisodesFragment.java @@ -39,7 +39,7 @@ public class NewEpisodesFragment extends AllEpisodesFragment { public void onEvent(QueueEvent event) { Log.d(TAG, "onEvent(" + event + ")"); - startItemLoader(); + loadItems(); } @Override @@ -69,7 +69,9 @@ public class NewEpisodesFragment extends AllEpisodesFragment { @Override public void remove(int which) { Log.d(TAG, "remove(" + which + ")"); - stopItemLoader(); + if(subscription != null) { + subscription.unsubscribe(); + } FeedItem item = (FeedItem) listView.getAdapter().getItem(which); // we're marking it as unplayed since the user didn't actually play it // but they don't want it considered 'NEW' anymore diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java index 530883667..e6460309b 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java @@ -1,9 +1,7 @@ package de.danoeh.antennapod.fragment; import android.app.Activity; -import android.content.Context; import android.content.res.TypedArray; -import android.os.AsyncTask; import android.os.Bundle; import android.os.Handler; import android.support.v4.app.ListFragment; @@ -33,6 +31,10 @@ import de.danoeh.antennapod.core.storage.DBReader; import de.danoeh.antennapod.core.storage.DBWriter; import de.danoeh.antennapod.core.util.LongList; import de.greenrobot.event.EventBus; +import rx.Observable; +import rx.Subscription; +import rx.android.schedulers.AndroidSchedulers; +import rx.schedulers.Schedulers; public class PlaybackHistoryFragment extends ListFragment { @@ -53,6 +55,8 @@ public class PlaybackHistoryFragment extends ListFragment { private DownloadObserver downloadObserver; private List<Downloader> downloaderList; + private Subscription subscription; + @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -63,7 +67,7 @@ public class PlaybackHistoryFragment extends ListFragment { @Override public void onResume() { super.onResume(); - startItemLoader(); + loadItems(); } @Override @@ -78,13 +82,17 @@ public class PlaybackHistoryFragment extends ListFragment { super.onStop(); EventDistributor.getInstance().unregister(contentUpdate); EventBus.getDefault().unregister(this); - stopItemLoader(); + if(subscription != null) { + subscription.unsubscribe(); + } } @Override public void onDetach() { super.onDetach(); - stopItemLoader(); + if(subscription != null) { + subscription.unsubscribe(); + } activity.set(null); } @@ -176,7 +184,7 @@ public class PlaybackHistoryFragment extends ListFragment { public void onEvent(QueueEvent event) { Log.d(TAG, "onEvent(" + event + ")"); - startItemLoader(); + loadItems(); } private EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() { @@ -184,7 +192,7 @@ public class PlaybackHistoryFragment extends ListFragment { @Override public void update(EventDistributor eventDistributor, Integer arg) { if ((arg & EVENTS) != 0) { - startItemLoader(); + loadItems(); getActivity().supportInvalidateOptionsMenu(); } } @@ -245,48 +253,32 @@ public class PlaybackHistoryFragment extends ListFragment { } }; - private ItemLoader itemLoader; - - private void startItemLoader() { - if (itemLoader != null) { - itemLoader.cancel(true); + private void loadItems() { + if(subscription != null) { + subscription.unsubscribe(); } - itemLoader = new ItemLoader(); - itemLoader.execute(); + subscription = Observable.defer(() -> Observable.just(loadData())) + .subscribeOn(Schedulers.newThread()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(result -> { + if (result != null) { + playbackHistory = result.first; + queue = result.second; + itemsLoaded = true; + if (viewsCreated) { + onFragmentLoaded(); + } + } + }, error -> { + Log.e(TAG, Log.getStackTraceString(error)); + }); } - private void stopItemLoader() { - if (itemLoader != null) { - itemLoader.cancel(true); - } + private Pair<List<FeedItem>, LongList> loadData() { + List<FeedItem> history = DBReader.getPlaybackHistory(); + LongList queue = DBReader.getQueueIDList(); + DBReader.loadFeedDataOfFeedItemlist(history); + return Pair.create(history, queue); } - private class ItemLoader extends AsyncTask<Void, Void, Pair<List<FeedItem>,LongList>> { - - @Override - protected Pair<List<FeedItem>,LongList> doInBackground(Void... params) { - Context context = activity.get(); - if (context != null) { - List<FeedItem> history = DBReader.getPlaybackHistory(); - LongList queue = DBReader.getQueueIDList(); - DBReader.loadFeedDataOfFeedItemlist(history); - return Pair.create(history, queue); - } else { - return null; - } - } - - @Override - protected void onPostExecute(Pair<List<FeedItem>,LongList> res) { - super.onPostExecute(res); - if (res != null) { - playbackHistory = res.first; - queue = res.second; - itemsLoaded = true; - if (viewsCreated) { - onFragmentLoaded(); - } - } - } - } } diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java index 63c319e03..7bcd98dc8 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java @@ -4,7 +4,6 @@ import android.app.Activity; import android.content.Context; import android.content.DialogInterface; import android.content.SharedPreferences; -import android.os.AsyncTask; import android.os.Bundle; import android.os.Handler; import android.support.v4.app.Fragment; @@ -55,6 +54,10 @@ import de.danoeh.antennapod.core.util.gui.UndoBarController; import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler; import de.danoeh.antennapod.menuhandler.MenuItemUtils; import de.greenrobot.event.EventBus; +import rx.Observable; +import rx.Subscription; +import rx.android.schedulers.AndroidSchedulers; +import rx.schedulers.Schedulers; /** * Shows all items in the queue @@ -98,6 +101,8 @@ public class QueueFragment extends Fragment { */ private boolean blockDownloadObserverUpdate = false; + private Subscription subscription; + @Override public void onCreate(Bundle savedInstanceState) { @@ -109,7 +114,7 @@ public class QueueFragment extends Fragment { @Override public void onResume() { super.onResume(); - startItemLoader(); + loadItems(); } @Override @@ -138,7 +143,9 @@ public class QueueFragment extends Fragment { super.onStop(); EventDistributor.getInstance().unregister(contentUpdate); EventBus.getDefault().unregister(this); - stopItemLoader(); + if(subscription != null) { + subscription.unsubscribe(); + } if(undoBarController.isShowing()) { undoBarController.close(); } @@ -156,7 +163,7 @@ public class QueueFragment extends Fragment { undoBarController.showUndoBar(false, getString(R.string.removed_from_queue), new FeedItemUndoToken(event.item, event.position)); } - startItemLoader(); + loadItems(); } private void saveScrollPosition() { @@ -398,7 +405,9 @@ public class QueueFragment extends Fragment { public void drop(int from, int to) { Log.d(TAG, "drop"); blockDownloadObserverUpdate = false; - stopItemLoader(); + if(subscription != null) { + subscription.unsubscribe(); + } final FeedItem item = queue.remove(from); queue.add(to, item); listAdapter.notifyDataSetChanged(); @@ -408,7 +417,9 @@ public class QueueFragment extends Fragment { @Override public void remove(int which) { Log.d(TAG, "remove(" + which + ")"); - stopItemLoader(); + if(subscription != null) { + subscription.unsubscribe(); + } FeedItem item = (FeedItem) listView.getAdapter().getItem(which); DBWriter.removeQueueItem(getActivity(), item, true); } @@ -556,7 +567,7 @@ public class QueueFragment extends Fragment { @Override public void update(EventDistributor eventDistributor, Integer arg) { if ((arg & EVENTS) != 0) { - startItemLoader(); + loadItems(); if (isUpdatingFeeds != updateRefreshMenuItemChecker.isRefreshing()) { getActivity().supportInvalidateOptionsMenu(); } @@ -564,55 +575,31 @@ public class QueueFragment extends Fragment { } }; - private ItemLoader itemLoader; - - private void startItemLoader() { - if (itemLoader != null) { - itemLoader.cancel(true); + private void loadItems() { + if(subscription != null) { + subscription.unsubscribe(); } - itemLoader = new ItemLoader(); - itemLoader.execute(); - } - - private void stopItemLoader() { - if (itemLoader != null) { - itemLoader.cancel(true); + if (viewsCreated && !itemsLoaded) { + listView.setVisibility(View.GONE); + txtvEmpty.setVisibility(View.GONE); + progLoading.setVisibility(View.VISIBLE); } + subscription = Observable.defer(() -> Observable.just(DBReader.getQueue())) + .subscribeOn(Schedulers.newThread()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(result -> { + listView.setVisibility(View.VISIBLE); + progLoading.setVisibility(View.GONE); + if(result != null) { + queue = result; + itemsLoaded = true; + if (viewsCreated && activity.get() != null) { + onFragmentLoaded(); + } + } + }, error -> { + Log.e(TAG, Log.getStackTraceString(error)); + }); } - private class ItemLoader extends AsyncTask<Void, Void, List<FeedItem>> { - @Override - protected void onPreExecute() { - super.onPreExecute(); - if (viewsCreated && !itemsLoaded) { - listView.setVisibility(View.GONE); - txtvEmpty.setVisibility(View.GONE); - progLoading.setVisibility(View.VISIBLE); - } - } - - @Override - protected void onPostExecute(List<FeedItem> feedItems) { - super.onPostExecute(feedItems); - listView.setVisibility(View.VISIBLE); - progLoading.setVisibility(View.GONE); - - if (feedItems != null) { - queue = feedItems; - itemsLoaded = true; - if (viewsCreated && activity.get() != null) { - onFragmentLoaded(); - } - } - } - - @Override - protected List<FeedItem> doInBackground(Void... params) { - Context context = activity.get(); - if (context != null) { - return DBReader.getQueue(); - } - return null; - } - } } diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java index 975493ce9..edd8cdd1a 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java @@ -1,19 +1,18 @@ package de.danoeh.antennapod.fragment; import android.content.Context; -import android.os.AsyncTask; import android.os.Bundle; import android.support.v4.app.ListFragment; import android.support.v4.view.MenuItemCompat; import android.support.v7.app.ActionBarActivity; import android.support.v7.widget.SearchView; +import android.util.Log; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.widget.ListView; -import java.util.Collections; import java.util.List; import de.danoeh.antennapod.R; @@ -25,6 +24,10 @@ import de.danoeh.antennapod.core.feed.FeedComponent; import de.danoeh.antennapod.core.feed.FeedItem; import de.danoeh.antennapod.core.feed.SearchResult; import de.danoeh.antennapod.core.storage.FeedSearcher; +import rx.Observable; +import rx.Subscription; +import rx.android.schedulers.AndroidSchedulers; +import rx.schedulers.Schedulers; /** * Performs a search operation on all feeds or one specific feed and displays the search result. @@ -41,6 +44,8 @@ public class SearchFragment extends ListFragment { private boolean viewCreated = false; private boolean itemsLoaded = false; + private Subscription subscription; + /** * Create a new SearchFragment that searches all feeds. */ @@ -68,7 +73,7 @@ public class SearchFragment extends ListFragment { super.onCreate(savedInstanceState); setRetainInstance(true); setHasOptionsMenu(true); - startSearchTask(); + search(); } @Override @@ -80,14 +85,18 @@ public class SearchFragment extends ListFragment { @Override public void onStop() { super.onStop(); - stopSearchTask(); + if(subscription != null) { + subscription.unsubscribe(); + } EventDistributor.getInstance().unregister(contentUpdate); } @Override public void onDetach() { super.onDetach(); - stopSearchTask(); + if(subscription != null) { + subscription.unsubscribe(); + } } @Override @@ -143,7 +152,7 @@ public class SearchFragment extends ListFragment { public boolean onQueryTextSubmit(String s) { getArguments().putString(ARG_QUERY, s); itemsLoaded = false; - startSearchTask(); + search(); return true; } @@ -161,7 +170,7 @@ public class SearchFragment extends ListFragment { public void update(EventDistributor eventDistributor, Integer arg) { if ((arg & (EventDistributor.UNREAD_ITEMS_UPDATE | EventDistributor.DOWNLOAD_HANDLED)) != 0) { - startSearchTask(); + search(); } } }; @@ -187,53 +196,36 @@ public class SearchFragment extends ListFragment { } }; - private SearchTask searchTask; - private void startSearchTask() { - if (searchTask != null) { - searchTask.cancel(true); + private void search() { + if(subscription != null) { + subscription.unsubscribe(); } - searchTask = new SearchTask(); - searchTask.execute(getArguments()); - } - - private void stopSearchTask() { - if (searchTask != null) { - searchTask.cancel(true); + if (viewCreated && !itemsLoaded) { + setListShown(false); } + subscription = Observable.defer(() -> Observable.just(performSearch())) + .subscribeOn(Schedulers.newThread()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(result -> { + if (result != null) { + itemsLoaded = true; + searchResults = result; + if (viewCreated) { + onFragmentLoaded(); + } + } + }, error -> { + Log.e(TAG, Log.getStackTraceString(error)); + }); } - private class SearchTask extends AsyncTask<Bundle, Void, List<SearchResult>> { - @Override - protected List<SearchResult> doInBackground(Bundle... params) { - String query = params[0].getString(ARG_QUERY); - long feed = params[0].getLong(ARG_FEED); - Context context = getActivity(); - if (context != null) { - return FeedSearcher.performSearch(context, query, feed); - } else { - return Collections.emptyList(); - } - } - - @Override - protected void onPreExecute() { - super.onPreExecute(); - if (viewCreated && !itemsLoaded) { - setListShown(false); - } - } - - @Override - protected void onPostExecute(List<SearchResult> results) { - super.onPostExecute(results); - if (results != null) { - itemsLoaded = true; - searchResults = results; - if (viewCreated) { - onFragmentLoaded(); - } - } - } + private List<SearchResult> performSearch() { + Bundle args = getArguments(); + String query = args.getString(ARG_QUERY); + long feed = args.getLong(ARG_FEED); + Context context = getActivity(); + return FeedSearcher.performSearch(context, query, feed); } + } diff --git a/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceController.java b/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceController.java index 3c9bf464c..1630898ec 100644 --- a/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceController.java +++ b/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceController.java @@ -460,6 +460,13 @@ public class PreferenceController { ui.findPreference(UserPreferences.PREF_ENABLE_AUTODL_ON_BATTERY) .setEnabled(UserPreferences.isEnableAutodownload()); + + if (Build.VERSION.SDK_INT >= 16) { + ui.findPreference(UserPreferences.PREF_SONIC).setEnabled(true); + } else { + Preference prefSonic = ui.findPreference(UserPreferences.PREF_SONIC); + prefSonic.setSummary("[Android 4.1+]\n" + prefSonic.getSummary()); + } } private void setParallelDownloadsText(int downloads) { diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index c9be65f2b..af20eddfd 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -223,7 +223,15 @@ <Preference android:key="prefAbout" android:title="@string/about_pref"/> + </PreferenceCategory> + <PreferenceCategory android:title="@string/experimental_pref"> + <CheckBoxPreference + android:defaultValue="false" + android:enabled="false" + android:key="prefSonic" + android:summary="@string/pref_sonic_message" + android:title="@string/pref_sonic_title"/> </PreferenceCategory> </PreferenceScreen> diff --git a/app/src/main/templates/about.html b/app/src/main/templates/about.html index 3f26c2366..6ed5fc41d 100644 --- a/app/src/main/templates/about.html +++ b/app/src/main/templates/about.html @@ -59,8 +59,7 @@ <h1>Used libraries</h1> <h2>Apache Commons <a href="http://commons.apache.org/">(Link)</a></h2> -by The Apache Software Foundation, licensed under the Apache 2.0 license <a - href="LICENSE_APACHE_COMMONS.txt">(View)</a> +by The Apache Software Foundation, licensed under the Apache 2.0 license <a href="LICENSE_APACHE_COMMONS.txt">(View)</a> <h2>DragSortListView <a href="https://github.com/bauerca/drag-sort-listview">(Link)</a></h2> by Carl Bauer, licensed under the Apache 2.0 license <a href="LICENSE_DSLV.txt">(View)</a> @@ -101,5 +100,8 @@ licensed under the Apache 2.0 license <a href="LICENSE_RX_ANDROID.txt">(View)</a <h2>StackBlur <a href="https://github.com/kikoso/android-stackblur">(Link)</a></h2> by Enrique López Mañas, licensed under the Apache 2.0 license <a href="LICENSE_STACKBLUR.txt">(View)</a> +<h2>AntennaPod-AudioPlayer <a href="https://github.com/AntennaPod/AntennaPod-AudioPlayer/">(Link)</a></h2> +by the AntennaPod team, licensed under the Apache 2.0 license <a href="LICENSE_ANTENNAPOD_AUDIOPLAYER.txt">(View)</a> + </body> </html> |