summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
Diffstat (limited to 'app')
-rw-r--r--app/build.gradle29
-rw-r--r--app/src/androidTest/java/de/test/antennapod/EspressoTestUtils.java32
-rw-r--r--app/src/androidTest/java/de/test/antennapod/service/playback/PlaybackServiceMediaPlayerTest.java12
-rw-r--r--app/src/androidTest/java/de/test/antennapod/ui/MainActivityTest.java47
-rw-r--r--app/src/androidTest/java/de/test/antennapod/ui/NavigationDrawerTest.java135
-rw-r--r--app/src/androidTest/java/de/test/antennapod/ui/PlaybackSonicTest.java16
-rw-r--r--app/src/androidTest/java/de/test/antennapod/ui/PlaybackTest.java4
-rw-r--r--app/src/androidTest/java/de/test/antennapod/ui/PreferencesTest.java2
-rw-r--r--app/src/androidTest/java/de/test/antennapod/ui/QueueFragmentTest.java58
-rw-r--r--app/src/androidTest/java/de/test/antennapod/ui/UITestUtils.java2
-rw-r--r--app/src/androidTest/java/de/test/antennapod/util/service/download/HTTPBin.java14
-rw-r--r--app/src/free/java/de/danoeh/antennapod/activity/CastEnabledActivity.java203
-rw-r--r--app/src/main/AndroidManifest.xml9
-rw-r--r--app/src/main/java/de/danoeh/antennapod/CrashReportWriter.java22
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/AboutActivity.java4
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/AudioplayerActivity.java23
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/BugReportActivity.java55
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/CastplayerActivity.java1
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java50
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java257
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/MediaplayerInfoActivity.java7
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java8
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/AllEpisodesRecycleAdapter.java29
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/DownloadedEpisodesListAdapter.java8
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/DownloadlistAdapter.java13
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistAdapter.java32
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistDescriptionAdapter.java12
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/QueueRecyclerAdapter.java40
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/SubscriptionsAdapter.java6
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/ItemActionButton.java6
-rw-r--r--app/src/main/java/de/danoeh/antennapod/asynctask/DocumentFileExportWorker.java72
-rw-r--r--app/src/main/java/de/danoeh/antennapod/config/PlaybackServiceCallbacksImpl.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/ChooseDataFolderDialog.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/EpisodesApplyActionFragment.java21
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/FilterDialog.java62
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/PlaybackControlsDialog.java214
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/RatingDialog.java9
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/VariableSpeedDialog.java1
-rw-r--r--app/src/main/java/de/danoeh/antennapod/discovery/CombinedSearcher.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/discovery/ItunesTopListLoader.java14
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/AddFeedFragment.java7
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java505
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ChaptersFragment.java13
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/EpisodesFragment.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java445
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java12
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/FavoriteEpisodesFragment.java3
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/FeedItemlistFragment.java (renamed from app/src/main/java/de/danoeh/antennapod/fragment/ItemlistFragment.java)37
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java14
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java29
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/NewEpisodesFragment.java12
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java113
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/SubscriptionFragment.java28
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/AutoDownloadPreferencesFragment.java71
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/MainPreferencesFragment.java55
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/StoragePreferencesFragment.java180
-rw-r--r--app/src/main/java/de/danoeh/antennapod/menuhandler/FeedItemMenuHandler.java92
-rw-r--r--app/src/main/java/de/danoeh/antennapod/menuhandler/FeedMenuHandler.java54
-rw-r--r--app/src/main/java/de/danoeh/antennapod/preferences/PreferenceUpgrader.java28
-rw-r--r--app/src/main/java/de/danoeh/antennapod/spa/SPAUtil.java4
-rw-r--r--app/src/main/play/listings/de-DE/full-description.txt7
-rw-r--r--app/src/main/play/listings/de-DE/title.txt1
-rw-r--r--app/src/main/play/listings/en-US/graphics/icon/icon_play.pngbin0 -> 25302 bytes
-rw-r--r--app/src/main/play/listings/es-ES/full-description.txt42
-rw-r--r--app/src/main/play/listings/es-ES/short-description.txt1
-rw-r--r--app/src/main/play/listings/es-ES/title.txt1
-rw-r--r--app/src/main/play/listings/fr-FR/full-description.txt5
-rw-r--r--app/src/main/play/listings/fr-FR/short-description.txt2
-rw-r--r--app/src/main/play/listings/fr-FR/title.txt1
-rw-r--r--app/src/main/play/listings/gl-ES/full-description.txt3
-rw-r--r--app/src/main/play/listings/gl-ES/title.txt1
-rw-r--r--app/src/main/play/listings/it-IT/title.txt1
-rw-r--r--app/src/main/play/listings/iw-IL/full-description.txt42
-rw-r--r--app/src/main/play/listings/iw-IL/short-description.txt1
-rw-r--r--app/src/main/play/listings/iw-IL/title.txt1
-rw-r--r--app/src/main/play/listings/ja-JP/full-description.txt3
-rw-r--r--app/src/main/play/listings/ja-JP/title.txt1
-rw-r--r--app/src/main/play/listings/nl-NL/full-description.txt3
-rw-r--r--app/src/main/play/listings/nl-NL/title.txt1
-rw-r--r--app/src/main/play/listings/sv-SE/full-description.txt42
-rw-r--r--app/src/main/play/listings/sv-SE/short-description.txt1
-rw-r--r--app/src/main/play/listings/sv-SE/title.txt1
-rw-r--r--app/src/main/play/listings/zh-CN/full-description.txt42
-rw-r--r--app/src/main/play/listings/zh-CN/short-description.txt2
-rw-r--r--app/src/main/play/listings/zh-CN/title.txt1
-rw-r--r--app/src/main/play/release-notes/en-US/default.txt3
-rw-r--r--app/src/main/res/layout/addfeed.xml22
-rw-r--r--app/src/main/res/layout/all_episodes_fragment.xml35
-rw-r--r--app/src/main/res/layout/bug_report.xml28
-rw-r--r--app/src/main/res/layout/checkbox_do_not_show_again.xml17
-rw-r--r--app/src/main/res/layout/downloaded_episodeslist_item.xml2
-rw-r--r--app/src/main/res/layout/episodes_apply_action_fragment.xml11
-rw-r--r--app/src/main/res/layout/feeditem_fragment.xml4
-rw-r--r--app/src/main/res/layout/feeditemlist_header.xml2
-rw-r--r--app/src/main/res/layout/gpodnet_podcast_listitem.xml2
-rw-r--r--app/src/main/res/layout/itunes_podcast_listitem.xml2
-rw-r--r--app/src/main/res/layout/mediaplayerinfo_activity.xml29
-rw-r--r--app/src/main/res/layout/nav_feedlistitem.xml2
-rw-r--r--app/src/main/res/layout/queue_listitem.xml2
-rw-r--r--app/src/main/res/layout/searchlist_item.xml2
-rw-r--r--app/src/main/res/layout/statistics_listitem.xml2
-rw-r--r--app/src/main/res/menu/allepisodes_context.xml86
-rw-r--r--app/src/main/res/menu/episodes.xml10
-rw-r--r--app/src/main/res/menu/feedinfo.xml2
-rw-r--r--app/src/main/res/menu/feeditem_options.xml5
-rw-r--r--app/src/main/res/menu/feeditemlist_context.xml5
-rw-r--r--app/src/main/res/menu/feedlist.xml4
-rw-r--r--app/src/main/res/menu/mediaplayer.xml8
-rw-r--r--app/src/main/res/menu/queue_context.xml73
-rw-r--r--app/src/main/res/menu/subscriptions.xml7
-rw-r--r--app/src/main/res/values-w300dp/dimens-fabspeeddial.xml4
-rw-r--r--app/src/main/res/xml/preferences.xml22
-rw-r--r--app/src/main/res/xml/preferences_user_interface.xml2
-rw-r--r--app/src/main/templates/about.html8
-rw-r--r--app/src/play/java/de/danoeh/antennapod/activity/CastEnabledActivity.java54
-rw-r--r--app/src/play/java/de/danoeh/antennapod/preferences/PreferenceControllerFlavorHelper.java15
116 files changed, 2270 insertions, 1645 deletions
diff --git a/app/build.gradle b/app/build.gradle
index 46b2e01d7..3996d7ba5 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -1,10 +1,9 @@
plugins {
- id('com.github.triplet.play') version '2.2.0'
+ id('com.android.application')
+ id('com.getkeepsafe.dexcount')
+ id('com.github.triplet.play') version '2.3.0' apply false
}
-apply plugin: "com.android.application"
-apply plugin: 'com.getkeepsafe.dexcount'
-
import org.apache.tools.ant.filters.ReplaceTokens
android {
@@ -19,8 +18,8 @@ android {
// "1.2.3-SNAPSHOT" -> 1020300
// "1.2.3-RC4" -> 1020304
// "1.2.3" -> 1020395
- versionCode 1070296
- versionName "1.7.2b"
+ versionCode 1070396
+ versionName "1.7.3b"
testApplicationId "de.test.antennapod"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
generatedDensities = []
@@ -173,9 +172,9 @@ dependencies {
implementation "com.github.AntennaPod:AntennaPod-AudioPlayer:$audioPlayerVersion"
implementation 'com.github.mfietz:fyydlin:v0.4.2'
- implementation 'com.github.ByteHamster:SearchPreference:v1.2.6'
- implementation "org.awaitility:awaitility:$awaitilityVersion"
+ implementation 'com.github.ByteHamster:SearchPreference:v1.3.0'
+ androidTestImplementation "org.awaitility:awaitility:$awaitilityVersion"
androidTestImplementation 'com.nanohttpd:nanohttpd-webserver:2.1.1'
androidTestImplementation "com.jayway.android.robotium:robotium-solo:$robotiumSoloVersion"
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
@@ -185,18 +184,12 @@ dependencies {
androidTestImplementation 'com.android.support.test:rules:1.0.2'
}
-play {
- track = 'alpha'
-
- if (project.hasProperty("antennaPodServiceAccountEmail")) {
+if (project.hasProperty("antennaPodServiceAccountEmail")) {
+ apply plugin: 'com.github.triplet.play'
+ play {
+ track = 'alpha'
serviceAccountEmail = antennaPodServiceAccountEmail
- } else {
- serviceAccountEmail = '522080222319-compute@developer.gserviceaccount.com'
- }
- if (project.hasProperty("antennaPodPk12File")) {
serviceAccountCredentials = file(antennaPodPk12File)
- } else {
- serviceAccountCredentials = file('../serviceaccount-c3d7d0f61387.p12')
}
}
diff --git a/app/src/androidTest/java/de/test/antennapod/EspressoTestUtils.java b/app/src/androidTest/java/de/test/antennapod/EspressoTestUtils.java
index 50109c71a..9e86275fc 100644
--- a/app/src/androidTest/java/de/test/antennapod/EspressoTestUtils.java
+++ b/app/src/androidTest/java/de/test/antennapod/EspressoTestUtils.java
@@ -1,6 +1,7 @@
package de.test.antennapod;
import android.content.Context;
+import android.content.SharedPreferences;
import android.support.annotation.StringRes;
import android.support.test.InstrumentationRegistry;
import android.support.test.espresso.PerformException;
@@ -12,6 +13,10 @@ import android.support.test.espresso.util.HumanReadables;
import android.support.test.espresso.util.TreeIterables;
import android.view.View;
import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.activity.MainActivity;
+import de.danoeh.antennapod.core.storage.PodDBAdapter;
+import de.danoeh.antennapod.dialog.RatingDialog;
+import de.danoeh.antennapod.fragment.QueueFragment;
import org.hamcrest.Matcher;
import java.io.File;
@@ -74,7 +79,7 @@ public class EspressoTestUtils {
/**
* Clear all app databases
*/
- public static void clearAppData() {
+ public static void clearPreferences() {
File root = InstrumentationRegistry.getTargetContext().getFilesDir().getParentFile();
String[] sharedPreferencesFileNames = new File(root, "shared_prefs").list();
for (String fileName : sharedPreferencesFileNames) {
@@ -84,6 +89,31 @@ public class EspressoTestUtils {
}
}
+ public static void makeNotFirstRun() {
+ InstrumentationRegistry.getTargetContext().getSharedPreferences(MainActivity.PREF_NAME, Context.MODE_PRIVATE)
+ .edit()
+ .putBoolean(MainActivity.PREF_IS_FIRST_LAUNCH, false)
+ .commit();
+
+ RatingDialog.init(InstrumentationRegistry.getTargetContext());
+ RatingDialog.saveRated();
+ }
+
+ public static void setLastNavFragment(String tag) {
+ InstrumentationRegistry.getTargetContext().getSharedPreferences(MainActivity.PREF_NAME, Context.MODE_PRIVATE)
+ .edit()
+ .putString(MainActivity.PREF_LAST_FRAGMENT_TAG, tag)
+ .commit();
+ }
+
+ public static void clearDatabase() {
+ PodDBAdapter.init(InstrumentationRegistry.getTargetContext());
+ PodDBAdapter.deleteDatabase();
+ PodDBAdapter adapter = PodDBAdapter.getInstance();
+ adapter.open();
+ adapter.close();
+ }
+
public static void clickPreference(@StringRes int title) {
onView(withId(R.id.list)).perform(
RecyclerViewActions.actionOnItem(hasDescendant(withText(title)),
diff --git a/app/src/androidTest/java/de/test/antennapod/service/playback/PlaybackServiceMediaPlayerTest.java b/app/src/androidTest/java/de/test/antennapod/service/playback/PlaybackServiceMediaPlayerTest.java
index 63ea697e9..d1ca25ad3 100644
--- a/app/src/androidTest/java/de/test/antennapod/service/playback/PlaybackServiceMediaPlayerTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/service/playback/PlaybackServiceMediaPlayerTest.java
@@ -64,15 +64,11 @@ public class PlaybackServiceMediaPlayerTest {
@Before
public void setUp() throws Exception {
assertionError = null;
- EspressoTestUtils.clearAppData();
- final Context context = InstrumentationRegistry.getTargetContext();
+ EspressoTestUtils.clearPreferences();
+ EspressoTestUtils.makeNotFirstRun();
+ EspressoTestUtils.clearDatabase();
- // create new database
- PodDBAdapter.init(context);
- PodDBAdapter.deleteDatabase();
- PodDBAdapter adapter = PodDBAdapter.getInstance();
- adapter.open();
- adapter.close();
+ final Context context = InstrumentationRegistry.getTargetContext();
httpServer = new HTTPBin();
httpServer.start();
diff --git a/app/src/androidTest/java/de/test/antennapod/ui/MainActivityTest.java b/app/src/androidTest/java/de/test/antennapod/ui/MainActivityTest.java
index d013ab9cf..0c65bb2e6 100644
--- a/app/src/androidTest/java/de/test/antennapod/ui/MainActivityTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/ui/MainActivityTest.java
@@ -5,8 +5,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.support.test.InstrumentationRegistry;
-import android.support.test.espresso.intent.Intents;
-import android.support.test.rule.ActivityTestRule;
+import android.support.test.espresso.intent.rule.IntentsTestRule;
import android.support.test.runner.AndroidJUnit4;
import com.robotium.solo.Solo;
import com.robotium.solo.Timeout;
@@ -29,7 +28,6 @@ import static android.support.test.InstrumentationRegistry.getInstrumentation;
import static android.support.test.espresso.Espresso.onView;
import static android.support.test.espresso.action.ViewActions.click;
import static android.support.test.espresso.contrib.ActivityResultMatchers.hasResultCode;
-import static android.support.test.espresso.matcher.ViewMatchers.withId;
import static android.support.test.espresso.matcher.ViewMatchers.withText;
import static de.test.antennapod.EspressoTestUtils.clickPreference;
import static de.test.antennapod.EspressoTestUtils.openNavDrawer;
@@ -45,47 +43,28 @@ public class MainActivityTest {
private Solo solo;
private UITestUtils uiTestUtils;
- private SharedPreferences prefs;
@Rule
- public ActivityTestRule<MainActivity> mActivityRule = new ActivityTestRule<>(MainActivity.class, false, false);
+ public IntentsTestRule<MainActivity> mActivityRule = new IntentsTestRule<>(MainActivity.class, false, false);
@Before
public void setUp() throws IOException {
- // override first launch preference
- // do this BEFORE calling getActivity()!
- EspressoTestUtils.clearAppData();
- prefs = InstrumentationRegistry.getContext()
- .getSharedPreferences(MainActivity.PREF_NAME, Context.MODE_PRIVATE);
- prefs.edit().putBoolean(MainActivity.PREF_IS_FIRST_LAUNCH, false).commit();
+ EspressoTestUtils.clearPreferences();
+ EspressoTestUtils.makeNotFirstRun();
+ EspressoTestUtils.clearDatabase();
mActivityRule.launchActivity(new Intent());
- Intents.init();
- Context context = mActivityRule.getActivity();
- uiTestUtils = new UITestUtils(context);
+ uiTestUtils = new UITestUtils(InstrumentationRegistry.getTargetContext());
uiTestUtils.setup();
- // create new database
- PodDBAdapter.init(context);
- PodDBAdapter.deleteDatabase();
- PodDBAdapter adapter = PodDBAdapter.getInstance();
- adapter.open();
- adapter.close();
-
- RatingDialog.init(context);
- RatingDialog.saveRated();
-
solo = new Solo(getInstrumentation(), mActivityRule.getActivity());
}
@After
public void tearDown() throws Exception {
uiTestUtils.tearDown();
- solo.finishOpenedActivities();
- Intents.release();
PodDBAdapter.deleteDatabase();
- prefs.edit().clear().commit();
}
@Test
@@ -93,13 +72,10 @@ public class MainActivityTest {
uiTestUtils.addHostedFeedData();
final Feed feed = uiTestUtils.hostedFeeds.get(0);
openNavDrawer();
- solo.clickOnText(solo.getString(R.string.add_feed_label));
+ onView(withText(R.string.add_feed_label)).perform(click());
solo.enterText(1, feed.getDownload_url());
- solo.clickOnButton(solo.getString(R.string.confirm_label));
- solo.waitForActivity(OnlineFeedViewActivity.class);
- solo.waitForView(R.id.butSubscribe);
- assertEquals(solo.getString(R.string.subscribe_label), solo.getButton(0).getText().toString());
- solo.clickOnButton(0);
+ onView(withText(R.string.confirm_label)).perform(click());
+ onView(withText(R.string.subscribe_label)).perform(click());
assertTrue(solo.waitForText(solo.getString(R.string.open_podcast), 0, Timeout.getLargeTimeout(), false));
}
@@ -119,7 +95,6 @@ public class MainActivityTest {
onView(withText(R.string.confirm_label)).perform(click());
solo.goBackToActivity(MainActivity.class.getSimpleName());
- solo.goBack(); // Close nav drawer
solo.goBack();
assertEquals(solo.getString(R.string.subscriptions_label), getActionbarTitle());
}
@@ -132,7 +107,6 @@ public class MainActivityTest {
clickPreference(R.string.pref_back_button_behavior_title);
onView(withText(R.string.back_button_open_drawer)).perform(click());
solo.goBackToActivity(MainActivity.class.getSimpleName());
- solo.goBack(); // Close nav drawer
solo.goBack();
assertTrue(((MainActivity)solo.getCurrentActivity()).isDrawerOpen());
}
@@ -145,7 +119,6 @@ public class MainActivityTest {
clickPreference(R.string.pref_back_button_behavior_title);
onView(withText(R.string.back_button_double_tap)).perform(click());
solo.goBackToActivity(MainActivity.class.getSimpleName());
- solo.goBack(); // Close nav drawer
solo.goBack();
solo.goBack();
assertThat(mActivityRule.getActivityResult(), hasResultCode(Activity.RESULT_CANCELED));
@@ -159,7 +132,6 @@ public class MainActivityTest {
clickPreference(R.string.pref_back_button_behavior_title);
onView(withText(R.string.back_button_show_prompt)).perform(click());
solo.goBackToActivity(MainActivity.class.getSimpleName());
- solo.goBack(); // Close nav drawer
solo.goBack();
onView(withText(R.string.yes)).perform(click());
Thread.sleep(100);
@@ -174,7 +146,6 @@ public class MainActivityTest {
clickPreference(R.string.pref_back_button_behavior_title);
onView(withText(R.string.back_button_default)).perform(click());
solo.goBackToActivity(MainActivity.class.getSimpleName());
- solo.goBack(); // Close nav drawer
solo.goBack();
assertThat(mActivityRule.getActivityResult(), hasResultCode(Activity.RESULT_CANCELED));
}
diff --git a/app/src/androidTest/java/de/test/antennapod/ui/NavigationDrawerTest.java b/app/src/androidTest/java/de/test/antennapod/ui/NavigationDrawerTest.java
index 0ed62010b..548843636 100644
--- a/app/src/androidTest/java/de/test/antennapod/ui/NavigationDrawerTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/ui/NavigationDrawerTest.java
@@ -4,13 +4,11 @@ import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.support.test.InstrumentationRegistry;
+import android.support.test.espresso.ViewInteraction;
import android.support.test.espresso.contrib.DrawerActions;
-import android.support.test.espresso.intent.Intents;
-import android.support.test.filters.FlakyTest;
-import android.support.test.rule.ActivityTestRule;
+import android.support.test.espresso.intent.rule.IntentsTestRule;
import android.support.test.runner.AndroidJUnit4;
-import android.widget.ListView;
-import com.robotium.solo.Solo;
+import android.view.View;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.activity.PreferenceActivity;
@@ -23,6 +21,7 @@ import de.danoeh.antennapod.fragment.EpisodesFragment;
import de.danoeh.antennapod.fragment.PlaybackHistoryFragment;
import de.danoeh.antennapod.fragment.QueueFragment;
import de.test.antennapod.EspressoTestUtils;
+import org.hamcrest.Matcher;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
@@ -34,20 +33,20 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
-import static android.support.test.InstrumentationRegistry.getInstrumentation;
import static android.support.test.espresso.Espresso.onView;
import static android.support.test.espresso.action.ViewActions.click;
import static android.support.test.espresso.action.ViewActions.longClick;
+import static android.support.test.espresso.action.ViewActions.scrollTo;
import static android.support.test.espresso.intent.Intents.intended;
import static android.support.test.espresso.intent.matcher.IntentMatchers.hasComponent;
import static android.support.test.espresso.matcher.ViewMatchers.isRoot;
import static android.support.test.espresso.matcher.ViewMatchers.withId;
import static android.support.test.espresso.matcher.ViewMatchers.withText;
-import static de.test.antennapod.NthMatcher.first;
import static de.test.antennapod.EspressoTestUtils.waitForView;
+import static de.test.antennapod.NthMatcher.first;
import static junit.framework.TestCase.assertTrue;
+import static org.hamcrest.Matchers.allOf;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.fail;
/**
* User interface tests for MainActivity drawer
@@ -55,49 +54,24 @@ import static org.junit.Assert.fail;
@RunWith(AndroidJUnit4.class)
public class NavigationDrawerTest {
- private Solo solo;
private UITestUtils uiTestUtils;
- private SharedPreferences prefs;
@Rule
- public ActivityTestRule<MainActivity> mActivityRule = new ActivityTestRule<>(MainActivity.class, false, false);
+ public IntentsTestRule<MainActivity> mActivityRule = new IntentsTestRule<>(MainActivity.class, false, false);
@Before
public void setUp() throws IOException {
- // override first launch preference
- // do this BEFORE calling getActivity()!
- EspressoTestUtils.clearAppData();
- prefs = InstrumentationRegistry.getContext()
- .getSharedPreferences(MainActivity.PREF_NAME, Context.MODE_PRIVATE);
- prefs.edit().putBoolean(MainActivity.PREF_IS_FIRST_LAUNCH, false).commit();
-
- mActivityRule.launchActivity(new Intent());
-
- Intents.init();
- Context context = mActivityRule.getActivity();
- uiTestUtils = new UITestUtils(context);
+ uiTestUtils = new UITestUtils(InstrumentationRegistry.getTargetContext());
uiTestUtils.setup();
- // create new database
- PodDBAdapter.init(context);
- PodDBAdapter.deleteDatabase();
- PodDBAdapter adapter = PodDBAdapter.getInstance();
- adapter.open();
- adapter.close();
-
- RatingDialog.init(context);
- RatingDialog.saveRated();
-
- solo = new Solo(getInstrumentation(), mActivityRule.getActivity());
+ EspressoTestUtils.clearPreferences();
+ EspressoTestUtils.makeNotFirstRun();
+ EspressoTestUtils.clearDatabase();
}
@After
public void tearDown() throws Exception {
uiTestUtils.tearDown();
- solo.finishOpenedActivities();
- Intents.release();
- PodDBAdapter.deleteDatabase();
- prefs.edit().clear().commit();
}
private void openNavDrawer() {
@@ -105,69 +79,66 @@ public class NavigationDrawerTest {
onView(withId(R.id.drawer_layout)).perform(DrawerActions.open());
}
+ private ViewInteraction onDrawerItem(Matcher<View> viewMatcher) {
+ return onView(allOf(viewMatcher, withId(R.id.txtvTitle)));
+ }
+
@Test
- @FlakyTest
public void testClickNavDrawer() throws Exception {
uiTestUtils.addLocalFeedData(false);
-
- setHiddenDrawerItems(new ArrayList<>());
+ UserPreferences.setHiddenDrawerItems(new ArrayList<>());
+ mActivityRule.launchActivity(new Intent());
+ MainActivity activity = mActivityRule.getActivity();
// queue
openNavDrawer();
- solo.clickOnText(solo.getString(R.string.queue_label));
- solo.waitForView(R.id.recyclerView);
- assertEquals(solo.getString(R.string.queue_label), getActionbarTitle());
+ onDrawerItem(withText(R.string.queue_label)).perform(click());
+ onView(isRoot()).perform(waitForView(withId(R.id.recyclerView), 1000));
+ assertEquals(activity.getString(R.string.queue_label), activity.getSupportActionBar().getTitle());
// episodes
openNavDrawer();
- solo.clickOnText(solo.getString(R.string.episodes_label));
- solo.waitForView(android.R.id.list);
- assertEquals(solo.getString(R.string.episodes_label), getActionbarTitle());
+ onDrawerItem(withText(R.string.episodes_label)).perform(click());
+ onView(isRoot()).perform(waitForView(withId(android.R.id.list), 1000));
+ assertEquals(activity.getString(R.string.episodes_label), activity.getSupportActionBar().getTitle());
// Subscriptions
openNavDrawer();
- solo.clickOnText(solo.getString(R.string.subscriptions_label));
- solo.waitForView(R.id.subscriptions_grid);
- assertEquals(solo.getString(R.string.subscriptions_label), getActionbarTitle());
+ onDrawerItem(withText(R.string.subscriptions_label)).perform(click());
+ onView(isRoot()).perform(waitForView(withId(R.id.subscriptions_grid), 1000));
+ assertEquals(activity.getString(R.string.subscriptions_label), activity.getSupportActionBar().getTitle());
// downloads
openNavDrawer();
- solo.clickOnText(solo.getString(R.string.downloads_label));
- solo.waitForView(android.R.id.list);
- assertEquals(solo.getString(R.string.downloads_label), getActionbarTitle());
+ onDrawerItem(withText(R.string.downloads_label)).perform(click());
+ onView(isRoot()).perform(waitForView(withId(android.R.id.list), 1000));
+ assertEquals(activity.getString(R.string.downloads_label), activity.getSupportActionBar().getTitle());
// playback history
openNavDrawer();
- solo.clickOnText(solo.getString(R.string.playback_history_label));
- solo.waitForView(android.R.id.list);
- assertEquals(solo.getString(R.string.playback_history_label), getActionbarTitle());
+ onDrawerItem(withText(R.string.playback_history_label)).perform(click());
+ onView(isRoot()).perform(waitForView(withId(android.R.id.list), 1000));
+ assertEquals(activity.getString(R.string.playback_history_label), activity.getSupportActionBar().getTitle());
// add podcast
openNavDrawer();
- solo.clickOnText(solo.getString(R.string.add_feed_label));
- solo.waitForView(R.id.txtvFeedurl);
- assertEquals(solo.getString(R.string.add_feed_label), getActionbarTitle());
+ onDrawerItem(withText(R.string.add_feed_label)).perform(click());
+ onView(isRoot()).perform(waitForView(withId(R.id.txtvFeedurl), 1000));
+ assertEquals(activity.getString(R.string.add_feed_label), activity.getSupportActionBar().getTitle());
// podcasts
- ListView list = (ListView) solo.getView(R.id.nav_list);
for (int i = 0; i < uiTestUtils.hostedFeeds.size(); i++) {
Feed f = uiTestUtils.hostedFeeds.get(i);
openNavDrawer();
- solo.scrollListToLine(list, i);
- solo.clickOnText(f.getTitle());
- solo.waitForView(android.R.id.list);
- assertEquals("", getActionbarTitle());
+ onDrawerItem(withText(f.getTitle())).perform(scrollTo(), click());
+ onView(isRoot()).perform(waitForView(withId(android.R.id.list), 1000));
+ assertEquals("", activity.getSupportActionBar().getTitle());
}
}
- private String getActionbarTitle() {
- return ((MainActivity) solo.getCurrentActivity()).getSupportActionBar().getTitle().toString();
- }
-
-
@Test
- @FlakyTest
public void testGoToPreferences() {
+ mActivityRule.launchActivity(new Intent());
openNavDrawer();
onView(withText(R.string.settings_label)).perform(click());
intended(hasComponent(PreferenceActivity.class.getName()));
@@ -175,9 +146,10 @@ public class NavigationDrawerTest {
@Test
public void testDrawerPreferencesHideSomeElements() {
- setHiddenDrawerItems(new ArrayList<>());
+ UserPreferences.setHiddenDrawerItems(new ArrayList<>());
+ mActivityRule.launchActivity(new Intent());
openNavDrawer();
- onView(first(withText(R.string.queue_label))).perform(longClick());
+ onDrawerItem(withText(R.string.queue_label)).perform(longClick());
onView(withText(R.string.episodes_label)).perform(click());
onView(withText(R.string.playback_history_label)).perform(click());
onView(withText(R.string.confirm_label)).perform(click());
@@ -191,7 +163,8 @@ public class NavigationDrawerTest {
@Test
public void testDrawerPreferencesUnhideSomeElements() {
List<String> hidden = Arrays.asList(PlaybackHistoryFragment.TAG, DownloadsFragment.TAG);
- setHiddenDrawerItems(hidden);
+ UserPreferences.setHiddenDrawerItems(hidden);
+ mActivityRule.launchActivity(new Intent());
openNavDrawer();
onView(first(withText(R.string.queue_label))).perform(longClick());
@@ -208,7 +181,8 @@ public class NavigationDrawerTest {
@Test
public void testDrawerPreferencesHideAllElements() {
- setHiddenDrawerItems(new ArrayList<>());
+ UserPreferences.setHiddenDrawerItems(new ArrayList<>());
+ mActivityRule.launchActivity(new Intent());
String[] titles = mActivityRule.getActivity().getResources().getStringArray(R.array.nav_drawer_titles);
openNavDrawer();
@@ -227,7 +201,8 @@ public class NavigationDrawerTest {
@Test
public void testDrawerPreferencesHideCurrentElement() {
- setHiddenDrawerItems(new ArrayList<>());
+ UserPreferences.setHiddenDrawerItems(new ArrayList<>());
+ mActivityRule.launchActivity(new Intent());
openNavDrawer();
onView(withText(R.string.downloads_label)).perform(click());
openNavDrawer();
@@ -240,14 +215,4 @@ public class NavigationDrawerTest {
assertEquals(1, hidden.size());
assertTrue(hidden.contains(DownloadsFragment.TAG));
}
-
- private void setHiddenDrawerItems(List<String> items) {
- UserPreferences.setHiddenDrawerItems(items);
- try {
- mActivityRule.runOnUiThread(() -> mActivityRule.getActivity().updateNavDrawer());
- } catch (Throwable throwable) {
- throwable.printStackTrace();
- fail();
- }
- }
}
diff --git a/app/src/androidTest/java/de/test/antennapod/ui/PlaybackSonicTest.java b/app/src/androidTest/java/de/test/antennapod/ui/PlaybackSonicTest.java
index 6b41ad0ea..22959917d 100644
--- a/app/src/androidTest/java/de/test/antennapod/ui/PlaybackSonicTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/ui/PlaybackSonicTest.java
@@ -52,12 +52,11 @@ public class PlaybackSonicTest {
@Before
public void setUp() throws Exception {
- EspressoTestUtils.clearAppData();
+ EspressoTestUtils.clearPreferences();
+ EspressoTestUtils.makeNotFirstRun();
+ EspressoTestUtils.clearDatabase();
context = InstrumentationRegistry.getTargetContext();
- PodDBAdapter.init(context);
- PodDBAdapter.deleteDatabase();
-
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
prefs.edit()
.clear()
@@ -71,11 +70,6 @@ public class PlaybackSonicTest {
uiTestUtils = new UITestUtils(context);
uiTestUtils.setup();
-
- // create database
- PodDBAdapter adapter = PodDBAdapter.getInstance();
- adapter.open();
- adapter.close();
}
@After
@@ -122,7 +116,7 @@ public class PlaybackSonicTest {
solo.clickOnText(solo.getString(R.string.all_episodes_short_label));
getInstrumentation().waitForIdleSync();
- final List<FeedItem> episodes = DBReader.getRecentlyPublishedEpisodes(10);
+ final List<FeedItem> episodes = DBReader.getRecentlyPublishedEpisodes(0, 10);
assertTrue(solo.waitForView(solo.getView(R.id.butSecondaryAction)));
solo.clickOnView(solo.getView(R.id.butSecondaryAction));
@@ -241,7 +235,7 @@ public class PlaybackSonicTest {
setContinuousPlaybackPreference(followQueue);
uiTestUtils.addLocalFeedData(true);
DBWriter.clearQueue().get();
- final List<FeedItem> episodes = DBReader.getRecentlyPublishedEpisodes(10);
+ final List<FeedItem> episodes = DBReader.getRecentlyPublishedEpisodes(0, 10);
startLocalPlayback();
long mediaId = episodes.get(0).getMedia().getId();
diff --git a/app/src/androidTest/java/de/test/antennapod/ui/PlaybackTest.java b/app/src/androidTest/java/de/test/antennapod/ui/PlaybackTest.java
index 099549a69..b302bcc91 100644
--- a/app/src/androidTest/java/de/test/antennapod/ui/PlaybackTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/ui/PlaybackTest.java
@@ -114,7 +114,7 @@ public class PlaybackTest {
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);
+ final List<FeedItem> episodes = DBReader.getRecentlyPublishedEpisodes(0, 10);
assertTrue(solo.waitForView(solo.getView(R.id.butSecondaryAction)));
solo.clickOnView(solo.getView(R.id.butSecondaryAction));
@@ -231,7 +231,7 @@ public class PlaybackTest {
setContinuousPlaybackPreference(followQueue);
uiTestUtils.addLocalFeedData(true);
DBWriter.clearQueue().get();
- final List<FeedItem> episodes = DBReader.getRecentlyPublishedEpisodes(10);
+ final List<FeedItem> episodes = DBReader.getRecentlyPublishedEpisodes(0, 10);
startLocalPlayback();
long mediaId = episodes.get(0).getMedia().getId();
diff --git a/app/src/androidTest/java/de/test/antennapod/ui/PreferencesTest.java b/app/src/androidTest/java/de/test/antennapod/ui/PreferencesTest.java
index 4b2dc75a9..1063ac90a 100644
--- a/app/src/androidTest/java/de/test/antennapod/ui/PreferencesTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/ui/PreferencesTest.java
@@ -49,7 +49,7 @@ public class PreferencesTest {
@Before
public void setUp() {
- EspressoTestUtils.clearAppData();
+ EspressoTestUtils.clearPreferences();
mActivityRule.launchActivity(new Intent());
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mActivityRule.getActivity());
prefs.edit().putBoolean(UserPreferences.PREF_ENABLE_AUTODL, true).commit();
diff --git a/app/src/androidTest/java/de/test/antennapod/ui/QueueFragmentTest.java b/app/src/androidTest/java/de/test/antennapod/ui/QueueFragmentTest.java
new file mode 100644
index 000000000..d1023dd9e
--- /dev/null
+++ b/app/src/androidTest/java/de/test/antennapod/ui/QueueFragmentTest.java
@@ -0,0 +1,58 @@
+package de.test.antennapod.ui;
+
+import android.content.Intent;
+import android.support.test.espresso.Espresso;
+import android.support.test.espresso.intent.rule.IntentsTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.activity.MainActivity;
+import de.danoeh.antennapod.fragment.QueueFragment;
+import de.test.antennapod.EspressoTestUtils;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.action.ViewActions.click;
+import static android.support.test.espresso.matcher.ViewMatchers.withContentDescription;
+import static android.support.test.espresso.matcher.ViewMatchers.withText;
+
+/**
+ * User interface tests for queue fragment
+ */
+@RunWith(AndroidJUnit4.class)
+public class QueueFragmentTest {
+
+ @Rule
+ public IntentsTestRule<MainActivity> mActivityRule = new IntentsTestRule<>(MainActivity.class, false, false);
+
+ @Before
+ public void setUp() {
+ EspressoTestUtils.clearPreferences();
+ EspressoTestUtils.makeNotFirstRun();
+ EspressoTestUtils.clearDatabase();
+ EspressoTestUtils.setLastNavFragment(QueueFragment.TAG);
+ mActivityRule.launchActivity(new Intent());
+ }
+
+ @Test
+ public void testLockEmptyQueue() {
+ onView(withContentDescription(R.string.lock_queue)).perform(click());
+ onView(withContentDescription(R.string.unlock_queue)).perform(click());
+ }
+
+ @Test
+ public void testSortEmptyQueue() {
+ Espresso.openContextualActionModeOverflowMenu();
+ onView(withText(R.string.sort)).perform(click());
+ onView(withText(R.string.random)).perform(click());
+ }
+
+ @Test
+ public void testKeepEmptyQueueSorted() {
+ Espresso.openContextualActionModeOverflowMenu();
+ onView(withText(R.string.sort)).perform(click());
+ onView(withText(R.string.keep_sorted)).perform(click());
+ }
+}
diff --git a/app/src/androidTest/java/de/test/antennapod/ui/UITestUtils.java b/app/src/androidTest/java/de/test/antennapod/ui/UITestUtils.java
index 20c54cb15..905c65c34 100644
--- a/app/src/androidTest/java/de/test/antennapod/ui/UITestUtils.java
+++ b/app/src/androidTest/java/de/test/antennapod/ui/UITestUtils.java
@@ -59,7 +59,7 @@ class UITestUtils {
public void setup() throws IOException {
destDir = new File(context.getFilesDir(), "test/UITestUtils");
- destDir.mkdir();
+ destDir.mkdirs();
hostedFeedDir = new File(destDir, "hostedFeeds");
hostedFeedDir.mkdir();
hostedMediaDir = new File(destDir, "hostedMediaDir");
diff --git a/app/src/androidTest/java/de/test/antennapod/util/service/download/HTTPBin.java b/app/src/androidTest/java/de/test/antennapod/util/service/download/HTTPBin.java
index 3d8417bf6..4158fd31c 100644
--- a/app/src/androidTest/java/de/test/antennapod/util/service/download/HTTPBin.java
+++ b/app/src/androidTest/java/de/test/antennapod/util/service/download/HTTPBin.java
@@ -72,20 +72,6 @@ public class HTTPBin extends NanoHTTPD {
return servedFiles.size() - 1;
}
- /**
- * Removes the file with the given ID from the server.
- *
- * @return True if a file was removed, false otherwise
- */
- public synchronized boolean removeFile(int id) {
- if (id < 0) throw new IllegalArgumentException("ID < 0");
- if (id >= servedFiles.size()) {
- return false;
- } else {
- return servedFiles.remove(id) != null;
- }
- }
-
public synchronized File accessFile(int id) {
if (id < 0 || id >= servedFiles.size()) {
return null;
diff --git a/app/src/free/java/de/danoeh/antennapod/activity/CastEnabledActivity.java b/app/src/free/java/de/danoeh/antennapod/activity/CastEnabledActivity.java
index 79d7e02f2..519d4c61b 100644
--- a/app/src/free/java/de/danoeh/antennapod/activity/CastEnabledActivity.java
+++ b/app/src/free/java/de/danoeh/antennapod/activity/CastEnabledActivity.java
@@ -7,212 +7,9 @@ import android.support.v7.app.AppCompatActivity;
* network.
*/
public abstract class CastEnabledActivity extends AppCompatActivity {
-// implements SharedPreferences.OnSharedPreferenceChangeListener {
public static final String TAG = "CastEnabledActivity";
-// protected CastManager castManager;
-// protected SwitchableMediaRouteActionProvider mediaRouteActionProvider;
-// private final CastButtonVisibilityManager castButtonVisibilityManager = new CastButtonVisibilityManager();
-//
-// @Override
-// protected void onCreate(Bundle savedInstanceState) {
-// super.onCreate(savedInstanceState);
-//
-// PreferenceManager.getDefaultSharedPreferences(getApplicationContext()).
-// registerOnSharedPreferenceChangeListener(this);
-//
-// castManager = CastManager.getInstance();
-// castManager.addCastConsumer(castConsumer);
-// castButtonVisibilityManager.setPrefEnabled(UserPreferences.isCastEnabled());
-// onCastConnectionChanged(castManager.isConnected());
-// }
-//
-// @Override
-// protected void onDestroy() {
-// PreferenceManager.getDefaultSharedPreferences(getApplicationContext())
-// .unregisterOnSharedPreferenceChangeListener(this);
-// castManager.removeCastConsumer(castConsumer);
-// super.onDestroy();
-// }
-//
-// @Override
-// @CallSuper
-// public boolean onCreateOptionsMenu(Menu menu) {
-// super.onCreateOptionsMenu(menu);
-// getMenuInflater().inflate(R.menu.cast_enabled, menu);
-// castButtonVisibilityManager.setMenu(menu);
-// return true;
-// }
-//
-// @Override
-// @CallSuper
-// public boolean onPrepareOptionsMenu(Menu menu) {
-// super.onPrepareOptionsMenu(menu);
-// mediaRouteActionProvider = castManager
-// .addMediaRouterButton(menu.findItem(R.id.media_route_menu_item));
-// mediaRouteActionProvider.setEnabled(castButtonVisibilityManager.shouldEnable());
-// return true;
-// }
-//
-// @Override
-// protected void onResume() {
-// super.onResume();
-// castButtonVisibilityManager.setResumed(true);
-// }
-//
-// @Override
-// protected void onPause() {
-// super.onPause();
-// castButtonVisibilityManager.setResumed(false);
-// }
-//
-// @Override
-// public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
-// if (UserPreferences.PREF_CAST_ENABLED.equals(key)) {
-// boolean newValue = UserPreferences.isCastEnabled();
-// Log.d(TAG, "onSharedPreferenceChanged(), isCastEnabled set to " + newValue);
-// castButtonVisibilityManager.setPrefEnabled(newValue);
-// // PlaybackService has its own listener, so if it's active we don't have to take action here.
-// if (!newValue && !PlaybackService.isRunning) {
-// CastManager.getInstance().disconnect();
-// }
-// }
-// }
-//
-// CastConsumer castConsumer = new DefaultCastConsumer() {
-// @Override
-// public void onApplicationConnected(ApplicationMetadata appMetadata, String sessionId, boolean wasLaunched) {
-// onCastConnectionChanged(true);
-// }
-//
-// @Override
-// public void onDisconnected() {
-// onCastConnectionChanged(false);
-// }
-// };
-//
-// private void onCastConnectionChanged(boolean connected) {
-// if (connected) {
-// castButtonVisibilityManager.onConnected();
-// setVolumeControlStream(AudioManager.USE_DEFAULT_STREAM_TYPE);
-// } else {
-// castButtonVisibilityManager.onDisconnected();
-// setVolumeControlStream(AudioManager.STREAM_MUSIC);
-// }
-// }
-//
-// /**
-// * Should be called by any activity or fragment for which the cast button should be shown.
-// *
-// * @param showAsAction refer to {@link MenuItem#setShowAsAction(int)}
-// */
public final void requestCastButton(int showAsAction) {
// no-op
}
-
-// private class CastButtonVisibilityManager {
-// private volatile boolean prefEnabled = false;
-// private volatile boolean viewRequested = false;
-// private volatile boolean resumed = false;
-// private volatile boolean connected = false;
-// private volatile int showAsAction = MenuItem.SHOW_AS_ACTION_IF_ROOM;
-// private Menu menu;
-//
-// public synchronized void setPrefEnabled(boolean newValue) {
-// if (prefEnabled != newValue && resumed && (viewRequested || connected)) {
-// if (newValue) {
-// castManager.incrementUiCounter();
-// } else {
-// castManager.decrementUiCounter();
-// }
-// }
-// prefEnabled = newValue;
-// if (mediaRouteActionProvider != null) {
-// mediaRouteActionProvider.setEnabled(prefEnabled && (viewRequested || connected));
-// }
-// }
-//
-// public synchronized void setResumed(boolean newValue) {
-// if (resumed == newValue) {
-// Log.e(TAG, "resumed should never change to the same value");
-// return;
-// }
-// resumed = newValue;
-// if (prefEnabled && (viewRequested || connected)) {
-// if (resumed) {
-// castManager.incrementUiCounter();
-// } else {
-// castManager.decrementUiCounter();
-// }
-// }
-// }
-//
-// public synchronized void setViewRequested(boolean newValue) {
-// if (viewRequested != newValue && resumed && prefEnabled && !connected) {
-// if (newValue) {
-// castManager.incrementUiCounter();
-// } else {
-// castManager.decrementUiCounter();
-// }
-// }
-// viewRequested = newValue;
-// if (mediaRouteActionProvider != null) {
-// mediaRouteActionProvider.setEnabled(prefEnabled && (viewRequested || connected));
-// }
-// }
-//
-// public synchronized void setConnected(boolean newValue) {
-// if (connected != newValue && resumed && prefEnabled && !prefEnabled) {
-// if (newValue) {
-// castManager.incrementUiCounter();
-// } else {
-// castManager.decrementUiCounter();
-// }
-// }
-// connected = newValue;
-// if (mediaRouteActionProvider != null) {
-// mediaRouteActionProvider.setEnabled(prefEnabled && (viewRequested || connected));
-// }
-// }
-//
-// public synchronized boolean shouldEnable() {
-// return prefEnabled && viewRequested;
-// }
-//
-// public void setMenu(Menu menu) {
-// setViewRequested(false);
-// showAsAction = MenuItem.SHOW_AS_ACTION_IF_ROOM;
-// this.menu = menu;
-// setShowAsAction();
-// }
-//
-// public void requestCastButton(int showAsAction) {
-// setViewRequested(true);
-// this.showAsAction = showAsAction;
-// setShowAsAction();
-// }
-//
-// public void onConnected() {
-// setConnected(true);
-// setShowAsAction();
-// }
-//
-// public void onDisconnected() {
-// setConnected(false);
-// setShowAsAction();
-// }
-//
-// private void setShowAsAction() {
-// if (menu == null) {
-// Log.d(TAG, "setShowAsAction() without a menu");
-// return;
-// }
-// MenuItem item = menu.findItem(R.id.media_route_menu_item);
-// if (item == null) {
-// Log.e(TAG, "setShowAsAction(), but cast button not inflated");
-// return;
-// }
-// MenuItemCompat.setShowAsAction(item, connected? MenuItem.SHOW_AS_ACTION_ALWAYS : showAsAction);
-// }
-// }
}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 73af654e9..c2c6f53c5 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -36,7 +36,7 @@
android:usesCleartextTraffic="true"
android:logo="@mipmap/ic_launcher">
<meta-data android:name="com.google.android.gms.car.notification.SmallIcon"
- android:resource="@drawable/ic_notification" />
+ android:resource="@drawable/ic_antenna" />
<meta-data
android:name="com.google.android.backup.api_key"
android:value="AEdPqrEAAAAI3a05VToCTlqBymJrbFGaKQMvF-bBAuLsOdavBA"/>
@@ -209,6 +209,13 @@
android:name=".activity.OpmlFeedChooserActivity"
android:label="@string/opml_import_label">
</activity>
+ <activity
+ android:name=".activity.BugReportActivity"
+ android:label="@string/bug_report_title">
+ <meta-data
+ android:name="android.support.PARENT_ACTIVITY"
+ android:value="de.danoeh.antennapod.activity.PreferenceActivity"/>
+ </activity>
<activity
android:name=".activity.VideoplayerActivity"
diff --git a/app/src/main/java/de/danoeh/antennapod/CrashReportWriter.java b/app/src/main/java/de/danoeh/antennapod/CrashReportWriter.java
index ea2166674..061ea9ae2 100644
--- a/app/src/main/java/de/danoeh/antennapod/CrashReportWriter.java
+++ b/app/src/main/java/de/danoeh/antennapod/CrashReportWriter.java
@@ -9,6 +9,9 @@ import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
import de.danoeh.antennapod.core.preferences.UserPreferences;
@@ -32,13 +35,7 @@ public class CrashReportWriter implements Thread.UncaughtExceptionHandler {
PrintWriter out = null;
try {
out = new PrintWriter(new FileWriter(path));
- out.println("[ Environment ]");
- out.println("Android version: " + Build.VERSION.RELEASE);
- out.println("OS version: " + System.getProperty("os.version"));
- out.println("AntennaPod version: " + BuildConfig.VERSION_NAME);
- out.println("Model: " + Build.MODEL);
- out.println("Device: " + Build.DEVICE);
- out.println("Product: " + Build.PRODUCT);
+ out.println(getSystemInfo());
out.println();
out.println("[ StackTrace ]");
ex.printStackTrace(out);
@@ -49,4 +46,15 @@ public class CrashReportWriter implements Thread.UncaughtExceptionHandler {
}
defaultHandler.uncaughtException(thread, ex);
}
+
+ public static String getSystemInfo() {
+ return "[ Environment ]" +
+ "\nAndroid version: " + Build.VERSION.RELEASE +
+ "\nOS version: " + System.getProperty("os.version") +
+ "\nAntennaPod version: " + BuildConfig.VERSION_NAME +
+ "\nModel: " + Build.MODEL +
+ "\nDevice: " + Build.DEVICE +
+ "\nProduct: " + Build.PRODUCT +
+ "\nTime: " + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()).format(new Date()) + "\n";
+ }
}
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 1bcdada44..821e2f347 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/AboutActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/AboutActivity.java
@@ -15,6 +15,7 @@ import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.LinearLayout;
+import de.danoeh.antennapod.core.util.IntentUtils;
import org.apache.commons.io.IOUtils;
import java.io.IOException;
@@ -57,8 +58,7 @@ public class AboutActivity extends AppCompatActivity {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (url.startsWith("http")) {
- Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
- startActivity(browserIntent);
+ IntentUtils.openInBrowser(AboutActivity.this, url);
return true;
} else {
url = url.replace("file:///android_asset/", "");
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/AudioplayerActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/AudioplayerActivity.java
index 2bcd7a461..07c970197 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/AudioplayerActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/AudioplayerActivity.java
@@ -7,6 +7,8 @@ import android.util.Log;
import android.view.View;
import java.text.DecimalFormat;
+import java.text.DecimalFormatSymbols;
+import java.util.Locale;
import java.util.concurrent.atomic.AtomicBoolean;
import de.danoeh.antennapod.core.feed.MediaType;
@@ -58,11 +60,13 @@ public class AudioplayerActivity extends MediaplayerInfoActivity {
}
if (controller == null) {
butPlaybackSpeed.setVisibility(View.GONE);
+ txtvPlaybackSpeed.setVisibility(View.GONE);
return;
}
updatePlaybackSpeedButtonText();
ViewCompat.setAlpha(butPlaybackSpeed, controller.canSetPlaybackSpeed() ? 1.0f : 0.5f);
butPlaybackSpeed.setVisibility(View.VISIBLE);
+ txtvPlaybackSpeed.setVisibility(View.VISIBLE);
}
@Override
@@ -72,21 +76,15 @@ public class AudioplayerActivity extends MediaplayerInfoActivity {
}
if (controller == null) {
butPlaybackSpeed.setVisibility(View.GONE);
+ txtvPlaybackSpeed.setVisibility(View.GONE);
return;
}
float speed = 1.0f;
if(controller.canSetPlaybackSpeed()) {
- try {
- // we can only retrieve the playback speed from the controller/playback service
- // once mediaplayer has been initialized
- speed = Float.parseFloat(UserPreferences.getPlaybackSpeed());
- } catch (NumberFormatException e) {
- Log.e(TAG, Log.getStackTraceString(e));
- UserPreferences.setPlaybackSpeed(String.valueOf(speed));
- }
+ speed = UserPreferences.getPlaybackSpeed();
}
- String speedStr = new DecimalFormat("0.00x").format(speed);
- butPlaybackSpeed.setText(speedStr);
+ String speedStr = new DecimalFormat("0.00").format(speed);
+ txtvPlaybackSpeed.setText(speedStr);
}
@Override
@@ -105,7 +103,9 @@ public class AudioplayerActivity extends MediaplayerInfoActivity {
}
if (controller.canSetPlaybackSpeed()) {
String[] availableSpeeds = UserPreferences.getPlaybackSpeedArray();
- String currentSpeed = UserPreferences.getPlaybackSpeed();
+ DecimalFormatSymbols format = new DecimalFormatSymbols(Locale.US);
+ format.setDecimalSeparator('.');
+ String currentSpeed = new DecimalFormat("0.00", format).format(UserPreferences.getPlaybackSpeed());
// Provide initial value in case the speed list has changed
// out from under us
@@ -139,6 +139,7 @@ public class AudioplayerActivity extends MediaplayerInfoActivity {
return true;
});
butPlaybackSpeed.setVisibility(View.VISIBLE);
+ txtvPlaybackSpeed.setVisibility(View.VISIBLE);
}
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/BugReportActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/BugReportActivity.java
new file mode 100644
index 000000000..7df973f9b
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/activity/BugReportActivity.java
@@ -0,0 +1,55 @@
+package de.danoeh.antennapod.activity;
+
+import android.content.ClipData;
+import android.content.ClipboardManager;
+import android.content.Context;
+import android.os.Bundle;
+import android.support.design.widget.Snackbar;
+import android.support.v7.app.AppCompatActivity;
+import android.widget.TextView;
+import de.danoeh.antennapod.CrashReportWriter;
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.core.preferences.UserPreferences;
+import de.danoeh.antennapod.core.util.IntentUtils;
+import org.apache.commons.io.IOUtils;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.nio.charset.Charset;
+
+/**
+ * Displays the 'crash report' screen
+ */
+public class BugReportActivity extends AppCompatActivity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ setTheme(UserPreferences.getTheme());
+ super.onCreate(savedInstanceState);
+ getSupportActionBar().setDisplayShowHomeEnabled(true);
+ setContentView(R.layout.bug_report);
+
+ TextView crashDetailsText = findViewById(R.id.crash_report_logs);
+
+ try {
+ File crashFile = CrashReportWriter.getFile();
+ String crashReportContent = IOUtils.toString(new FileInputStream(crashFile), Charset.forName("UTF-8"));
+ crashDetailsText.setText(crashReportContent);
+ } catch (IOException e) {
+ e.printStackTrace();
+ crashDetailsText.setText("No crash report recorded\n" + CrashReportWriter.getSystemInfo());
+ }
+
+ findViewById(R.id.btn_open_bug_tracker).setOnClickListener(v -> {
+ IntentUtils.openInBrowser(BugReportActivity.this, "https://github.com/AntennaPod/AntennaPod/issues");
+ });
+
+ findViewById(R.id.btn_copy_log).setOnClickListener(v -> {
+ ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
+ ClipData clip = ClipData.newPlainText(getString(R.string.bug_report_title), crashDetailsText.getText());
+ clipboard.setPrimaryClip(clip);
+ Snackbar.make(findViewById(android.R.id.content), R.string.copied_to_clipboard, Snackbar.LENGTH_SHORT).show();
+ });
+ }
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/CastplayerActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/CastplayerActivity.java
index 871e9c279..c60c7b769 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/CastplayerActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/CastplayerActivity.java
@@ -49,6 +49,7 @@ public class CastplayerActivity extends MediaplayerInfoActivity {
super.setupGUI();
if (butPlaybackSpeed != null) {
butPlaybackSpeed.setVisibility(View.GONE);
+ txtvPlaybackSpeed.setVisibility(View.GONE);
}
// if (butCastDisconnect != null) {
// butCastDisconnect.setOnClickListener(v -> castManager.disconnect());
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java
index 102d30bcf..b028f7983 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java
@@ -2,6 +2,7 @@ package de.danoeh.antennapod.activity;
import android.annotation.TargetApi;
import android.app.ProgressDialog;
+import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
@@ -10,6 +11,7 @@ import android.database.DataSetObserver;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
+import android.support.annotation.NonNull;
import android.support.annotation.VisibleForTesting;
import android.support.design.widget.Snackbar;
import android.support.v4.app.Fragment;
@@ -32,9 +34,11 @@ import android.widget.Toast;
import com.bumptech.glide.Glide;
-import de.danoeh.antennapod.preferences.PreferenceUpgrader;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.Validate;
+import org.greenrobot.eventbus.EventBus;
+import org.greenrobot.eventbus.Subscribe;
+import org.greenrobot.eventbus.ThreadMode;
import java.util.List;
@@ -43,7 +47,6 @@ import de.danoeh.antennapod.adapter.NavListAdapter;
import de.danoeh.antennapod.core.asynctask.FeedRemover;
import de.danoeh.antennapod.core.dialog.ConfirmationDialog;
import de.danoeh.antennapod.core.event.MessageEvent;
-import de.danoeh.antennapod.core.event.ProgressEvent;
import de.danoeh.antennapod.core.event.QueueEvent;
import de.danoeh.antennapod.core.feed.EventDistributor;
import de.danoeh.antennapod.core.feed.Feed;
@@ -56,25 +59,22 @@ import de.danoeh.antennapod.core.util.FeedItemUtil;
import de.danoeh.antennapod.core.util.Flavors;
import de.danoeh.antennapod.core.util.IntentUtils;
import de.danoeh.antennapod.core.util.StorageUtils;
-import de.danoeh.antennapod.core.util.gui.NotificationUtils;
import de.danoeh.antennapod.dialog.RatingDialog;
import de.danoeh.antennapod.dialog.RenameFeedDialog;
import de.danoeh.antennapod.fragment.AddFeedFragment;
import de.danoeh.antennapod.fragment.DownloadsFragment;
import de.danoeh.antennapod.fragment.EpisodesFragment;
import de.danoeh.antennapod.fragment.ExternalPlayerFragment;
-import de.danoeh.antennapod.fragment.ItemlistFragment;
+import de.danoeh.antennapod.fragment.FeedItemlistFragment;
import de.danoeh.antennapod.fragment.PlaybackHistoryFragment;
import de.danoeh.antennapod.fragment.QueueFragment;
import de.danoeh.antennapod.fragment.SubscriptionFragment;
import de.danoeh.antennapod.menuhandler.NavDrawerActivity;
+import de.danoeh.antennapod.preferences.PreferenceUpgrader;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
-import org.greenrobot.eventbus.EventBus;
-import org.greenrobot.eventbus.Subscribe;
-import org.greenrobot.eventbus.ThreadMode;
/**
* The activity that is shown when the user launches the app.
@@ -88,13 +88,13 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
public static final String PREF_NAME = "MainActivityPrefs";
public static final String PREF_IS_FIRST_LAUNCH = "prefMainActivityIsFirstLaunch";
- private static final String PREF_LAST_FRAGMENT_TAG = "prefMainActivityLastFragmentTag";
+ public static final String PREF_LAST_FRAGMENT_TAG = "prefMainActivityLastFragmentTag";
public static final String EXTRA_NAV_TYPE = "nav_type";
public static final String EXTRA_NAV_INDEX = "nav_index";
public static final String EXTRA_FRAGMENT_TAG = "fragment_tag";
public static final String EXTRA_FRAGMENT_ARGS = "fragment_args";
- public static final String EXTRA_FEED_ID = "fragment_feed_id";
+ private static final String EXTRA_FEED_ID = "fragment_feed_id";
private static final String SAVE_BACKSTACK_COUNT = "backstackCount";
private static final String SAVE_TITLE = "title";
@@ -128,6 +128,14 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
private long lastBackButtonPressTime = 0;
+ @NonNull
+ public static Intent getIntentToOpenFeed(@NonNull Context context, long feedId) {
+ Intent intent = new Intent(context.getApplicationContext(), MainActivity.class);
+ intent.putExtra(MainActivity.EXTRA_FEED_ID, feedId);
+ intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ return intent;
+ }
+
@Override
public void onCreate(Bundle savedInstanceState) {
setTheme(UserPreferences.getNoTitleTheme());
@@ -233,6 +241,7 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
private void checkFirstLaunch() {
SharedPreferences prefs = getSharedPreferences(PREF_NAME, MODE_PRIVATE);
if (prefs.getBoolean(PREF_IS_FIRST_LAUNCH, true)) {
+ loadFragment(AddFeedFragment.TAG, null);
new Handler().postDelayed(() -> drawerLayout.openDrawer(navDrawer), 1500);
// for backward compatibility, we only change defaults for fresh installs
@@ -240,7 +249,7 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
SharedPreferences.Editor edit = prefs.edit();
edit.putBoolean(PREF_IS_FIRST_LAUNCH, false);
- edit.commit();
+ edit.apply();
}
}
@@ -337,7 +346,7 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
}
public void loadFeedFragmentById(long feedId, Bundle args) {
- Fragment fragment = ItemlistFragment.newInstance(feedId);
+ Fragment fragment = FeedItemlistFragment.newInstance(feedId);
if(args != null) {
fragment.setArguments(args);
}
@@ -778,25 +787,6 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
}
@Subscribe(threadMode = ThreadMode.MAIN)
- public void onEventMainThread(ProgressEvent event) {
- Log.d(TAG, "onEvent(" + event + ")");
- switch(event.action) {
- case START:
- pd = new ProgressDialog(this);
- pd.setMessage(event.message);
- pd.setIndeterminate(true);
- pd.setCancelable(false);
- pd.show();
- break;
- case END:
- if(pd != null) {
- pd.dismiss();
- }
- break;
- }
- }
-
- @Subscribe(threadMode = ThreadMode.MAIN)
public void onEventMainThread(MessageEvent event) {
Log.d(TAG, "onEvent(" + event + ")");
View parentLayout = findViewById(R.id.drawer_layout);
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java
index 3946400a4..b1269b833 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java
@@ -22,8 +22,6 @@ import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
-import android.widget.Button;
-import android.widget.CheckBox;
import android.widget.ImageButton;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
@@ -35,16 +33,14 @@ import com.bumptech.glide.Glide;
import com.joanzapata.iconify.IconDrawable;
import com.joanzapata.iconify.fonts.FontAwesomeIcons;
-import java.util.Locale;
-
import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.feed.MediaType;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.playback.PlaybackService;
import de.danoeh.antennapod.core.storage.DBReader;
-import de.danoeh.antennapod.core.storage.DBTasks;
import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.util.Consumer;
import de.danoeh.antennapod.core.util.Converter;
@@ -62,12 +58,15 @@ import de.danoeh.antennapod.core.util.playback.MediaPlayerError;
import de.danoeh.antennapod.core.util.playback.Playable;
import de.danoeh.antennapod.core.util.playback.PlaybackController;
import de.danoeh.antennapod.core.util.playback.PlaybackServiceStarter;
+import de.danoeh.antennapod.dialog.PlaybackControlsDialog;
import de.danoeh.antennapod.dialog.SleepTimerDialog;
-import de.danoeh.antennapod.dialog.VariableSpeedDialog;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
+import org.greenrobot.eventbus.EventBus;
+import org.greenrobot.eventbus.Subscribe;
+import org.greenrobot.eventbus.ThreadMode;
/**
@@ -79,9 +78,6 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
private static final String PREFS = "MediaPlayerActivityPreferences";
private static final String PREF_SHOW_TIME_LEFT = "showTimeLeft";
private static final int REQUEST_CODE_STORAGE = 42;
- private static final float PLAYBACK_SPEED_STEP = 0.05f;
- private static final float DEFAULT_MIN_PLAYBACK_SPEED = 0.5f;
- private static final float DEFAULT_MAX_PLAYBACK_SPEED = 2.5f;
PlaybackController controller;
@@ -202,6 +198,11 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
};
}
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ public void onEventMainThread(PlaybackPositionEvent event) {
+ onPositionObserverUpdate();
+ }
+
private static TextView getTxtvFFFromActivity(MediaplayerActivity activity) {
return activity.txtvFF;
}
@@ -282,6 +283,7 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
controller.init();
loadMediaInfo();
onPositionObserverUpdate();
+ EventBus.getDefault().register(this);
}
@Override
@@ -294,6 +296,7 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
if (disposable != null) {
disposable.dispose();
}
+ EventBus.getDefault().unregister(this);
super.onStop();
}
@@ -330,6 +333,8 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
Playable media = controller.getMedia();
boolean isFeedMedia = media != null && (media instanceof FeedMedia);
+ menu.findItem(R.id.open_feed_item).setVisible(isFeedMedia); // FeedMedia implies it belongs to a Feed
+
boolean hasWebsiteLink = ( getWebsiteLinkWithFallback(media) != null );
menu.findItem(R.id.visit_website_item).setVisible(hasWebsiteLink);
@@ -365,7 +370,8 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
menu.findItem(R.id.audio_controls).setIcon(new IconDrawable(this,
FontAwesomeIcons.fa_sliders).color(textColor).actionBarSize());
} else {
- menu.findItem(R.id.audio_controls).setVisible(false);
+ menu.findItem(R.id.audio_controls).setIcon(new IconDrawable(this,
+ FontAwesomeIcons.fa_sliders).color(0xffffffff).actionBarSize());
}
return true;
@@ -396,29 +402,24 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
return true;
} else {
if (media != null) {
+ final @Nullable FeedItem feedItem = getFeedItem(media); // some options option requires FeedItem
switch (item.getItemId()) {
case R.id.add_to_favorites_item:
- if(media instanceof FeedMedia) {
- FeedItem feedItem = ((FeedMedia)media).getItem();
- if(feedItem != null) {
- DBWriter.addFavoriteItem(feedItem);
- isFavorite = true;
- invalidateOptionsMenu();
- Toast.makeText(this, R.string.added_to_favorites, Toast.LENGTH_SHORT)
- .show();
- }
+ if (feedItem != null) {
+ DBWriter.addFavoriteItem(feedItem);
+ isFavorite = true;
+ invalidateOptionsMenu();
+ Toast.makeText(this, R.string.added_to_favorites, Toast.LENGTH_SHORT)
+ .show();
}
break;
case R.id.remove_from_favorites_item:
- if(media instanceof FeedMedia) {
- FeedItem feedItem = ((FeedMedia)media).getItem();
- if(feedItem != null) {
- DBWriter.removeFavoriteItem(feedItem);
- isFavorite = false;
- invalidateOptionsMenu();
- Toast.makeText(this, R.string.removed_from_favorites, Toast.LENGTH_SHORT)
- .show();
- }
+ if (feedItem != null) {
+ DBWriter.removeFavoriteItem(feedItem);
+ isFavorite = false;
+ invalidateOptionsMenu();
+ Toast.makeText(this, R.string.removed_from_favorites, Toast.LENGTH_SHORT)
+ .show();
}
break;
case R.id.disable_sleeptimer_item:
@@ -451,171 +452,37 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
}
break;
case R.id.audio_controls:
- MaterialDialog dialog = new MaterialDialog.Builder(this)
- .title(R.string.audio_controls)
- .customView(R.layout.audio_controls, true)
- .neutralText(R.string.close_label)
- .onNeutral((dialog1, which) -> {
- final SeekBar left = (SeekBar) dialog1.findViewById(R.id.volume_left);
- final SeekBar right = (SeekBar) dialog1.findViewById(R.id.volume_right);
- UserPreferences.setVolume(left.getProgress(), right.getProgress());
- })
- .show();
- final SeekBar barPlaybackSpeed = (SeekBar) dialog.findViewById(R.id.playback_speed);
- final Button butDecSpeed = (Button) dialog.findViewById(R.id.butDecSpeed);
- butDecSpeed.setOnClickListener(v -> {
- if(controller != null && controller.canSetPlaybackSpeed()) {
- barPlaybackSpeed.setProgress(barPlaybackSpeed.getProgress() - 1);
- } else {
- VariableSpeedDialog.showGetPluginDialog(this);
- }
- });
- final Button butIncSpeed = (Button) dialog.findViewById(R.id.butIncSpeed);
- butIncSpeed.setOnClickListener(v -> {
- if(controller != null && controller.canSetPlaybackSpeed()) {
- barPlaybackSpeed.setProgress(barPlaybackSpeed.getProgress() + 1);
- } else {
- VariableSpeedDialog.showGetPluginDialog(this);
- }
- });
-
- final TextView txtvPlaybackSpeed = (TextView) dialog.findViewById(R.id.txtvPlaybackSpeed);
- float currentSpeed = 1.0f;
- try {
- currentSpeed = Float.parseFloat(UserPreferences.getPlaybackSpeed());
- } catch (NumberFormatException e) {
- Log.e(TAG, Log.getStackTraceString(e));
- UserPreferences.setPlaybackSpeed(String.valueOf(currentSpeed));
- }
-
- String[] availableSpeeds = UserPreferences.getPlaybackSpeedArray();
- final float minPlaybackSpeed = availableSpeeds.length > 1 ?
- Float.valueOf(availableSpeeds[0]) : DEFAULT_MIN_PLAYBACK_SPEED;
- float maxPlaybackSpeed = availableSpeeds.length > 1 ?
- Float.valueOf(availableSpeeds[availableSpeeds.length - 1]) : DEFAULT_MAX_PLAYBACK_SPEED;
- int progressMax = (int) ((maxPlaybackSpeed - minPlaybackSpeed) / PLAYBACK_SPEED_STEP);
- barPlaybackSpeed.setMax(progressMax);
-
- txtvPlaybackSpeed.setText(String.format("%.2fx", currentSpeed));
- barPlaybackSpeed.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
- @Override
- public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
- if(controller != null && controller.canSetPlaybackSpeed()) {
- float playbackSpeed = progress * PLAYBACK_SPEED_STEP + minPlaybackSpeed;
- controller.setPlaybackSpeed(playbackSpeed);
- String speedPref = String.format(Locale.US, "%.2f", playbackSpeed);
- UserPreferences.setPlaybackSpeed(speedPref);
- String speedStr = String.format("%.2fx", playbackSpeed);
- txtvPlaybackSpeed.setText(speedStr);
- } else if(fromUser) {
- float speed = Float.valueOf(UserPreferences.getPlaybackSpeed());
- barPlaybackSpeed.post(() -> barPlaybackSpeed.setProgress(
- (int) ((speed - minPlaybackSpeed) / PLAYBACK_SPEED_STEP)));
- }
- }
-
- @Override
- public void onStartTrackingTouch(SeekBar seekBar) {
- if(controller != null && !controller.canSetPlaybackSpeed()) {
- VariableSpeedDialog.showGetPluginDialog(MediaplayerActivity.this);
- }
- }
-
- @Override
- public void onStopTrackingTouch(SeekBar seekBar) {
- }
- });
- barPlaybackSpeed.setProgress((int) ((currentSpeed - minPlaybackSpeed) / PLAYBACK_SPEED_STEP));
-
- final SeekBar barLeftVolume = (SeekBar) dialog.findViewById(R.id.volume_left);
- barLeftVolume.setProgress(UserPreferences.getLeftVolumePercentage());
- final SeekBar barRightVolume = (SeekBar) dialog.findViewById(R.id.volume_right);
- barRightVolume.setProgress(UserPreferences.getRightVolumePercentage());
- final CheckBox stereoToMono = (CheckBox) dialog.findViewById(R.id.stereo_to_mono);
- stereoToMono.setChecked(UserPreferences.stereoToMono());
- if (controller != null && !controller.canDownmix()) {
- stereoToMono.setEnabled(false);
- String sonicOnly = getString(R.string.sonic_only);
- stereoToMono.setText(stereoToMono.getText() + " [" + sonicOnly + "]");
- }
-
- if (UserPreferences.useExoplayer()) {
- barRightVolume.setEnabled(false);
- }
-
- final CheckBox skipSilence = (CheckBox) dialog.findViewById(R.id.skipSilence);
- skipSilence.setChecked(UserPreferences.isSkipSilence());
- if (!UserPreferences.useExoplayer()) {
- skipSilence.setEnabled(false);
- String exoplayerOnly = getString(R.string.exoplayer_only);
- skipSilence.setText(skipSilence.getText() + " [" + exoplayerOnly + "]");
+ boolean isPlayingVideo = controller.getMedia().getMediaType() == MediaType.VIDEO;
+ PlaybackControlsDialog dialog = PlaybackControlsDialog.newInstance(isPlayingVideo);
+ dialog.show(getSupportFragmentManager(), "playback_controls");
+ break;
+ case R.id.open_feed_item:
+ if (feedItem != null) {
+ Intent intent = MainActivity.getIntentToOpenFeed(this, feedItem.getFeedId());
+ startActivity(intent);
}
- skipSilence.setOnCheckedChangeListener((buttonView, isChecked) -> {
- UserPreferences.setSkipSilence(isChecked);
- controller.setSkipSilence(isChecked);
- });
-
- barLeftVolume.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
- @Override
- public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
- controller.setVolume(
- Converter.getVolumeFromPercentage(progress),
- Converter.getVolumeFromPercentage(barRightVolume.getProgress()));
- }
-
- @Override
- public void onStartTrackingTouch(SeekBar seekBar) {
- }
-
- @Override
- public void onStopTrackingTouch(SeekBar seekBar) {
- }
- });
- barRightVolume.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
- @Override
- public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
- controller.setVolume(
- Converter.getVolumeFromPercentage(barLeftVolume.getProgress()),
- Converter.getVolumeFromPercentage(progress));
- }
-
- @Override
- public void onStartTrackingTouch(SeekBar seekBar) {
- }
-
- @Override
- public void onStopTrackingTouch(SeekBar seekBar) {
- }
- });
- stereoToMono.setOnCheckedChangeListener((buttonView, isChecked) -> {
- UserPreferences.stereoToMono(isChecked);
- if (controller != null) {
- controller.setDownmix(isChecked);
- }
- });
break;
case R.id.visit_website_item:
- Uri uri = Uri.parse(getWebsiteLinkWithFallback(media));
- startActivity(new Intent(Intent.ACTION_VIEW, uri));
+ IntentUtils.openInBrowser(MediaplayerActivity.this, getWebsiteLinkWithFallback(media));
break;
case R.id.share_link_item:
- if (media instanceof FeedMedia) {
- ShareUtils.shareFeedItemLink(this, ((FeedMedia) media).getItem());
+ if (feedItem != null) {
+ ShareUtils.shareFeedItemLink(this, feedItem);
}
break;
case R.id.share_download_url_item:
- if (media instanceof FeedMedia) {
- ShareUtils.shareFeedItemDownloadLink(this, ((FeedMedia) media).getItem());
+ if (feedItem != null) {
+ ShareUtils.shareFeedItemDownloadLink(this, feedItem);
}
break;
case R.id.share_link_with_position_item:
- if (media instanceof FeedMedia) {
- ShareUtils.shareFeedItemLink(this, ((FeedMedia) media).getItem(), true);
+ if (feedItem != null) {
+ ShareUtils.shareFeedItemLink(this, feedItem, true);
}
break;
case R.id.share_download_url_with_position_item:
- if (media instanceof FeedMedia) {
- ShareUtils.shareFeedItemDownloadLink(this, ((FeedMedia) media).getItem(), true);
+ if (feedItem != null) {
+ ShareUtils.shareFeedItemDownloadLink(this, feedItem, true);
}
break;
case R.id.share_file:
@@ -666,9 +533,10 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
return;
}
- int currentPosition = TimeSpeedConverter.convert(controller.getPosition());
- int duration = TimeSpeedConverter.convert(controller.getDuration());
- int remainingTime = TimeSpeedConverter.convert(
+ TimeSpeedConverter converter = new TimeSpeedConverter(controller.getCurrentPlaybackSpeedMultiplier());
+ int currentPosition = converter.convert(controller.getPosition());
+ int duration = converter.convert(controller.getDuration());
+ int remainingTime = converter.convert(
controller.getDuration() - controller.getPosition());
Log.d(TAG, "currentPosition " + Converter.getDurationStringLong(currentPosition));
if (currentPosition == PlaybackService.INVALID_TIME ||
@@ -780,7 +648,7 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
}
}
- static public void showSkipPreference(Activity activity, SkipDirection direction) {
+ public static void showSkipPreference(Activity activity, SkipDirection direction) {
int checked = 0;
int skipSecs = direction.getPrefSkipSeconds();
final int[] values = activity.getResources().getIntArray(R.array.seek_delta_values);
@@ -824,14 +692,15 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
return;
}
+ TimeSpeedConverter converter = new TimeSpeedConverter(controller.getCurrentPlaybackSpeedMultiplier());
String length;
if (showTimeLeft) {
- int remainingTime = TimeSpeedConverter.convert(
+ int remainingTime = converter.convert(
media.getDuration() - media.getPosition());
length = "-" + Converter.getDurationStringLong(remainingTime);
} else {
- int duration = TimeSpeedConverter.convert(media.getDuration());
+ int duration = converter.convert(media.getDuration());
length = Converter.getDurationStringLong(duration);
}
txtvLength.setText(length);
@@ -935,7 +804,8 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
prog = controller.onSeekBarProgressChanged(seekBar, progress, fromUser, txtvPosition);
if (showTimeLeft && prog != 0) {
int duration = controller.getDuration();
- int timeLeft = TimeSpeedConverter.convert(duration - (int) (prog * duration));
+ TimeSpeedConverter converter = new TimeSpeedConverter(controller.getCurrentPlaybackSpeedMultiplier());
+ int timeLeft = converter.convert(duration - (int) (prog * duration));
String length = "-" + Converter.getDurationStringLong(timeLeft);
txtvLength.setText(length);
}
@@ -956,11 +826,7 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
}
private void checkFavorite() {
- Playable playable = controller.getMedia();
- if (!(playable instanceof FeedMedia)) {
- return;
- }
- FeedItem feedItem = ((FeedMedia) playable).getItem();
+ FeedItem feedItem = getFeedItem(controller.getMedia());
if (feedItem == null) {
return;
}
@@ -1015,4 +881,13 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
}
}
}
+
+ @Nullable
+ private static FeedItem getFeedItem(@Nullable Playable playable) {
+ if ((playable != null) && (playable instanceof FeedMedia)) {
+ return ((FeedMedia)playable).getItem();
+ } else {
+ return null;
+ }
+ }
}
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerInfoActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerInfoActivity.java
index 4fec1cfc5..234962a8d 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerInfoActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerInfoActivity.java
@@ -26,6 +26,7 @@ import android.widget.AdapterView;
import android.widget.Button;
import android.widget.ImageButton;
import android.widget.ListView;
+import android.widget.TextView;
import android.widget.Toast;
import com.viewpagerindicator.CirclePageIndicator;
@@ -92,7 +93,8 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem
NavListAdapter.SUBSCRIPTION_LIST_TAG
};
- Button butPlaybackSpeed;
+ ImageButton butPlaybackSpeed;
+ TextView txtvPlaybackSpeed;
ImageButton butCastDisconnect;
private DrawerLayout drawerLayout;
private NavListAdapter navAdapter;
@@ -120,7 +122,6 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem
disposable.dispose();
}
EventDistributor.getInstance().unregister(contentUpdate);
- EventBus.getDefault().unregister(this);
saveCurrentFragment();
}
@@ -173,7 +174,6 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem
protected void onStart() {
super.onStart();
EventDistributor.getInstance().register(contentUpdate);
- EventBus.getDefault().register(this);
loadData();
}
@@ -258,6 +258,7 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem
});
butPlaybackSpeed = findViewById(R.id.butPlaybackSpeed);
+ txtvPlaybackSpeed = findViewById(R.id.txtvPlaybackSpeed);
butCastDisconnect = findViewById(R.id.butCastDisconnect);
pager = findViewById(R.id.pager);
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java
index a0c449275..732570d8a 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java
@@ -32,8 +32,6 @@ import android.widget.TextView;
import com.bumptech.glide.Glide;
import com.bumptech.glide.request.RequestOptions;
-import de.danoeh.antennapod.core.feed.VolumeReductionSetting;
-import de.danoeh.antennapod.core.glide.FastBlurTransformation;
import org.apache.commons.lang3.StringUtils;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
@@ -55,7 +53,9 @@ 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.FeedPreferences;
+import de.danoeh.antennapod.core.feed.VolumeReductionSetting;
import de.danoeh.antennapod.core.glide.ApGlideSettings;
+import de.danoeh.antennapod.core.glide.FastBlurTransformation;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.download.DownloadRequest;
import de.danoeh.antennapod.core.service.download.DownloadStatus;
@@ -443,11 +443,9 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
subscribeButton.setOnClickListener(v -> {
if(feedInFeedlist(feed)) {
- Intent intent = new Intent(OnlineFeedViewActivity.this, MainActivity.class);
// feed.getId() is always 0, we have to retrieve the id from the feed list from
// the database
- intent.putExtra(MainActivity.EXTRA_FEED_ID, getFeedId(feed));
- intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ Intent intent = MainActivity.getIntentToOpenFeed(this, getFeedId(feed));
startActivity(intent);
} else {
Feed f = new Feed(selectedDownloadUrl, null, feed.getTitle());
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/AllEpisodesRecycleAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/AllEpisodesRecycleAdapter.java
index 2eff33339..9eb181edc 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/AllEpisodesRecycleAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/AllEpisodesRecycleAdapter.java
@@ -1,6 +1,7 @@
package de.danoeh.antennapod.adapter;
import android.os.Build;
+import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.content.ContextCompat;
import android.support.v7.widget.RecyclerView;
@@ -23,10 +24,12 @@ import android.widget.TextView;
import com.joanzapata.iconify.Iconify;
import java.lang.ref.WeakReference;
+import java.util.List;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.adapter.actionbutton.ItemActionButton;
+import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.storage.DownloadRequester;
@@ -50,6 +53,7 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR
private final boolean showOnlyNewEpisodes;
private FeedItem selectedItem;
+ private Holder currentlyPlayingItem = null;
private final int playingBackGroundColor;
private final int normalBackGroundColor;
@@ -165,8 +169,9 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR
holder.progress.setVisibility(View.INVISIBLE);
}
- if(media.isCurrentlyPlaying()) {
+ if (media.isCurrentlyPlaying()) {
holder.container.setBackgroundColor(playingBackGroundColor);
+ currentlyPlayingItem = holder;
} else {
holder.container.setBackgroundColor(normalBackGroundColor);
}
@@ -196,6 +201,22 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR
.load();
}
+ @Override
+ public void onBindViewHolder(@NonNull Holder holder, int pos, List<Object> payload) {
+ onBindViewHolder(holder, pos);
+
+ if (holder == currentlyPlayingItem && payload.size() == 1 && payload.get(0) instanceof PlaybackPositionEvent) {
+ PlaybackPositionEvent event = (PlaybackPositionEvent) payload.get(0);
+ holder.progress.setProgress((int) (100.0 * event.getPosition() / event.getDuration()));
+ }
+ }
+
+ public void notifyCurrentlyPlayingItemChanged(PlaybackPositionEvent event) {
+ if (currentlyPlayingItem != null && currentlyPlayingItem.getAdapterPosition() != RecyclerView.NO_POSITION) {
+ notifyItemChanged(currentlyPlayingItem.getAdapterPosition(), event);
+ }
+ }
+
@Nullable
public FeedItem getSelectedItem() {
return selectedItem;
@@ -262,7 +283,7 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR
FeedItem item = itemAccess.getItem(getAdapterPosition());
MenuInflater inflater = mainActivityRef.get().getMenuInflater();
- inflater.inflate(R.menu.allepisodes_context, menu);
+ inflater.inflate(R.menu.feeditemlist_context, menu);
if (item != null) {
menu.setHeaderTitle(item.getTitle());
@@ -277,9 +298,7 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR
item1.setVisible(visible);
}
};
- FeedItemMenuHandler.onPrepareMenu(contextMenuInterface, item, true, null);
-
- contextMenuInterface.setItemVisibility(R.id.remove_new_flag_item, item.isNew());
+ FeedItemMenuHandler.onPrepareMenu(contextMenuInterface, item);
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/DownloadedEpisodesListAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/DownloadedEpisodesListAdapter.java
index cd636af43..98d55dd97 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/DownloadedEpisodesListAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/DownloadedEpisodesListAdapter.java
@@ -100,14 +100,6 @@ public class DownloadedEpisodesListAdapter extends BaseAdapter {
String pubDateStr = DateUtils.formatAbbrev(context, item.getPubDate());
holder.pubDate.setText(pubDateStr);
- FeedItem.State state = item.getState();
- if (state == FeedItem.State.PLAYING && PlaybackService.isRunning) {
- holder.butSecondary.setEnabled(false);
- holder.butSecondary.setAlpha(0.5f);
- } else {
- holder.butSecondary.setEnabled(true);
- holder.butSecondary.setAlpha(1.0f);
- }
holder.butSecondary.setFocusable(false);
holder.butSecondary.setTag(item);
holder.butSecondary.setOnClickListener(secondaryActionListener);
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/DownloadlistAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/DownloadlistAdapter.java
index b85d1d35d..b0ee87b7e 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/DownloadlistAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/DownloadlistAdapter.java
@@ -1,7 +1,6 @@
package de.danoeh.antennapod.adapter;
import android.content.Context;
-import android.support.v4.content.ContextCompat;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -15,20 +14,15 @@ import de.danoeh.antennapod.core.service.download.DownloadRequest;
import de.danoeh.antennapod.core.service.download.DownloadStatus;
import de.danoeh.antennapod.core.service.download.Downloader;
import de.danoeh.antennapod.core.util.Converter;
-import de.danoeh.antennapod.core.util.ThemeUtils;
public class DownloadlistAdapter extends BaseAdapter {
- private static final int SELECTION_NONE = -1;
-
- private int selectedItemIndex;
private final ItemAccess itemAccess;
private final Context context;
public DownloadlistAdapter(Context context,
ItemAccess itemAccess) {
super();
- this.selectedItemIndex = SELECTION_NONE;
this.context = context;
this.itemAccess = itemAccess;
}
@@ -74,13 +68,6 @@ public class DownloadlistAdapter extends BaseAdapter {
holder = (Holder) convertView.getTag();
}
- if (position == selectedItemIndex) {
- convertView.setBackgroundColor(ContextCompat.getColor(convertView.getContext(),
- ThemeUtils.getSelectionBackgroundColor()));
- } else {
- convertView.setBackgroundResource(0);
- }
-
holder.title.setText(request.getTitle());
holder.progbar.setIndeterminate(request.getSoFar() <= 0);
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistAdapter.java
index a365b1b2e..463ad10a0 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistAdapter.java
@@ -13,15 +13,18 @@ import android.widget.BaseAdapter;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
+import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.adapter.actionbutton.ItemActionButton;
+import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.feed.MediaType;
import de.danoeh.antennapod.core.storage.DownloadRequester;
+import de.danoeh.antennapod.core.util.Converter;
import de.danoeh.antennapod.core.util.DateUtils;
import de.danoeh.antennapod.core.util.LongList;
import de.danoeh.antennapod.core.util.ThemeUtils;
@@ -34,15 +37,13 @@ public class FeedItemlistAdapter extends BaseAdapter {
private final ItemAccess itemAccess;
private final Context context;
private final boolean showFeedtitle;
- private final int selectedItemIndex;
/** true if played items should be made partially transparent */
private final boolean makePlayedItemsTransparent;
-
- private static final int SELECTION_NONE = -1;
-
private final int playingBackGroundColor;
private final int normalBackGroundColor;
+ private int currentlyPlayingItem = -1;
+
public FeedItemlistAdapter(Context context,
ItemAccess itemAccess,
boolean showFeedtitle,
@@ -51,7 +52,6 @@ public class FeedItemlistAdapter extends BaseAdapter {
this.context = context;
this.itemAccess = itemAccess;
this.showFeedtitle = showFeedtitle;
- this.selectedItemIndex = SELECTION_NONE;
this.makePlayedItemsTransparent = makePlayedItemsTransparent;
playingBackGroundColor = ThemeUtils.getColorFromAttr(context, R.attr.currently_playing_background);
@@ -112,12 +112,6 @@ public class FeedItemlistAdapter extends BaseAdapter {
if (!(getItemViewType(position) == Adapter.IGNORE_ITEM_VIEW_TYPE)) {
convertView.setVisibility(View.VISIBLE);
- if (position == selectedItemIndex) {
- convertView.setBackgroundColor(ContextCompat.getColor(convertView.getContext(),
- ThemeUtils.getSelectionBackgroundColor()));
- } else {
- convertView.setBackgroundResource(0);
- }
StringBuilder buffer = new StringBuilder(item.getTitle());
if (showFeedtitle) {
@@ -187,8 +181,9 @@ public class FeedItemlistAdapter extends BaseAdapter {
}
typeDrawables.recycle();
- if(media.isCurrentlyPlaying()) {
+ if (media.isCurrentlyPlaying()) {
holder.container.setBackgroundColor(playingBackGroundColor);
+ currentlyPlayingItem = position;
} else {
holder.container.setBackgroundColor(normalBackGroundColor);
}
@@ -206,6 +201,19 @@ public class FeedItemlistAdapter extends BaseAdapter {
return convertView;
}
+ public void notifyCurrentlyPlayingItemChanged(PlaybackPositionEvent event, ListView listView) {
+ if (currentlyPlayingItem != -1 && currentlyPlayingItem < getCount()) {
+ View view = listView.getChildAt(currentlyPlayingItem
+ - listView.getFirstVisiblePosition() + listView.getHeaderViewsCount());
+ if (view == null) {
+ return;
+ }
+ Holder holder = (Holder) view.getTag();
+ holder.episodeProgress.setProgress((int) (100.0 * event.getPosition() / event.getDuration()));
+ holder.lenSize.setText(Converter.getDurationStringLong(event.getDuration() - event.getPosition()));
+ }
+ }
+
static class Holder {
LinearLayout container;
TextView title;
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistDescriptionAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistDescriptionAdapter.java
index c10bb7638..8d469c7a6 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistDescriptionAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistDescriptionAdapter.java
@@ -1,6 +1,7 @@
package de.danoeh.antennapod.adapter;
import android.content.Context;
+import android.os.Build;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -51,6 +52,17 @@ public class FeedItemlistDescriptionAdapter extends ArrayAdapter<FeedItem> {
.replaceAll("\\s+", " ")
.trim();
holder.description.setText(description);
+
+ final int MAX_LINES_COLLAPSED = 3;
+ holder.description.setMaxLines(MAX_LINES_COLLAPSED);
+ holder.description.setOnClickListener(v -> {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN
+ && holder.description.getMaxLines() > MAX_LINES_COLLAPSED) {
+ holder.description.setMaxLines(MAX_LINES_COLLAPSED);
+ } else {
+ holder.description.setMaxLines(2000);
+ }
+ });
}
return convertView;
}
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/QueueRecyclerAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/QueueRecyclerAdapter.java
index 382abfb32..148b36d21 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/QueueRecyclerAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/QueueRecyclerAdapter.java
@@ -1,7 +1,7 @@
package de.danoeh.antennapod.adapter;
-import android.content.Context;
import android.os.Build;
+import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.content.ContextCompat;
import android.support.v4.view.MotionEventCompat;
@@ -25,9 +25,11 @@ import android.widget.TextView;
import com.joanzapata.iconify.Iconify;
+import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
import org.apache.commons.lang3.ArrayUtils;
import java.lang.ref.WeakReference;
+import java.util.List;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
@@ -58,6 +60,7 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap
private boolean locked;
private FeedItem selectedItem;
+ private ViewHolder currentlyPlayingItem = null;
private final int playingBackGroundColor;
private final int normalBackGroundColor;
@@ -94,6 +97,18 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap
});
}
+ @Override
+ public void onBindViewHolder(@NonNull ViewHolder holder, int pos, List<Object> payload) {
+ onBindViewHolder(holder, pos);
+
+ if (holder == currentlyPlayingItem && payload.size() == 1 && payload.get(0) instanceof PlaybackPositionEvent) {
+ PlaybackPositionEvent event = (PlaybackPositionEvent) payload.get(0);
+ holder.progressBar.setProgress((int) (100.0 * event.getPosition() / event.getDuration()));
+ holder.progressLeft.setText(Converter.getDurationStringLong(event.getPosition()));
+ holder.progressRight.setText(Converter.getDurationStringLong(event.getDuration()));
+ }
+ }
+
@Nullable
public FeedItem getSelectedItem() {
return selectedItem;
@@ -109,6 +124,12 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap
return itemAccess.getCount();
}
+ public void notifyCurrentlyPlayingItemChanged(PlaybackPositionEvent event) {
+ if (currentlyPlayingItem != null && currentlyPlayingItem.getAdapterPosition() != RecyclerView.NO_POSITION) {
+ notifyItemChanged(currentlyPlayingItem.getAdapterPosition(), event);
+ }
+ }
+
public class ViewHolder extends RecyclerView.ViewHolder
implements View.OnClickListener,
View.OnCreateContextMenuListener,
@@ -169,7 +190,8 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap
FeedItem item = itemAccess.getItem(getAdapterPosition());
MenuInflater inflater = mainActivity.get().getMenuInflater();
- inflater.inflate(R.menu.queue_context, menu);
+ inflater.inflate(R.menu.queue_context, menu); // queue-specific menu items
+ inflater.inflate(R.menu.feeditemlist_context, menu); // generic menu items for item feeds
if (item != null) {
menu.setHeaderTitle(item.getTitle());
@@ -184,7 +206,18 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap
item1.setVisible(visible);
}
};
- FeedItemMenuHandler.onPrepareMenu(contextMenuInterface, item, true, itemAccess.getQueueIds());
+
+ FeedItemMenuHandler.onPrepareMenu(contextMenuInterface, item,
+ R.id.skip_episode_item); // Skip Episode is not useful in Queue, so hide it.
+ // Queue-specific menu preparation
+ final boolean keepSorted = UserPreferences.isQueueKeepSorted();
+ final LongList queueAccess = itemAccess.getQueueIds();
+ if (queueAccess.size() == 0 || queueAccess.get(0) == item.getId() || keepSorted) {
+ contextMenuInterface.setItemVisibility(R.id.move_to_top_item, false);
+ }
+ if (queueAccess.size() == 0 || queueAccess.get(queueAccess.size()-1) == item.getId() || keepSorted) {
+ contextMenuInterface.setItemVisibility(R.id.move_to_bottom_item, false);
+ }
}
@Override
@@ -276,6 +309,7 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap
if(media.isCurrentlyPlaying()) {
container.setBackgroundColor(playingBackGroundColor);
+ currentlyPlayingItem = this;
} else {
container.setBackgroundColor(normalBackGroundColor);
}
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/SubscriptionsAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/SubscriptionsAdapter.java
index 763dcb57d..e0fb65c61 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/SubscriptionsAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/SubscriptionsAdapter.java
@@ -14,13 +14,11 @@ import com.bumptech.glide.Glide;
import java.lang.ref.WeakReference;
-import com.bumptech.glide.request.RequestOptions;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.core.feed.Feed;
-import de.danoeh.antennapod.core.glide.ApGlideSettings;
import de.danoeh.antennapod.fragment.AddFeedFragment;
-import de.danoeh.antennapod.fragment.ItemlistFragment;
+import de.danoeh.antennapod.fragment.FeedItemlistFragment;
import jp.shts.android.library.TriangleLabelView;
/**
@@ -142,7 +140,7 @@ public class SubscriptionsAdapter extends BaseAdapter implements AdapterView.OnI
if (position == getAddTilePosition()) {
mainActivityRef.get().loadChildFragment(new AddFeedFragment());
} else {
- Fragment fragment = ItemlistFragment.newInstance(getItemId(position));
+ Fragment fragment = FeedItemlistFragment.newInstance(getItemId(position));
mainActivityRef.get().loadChildFragment(fragment);
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/ItemActionButton.java b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/ItemActionButton.java
index da5ebf6e1..6dbeccfc9 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/ItemActionButton.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/ItemActionButton.java
@@ -20,12 +20,12 @@ public abstract class ItemActionButton {
}
@StringRes
- abstract public int getLabel();
+ public abstract int getLabel();
@AttrRes
- abstract public int getDrawable();
+ public abstract int getDrawable();
- abstract public void onClick(Context context);
+ public abstract void onClick(Context context);
public int getVisibility() {
return View.VISIBLE;
diff --git a/app/src/main/java/de/danoeh/antennapod/asynctask/DocumentFileExportWorker.java b/app/src/main/java/de/danoeh/antennapod/asynctask/DocumentFileExportWorker.java
new file mode 100644
index 000000000..0e9572a82
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/asynctask/DocumentFileExportWorker.java
@@ -0,0 +1,72 @@
+package de.danoeh.antennapod.asynctask;
+
+import android.content.Context;
+import android.net.Uri;
+import android.support.annotation.NonNull;
+import android.support.v4.provider.DocumentFile;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+
+import de.danoeh.antennapod.core.export.ExportWriter;
+import de.danoeh.antennapod.core.storage.DBReader;
+import de.danoeh.antennapod.core.util.LangUtils;
+import io.reactivex.Observable;
+
+/**
+ * Writes an OPML file into the user selected export directory in the background.
+ */
+public class DocumentFileExportWorker {
+
+ private final @NonNull ExportWriter exportWriter;
+ private @NonNull Context context;
+ private @NonNull Uri outputFileUri;
+
+ public DocumentFileExportWorker(@NonNull ExportWriter exportWriter, @NonNull Context context, @NonNull Uri outputFileUri) {
+ this.exportWriter = exportWriter;
+ this.context = context;
+ this.outputFileUri = outputFileUri;
+ }
+
+ public Observable<DocumentFile> exportObservable() {
+ DocumentFile output = DocumentFile.fromSingleUri(context, outputFileUri);
+ return Observable.create(subscriber -> {
+ OutputStream outputStream = null;
+ OutputStreamWriter writer = null;
+ try {
+ Uri uri = output.getUri();
+ if (uri == null) {
+ throw new FileNotFoundException("Export file not found.");
+ }
+ outputStream = context.getContentResolver().openOutputStream(uri);
+ if (outputStream == null) {
+ throw new IOException();
+ }
+ writer = new OutputStreamWriter(outputStream, LangUtils.UTF_8);
+ exportWriter.writeDocument(DBReader.getFeedList(), writer);
+ subscriber.onNext(output);
+ } catch (IOException e) {
+ subscriber.onError(e);
+ } finally {
+ if (writer != null) {
+ try {
+ writer.close();
+ } catch (IOException e) {
+ subscriber.onError(e);
+ }
+ }
+ if (outputStream != null) {
+ try {
+ outputStream.close();
+ } catch (IOException e) {
+ subscriber.onError(e);
+ }
+ }
+ subscriber.onComplete();
+ }
+ });
+ }
+
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/config/PlaybackServiceCallbacksImpl.java b/app/src/main/java/de/danoeh/antennapod/config/PlaybackServiceCallbacksImpl.java
index eb70d8e0b..c3f5d898c 100644
--- a/app/src/main/java/de/danoeh/antennapod/config/PlaybackServiceCallbacksImpl.java
+++ b/app/src/main/java/de/danoeh/antennapod/config/PlaybackServiceCallbacksImpl.java
@@ -35,6 +35,6 @@ public class PlaybackServiceCallbacksImpl implements PlaybackServiceCallbacks {
@Override
public int getNotificationIconResource(Context context) {
- return R.drawable.ic_stat_antenna_default;
+ return R.drawable.ic_antenna;
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/ChooseDataFolderDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/ChooseDataFolderDialog.java
index c185a5557..4cfa7e870 100644
--- a/app/src/main/java/de/danoeh/antennapod/dialog/ChooseDataFolderDialog.java
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/ChooseDataFolderDialog.java
@@ -9,7 +9,7 @@ import de.danoeh.antennapod.adapter.DataFolderAdapter;
public class ChooseDataFolderDialog {
- public static abstract class RunnableWithString implements Runnable {
+ public abstract static class RunnableWithString implements Runnable {
public RunnableWithString() {
super();
}
diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/EpisodesApplyActionFragment.java b/app/src/main/java/de/danoeh/antennapod/dialog/EpisodesApplyActionFragment.java
index bb47f8baa..ed35495fa 100644
--- a/app/src/main/java/de/danoeh/antennapod/dialog/EpisodesApplyActionFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/EpisodesApplyActionFragment.java
@@ -3,7 +3,6 @@ package de.danoeh.antennapod.dialog;
import android.app.AlertDialog;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
-import android.os.Build;
import android.os.Bundle;
import android.support.annotation.IdRes;
import android.support.annotation.NonNull;
@@ -13,7 +12,6 @@ import android.support.design.widget.Snackbar;
import android.support.v4.app.ActivityCompat;
import android.support.v4.app.Fragment;
import android.support.v4.util.ArrayMap;
-import android.support.v4.view.ViewCompat;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
@@ -168,7 +166,8 @@ public class EpisodesApplyActionFragment extends Fragment {
return true;
});
- for(FeedItem episode : episodes) {
+ titles.clear();
+ for (FeedItem episode : episodes) {
titles.add(episode.getTitle());
}
@@ -205,10 +204,6 @@ public class EpisodesApplyActionFragment extends Fragment {
return true;
});
- if (Build.VERSION.SDK_INT == 23 || Build.VERSION.SDK_INT == 24) {
- ViewCompat.setElevation(view.findViewById(R.id.fabSDScrollCtr), 8);
- }
-
showSpeedDialIfAnyChecked();
return view;
@@ -221,7 +216,13 @@ public class EpisodesApplyActionFragment extends Fragment {
}
private void showSpeedDialIfAnyChecked() {
- mSpeedDialView.setVisibility(checkedIds.size() > 0 ? View.VISIBLE : View.GONE);
+ if (checkedIds.size() > 0) {
+ if (!mSpeedDialView.isShown()) {
+ mSpeedDialView.show();
+ }
+ } else {
+ mSpeedDialView.hide(); // hide() also handles UI, e.g., overlay properly.
+ }
}
@Override
@@ -245,10 +246,13 @@ public class EpisodesApplyActionFragment extends Fragment {
// Prepare icon for select toggle button
int[] icon = new int[1];
+ @StringRes int titleResId;
if (checkedIds.size() == episodes.size()) {
icon[0] = R.attr.ic_select_none;
+ titleResId = R.string.deselect_all_label;
} else {
icon[0] = R.attr.ic_select_all;
+ titleResId = R.string.select_all_label;
}
TypedArray a = getActivity().obtainStyledAttributes(icon);
@@ -256,6 +260,7 @@ public class EpisodesApplyActionFragment extends Fragment {
a.recycle();
mSelectToggle.setIcon(iconDrawable);
+ mSelectToggle.setTitle(titleResId);
}
@Override
diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/FilterDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/FilterDialog.java
new file mode 100644
index 000000000..607084c42
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/FilterDialog.java
@@ -0,0 +1,62 @@
+package de.danoeh.antennapod.dialog;
+
+import android.content.Context;
+import android.support.v7.app.AlertDialog;
+import android.text.TextUtils;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.core.feed.FeedItemFilter;
+
+public abstract class FilterDialog {
+
+ protected FeedItemFilter filter;
+ protected Context context;
+
+ public FilterDialog(Context context, FeedItemFilter feedItemFilter) {
+ this.context = context;
+ this.filter = feedItemFilter;
+ }
+
+ public void openDialog() {
+ final String[] items = context.getResources().getStringArray(R.array.episode_filter_options);
+ final String[] values = context.getResources().getStringArray(R.array.episode_filter_values);
+ final boolean[] checkedItems = new boolean[items.length];
+
+ final Set<String> filterValues = new HashSet<>(Arrays.asList(filter.getValues()));
+
+ // make sure we have no empty strings in the filter list
+ for (String filterValue : filterValues) {
+ if (TextUtils.isEmpty(filterValue)) {
+ filterValues.remove(filterValue);
+ }
+ }
+
+ for (int i = 0; i < values.length; i++) {
+ String value = values[i];
+ if (filterValues.contains(value)) {
+ checkedItems[i] = true;
+ }
+ }
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ builder.setTitle(R.string.filter);
+ builder.setMultiChoiceItems(items, checkedItems, (dialog, which, isChecked) -> {
+ if (isChecked) {
+ filterValues.add(values[which]);
+ } else {
+ filterValues.remove(values[which]);
+ }
+ });
+ builder.setPositiveButton(R.string.confirm_label, (dialog, which) -> {
+ updateFilter(filterValues);
+ });
+ builder.setNegativeButton(R.string.cancel_label, null);
+ builder.create().show();
+ }
+
+ protected abstract void updateFilter(Set<String> filterValues);
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/PlaybackControlsDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/PlaybackControlsDialog.java
new file mode 100644
index 000000000..e8c7520b7
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/PlaybackControlsDialog.java
@@ -0,0 +1,214 @@
+package de.danoeh.antennapod.dialog;
+
+import android.app.Dialog;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.v4.app.DialogFragment;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.SeekBar;
+import android.widget.TextView;
+import com.afollestad.materialdialogs.MaterialDialog;
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.core.preferences.UserPreferences;
+import de.danoeh.antennapod.core.util.Converter;
+import de.danoeh.antennapod.core.util.playback.PlaybackController;
+
+import java.util.Locale;
+
+public class PlaybackControlsDialog extends DialogFragment {
+ private static final float PLAYBACK_SPEED_STEP = 0.05f;
+ private static final float DEFAULT_MIN_PLAYBACK_SPEED = 0.5f;
+ private static final float DEFAULT_MAX_PLAYBACK_SPEED = 2.5f;
+ private static final String ARGUMENT_IS_PLAYING_VIDEO = "isPlayingVideo";
+
+ private PlaybackController controller;
+ private MaterialDialog dialog;
+ private boolean isPlayingVideo;
+
+ public static PlaybackControlsDialog newInstance(boolean isPlayingVideo) {
+ Bundle arguments = new Bundle();
+ arguments.putBoolean(ARGUMENT_IS_PLAYING_VIDEO, isPlayingVideo);
+ PlaybackControlsDialog dialog = new PlaybackControlsDialog();
+ dialog.setArguments(arguments);
+ return dialog;
+ }
+
+ public PlaybackControlsDialog() {
+ // Empty constructor required for DialogFragment
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ controller = new PlaybackController(getActivity(), false);
+ controller.init();
+ setupUi();
+ }
+
+ @Override
+ public void onStop() {
+ super.onStop();
+ controller.release();
+ controller = null;
+ }
+
+ @NonNull
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ isPlayingVideo = getArguments() != null && getArguments().getBoolean(ARGUMENT_IS_PLAYING_VIDEO);
+
+ dialog = new MaterialDialog.Builder(getContext())
+ .title(R.string.audio_controls)
+ .customView(R.layout.audio_controls, true)
+ .neutralText(R.string.close_label)
+ .onNeutral((dialog1, which) -> {
+ final SeekBar left = (SeekBar) dialog1.findViewById(R.id.volume_left);
+ final SeekBar right = (SeekBar) dialog1.findViewById(R.id.volume_right);
+ UserPreferences.setVolume(left.getProgress(), right.getProgress());
+ }).build();
+ return dialog;
+ }
+
+ private void setupUi() {
+ final SeekBar barPlaybackSpeed = (SeekBar) dialog.findViewById(R.id.playback_speed);
+ final Button butDecSpeed = (Button) dialog.findViewById(R.id.butDecSpeed);
+ butDecSpeed.setOnClickListener(v -> {
+ if (controller != null && controller.canSetPlaybackSpeed()) {
+ barPlaybackSpeed.setProgress(barPlaybackSpeed.getProgress() - 1);
+ } else {
+ VariableSpeedDialog.showGetPluginDialog(getContext());
+ }
+ });
+ final Button butIncSpeed = (Button) dialog.findViewById(R.id.butIncSpeed);
+ butIncSpeed.setOnClickListener(v -> {
+ if (controller != null && controller.canSetPlaybackSpeed()) {
+ barPlaybackSpeed.setProgress(barPlaybackSpeed.getProgress() + 1);
+ } else {
+ VariableSpeedDialog.showGetPluginDialog(getContext());
+ }
+ });
+
+ final TextView txtvPlaybackSpeed = (TextView) dialog.findViewById(R.id.txtvPlaybackSpeed);
+ float currentSpeed = getCurrentSpeed();
+
+ String[] availableSpeeds = UserPreferences.getPlaybackSpeedArray();
+ final float minPlaybackSpeed = availableSpeeds.length > 1 ?
+ Float.valueOf(availableSpeeds[0]) : DEFAULT_MIN_PLAYBACK_SPEED;
+ float maxPlaybackSpeed = availableSpeeds.length > 1 ?
+ Float.valueOf(availableSpeeds[availableSpeeds.length - 1]) : DEFAULT_MAX_PLAYBACK_SPEED;
+ int progressMax = (int) ((maxPlaybackSpeed - minPlaybackSpeed) / PLAYBACK_SPEED_STEP);
+ barPlaybackSpeed.setMax(progressMax);
+
+ txtvPlaybackSpeed.setText(String.format("%.2fx", currentSpeed));
+ barPlaybackSpeed.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+ if (controller != null && controller.canSetPlaybackSpeed()) {
+ float playbackSpeed = progress * PLAYBACK_SPEED_STEP + minPlaybackSpeed;
+ controller.setPlaybackSpeed(playbackSpeed);
+ String speedPref = String.format(Locale.US, "%.2f", playbackSpeed);
+
+ if (isPlayingVideo) {
+ UserPreferences.setVideoPlaybackSpeed(speedPref);
+ } else {
+ UserPreferences.setPlaybackSpeed(speedPref);
+ }
+
+ String speedStr = String.format("%.2fx", playbackSpeed);
+ txtvPlaybackSpeed.setText(speedStr);
+ } else if (fromUser) {
+ float speed = getCurrentSpeed();
+ barPlaybackSpeed.post(() -> barPlaybackSpeed.setProgress(
+ (int) ((speed - minPlaybackSpeed) / PLAYBACK_SPEED_STEP)));
+ }
+ }
+
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {
+ if (controller != null && !controller.canSetPlaybackSpeed()) {
+ VariableSpeedDialog.showGetPluginDialog(getContext());
+ }
+ }
+
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {
+ }
+ });
+ barPlaybackSpeed.setProgress((int) ((currentSpeed - minPlaybackSpeed) / PLAYBACK_SPEED_STEP));
+
+ final SeekBar barLeftVolume = (SeekBar) dialog.findViewById(R.id.volume_left);
+ barLeftVolume.setProgress(UserPreferences.getLeftVolumePercentage());
+ final SeekBar barRightVolume = (SeekBar) dialog.findViewById(R.id.volume_right);
+ barRightVolume.setProgress(UserPreferences.getRightVolumePercentage());
+ final CheckBox stereoToMono = (CheckBox) dialog.findViewById(R.id.stereo_to_mono);
+ stereoToMono.setChecked(UserPreferences.stereoToMono());
+ if (controller != null && !controller.canDownmix()) {
+ stereoToMono.setEnabled(false);
+ String sonicOnly = getString(R.string.sonic_only);
+ stereoToMono.setText(stereoToMono.getText() + " [" + sonicOnly + "]");
+ }
+
+ if (UserPreferences.useExoplayer()) {
+ barRightVolume.setEnabled(false);
+ }
+
+ final CheckBox skipSilence = (CheckBox) dialog.findViewById(R.id.skipSilence);
+ skipSilence.setChecked(UserPreferences.isSkipSilence());
+ if (!UserPreferences.useExoplayer()) {
+ skipSilence.setEnabled(false);
+ String exoplayerOnly = getString(R.string.exoplayer_only);
+ skipSilence.setText(skipSilence.getText() + " [" + exoplayerOnly + "]");
+ }
+ skipSilence.setOnCheckedChangeListener((buttonView, isChecked) -> {
+ UserPreferences.setSkipSilence(isChecked);
+ controller.setSkipSilence(isChecked);
+ });
+
+ barLeftVolume.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+ controller.setVolume(
+ Converter.getVolumeFromPercentage(progress),
+ Converter.getVolumeFromPercentage(barRightVolume.getProgress()));
+ }
+
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {
+ }
+
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {
+ }
+ });
+ barRightVolume.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+ controller.setVolume(
+ Converter.getVolumeFromPercentage(barLeftVolume.getProgress()),
+ Converter.getVolumeFromPercentage(progress));
+ }
+
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {
+ }
+
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {
+ }
+ });
+ stereoToMono.setOnCheckedChangeListener((buttonView, isChecked) -> {
+ UserPreferences.stereoToMono(isChecked);
+ if (controller != null) {
+ controller.setDownmix(isChecked);
+ }
+ });
+ }
+
+ private float getCurrentSpeed() {
+ if (isPlayingVideo) {
+ return UserPreferences.getVideoPlaybackSpeed();
+ }
+ return UserPreferences.getPlaybackSpeed();
+ }
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/RatingDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/RatingDialog.java
index 24656ed29..5969963f2 100644
--- a/app/src/main/java/de/danoeh/antennapod/dialog/RatingDialog.java
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/RatingDialog.java
@@ -15,6 +15,7 @@ import java.lang.ref.WeakReference;
import java.util.concurrent.TimeUnit;
import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.core.util.IntentUtils;
public class RatingDialog {
@@ -59,14 +60,10 @@ public class RatingDialog {
private static void rateNow() {
Context context = mContext.get();
- if(context == null) {
+ if (context == null) {
return;
}
- final String appPackage = "de.danoeh.antennapod";
- final Uri uri = Uri.parse("https://play.google.com/store/apps/details?id=" + appPackage);
- Intent intent = new Intent(Intent.ACTION_VIEW, uri);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- context.startActivity(intent);
+ IntentUtils.openInBrowser(context, "https://play.google.com/store/apps/details?id=de.danoeh.antennapod");
saveRated();
}
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 cf9a2907b..bf3faf89a 100644
--- a/app/src/main/java/de/danoeh/antennapod/dialog/VariableSpeedDialog.java
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/VariableSpeedDialog.java
@@ -32,6 +32,7 @@ public class VariableSpeedDialog {
public static void showDialog(final Context context) {
if (org.antennapod.audio.MediaPlayer.isPrestoLibraryInstalled(context)
|| UserPreferences.useSonic()
+ || UserPreferences.useExoplayer()
|| Build.VERSION.SDK_INT >= 23) {
showSpeedSelectorDialog(context);
} else {
diff --git a/app/src/main/java/de/danoeh/antennapod/discovery/CombinedSearcher.java b/app/src/main/java/de/danoeh/antennapod/discovery/CombinedSearcher.java
index 18d65a03c..e34d8539c 100644
--- a/app/src/main/java/de/danoeh/antennapod/discovery/CombinedSearcher.java
+++ b/app/src/main/java/de/danoeh/antennapod/discovery/CombinedSearcher.java
@@ -24,7 +24,7 @@ public class CombinedSearcher implements PodcastSearcher {
public CombinedSearcher(Context context) {
addProvider(new FyydPodcastSearcher(), 1.f);
addProvider(new ItunesPodcastSearcher(context), 1.f);
- addProvider(new GpodnetPodcastSearcher(), 0.6f);
+ //addProvider(new GpodnetPodcastSearcher(), 0.6f);
}
private void addProvider(PodcastSearcher provider, float priority) {
diff --git a/app/src/main/java/de/danoeh/antennapod/discovery/ItunesTopListLoader.java b/app/src/main/java/de/danoeh/antennapod/discovery/ItunesTopListLoader.java
index bc9133258..79ccd9532 100644
--- a/app/src/main/java/de/danoeh/antennapod/discovery/ItunesTopListLoader.java
+++ b/app/src/main/java/de/danoeh/antennapod/discovery/ItunesTopListLoader.java
@@ -33,16 +33,12 @@ public class ItunesTopListLoader {
OkHttpClient client = AntennapodHttpClient.getHttpClient();
String feedString;
try {
- try {
- feedString = getTopListFeed(client, lang, limit);
- } catch (IOException e) {
- feedString = getTopListFeed(client, "us", limit);
- }
- List<PodcastSearchResult> podcasts = parseFeed(feedString);
- emitter.onSuccess(podcasts);
- } catch (IOException | JSONException e) {
- emitter.onError(e);
+ feedString = getTopListFeed(client, lang, limit);
+ } catch (IOException e) {
+ feedString = getTopListFeed(client, "us", limit);
}
+ List<PodcastSearchResult> podcasts = parseFeed(feedString);
+ emitter.onSuccess(podcasts);
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/AddFeedFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/AddFeedFragment.java
index 35bcaa76e..3ef010f88 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/AddFeedFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/AddFeedFragment.java
@@ -70,11 +70,8 @@ public class AddFeedFragment extends Fragment {
combinedFeedSearchBox = root.findViewById(R.id.combinedFeedSearchBox);
combinedFeedSearchBox.setOnEditorActionListener((v, actionId, event) -> {
- if (actionId == EditorInfo.IME_ACTION_SEARCH) {
- performSearch();
- return true;
- }
- return false;
+ performSearch();
+ return true;
});
}
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 62d798cf6..bb52b26b7 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java
@@ -1,18 +1,9 @@
package de.danoeh.antennapod.fragment;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.SharedPreferences;
import android.os.Bundle;
-import android.os.Handler;
import android.support.annotation.NonNull;
-import android.support.design.widget.Snackbar;
-import android.support.v4.app.Fragment;
-import android.support.v4.view.MenuItemCompat;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
-import android.support.v7.widget.SearchView;
-import android.support.v7.widget.SimpleItemAnimator;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
@@ -20,231 +11,50 @@ import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
-import android.widget.ProgressBar;
-import android.widget.Toast;
-
-import com.yqritc.recyclerviewflexibledivider.HorizontalDividerItemDecoration;
-
-import org.greenrobot.eventbus.EventBus;
-import org.greenrobot.eventbus.Subscribe;
-import org.greenrobot.eventbus.ThreadMode;
-
-import java.util.ArrayList;
-import java.util.List;
-
+import com.joanzapata.iconify.Iconify;
import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.activity.MainActivity;
-import de.danoeh.antennapod.adapter.AllEpisodesRecycleAdapter;
-import de.danoeh.antennapod.core.dialog.ConfirmationDialog;
-import de.danoeh.antennapod.core.event.DownloadEvent;
-import de.danoeh.antennapod.core.event.DownloaderUpdate;
-import de.danoeh.antennapod.core.event.FeedItemEvent;
-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.preferences.UserPreferences;
-import de.danoeh.antennapod.core.service.download.DownloadRequest;
-import de.danoeh.antennapod.core.service.download.DownloadService;
-import de.danoeh.antennapod.core.service.download.Downloader;
+import de.danoeh.antennapod.core.feed.FeedItemFilter;
import de.danoeh.antennapod.core.storage.DBReader;
-import de.danoeh.antennapod.core.storage.DBTasks;
-import de.danoeh.antennapod.core.storage.DBWriter;
-import de.danoeh.antennapod.core.storage.DownloadRequester;
-import de.danoeh.antennapod.core.util.FeedItemUtil;
-import de.danoeh.antennapod.core.util.LongList;
-import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler;
-import de.danoeh.antennapod.menuhandler.MenuItemUtils;
-import de.danoeh.antennapod.view.EmptyViewHandler;
+import de.danoeh.antennapod.dialog.FilterDialog;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
-import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
+import java.util.List;
+import java.util.Set;
+
/**
- * Shows unread or recently published episodes
+ * Like 'EpisodesFragment' except that it only shows new episodes and
+ * supports swiping to mark as read.
*/
-public class AllEpisodesFragment extends Fragment {
+public class AllEpisodesFragment extends EpisodesListFragment {
public static final String TAG = "AllEpisodesFragment";
+ private static final String PREF_NAME = "PrefAllEpisodesFragment";
- private static final int EVENTS = EventDistributor.FEED_LIST_UPDATE |
- EventDistributor.UNREAD_ITEMS_UPDATE |
- EventDistributor.PLAYER_STATUS_UPDATE;
-
- private static final int RECENT_EPISODES_LIMIT = 150;
- private static final String DEFAULT_PREF_NAME = "PrefAllEpisodesFragment";
- private static final String PREF_SCROLL_POSITION = "scroll_position";
- private static final String PREF_SCROLL_OFFSET = "scroll_offset";
-
- RecyclerView recyclerView;
- AllEpisodesRecycleAdapter listAdapter;
- private ProgressBar progLoading;
- EmptyViewHandler emptyView;
-
- @NonNull
- List<FeedItem> episodes = new ArrayList<>();
- @NonNull
- private List<Downloader> downloaderList = new ArrayList<>();
-
- private boolean isUpdatingFeeds;
- boolean isMenuInvalidationAllowed = false;
-
- Disposable disposable;
- private LinearLayoutManager layoutManager;
-
- boolean showOnlyNewEpisodes() {
- return false;
- }
-
- String getPrefName() {
- return DEFAULT_PREF_NAME;
- }
-
- @Override
- public void onStart() {
- super.onStart();
- setHasOptionsMenu(true);
- EventDistributor.getInstance().register(contentUpdate);
- EventBus.getDefault().register(this);
- loadItems();
- }
-
- @Override
- public void onResume() {
- super.onResume();
- registerForContextMenu(recyclerView);
- }
-
- @Override
- public void onPause() {
- super.onPause();
- saveScrollPosition();
- unregisterForContextMenu(recyclerView);
- }
-
- @Override
- public void onStop() {
- super.onStop();
- EventBus.getDefault().unregister(this);
- EventDistributor.getInstance().unregister(contentUpdate);
- if (disposable != null) {
- disposable.dispose();
- }
- }
-
- private void saveScrollPosition() {
- int firstItem = layoutManager.findFirstVisibleItemPosition();
- View firstItemView = layoutManager.findViewByPosition(firstItem);
- float topOffset;
- if (firstItemView == null) {
- topOffset = 0;
- } else {
- topOffset = firstItemView.getTop();
- }
+ private static final int EPISODES_PER_PAGE = 150;
+ private static final int VISIBLE_EPISODES_SCROLL_THRESHOLD = 5;
+ private static int page = 1;
- SharedPreferences prefs = getActivity().getSharedPreferences(getPrefName(), Context.MODE_PRIVATE);
- SharedPreferences.Editor editor = prefs.edit();
- editor.putInt(PREF_SCROLL_POSITION, firstItem);
- editor.putFloat(PREF_SCROLL_OFFSET, topOffset);
- editor.commit();
- }
-
- private void restoreScrollPosition() {
- SharedPreferences prefs = getActivity().getSharedPreferences(getPrefName(), Context.MODE_PRIVATE);
- int position = prefs.getInt(PREF_SCROLL_POSITION, 0);
- float offset = prefs.getFloat(PREF_SCROLL_OFFSET, 0.0f);
- if (position > 0 || offset > 0) {
- layoutManager.scrollToPositionWithOffset(position, (int) offset);
- // restore once, then forget
- SharedPreferences.Editor editor = prefs.edit();
- editor.putInt(PREF_SCROLL_POSITION, 0);
- editor.putFloat(PREF_SCROLL_OFFSET, 0.0f);
- editor.commit();
- }
- }
-
- private final MenuItemUtils.UpdateRefreshMenuItemChecker updateRefreshMenuItemChecker =
- () -> DownloadService.isRunning && DownloadRequester.getInstance().isDownloadingFeeds();
+ private static FeedItemFilter feedItemFilter = new FeedItemFilter("");
@Override
- public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
- if (!isAdded()) {
- return;
- }
- super.onCreateOptionsMenu(menu, inflater);
- inflater.inflate(R.menu.episodes, menu);
-
- MenuItem searchItem = menu.findItem(R.id.action_search);
- final SearchView sv = (SearchView) MenuItemCompat.getActionView(searchItem);
- MenuItemUtils.adjustTextColor(getActivity(), sv);
- sv.setQueryHint(getString(R.string.search_hint));
- sv.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
- @Override
- public boolean onQueryTextSubmit(String s) {
- sv.clearFocus();
- ((MainActivity) requireActivity()).loadChildFragment(SearchFragment.newInstance(s));
- return true;
- }
-
- @Override
- public boolean onQueryTextChange(String s) {
- return false;
- }
- });
- isUpdatingFeeds = MenuItemUtils.updateRefreshMenuItem(menu, R.id.refresh_item, updateRefreshMenuItemChecker);
+ protected boolean showOnlyNewEpisodes() {
+ return false;
}
@Override
- public void onPrepareOptionsMenu(Menu menu) {
- super.onPrepareOptionsMenu(menu);
- MenuItem markAllRead = menu.findItem(R.id.mark_all_read_item);
- if (markAllRead != null) {
- markAllRead.setVisible(!showOnlyNewEpisodes() && !episodes.isEmpty());
- }
- MenuItem removeAllNewFlags = menu.findItem(R.id.remove_all_new_flags_item);
- if (removeAllNewFlags != null) {
- removeAllNewFlags.setVisible(showOnlyNewEpisodes() && !episodes.isEmpty());
- }
+ protected String getPrefName() {
+ return PREF_NAME;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (!super.onOptionsItemSelected(item)) {
switch (item.getItemId()) {
- case R.id.refresh_item:
- List<Feed> feeds = ((MainActivity) getActivity()).getFeeds();
- if (feeds != null) {
- DBTasks.refreshAllFeeds(getActivity(), feeds);
- }
- return true;
- case R.id.mark_all_read_item:
- ConfirmationDialog markAllReadConfirmationDialog = new ConfirmationDialog(getActivity(),
- R.string.mark_all_read_label,
- R.string.mark_all_read_confirmation_msg) {
-
- @Override
- public void onConfirmButtonPressed(DialogInterface dialog) {
- dialog.dismiss();
- DBWriter.markAllItemsRead();
- Toast.makeText(getActivity(), R.string.mark_all_read_msg, Toast.LENGTH_SHORT).show();
- }
- };
- markAllReadConfirmationDialog.createNewDialog().show();
- return true;
- case R.id.remove_all_new_flags_item:
- ConfirmationDialog removeAllNewFlagsConfirmationDialog = new ConfirmationDialog(getActivity(),
- R.string.remove_all_new_flags_label,
- R.string.remove_all_new_flags_confirmation_msg) {
-
- @Override
- public void onConfirmButtonPressed(DialogInterface dialog) {
- dialog.dismiss();
- DBWriter.removeAllNewFlags();
- Toast.makeText(getActivity(), R.string.removed_all_new_flags_msg, Toast.LENGTH_SHORT).show();
- }
- };
- removeAllNewFlagsConfirmationDialog.createNewDialog().show();
+ case R.id.filter_items:
+ showFilterDialog();
return true;
default:
return false;
@@ -252,250 +62,105 @@ public class AllEpisodesFragment extends Fragment {
} else {
return true;
}
-
- }
-
- @Override
- public boolean onContextItemSelected(MenuItem item) {
- Log.d(TAG, "onContextItemSelected() called with: " + "item = [" + item + "]");
- if (!getUserVisibleHint()) {
- return false;
- }
- if (!isVisible()) {
- return false;
- }
- if (item.getItemId() == R.id.share_item) {
- return true; // avoids that the position is reset when we need it in the submenu
- }
-
- if (listAdapter.getSelectedItem() == null) {
- Log.i(TAG, "Selected item or listAdapter was null, ignoring selection");
- return super.onContextItemSelected(item);
- }
- FeedItem selectedItem = listAdapter.getSelectedItem();
-
- // Remove new flag contains UI logic specific to All/New/FavoriteSegments,
- // e.g., Undo with Snackbar,
- // and is handled by this class rather than the generic FeedItemMenuHandler
- // Undo is useful for Remove new flag, given there is no UI to undo it otherwise,
- // i.e., there is context menu item for Mark as new
- if (R.id.remove_new_flag_item == item.getItemId()) {
- removeNewFlagWithUndo(selectedItem);
- return true;
- }
-
- return FeedItemMenuHandler.onMenuItemClicked(getActivity(), item.getItemId(), selectedItem);
}
@NonNull
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
- super.onCreateView(inflater, container, savedInstanceState);
- View root = inflater.inflate(R.layout.all_episodes_fragment, container, false);
-
- layoutManager = new LinearLayoutManager(getActivity());
- recyclerView = root.findViewById(android.R.id.list);
- recyclerView.setLayoutManager(layoutManager);
- recyclerView.setHasFixedSize(true);
- recyclerView.addItemDecoration(new HorizontalDividerItemDecoration.Builder(getActivity()).build());
- recyclerView.setVisibility(View.GONE);
-
- RecyclerView.ItemAnimator animator = recyclerView.getItemAnimator();
- if (animator instanceof SimpleItemAnimator) {
- ((SimpleItemAnimator) animator).setSupportsChangeAnimations(false);
- }
-
- progLoading = root.findViewById(R.id.progLoading);
- progLoading.setVisibility(View.VISIBLE);
-
- emptyView = new EmptyViewHandler(getContext());
- emptyView.attachToRecyclerView(recyclerView);
- emptyView.setIcon(R.attr.feed);
- emptyView.setTitle(R.string.no_all_episodes_head_label);
- emptyView.setMessage(R.string.no_all_episodes_label);
-
- createRecycleAdapter(recyclerView, emptyView);
- emptyView.hide();
+ View root = super.onCreateView(inflater, container, savedInstanceState);
- return root;
- }
+ recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
- private void onFragmentLoaded(List<FeedItem> episodes) {
- this.episodes = episodes;
- listAdapter.notifyDataSetChanged();
+ /* Total number of episodes after last load */
+ private int previousTotalEpisodes = 0;
- if (episodes.size() == 0) {
- createRecycleAdapter(recyclerView, emptyView);
- }
-
- restoreScrollPosition();
- requireActivity().invalidateOptionsMenu();
- }
+ /* True if loading more episodes is still in progress */
+ private boolean isLoadingMore = true;
- /**
- * Currently, we need to recreate the list adapter in order to be able to undo last item via the
- * snackbar. See #3084 for details.
- */
- private void createRecycleAdapter(RecyclerView recyclerView, EmptyViewHandler emptyViewHandler) {
- MainActivity mainActivity = (MainActivity) getActivity();
- listAdapter = new AllEpisodesRecycleAdapter(mainActivity, itemAccess, showOnlyNewEpisodes());
- listAdapter.setHasStableIds(true);
- recyclerView.setAdapter(listAdapter);
- emptyViewHandler.updateAdapter(listAdapter);
- }
-
- private final AllEpisodesRecycleAdapter.ItemAccess itemAccess = new AllEpisodesRecycleAdapter.ItemAccess() {
-
- @Override
- public int getCount() {
- return episodes.size();
- }
-
- @Override
- public FeedItem getItem(int position) {
- if (0 <= position && position < episodes.size()) {
- return episodes.get(position);
- }
- return null;
- }
-
- @Override
- public LongList getItemsIds() {
- LongList ids = new LongList(episodes.size());
- for (FeedItem episode : episodes) {
- ids.add(episode.getId());
- }
- return ids;
- }
-
- @Override
- public int getItemDownloadProgressPercent(FeedItem item) {
- for (Downloader downloader : downloaderList) {
- DownloadRequest downloadRequest = downloader.getDownloadRequest();
- if (downloadRequest.getFeedfileType() == FeedMedia.FEEDFILETYPE_FEEDMEDIA
- && downloadRequest.getFeedfileId() == item.getMedia().getId()) {
- return downloadRequest.getProgressPercent();
+ @Override
+ public void onScrolled(RecyclerView recyclerView, int deltaX, int deltaY) {
+ super.onScrolled(recyclerView, deltaX, deltaY);
+
+ int visibleEpisodeCount = recyclerView.getChildCount();
+ int totalEpisodeCount = recyclerView.getLayoutManager().getItemCount();
+ int firstVisibleEpisode = ((LinearLayoutManager) recyclerView.getLayoutManager()).findFirstVisibleItemPosition();
+
+ /* Determine if loading more episodes has finished */
+ if (isLoadingMore) {
+ if (totalEpisodeCount > previousTotalEpisodes) {
+ isLoadingMore = false;
+ previousTotalEpisodes = totalEpisodeCount;
+ }
}
- }
- return 0;
- }
- @Override
- public boolean isInQueue(FeedItem item) {
- return item != null && item.isTagged(FeedItem.TAG_QUEUE);
- }
+ /* Determine if the user scrolled to the bottom and loading more episodes is not already in progress */
+ if (!isLoadingMore && (totalEpisodeCount - visibleEpisodeCount)
+ <= (firstVisibleEpisode + VISIBLE_EPISODES_SCROLL_THRESHOLD)) {
- @Override
- public LongList getQueueIds() {
- LongList queueIds = new LongList();
- for (FeedItem item : episodes) {
- if (item.isTagged(FeedItem.TAG_QUEUE)) {
- queueIds.add(item.getId());
+ /* The end of the list has been reached. Load more data. */
+ page++;
+ loadMoreItems();
+ isLoadingMore = true;
}
}
- return queueIds;
- }
-
- };
+ });
- @Subscribe(threadMode = ThreadMode.MAIN)
- public void onEventMainThread(FeedItemEvent event) {
- Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
- for (FeedItem item : event.items) {
- int pos = FeedItemUtil.indexOfItemWithId(episodes, item.getId());
- if (pos >= 0) {
- episodes.remove(pos);
- if (shouldUpdatedItemRemainInList(item)) {
- episodes.add(pos, item);
- listAdapter.notifyItemChanged(pos);
- } else {
- listAdapter.notifyItemRemoved(pos);
- }
- }
- }
+ return root;
}
- protected boolean shouldUpdatedItemRemainInList(FeedItem item) {
- return true;
+ @Override
+ public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+ super.onCreateOptionsMenu(menu, inflater);
+ menu.findItem(R.id.filter_items).setVisible(true);
+ menu.findItem(R.id.mark_all_read_item).setVisible(!episodes.isEmpty());
}
- @Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
- public void onEventMainThread(DownloadEvent event) {
- Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
- DownloaderUpdate update = event.update;
- downloaderList = update.downloaders;
- if (isMenuInvalidationAllowed && isUpdatingFeeds != update.feedIds.length > 0) {
- requireActivity().invalidateOptionsMenu();
- }
- if (update.mediaIds.length > 0) {
- for (long mediaId : update.mediaIds) {
- int pos = FeedItemUtil.indexOfItemWithMediaId(episodes, mediaId);
- if (pos >= 0) {
- listAdapter.notifyItemChanged(pos);
- }
- }
- }
- }
+ @Override
+ protected void onFragmentLoaded(List<FeedItem> episodes) {
+ super.onFragmentLoaded(episodes);
- private final EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
- @Override
- public void update(EventDistributor eventDistributor, Integer arg) {
- if ((arg & EVENTS) != 0) {
- loadItems();
- if (isUpdatingFeeds != updateRefreshMenuItemChecker.isRefreshing()) {
- requireActivity().invalidateOptionsMenu();
- }
- }
+ if (feedItemFilter.getValues().length > 0) {
+ txtvInformation.setText("{fa-info-circle} " + this.getString(R.string.filtered_label));
+ Iconify.addIcons(txtvInformation);
+ txtvInformation.setVisibility(View.VISIBLE);
+ } else {
+ txtvInformation.setVisibility(View.GONE);
}
- };
+ }
- void loadItems() {
+ private void loadMoreItems() {
if (disposable != null) {
disposable.dispose();
}
- disposable = Observable.fromCallable(this::loadData)
+ disposable = Observable.fromCallable(this::loadMoreData)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(data -> {
progLoading.setVisibility(View.GONE);
- onFragmentLoaded(data);
+ episodes.addAll(data);
+ onFragmentLoaded(episodes);
}, error -> Log.e(TAG, Log.getStackTraceString(error)));
}
- @NonNull
- List<FeedItem> loadData() {
- return DBReader.getRecentlyPublishedEpisodes(RECENT_EPISODES_LIMIT);
- }
-
- void removeNewFlagWithUndo(FeedItem item) {
- if (item == null) {
- return;
- }
-
- Log.d(TAG, "removeNewFlagWithUndo(" + item.getId() + ")");
- if (disposable != null) {
- disposable.dispose();
- }
- // we're marking it as unplayed since the user didn't actually play it
- // but they don't want it considered 'NEW' anymore
- DBWriter.markItemPlayed(FeedItem.UNPLAYED, item.getId());
-
- final Handler h = new Handler(getActivity().getMainLooper());
- final Runnable r = () -> {
- FeedMedia media = item.getMedia();
- if (media != null && media.hasAlmostEnded() && UserPreferences.isAutoDelete()) {
- DBWriter.deleteFeedMediaOfItem(getActivity(), media.getId());
+ private void showFilterDialog() {
+ FilterDialog filterDialog = new FilterDialog(getContext(), feedItemFilter) {
+ @Override
+ protected void updateFilter(Set<String> filterValues) {
+ feedItemFilter = new FeedItemFilter(filterValues.toArray(new String[filterValues.size()]));
+ loadItems();
}
};
- Snackbar snackbar = Snackbar.make(getView(), getString(R.string.removed_new_flag_label),
- Snackbar.LENGTH_LONG);
- snackbar.setAction(getString(R.string.undo), v -> {
- DBWriter.markItemPlayed(FeedItem.NEW, item.getId());
- // don't forget to cancel the thing that's going to remove the media
- h.removeCallbacks(r);
- });
- snackbar.show();
- h.postDelayed(r, (int) Math.ceil(snackbar.getDuration() * 1.05f));
+ filterDialog.openDialog();
+ }
+
+ @NonNull
+ @Override
+ protected List<FeedItem> loadData() {
+ return feedItemFilter.filter( DBReader.getRecentlyPublishedEpisodes(0, page * EPISODES_PER_PAGE));
+ }
+
+ List<FeedItem> loadMoreData() {
+ return feedItemFilter.filter( DBReader.getRecentlyPublishedEpisodes((page - 1) * EPISODES_PER_PAGE, EPISODES_PER_PAGE));
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ChaptersFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ChaptersFragment.java
index 4bebfe4c9..bb8f4df9a 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/ChaptersFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/ChaptersFragment.java
@@ -87,11 +87,10 @@ public class ChaptersFragment extends ListFragment {
controller = null;
}
- private void scrollTo(int position) {
- getListView().setSelection(position);
- }
-
private int getCurrentChapter(Playable media) {
+ if (media == null || media.getChapters() == null || media.getChapters().size() == 0 || controller == null) {
+ return -1;
+ }
int currentPosition = controller.getPosition();
List<Chapter> chapters = media.getChapters();
@@ -126,8 +125,10 @@ public class ChaptersFragment extends ListFragment {
if (adapter != null) {
adapter.setMedia(media);
adapter.notifyDataSetChanged();
- if (media != null && media.getChapters() != null && media.getChapters().size() != 0) {
- scrollTo(getCurrentChapter(media));
+
+ int positionOfCurrentChapter = getCurrentChapter(media);
+ if (positionOfCurrentChapter != -1) {
+ getListView().setSelection(positionOfCurrentChapter);
}
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesFragment.java
index 0610bfd24..ca21df661 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesFragment.java
@@ -79,7 +79,7 @@ public class EpisodesFragment extends Fragment {
public static class EpisodesPagerAdapter extends FragmentPagerAdapter {
private final Resources resources;
- private final AllEpisodesFragment[] fragments = {
+ private final EpisodesListFragment[] fragments = {
new NewEpisodesFragment(),
new AllEpisodesFragment(),
new FavoriteEpisodesFragment()
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java
new file mode 100644
index 000000000..b6e3d14dd
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java
@@ -0,0 +1,445 @@
+package de.danoeh.antennapod.fragment;
+
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.v4.app.Fragment;
+import android.support.v4.view.MenuItemCompat;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.support.v7.widget.SearchView;
+import android.support.v7.widget.SimpleItemAnimator;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.yqritc.recyclerviewflexibledivider.HorizontalDividerItemDecoration;
+
+import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
+import org.greenrobot.eventbus.EventBus;
+import org.greenrobot.eventbus.Subscribe;
+import org.greenrobot.eventbus.ThreadMode;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.activity.MainActivity;
+import de.danoeh.antennapod.adapter.AllEpisodesRecycleAdapter;
+import de.danoeh.antennapod.core.dialog.ConfirmationDialog;
+import de.danoeh.antennapod.core.event.DownloadEvent;
+import de.danoeh.antennapod.core.event.DownloaderUpdate;
+import de.danoeh.antennapod.core.event.FeedItemEvent;
+import de.danoeh.antennapod.core.feed.EventDistributor;
+import de.danoeh.antennapod.core.feed.FeedItem;
+import de.danoeh.antennapod.core.feed.FeedMedia;
+import de.danoeh.antennapod.core.service.download.DownloadRequest;
+import de.danoeh.antennapod.core.service.download.DownloadService;
+import de.danoeh.antennapod.core.service.download.Downloader;
+import de.danoeh.antennapod.core.storage.DBWriter;
+import de.danoeh.antennapod.core.storage.DownloadRequester;
+import de.danoeh.antennapod.core.util.FeedItemUtil;
+import de.danoeh.antennapod.core.util.LongList;
+import de.danoeh.antennapod.core.util.download.AutoUpdateManager;
+import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler;
+import de.danoeh.antennapod.menuhandler.MenuItemUtils;
+import de.danoeh.antennapod.view.EmptyViewHandler;
+import io.reactivex.Observable;
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.disposables.Disposable;
+import io.reactivex.schedulers.Schedulers;
+
+/**
+ * Shows unread or recently published episodes
+ */
+public abstract class EpisodesListFragment extends Fragment {
+
+ public static final String TAG = "EpisodesListFragment";
+
+ private static final int EVENTS = EventDistributor.FEED_LIST_UPDATE |
+ EventDistributor.UNREAD_ITEMS_UPDATE |
+ EventDistributor.PLAYER_STATUS_UPDATE;
+
+ private static final String DEFAULT_PREF_NAME = "PrefAllEpisodesFragment";
+ private static final String PREF_SCROLL_POSITION = "scroll_position";
+ private static final String PREF_SCROLL_OFFSET = "scroll_offset";
+
+ RecyclerView recyclerView;
+ AllEpisodesRecycleAdapter listAdapter;
+ ProgressBar progLoading;
+ EmptyViewHandler emptyView;
+
+ @NonNull
+ List<FeedItem> episodes = new ArrayList<>();
+ @NonNull
+ private List<Downloader> downloaderList = new ArrayList<>();
+
+ private boolean isUpdatingFeeds;
+ boolean isMenuInvalidationAllowed = false;
+
+ protected Disposable disposable;
+ private LinearLayoutManager layoutManager;
+ protected TextView txtvInformation;
+
+ boolean showOnlyNewEpisodes() {
+ return false;
+ }
+
+ String getPrefName() {
+ return DEFAULT_PREF_NAME;
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ setHasOptionsMenu(true);
+ EventDistributor.getInstance().register(contentUpdate);
+ EventBus.getDefault().register(this);
+ loadItems();
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ registerForContextMenu(recyclerView);
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ saveScrollPosition();
+ unregisterForContextMenu(recyclerView);
+ }
+
+ @Override
+ public void onStop() {
+ super.onStop();
+ EventBus.getDefault().unregister(this);
+ EventDistributor.getInstance().unregister(contentUpdate);
+ if (disposable != null) {
+ disposable.dispose();
+ }
+ }
+
+ private void saveScrollPosition() {
+ int firstItem = layoutManager.findFirstVisibleItemPosition();
+ View firstItemView = layoutManager.findViewByPosition(firstItem);
+ float topOffset;
+ if (firstItemView == null) {
+ topOffset = 0;
+ } else {
+ topOffset = firstItemView.getTop();
+ }
+
+ SharedPreferences prefs = getActivity().getSharedPreferences(getPrefName(), Context.MODE_PRIVATE);
+ SharedPreferences.Editor editor = prefs.edit();
+ editor.putInt(PREF_SCROLL_POSITION, firstItem);
+ editor.putFloat(PREF_SCROLL_OFFSET, topOffset);
+ editor.apply();
+ }
+
+ private void restoreScrollPosition() {
+ SharedPreferences prefs = getActivity().getSharedPreferences(getPrefName(), Context.MODE_PRIVATE);
+ int position = prefs.getInt(PREF_SCROLL_POSITION, 0);
+ float offset = prefs.getFloat(PREF_SCROLL_OFFSET, 0.0f);
+ if (position > 0 || offset > 0) {
+ layoutManager.scrollToPositionWithOffset(position, (int) offset);
+ // restore once, then forget
+ SharedPreferences.Editor editor = prefs.edit();
+ editor.putInt(PREF_SCROLL_POSITION, 0);
+ editor.putFloat(PREF_SCROLL_OFFSET, 0.0f);
+ editor.apply();
+ }
+ }
+
+ private final MenuItemUtils.UpdateRefreshMenuItemChecker updateRefreshMenuItemChecker =
+ () -> DownloadService.isRunning && DownloadRequester.getInstance().isDownloadingFeeds();
+
+ @Override
+ public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+ if (!isAdded()) {
+ return;
+ }
+ super.onCreateOptionsMenu(menu, inflater);
+ inflater.inflate(R.menu.episodes, menu);
+
+ MenuItem searchItem = menu.findItem(R.id.action_search);
+ final SearchView sv = (SearchView) MenuItemCompat.getActionView(searchItem);
+ MenuItemUtils.adjustTextColor(getActivity(), sv);
+ sv.setQueryHint(getString(R.string.search_hint));
+ sv.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
+ @Override
+ public boolean onQueryTextSubmit(String s) {
+ sv.clearFocus();
+ ((MainActivity) requireActivity()).loadChildFragment(SearchFragment.newInstance(s));
+ return true;
+ }
+
+ @Override
+ public boolean onQueryTextChange(String s) {
+ return false;
+ }
+ });
+ isUpdatingFeeds = MenuItemUtils.updateRefreshMenuItem(menu, R.id.refresh_item, updateRefreshMenuItemChecker);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if (!super.onOptionsItemSelected(item)) {
+ switch (item.getItemId()) {
+ case R.id.refresh_item:
+ AutoUpdateManager.runImmediate(requireContext());
+ return true;
+ case R.id.mark_all_read_item:
+ ConfirmationDialog markAllReadConfirmationDialog = new ConfirmationDialog(getActivity(),
+ R.string.mark_all_read_label,
+ R.string.mark_all_read_confirmation_msg) {
+
+ @Override
+ public void onConfirmButtonPressed(DialogInterface dialog) {
+ dialog.dismiss();
+ DBWriter.markAllItemsRead();
+ Toast.makeText(getActivity(), R.string.mark_all_read_msg, Toast.LENGTH_SHORT).show();
+ }
+ };
+ markAllReadConfirmationDialog.createNewDialog().show();
+ return true;
+ case R.id.remove_all_new_flags_item:
+ ConfirmationDialog removeAllNewFlagsConfirmationDialog = new ConfirmationDialog(getActivity(),
+ R.string.remove_all_new_flags_label,
+ R.string.remove_all_new_flags_confirmation_msg) {
+
+ @Override
+ public void onConfirmButtonPressed(DialogInterface dialog) {
+ dialog.dismiss();
+ DBWriter.removeAllNewFlags();
+ Toast.makeText(getActivity(), R.string.removed_all_new_flags_msg, Toast.LENGTH_SHORT).show();
+ }
+ };
+ removeAllNewFlagsConfirmationDialog.createNewDialog().show();
+ return true;
+ default:
+ return false;
+ }
+ } else {
+ return true;
+ }
+ }
+
+ @Override
+ public boolean onContextItemSelected(MenuItem item) {
+ Log.d(TAG, "onContextItemSelected() called with: " + "item = [" + item + "]");
+ if (!getUserVisibleHint()) {
+ return false;
+ }
+ if (!isVisible()) {
+ return false;
+ }
+ if (item.getItemId() == R.id.share_item) {
+ return true; // avoids that the position is reset when we need it in the submenu
+ }
+
+ if (listAdapter.getSelectedItem() == null) {
+ Log.i(TAG, "Selected item or listAdapter was null, ignoring selection");
+ return super.onContextItemSelected(item);
+ }
+ FeedItem selectedItem = listAdapter.getSelectedItem();
+
+ return FeedItemMenuHandler.onMenuItemClicked(this, item.getItemId(), selectedItem);
+ }
+
+ @NonNull
+ @Override
+ public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ super.onCreateView(inflater, container, savedInstanceState);
+ View root = inflater.inflate(R.layout.all_episodes_fragment, container, false);
+ txtvInformation = root.findViewById(R.id.txtvInformation);
+
+ layoutManager = new LinearLayoutManager(getActivity());
+ recyclerView = root.findViewById(android.R.id.list);
+ recyclerView.setLayoutManager(layoutManager);
+ recyclerView.setHasFixedSize(true);
+ recyclerView.addItemDecoration(new HorizontalDividerItemDecoration.Builder(getActivity()).build());
+ recyclerView.setVisibility(View.GONE);
+
+ RecyclerView.ItemAnimator animator = recyclerView.getItemAnimator();
+ if (animator instanceof SimpleItemAnimator) {
+ ((SimpleItemAnimator) animator).setSupportsChangeAnimations(false);
+ }
+
+ progLoading = root.findViewById(R.id.progLoading);
+ progLoading.setVisibility(View.VISIBLE);
+
+ emptyView = new EmptyViewHandler(getContext());
+ emptyView.attachToRecyclerView(recyclerView);
+ emptyView.setIcon(R.attr.feed);
+ emptyView.setTitle(R.string.no_all_episodes_head_label);
+ emptyView.setMessage(R.string.no_all_episodes_label);
+
+ createRecycleAdapter(recyclerView, emptyView);
+ emptyView.hide();
+
+ return root;
+ }
+
+ protected void onFragmentLoaded(List<FeedItem> episodes) {
+ listAdapter.notifyDataSetChanged();
+
+ if (episodes.size() == 0) {
+ createRecycleAdapter(recyclerView, emptyView);
+ }
+
+ restoreScrollPosition();
+ requireActivity().invalidateOptionsMenu();
+ }
+
+ /**
+ * Currently, we need to recreate the list adapter in order to be able to undo last item via the
+ * snackbar. See #3084 for details.
+ */
+ private void createRecycleAdapter(RecyclerView recyclerView, EmptyViewHandler emptyViewHandler) {
+ MainActivity mainActivity = (MainActivity) getActivity();
+ listAdapter = new AllEpisodesRecycleAdapter(mainActivity, itemAccess, showOnlyNewEpisodes());
+ listAdapter.setHasStableIds(true);
+ recyclerView.setAdapter(listAdapter);
+ emptyViewHandler.updateAdapter(listAdapter);
+ }
+
+ private final AllEpisodesRecycleAdapter.ItemAccess itemAccess = new AllEpisodesRecycleAdapter.ItemAccess() {
+
+ @Override
+ public int getCount() {
+ return episodes.size();
+ }
+
+ @Override
+ public FeedItem getItem(int position) {
+ if (0 <= position && position < episodes.size()) {
+ return episodes.get(position);
+ }
+ return null;
+ }
+
+ @Override
+ public LongList getItemsIds() {
+ LongList ids = new LongList(episodes.size());
+ for (FeedItem episode : episodes) {
+ ids.add(episode.getId());
+ }
+ return ids;
+ }
+
+ @Override
+ public int getItemDownloadProgressPercent(FeedItem item) {
+ for (Downloader downloader : downloaderList) {
+ DownloadRequest downloadRequest = downloader.getDownloadRequest();
+ if (downloadRequest.getFeedfileType() == FeedMedia.FEEDFILETYPE_FEEDMEDIA
+ && downloadRequest.getFeedfileId() == item.getMedia().getId()) {
+ return downloadRequest.getProgressPercent();
+ }
+ }
+ return 0;
+ }
+
+ @Override
+ public boolean isInQueue(FeedItem item) {
+ return item != null && item.isTagged(FeedItem.TAG_QUEUE);
+ }
+
+ @Override
+ public LongList getQueueIds() {
+ LongList queueIds = new LongList();
+ for (FeedItem item : episodes) {
+ if (item.isTagged(FeedItem.TAG_QUEUE)) {
+ queueIds.add(item.getId());
+ }
+ }
+ return queueIds;
+ }
+
+ };
+
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ public void onEventMainThread(FeedItemEvent event) {
+ Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
+ for (FeedItem item : event.items) {
+ int pos = FeedItemUtil.indexOfItemWithId(episodes, item.getId());
+ if (pos >= 0) {
+ episodes.remove(pos);
+ if (shouldUpdatedItemRemainInList(item)) {
+ episodes.add(pos, item);
+ listAdapter.notifyItemChanged(pos);
+ } else {
+ listAdapter.notifyItemRemoved(pos);
+ }
+ }
+ }
+ }
+
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ public void onEventMainThread(PlaybackPositionEvent event) {
+ if (listAdapter != null) {
+ listAdapter.notifyCurrentlyPlayingItemChanged(event);
+ }
+ }
+
+ protected boolean shouldUpdatedItemRemainInList(FeedItem item) {
+ return true;
+ }
+
+ @Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
+ public void onEventMainThread(DownloadEvent event) {
+ Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
+ DownloaderUpdate update = event.update;
+ downloaderList = update.downloaders;
+ if (isMenuInvalidationAllowed && event.hasChangedFeedUpdateStatus(isUpdatingFeeds)) {
+ requireActivity().invalidateOptionsMenu();
+ }
+ if (update.mediaIds.length > 0) {
+ for (long mediaId : update.mediaIds) {
+ int pos = FeedItemUtil.indexOfItemWithMediaId(episodes, mediaId);
+ if (pos >= 0) {
+ listAdapter.notifyItemChanged(pos);
+ }
+ }
+ }
+ }
+
+ private final EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
+ @Override
+ public void update(EventDistributor eventDistributor, Integer arg) {
+ if ((arg & EVENTS) != 0) {
+ loadItems();
+ if (isUpdatingFeeds != updateRefreshMenuItemChecker.isRefreshing()) {
+ requireActivity().invalidateOptionsMenu();
+ }
+ }
+ }
+ };
+
+ void loadItems() {
+ if (disposable != null) {
+ disposable.dispose();
+ }
+ disposable = Observable.fromCallable(this::loadData)
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(data -> {
+ progLoading.setVisibility(View.GONE);
+ episodes = data;
+ onFragmentLoaded(episodes);
+ }, error -> Log.e(TAG, Log.getStackTraceString(error)));
+ }
+
+ @NonNull
+ protected abstract List<FeedItem> loadData();
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java
index 348c73b92..9f136490a 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java
@@ -18,7 +18,7 @@ import com.bumptech.glide.Glide;
import com.bumptech.glide.request.RequestOptions;
import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.core.event.ServiceEvent;
+import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
import de.danoeh.antennapod.core.feed.MediaType;
import de.danoeh.antennapod.core.glide.ApGlideSettings;
import de.danoeh.antennapod.core.service.playback.PlaybackService;
@@ -28,6 +28,9 @@ import io.reactivex.Maybe;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
+import org.greenrobot.eventbus.EventBus;
+import org.greenrobot.eventbus.Subscribe;
+import org.greenrobot.eventbus.ThreadMode;
/**
* Fragment which is supposed to be displayed outside of the MediaplayerActivity
@@ -138,6 +141,7 @@ public class ExternalPlayerFragment extends Fragment {
controller = setupPlaybackController();
controller.init();
loadMediaInfo();
+ EventBus.getDefault().register(this);
}
@Override
@@ -147,6 +151,12 @@ public class ExternalPlayerFragment extends Fragment {
controller.release();
controller = null;
}
+ EventBus.getDefault().unregister(this);
+ }
+
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ public void onEventMainThread(PlaybackPositionEvent event) {
+ onPositionObserverUpdate();
}
@Override
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/FavoriteEpisodesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/FavoriteEpisodesFragment.java
index bb029b731..536ebd468 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/FavoriteEpisodesFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/FavoriteEpisodesFragment.java
@@ -7,6 +7,7 @@ import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.helper.ItemTouchHelper;
import android.util.Log;
import android.view.LayoutInflater;
+import android.view.Menu;
import android.view.View;
import android.view.ViewGroup;
@@ -25,7 +26,7 @@ import de.danoeh.antennapod.core.storage.DBWriter;
* Like 'EpisodesFragment' except that it only shows favorite episodes and
* supports swiping to remove from favorites.
*/
-public class FavoriteEpisodesFragment extends AllEpisodesFragment {
+public class FavoriteEpisodesFragment extends EpisodesListFragment {
private static final String TAG = "FavoriteEpisodesFrag";
private static final String PREF_NAME = "PrefFavoriteEpisodesFragment";
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ItemlistFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/FeedItemlistFragment.java
index 0c75af986..8fe0883b2 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/ItemlistFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/FeedItemlistFragment.java
@@ -29,6 +29,7 @@ import com.bumptech.glide.request.RequestOptions;
import com.joanzapata.iconify.Iconify;
import com.joanzapata.iconify.widget.IconTextView;
+import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
import org.apache.commons.lang3.Validate;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
@@ -79,7 +80,7 @@ import io.reactivex.schedulers.Schedulers;
* Displays a list of FeedItems.
*/
@SuppressLint("ValidFragment")
-public class ItemlistFragment extends ListFragment {
+public class FeedItemlistFragment extends ListFragment {
private static final String TAG = "ItemlistFragment";
private static final int EVENTS = EventDistributor.UNREAD_ITEMS_UPDATE
@@ -120,8 +121,8 @@ public class ItemlistFragment extends ListFragment {
* @param feedId The id of the feed to show
* @return the newly created instance of an ItemlistFragment
*/
- public static ItemlistFragment newInstance(long feedId) {
- ItemlistFragment i = new ItemlistFragment();
+ public static FeedItemlistFragment newInstance(long feedId) {
+ FeedItemlistFragment i = new FeedItemlistFragment();
Bundle b = new Bundle();
b.putLong(ARGUMENT_FEED_ID, feedId);
i.setArguments(b);
@@ -195,6 +196,21 @@ public class ItemlistFragment extends ListFragment {
final SearchView sv = (SearchView) MenuItemCompat.getActionView(searchItem);
MenuItemUtils.adjustTextColor(getActivity(), sv);
sv.setQueryHint(getString(R.string.search_hint));
+ searchItem.setOnActionExpandListener(new MenuItem.OnActionExpandListener() {
+ @Override
+ public boolean onMenuItemActionExpand(MenuItem item) {
+ menu.findItem(R.id.filter_items).setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
+ menu.findItem(R.id.episode_actions).setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
+ menu.findItem(R.id.refresh_item).setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
+ return true;
+ }
+
+ @Override
+ public boolean onMenuItemActionCollapse(MenuItem item) {
+ getActivity().invalidateOptionsMenu();
+ return true;
+ }
+ });
sv.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
@Override
public boolean onQueryTextSubmit(String s) {
@@ -308,7 +324,7 @@ public class ItemlistFragment extends ListFragment {
contextMenu = menu;
lastMenuInfo = (AdapterView.AdapterContextMenuInfo) menuInfo;
- FeedItemMenuHandler.onPrepareMenu(contextMenuInterface, item, true, null);
+ FeedItemMenuHandler.onPrepareMenu(contextMenuInterface, item);
}
@Override
@@ -325,7 +341,7 @@ public class ItemlistFragment extends ListFragment {
return super.onContextItemSelected(item);
}
- return FeedItemMenuHandler.onMenuItemClicked(getActivity(), item.getItemId(), selectedItem);
+ return FeedItemMenuHandler.onMenuItemClicked(this, item.getItemId(), selectedItem);
}
@Override
@@ -375,14 +391,21 @@ public class ItemlistFragment extends ListFragment {
Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
DownloaderUpdate update = event.update;
downloaderList = update.downloaders;
- if (isUpdatingFeed != event.update.feedIds.length > 0) {
+ if (event.hasChangedFeedUpdateStatus(isUpdatingFeed)) {
updateProgressBarVisibility();
}
- if(adapter != null && update.mediaIds.length > 0) {
+ if (adapter != null && update.mediaIds.length > 0) {
adapter.notifyDataSetChanged();
}
}
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ public void onEventMainThread(PlaybackPositionEvent event) {
+ if (adapter != null) {
+ adapter.notifyCurrentlyPlayingItemChanged(event, getListView());
+ }
+ }
+
private final EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
@Override
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java
index 5cf2c5eeb..bfca90b2a 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java
@@ -96,13 +96,7 @@ public class ItemDescriptionFragment extends Fragment {
if (Timeline.isTimecodeLink(url)) {
onTimecodeLinkSelected(url);
} else {
- Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
- try {
- startActivity(intent);
- } catch (ActivityNotFoundException e) {
- e.printStackTrace();
- return true;
- }
+ IntentUtils.openInBrowser(getContext(), url);
}
return true;
}
@@ -159,11 +153,7 @@ public class ItemDescriptionFragment extends Fragment {
if (selectedURL != null) {
switch (item.getItemId()) {
case R.id.open_in_browser_item:
- Uri uri = Uri.parse(selectedURL);
- final Intent intent = new Intent(Intent.ACTION_VIEW, uri);
- if(IntentUtils.isCallable(getActivity(), intent)) {
- getActivity().startActivity(intent);
- }
+ IntentUtils.openInBrowser(getContext(), selectedURL);
break;
case R.id.share_url_item:
ShareUtils.shareLink(getActivity(), selectedURL);
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java
index 432ada44e..9395b0994 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java
@@ -55,7 +55,6 @@ import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.glide.ApGlideSettings;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.download.Downloader;
-import de.danoeh.antennapod.core.service.playback.PlaybackService;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBTasks;
import de.danoeh.antennapod.core.storage.DBWriter;
@@ -211,10 +210,7 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
webvDescription.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
- Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
- if(IntentUtils.isCallable(getActivity(), intent)) {
- startActivity(intent);
- }
+ IntentUtils.openInBrowser(getContext(), url);
return true;
}
});
@@ -336,10 +332,10 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
inflater.inflate(R.menu.feeditem_options, menu);
popupMenu = menu;
if (item.hasMedia()) {
- FeedItemMenuHandler.onPrepareMenu(popupMenuInterface, item, true, null);
+ FeedItemMenuHandler.onPrepareMenu(popupMenuInterface, item);
} else {
// these are already available via button1 and button2
- FeedItemMenuHandler.onPrepareMenu(popupMenuInterface, item, true, null,
+ FeedItemMenuHandler.onPrepareMenu(popupMenuInterface, item,
R.id.mark_read_item, R.id.visit_website_item);
}
}
@@ -351,7 +347,7 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
openPodcast();
return true;
default:
- return FeedItemMenuHandler.onMenuItemClicked(getActivity(), menuItem.getItemId(), item);
+ return FeedItemMenuHandler.onMenuItemClicked(this, menuItem.getItemId(), item);
}
}
@@ -446,15 +442,6 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
}
}
- FeedItem.State state = item.getState();
- if (butAction2Text == R.string.delete_label && state == FeedItem.State.PLAYING && PlaybackService.isRunning) {
- butAction2.setEnabled(false);
- butAction2.setAlpha(0.5f);
- } else {
- butAction2.setEnabled(true);
- butAction2.setAlpha(1.0f);
- }
-
if(butAction1Icon != null && butAction1Text != 0) {
butAction1.setText(butAction1Icon +"\u0020\u0020" + getActivity().getString(butAction1Text));
Iconify.addIcons(butAction1);
@@ -494,11 +481,7 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
if (selectedURL != null) {
switch (item.getItemId()) {
case R.id.open_in_browser_item:
- Uri uri = Uri.parse(selectedURL);
- final Intent intent = new Intent(Intent.ACTION_VIEW, uri);
- if(IntentUtils.isCallable(getActivity(), intent)) {
- getActivity().startActivity(intent);
- }
+ IntentUtils.openInBrowser(getContext(), selectedURL);
break;
case R.id.share_url_item:
ShareUtils.shareLink(getActivity(), selectedURL);
@@ -543,7 +526,7 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
}
private void openPodcast() {
- Fragment fragment = ItemlistFragment.newInstance(item.getFeedId());
+ Fragment fragment = FeedItemlistFragment.newInstance(item.getFeedId());
((MainActivity)getActivity()).loadChildFragment(fragment);
}
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 1bf907aee..07667118d 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/NewEpisodesFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/NewEpisodesFragment.java
@@ -5,6 +5,7 @@ import android.support.annotation.NonNull;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.helper.ItemTouchHelper;
import android.view.LayoutInflater;
+import android.view.Menu;
import android.view.View;
import android.view.ViewGroup;
@@ -14,12 +15,13 @@ import de.danoeh.antennapod.R;
import de.danoeh.antennapod.adapter.AllEpisodesRecycleAdapter;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.storage.DBReader;
+import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler;
/**
* Like 'EpisodesFragment' except that it only shows new episodes and
* supports swiping to mark as read.
*/
-public class NewEpisodesFragment extends AllEpisodesFragment {
+public class NewEpisodesFragment extends EpisodesListFragment {
public static final String TAG = "NewEpisodesFragment";
private static final String PREF_NAME = "PrefNewEpisodesFragment";
@@ -39,6 +41,12 @@ public class NewEpisodesFragment extends AllEpisodesFragment {
return item.isNew();
}
+ @Override
+ public void onPrepareOptionsMenu(Menu menu) {
+ super.onPrepareOptionsMenu(menu);
+ menu.findItem(R.id.remove_all_new_flags_item).setVisible(!episodes.isEmpty());
+ }
+
@NonNull
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
@@ -55,7 +63,7 @@ public class NewEpisodesFragment extends AllEpisodesFragment {
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int swipeDir) {
AllEpisodesRecycleAdapter.Holder holder = (AllEpisodesRecycleAdapter.Holder) viewHolder;
- removeNewFlagWithUndo(holder.getFeedItem());
+ FeedItemMenuHandler.removeNewFlagWithUndo(NewEpisodesFragment.this, holder.getFeedItem());
}
@Override
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 0fe413954..ff1e9a28e 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java
@@ -7,6 +7,7 @@ import android.os.Bundle;
import android.support.design.widget.Snackbar;
import android.support.v4.app.Fragment;
import android.support.v4.view.MenuItemCompat;
+import android.support.v7.app.AlertDialog;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.SearchView;
@@ -19,11 +20,17 @@ import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
+import android.widget.CheckBox;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.yqritc.recyclerviewflexibledivider.HorizontalDividerItemDecoration;
+import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
+import org.greenrobot.eventbus.EventBus;
+import org.greenrobot.eventbus.Subscribe;
+import org.greenrobot.eventbus.ThreadMode;
+
import java.util.List;
import de.danoeh.antennapod.R;
@@ -35,14 +42,12 @@ import de.danoeh.antennapod.core.event.DownloaderUpdate;
import de.danoeh.antennapod.core.event.FeedItemEvent;
import de.danoeh.antennapod.core.event.QueueEvent;
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.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.download.DownloadService;
import de.danoeh.antennapod.core.service.download.Downloader;
import de.danoeh.antennapod.core.storage.DBReader;
-import de.danoeh.antennapod.core.storage.DBTasks;
import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.storage.DownloadRequester;
import de.danoeh.antennapod.core.util.Converter;
@@ -50,18 +55,15 @@ import de.danoeh.antennapod.core.util.FeedItemUtil;
import de.danoeh.antennapod.core.util.LongList;
import de.danoeh.antennapod.core.util.QueueSorter;
import de.danoeh.antennapod.core.util.SortOrder;
+import de.danoeh.antennapod.core.util.download.AutoUpdateManager;
import de.danoeh.antennapod.dialog.EpisodesApplyActionFragment;
import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler;
import de.danoeh.antennapod.menuhandler.MenuItemUtils;
-
import de.danoeh.antennapod.view.EmptyViewHandler;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
-import org.greenrobot.eventbus.EventBus;
-import org.greenrobot.eventbus.Subscribe;
-import org.greenrobot.eventbus.ThreadMode;
import static de.danoeh.antennapod.dialog.EpisodesApplyActionFragment.ACTION_DELETE;
import static de.danoeh.antennapod.dialog.EpisodesApplyActionFragment.ACTION_REMOVE_FROM_QUEUE;
@@ -91,10 +93,12 @@ public class QueueFragment extends Fragment {
private static final String PREFS = "QueueFragment";
private static final String PREF_SCROLL_POSITION = "scroll_position";
private static final String PREF_SCROLL_OFFSET = "scroll_offset";
+ private static final String PREF_SHOW_LOCK_WARNING = "show_lock_warning";
private Disposable disposable;
private LinearLayoutManager layoutManager;
private ItemTouchHelper itemTouchHelper;
+ private SharedPreferences prefs;
@Override
@@ -102,6 +106,7 @@ public class QueueFragment extends Fragment {
super.onCreate(savedInstanceState);
setRetainInstance(true);
setHasOptionsMenu(true);
+ prefs = getActivity().getSharedPreferences(PREFS, Context.MODE_PRIVATE);
}
@Override
@@ -196,8 +201,8 @@ public class QueueFragment extends Fragment {
Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
DownloaderUpdate update = event.update;
downloaderList = update.downloaders;
- if (isUpdatingFeeds != update.feedIds.length > 0) {
- getActivity().supportInvalidateOptionsMenu();
+ if (event.hasChangedFeedUpdateStatus(isUpdatingFeeds)) {
+ getActivity().invalidateOptionsMenu();
}
if (recyclerAdapter != null && update.mediaIds.length > 0) {
for (long mediaId : update.mediaIds) {
@@ -209,6 +214,13 @@ public class QueueFragment extends Fragment {
}
}
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ public void onEventMainThread(PlaybackPositionEvent event) {
+ if (recyclerAdapter != null) {
+ recyclerAdapter.notifyCurrentlyPlayingItemChanged(event);
+ }
+ }
+
private void saveScrollPosition() {
int firstItem = layoutManager.findFirstVisibleItemPosition();
View firstItemView = layoutManager.findViewByPosition(firstItem);
@@ -219,15 +231,13 @@ public class QueueFragment extends Fragment {
topOffset = firstItemView.getTop();
}
- SharedPreferences prefs = getActivity().getSharedPreferences(PREFS, Context.MODE_PRIVATE);
- SharedPreferences.Editor editor = prefs.edit();
- editor.putInt(PREF_SCROLL_POSITION, firstItem);
- editor.putFloat(PREF_SCROLL_OFFSET, topOffset);
- editor.commit();
+ prefs.edit()
+ .putInt(PREF_SCROLL_POSITION, firstItem)
+ .putFloat(PREF_SCROLL_OFFSET, topOffset)
+ .apply();
}
private void restoreScrollPosition() {
- SharedPreferences prefs = getActivity().getSharedPreferences(PREFS, Context.MODE_PRIVATE);
int position = prefs.getInt(PREF_SCROLL_POSITION, 0);
float offset = prefs.getFloat(PREF_SCROLL_OFFSET, 0.0f);
if (position > 0 || offset > 0) {
@@ -299,25 +309,10 @@ public class QueueFragment extends Fragment {
if (!super.onOptionsItemSelected(item)) {
switch (item.getItemId()) {
case R.id.queue_lock:
- boolean newLockState = !UserPreferences.isQueueLocked();
- UserPreferences.setQueueLocked(newLockState);
- getActivity().supportInvalidateOptionsMenu();
- if (recyclerAdapter != null) {
- recyclerAdapter.setLocked(newLockState);
- }
- if (newLockState) {
- Snackbar.make(getActivity().findViewById(R.id.content), R.string
- .queue_locked, Snackbar.LENGTH_SHORT).show();
- } else {
- Snackbar.make(getActivity().findViewById(R.id.content), R.string
- .queue_unlocked, Snackbar.LENGTH_SHORT).show();
- }
+ toggleQueueLock();
return true;
case R.id.refresh_item:
- List<Feed> feeds = ((MainActivity) getActivity()).getFeeds();
- if (feeds != null) {
- DBTasks.refreshAllFeeds(getActivity(), feeds);
- }
+ AutoUpdateManager.runImmediate(requireContext());
return true;
case R.id.clear_queue:
// make sure the user really wants to clear the queue
@@ -378,8 +373,10 @@ public class QueueFragment extends Fragment {
if (keepSortedNew) {
SortOrder sortOrder = UserPreferences.getQueueKeepSortedOrder();
QueueSorter.sort(sortOrder, true);
- recyclerAdapter.setLocked(true);
- } else {
+ if (recyclerAdapter != null) {
+ recyclerAdapter.setLocked(true);
+ }
+ } else if (recyclerAdapter != null) {
recyclerAdapter.setLocked(UserPreferences.isQueueLocked());
}
getActivity().invalidateOptionsMenu();
@@ -392,6 +389,48 @@ public class QueueFragment extends Fragment {
}
}
+ private void toggleQueueLock() {
+ boolean isLocked = UserPreferences.isQueueLocked();
+ if (isLocked) {
+ setQueueLocked(false);
+ } else {
+ boolean shouldShowLockWarning = prefs.getBoolean(PREF_SHOW_LOCK_WARNING, true);
+ if (!shouldShowLockWarning) {
+ setQueueLocked(true);
+ } else {
+ AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
+ builder.setTitle(R.string.lock_queue);
+ builder.setMessage(R.string.queue_lock_warning);
+
+ View view = View.inflate(getContext(), R.layout.checkbox_do_not_show_again, null);
+ CheckBox checkDoNotShowAgain = view.findViewById(R.id.checkbox_do_not_show_again);
+ builder.setView(view);
+
+ builder.setPositiveButton(R.string.lock_queue, (dialog, which) -> {
+ prefs.edit().putBoolean(PREF_SHOW_LOCK_WARNING, !checkDoNotShowAgain.isChecked()).apply();
+ setQueueLocked(true);
+ });
+ builder.setNegativeButton(R.string.cancel_label, null);
+ builder.show();
+ }
+ }
+ }
+
+ private void setQueueLocked(boolean locked) {
+ UserPreferences.setQueueLocked(locked);
+ getActivity().supportInvalidateOptionsMenu();
+ if (recyclerAdapter != null) {
+ recyclerAdapter.setLocked(locked);
+ }
+ if (locked) {
+ Snackbar.make(getActivity().findViewById(R.id.content), R.string
+ .queue_locked, Snackbar.LENGTH_SHORT).show();
+ } else {
+ Snackbar.make(getActivity().findViewById(R.id.content), R.string
+ .queue_unlocked, Snackbar.LENGTH_SHORT).show();
+ }
+ }
+
/**
* This method is called if the user clicks on a sort order menu item.
*
@@ -428,7 +467,7 @@ public class QueueFragment extends Fragment {
DBWriter.moveQueueItemToBottom(selectedItem.getId(), true);
return true;
default:
- return FeedItemMenuHandler.onMenuItemClicked(getActivity(), item.getItemId(), selectedItem);
+ return FeedItemMenuHandler.onMenuItemClicked(this, item.getItemId(), selectedItem);
}
}
@@ -503,7 +542,7 @@ public class QueueFragment extends Fragment {
@Override
public boolean isLongPressDragEnabled() {
- return !UserPreferences.isQueueLocked();
+ return false;
}
@Override
@@ -596,7 +635,7 @@ public class QueueFragment extends Fragment {
String info = queue.size() + getString(R.string.episodes_suffix);
if(queue.size() > 0) {
long timeLeft = 0;
- float playbackSpeed = Float.valueOf(UserPreferences.getPlaybackSpeed());
+ float playbackSpeed = UserPreferences.getPlaybackSpeed();
for(FeedItem item : queue) {
if(item.getMedia() != null) {
timeLeft +=
@@ -604,7 +643,7 @@ public class QueueFragment extends Fragment {
/ playbackSpeed);
}
}
- info += " \u2022 ";
+ info += " • ";
info += getString(R.string.time_left_label);
info += Converter.getDurationStringLocalized(getActivity(), timeLeft);
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/SubscriptionFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/SubscriptionFragment.java
index 15c6052a9..ed315050b 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/SubscriptionFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/SubscriptionFragment.java
@@ -25,19 +25,28 @@ import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.adapter.SubscriptionsAdapter;
import de.danoeh.antennapod.core.asynctask.FeedRemover;
import de.danoeh.antennapod.core.dialog.ConfirmationDialog;
+import de.danoeh.antennapod.core.event.DownloadEvent;
+import de.danoeh.antennapod.core.event.DownloaderUpdate;
import de.danoeh.antennapod.core.feed.EventDistributor;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.preferences.PlaybackPreferences;
+import de.danoeh.antennapod.core.service.download.DownloadService;
import de.danoeh.antennapod.core.service.playback.PlaybackService;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBWriter;
+import de.danoeh.antennapod.core.storage.DownloadRequester;
import de.danoeh.antennapod.core.util.FeedItemUtil;
import de.danoeh.antennapod.core.util.IntentUtils;
+import de.danoeh.antennapod.core.util.download.AutoUpdateManager;
import de.danoeh.antennapod.dialog.RenameFeedDialog;
+import de.danoeh.antennapod.menuhandler.MenuItemUtils;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
+import org.greenrobot.eventbus.EventBus;
+import org.greenrobot.eventbus.Subscribe;
+import org.greenrobot.eventbus.ThreadMode;
/**
* Fragment for displaying feed subscriptions
@@ -56,6 +65,7 @@ public class SubscriptionFragment extends Fragment {
private SubscriptionsAdapter subscriptionAdapter;
private int mPosition = -1;
+ private boolean isUpdatingFeeds = false;
private Disposable disposable;
private SharedPreferences prefs;
@@ -89,6 +99,8 @@ public class SubscriptionFragment extends Fragment {
menu.findItem(R.id.subscription_num_columns_3).setChecked(columns == 3);
menu.findItem(R.id.subscription_num_columns_4).setChecked(columns == 4);
menu.findItem(R.id.subscription_num_columns_5).setChecked(columns == 5);
+
+ isUpdatingFeeds = MenuItemUtils.updateRefreshMenuItem(menu, R.id.refresh_item, updateRefreshMenuItemChecker);
}
@Override
@@ -97,6 +109,9 @@ public class SubscriptionFragment extends Fragment {
return true;
}
switch (item.getItemId()) {
+ case R.id.refresh_item:
+ AutoUpdateManager.runImmediate(requireContext());
+ return true;
case R.id.subscription_num_columns_2:
setColumnNumber(2);
return true;
@@ -136,6 +151,7 @@ public class SubscriptionFragment extends Fragment {
public void onStart() {
super.onStart();
EventDistributor.getInstance().register(contentUpdate);
+ EventBus.getDefault().register(this);
loadSubscriptions();
}
@@ -143,6 +159,7 @@ public class SubscriptionFragment extends Fragment {
public void onStop() {
super.onStop();
EventDistributor.getInstance().unregister(contentUpdate);
+ EventBus.getDefault().unregister(this);
if(disposable != null) {
disposable.dispose();
}
@@ -278,6 +295,17 @@ public class SubscriptionFragment extends Fragment {
}
};
+ @Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
+ public void onEventMainThread(DownloadEvent event) {
+ Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
+ if (event.hasChangedFeedUpdateStatus(isUpdatingFeeds)) {
+ getActivity().invalidateOptionsMenu();
+ }
+ }
+
+ private final MenuItemUtils.UpdateRefreshMenuItemChecker updateRefreshMenuItemChecker =
+ () -> DownloadService.isRunning && DownloadRequester.getInstance().isDownloadingFeeds();
+
private final SubscriptionsAdapter.ItemAccess itemAccess = new SubscriptionsAdapter.ItemAccess() {
@Override
public int getCount() {
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AutoDownloadPreferencesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AutoDownloadPreferencesFragment.java
index a04615a00..d0c209326 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AutoDownloadPreferencesFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AutoDownloadPreferencesFragment.java
@@ -1,29 +1,41 @@
package de.danoeh.antennapod.fragment.preferences;
+import android.Manifest;
import android.app.Activity;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiManager;
+import android.os.Build;
import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.v4.content.ContextCompat;
import android.support.v7.preference.CheckBoxPreference;
import android.support.v7.preference.ListPreference;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceFragmentCompat;
import android.support.v7.preference.PreferenceScreen;
import android.util.Log;
-import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.core.preferences.UserPreferences;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.core.preferences.UserPreferences;
+
public class AutoDownloadPreferencesFragment extends PreferenceFragmentCompat {
private static final String TAG = "AutoDnldPrefFragment";
+
+ private static final int LOCATION_PERMISSION_REQUEST_CODE = 1;
+ private static final String PREF_KEY_LOCATION_PERMISSION_REQUEST_PROMPT = "prefAutoDownloadWifiFilterAndroid10PermissionPrompt";
+
private CheckBoxPreference[] selectedNetworks;
+ private Preference prefPermissionRequestPromptOnAndroid10 = null;
+
@Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
addPreferencesFromResource(R.xml.preferences_autodownload);
@@ -175,10 +187,65 @@ public class AutoDownloadPreferencesFragment extends PreferenceFragmentCompat {
}
private void setSelectedNetworksEnabled(boolean b) {
+ if (showPermissionRequestPromptOnAndroid10IfNeeded(b)) {
+ return;
+ }
+
if (selectedNetworks != null) {
for (Preference p : selectedNetworks) {
p.setEnabled(b);
}
}
}
+
+ @Override
+ public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
+ if (requestCode != LOCATION_PERMISSION_REQUEST_CODE) {
+ return;
+ }
+ if (permissions.length > 0 && permissions[0].equals(Manifest.permission.ACCESS_FINE_LOCATION) &&
+ grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+ buildAutodownloadSelectedNetworksPreference();
+ }
+ }
+
+ private boolean showPermissionRequestPromptOnAndroid10IfNeeded(boolean wifiFilterEnabled) {
+ if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.P) {
+ return false;
+ }
+
+ // Cases Android 10(Q) or later
+ if (prefPermissionRequestPromptOnAndroid10 != null) {
+ getPreferenceScreen().removePreference(prefPermissionRequestPromptOnAndroid10);
+ prefPermissionRequestPromptOnAndroid10 = null;
+ }
+
+ if (ContextCompat.checkSelfPermission(requireContext(), Manifest.permission.ACCESS_FINE_LOCATION)
+ == PackageManager.PERMISSION_GRANTED) {
+ return false;
+ }
+
+ // Case location permission not yet granted, permission-specific UI is needed
+ if (!wifiFilterEnabled) {
+ // Don't show the UI when WiFi filter disabled.
+ // it still return true, so that the caller knows
+ // it does not have required permission, and will not invoke codes that require so.
+ return true;
+ }
+
+ Preference pref = new Preference(requireActivity());
+ pref.setKey(PREF_KEY_LOCATION_PERMISSION_REQUEST_PROMPT);
+ pref.setTitle(R.string.autodl_wifi_filter_permission_title);
+ pref.setSummary(R.string.autodl_wifi_filter_permission_message);
+ pref.setIcon(R.drawable.ic_warning_red);
+ pref.setOnPreferenceClickListener(preference -> {
+ requestPermissions(new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, LOCATION_PERMISSION_REQUEST_CODE);
+ return true;
+ });
+ pref.setPersistent(false);
+ getPreferenceScreen().addPreference(pref);
+ prefPermissionRequestPromptOnAndroid10 = pref;
+ return true;
+ }
+
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/MainPreferencesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/MainPreferencesFragment.java
index 701d21ce0..9f36e1355 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/MainPreferencesFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/MainPreferencesFragment.java
@@ -1,27 +1,21 @@
package de.danoeh.antennapod.fragment.preferences;
import android.content.ActivityNotFoundException;
-import android.content.Context;
import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
import android.net.Uri;
-import android.os.Build;
import android.os.Bundle;
-import android.support.v4.content.FileProvider;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.preference.PreferenceFragmentCompat;
import android.util.Log;
import android.widget.Toast;
import com.bytehamster.lib.preferencesearch.SearchConfiguration;
import com.bytehamster.lib.preferencesearch.SearchPreference;
-import de.danoeh.antennapod.CrashReportWriter;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.AboutActivity;
+import de.danoeh.antennapod.activity.BugReportActivity;
import de.danoeh.antennapod.activity.PreferenceActivity;
import de.danoeh.antennapod.activity.StatisticsActivity;
-
-import java.util.List;
+import de.danoeh.antennapod.core.util.IntentUtils;
public class MainPreferencesFragment extends PreferenceFragmentCompat {
private static final String TAG = "MainPreferencesFragment";
@@ -31,9 +25,9 @@ public class MainPreferencesFragment extends PreferenceFragmentCompat {
private static final String PREF_SCREEN_NETWORK = "prefScreenNetwork";
private static final String PREF_SCREEN_INTEGRATIONS = "prefScreenIntegrations";
private static final String PREF_SCREEN_STORAGE = "prefScreenStorage";
- private static final String PREF_KNOWN_ISSUES = "prefKnownIssues";
private static final String PREF_FAQ = "prefFaq";
- private static final String PREF_SEND_CRASH_REPORT = "prefSendCrashReport";
+ private static final String PREF_VIEW_MAILING_LIST = "prefViewMailingList";
+ private static final String PREF_SEND_BUG_REPORT = "prefSendBugReport";
private static final String STATISTICS = "statistics";
private static final String PREF_ABOUT = "prefAbout";
@@ -78,49 +72,20 @@ public class MainPreferencesFragment extends PreferenceFragmentCompat {
return true;
}
);
- findPreference(PREF_KNOWN_ISSUES).setOnPreferenceClickListener(preference -> {
- openInBrowser("https://github.com/AntennaPod/AntennaPod/labels/bug");
+ findPreference(PREF_FAQ).setOnPreferenceClickListener(preference -> {
+ IntentUtils.openInBrowser(getContext(), "https://antennapod.org/faq.html");
return true;
});
- findPreference(PREF_FAQ).setOnPreferenceClickListener(preference -> {
- openInBrowser("http://antennapod.org/faq.html");
+ findPreference(PREF_VIEW_MAILING_LIST).setOnPreferenceClickListener(preference -> {
+ IntentUtils.openInBrowser(getContext(), "https://groups.google.com/forum/#!forum/antennapod");
return true;
});
- findPreference(PREF_SEND_CRASH_REPORT).setOnPreferenceClickListener(preference -> {
- Context context = getActivity().getApplicationContext();
- Intent emailIntent = new Intent(Intent.ACTION_SEND);
- emailIntent.setType("text/plain");
- emailIntent.putExtra(Intent.EXTRA_EMAIL, new String[]{"Martin.Fietz@gmail.com"});
- emailIntent.putExtra(Intent.EXTRA_SUBJECT, "AntennaPod Crash Report");
- emailIntent.putExtra(Intent.EXTRA_TEXT, "Please describe what you were doing when the app crashed");
- // the attachment
- Uri fileUri = FileProvider.getUriForFile(context, context.getString(R.string.provider_authority),
- CrashReportWriter.getFile());
- emailIntent.putExtra(Intent.EXTRA_STREAM, fileUri);
- emailIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
- String intentTitle = getActivity().getString(R.string.send_email);
- if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) {
- List<ResolveInfo> resInfoList = context.getPackageManager().queryIntentActivities(emailIntent, PackageManager.MATCH_DEFAULT_ONLY);
- for (ResolveInfo resolveInfo : resInfoList) {
- String packageName = resolveInfo.activityInfo.packageName;
- context.grantUriPermission(packageName, fileUri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
- }
- }
- getActivity().startActivity(Intent.createChooser(emailIntent, intentTitle));
+ findPreference(PREF_SEND_BUG_REPORT).setOnPreferenceClickListener(preference -> {
+ startActivity(new Intent(getActivity(), BugReportActivity.class));
return true;
});
}
- private void openInBrowser(String url) {
- try {
- Intent myIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
- startActivity(myIntent);
- } catch (ActivityNotFoundException e) {
- Toast.makeText(getActivity(), R.string.pref_no_browser_found, Toast.LENGTH_LONG).show();
- Log.e(TAG, Log.getStackTraceString(e));
- }
- }
-
private void setupSearch() {
SearchPreference searchPreference = (SearchPreference) findPreference("searchPreference");
SearchConfiguration config = searchPreference.getSearchConfiguration();
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/StoragePreferencesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/StoragePreferencesFragment.java
index b4226b546..e36476c6f 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/StoragePreferencesFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/StoragePreferencesFragment.java
@@ -4,6 +4,7 @@ import android.Manifest;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.ProgressDialog;
+import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
@@ -13,13 +14,16 @@ import android.os.Build;
import android.os.Bundle;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.FileProvider;
+import android.support.v4.provider.DocumentFile;
import android.support.v7.app.AlertDialog;
import android.support.v7.preference.PreferenceFragmentCompat;
import android.util.Log;
+
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.DirectoryChooserActivity;
import de.danoeh.antennapod.activity.ImportExportActivity;
import de.danoeh.antennapod.activity.OpmlImportFromPathActivity;
+import de.danoeh.antennapod.asynctask.DocumentFileExportWorker;
import de.danoeh.antennapod.asynctask.ExportWorker;
import de.danoeh.antennapod.core.export.ExportWriter;
import de.danoeh.antennapod.core.export.html.HtmlWriter;
@@ -45,6 +49,12 @@ public class StoragePreferencesFragment extends PreferenceFragmentCompat {
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE };
private static final int PERMISSION_REQUEST_EXTERNAL_STORAGE = 41;
+ private static final int CHOOSE_OPML_EXPORT_PATH = 1;
+ private static final String DEFAULT_OPML_OUTPUT_NAME = "antennapod-feeds.opml";
+ private static final String CONTENT_TYPE_OPML = "text/x-opml";
+ private static final int CHOOSE_HTML_EXPORT_PATH = 2;
+ private static final String DEFAULT_HTML_OUTPUT_NAME = "antennapod-feeds.html";
+ private static final String CONTENT_TYPE_HTML = "text/html";
private Disposable disposable;
@Override
@@ -59,6 +69,14 @@ public class StoragePreferencesFragment extends PreferenceFragmentCompat {
setDataFolderText();
}
+ @Override
+ public void onStop() {
+ super.onStop();
+ if (disposable != null) {
+ disposable.dispose();
+ }
+ }
+
private void setupStorageScreen() {
final Activity activity = getActivity();
@@ -69,9 +87,16 @@ public class StoragePreferencesFragment extends PreferenceFragmentCompat {
}
);
findPreference(PREF_OPML_EXPORT).setOnPreferenceClickListener(
- preference -> export(new OpmlWriter()));
+ preference -> {
+ openOpmlExportPathPicker();
+ return true;
+ }
+ );
findPreference(PREF_HTML_EXPORT).setOnPreferenceClickListener(
- preference -> export(new HtmlWriter()));
+ preference -> {
+ openHtmlExportPathPicker();
+ return true;
+ });
findPreference(PREF_OPML_IMPORT).setOnPreferenceClickListener(
preference -> {
activity.startActivity(new Intent(activity, OpmlImportFromPathActivity.class));
@@ -129,52 +154,65 @@ public class StoragePreferencesFragment extends PreferenceFragmentCompat {
}
private boolean export(ExportWriter exportWriter) {
+ return export(exportWriter, null);
+ }
+
+ private boolean export(ExportWriter exportWriter, final Uri uri) {
Context context = getActivity();
final ProgressDialog progressDialog = new ProgressDialog(context);
progressDialog.setMessage(context.getString(R.string.exporting_label));
progressDialog.setIndeterminate(true);
progressDialog.show();
- final AlertDialog.Builder alert = new AlertDialog.Builder(context)
- .setNeutralButton(android.R.string.ok, (dialog, which) -> dialog.dismiss());
- Observable<File> observable = new ExportWorker(exportWriter).exportObservable();
- disposable = observable.subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe(output -> {
- alert.setTitle(R.string.export_success_title);
- String message = context.getString(R.string.export_success_sum, output.toString());
- alert.setMessage(message);
- alert.setPositiveButton(R.string.send_label, (dialog, which) -> {
+ if (uri == null) {
+ Observable<File> observable = new ExportWorker(exportWriter).exportObservable();
+ disposable = observable.subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(output -> {
Uri fileUri = FileProvider.getUriForFile(context.getApplicationContext(),
context.getString(R.string.provider_authority), output);
- Intent sendIntent = new Intent(Intent.ACTION_SEND);
- sendIntent.putExtra(Intent.EXTRA_SUBJECT,
- context.getResources().getText(R.string.opml_export_label));
- sendIntent.putExtra(Intent.EXTRA_STREAM, fileUri);
- sendIntent.setType("text/plain");
- sendIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
- if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) {
- List<ResolveInfo> resInfoList = context.getPackageManager().queryIntentActivities(sendIntent, PackageManager.MATCH_DEFAULT_ONLY);
- for (ResolveInfo resolveInfo : resInfoList) {
- String packageName = resolveInfo.activityInfo.packageName;
- context.grantUriPermission(packageName, fileUri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
- }
- }
- context.startActivity(Intent.createChooser(sendIntent,
- context.getResources().getText(R.string.send_label)));
- });
- alert.create().show();
- }, error -> {
- alert.setTitle(R.string.export_error_label);
- alert.setMessage(error.getMessage());
- alert.show();
- }, progressDialog::dismiss);
+ showExportSuccessDialog(context.getString(R.string.export_success_sum, output.toString()), fileUri);
+ }, this::showExportErrorDialog, progressDialog::dismiss);
+ } else {
+ Observable<DocumentFile> observable = new DocumentFileExportWorker(exportWriter, context, uri).exportObservable();
+ disposable = observable.subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(output -> {
+ showExportSuccessDialog(context.getString(R.string.export_success_sum, output.getUri()), output.getUri());
+ }, this::showExportErrorDialog, progressDialog::dismiss);
+ }
return true;
}
- public void unsubscribeExportSubscription() {
- if (disposable != null) {
- disposable.dispose();
- }
+ private void showExportSuccessDialog(final String message, final Uri streamUri) {
+ final AlertDialog.Builder alert = new AlertDialog.Builder(getContext())
+ .setNeutralButton(android.R.string.ok, (dialog, which) -> dialog.dismiss());
+ alert.setTitle(R.string.export_success_title);
+ alert.setMessage(message);
+ alert.setPositiveButton(R.string.send_label, (dialog, which) -> {
+ Intent sendIntent = new Intent(Intent.ACTION_SEND);
+ sendIntent.putExtra(Intent.EXTRA_SUBJECT, getString(R.string.opml_export_label));
+ sendIntent.putExtra(Intent.EXTRA_STREAM, streamUri);
+ sendIntent.setType("text/plain");
+ sendIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) {
+ List<ResolveInfo> resInfoList = getContext().getPackageManager()
+ .queryIntentActivities(sendIntent, PackageManager.MATCH_DEFAULT_ONLY);
+ for (ResolveInfo resolveInfo : resInfoList) {
+ String packageName = resolveInfo.activityInfo.packageName;
+ getContext().grantUriPermission(packageName, streamUri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ }
+ }
+ getContext().startActivity(Intent.createChooser(sendIntent, getString(R.string.send_label)));
+ });
+ alert.create().show();
+ }
+
+ private void showExportErrorDialog(final Throwable error) {
+ final AlertDialog.Builder alert = new AlertDialog.Builder(getContext())
+ .setNeutralButton(android.R.string.ok, (dialog, which) -> dialog.dismiss());
+ alert.setTitle(R.string.export_error_label);
+ alert.setMessage(error.getMessage());
+ alert.show();
}
@SuppressLint("NewApi")
@@ -184,22 +222,22 @@ public class StoragePreferencesFragment extends PreferenceFragmentCompat {
String dir = data.getStringExtra(DirectoryChooserActivity.RESULT_SELECTED_DIR);
File path;
- if(dir != null) {
+ if (dir != null) {
path = new File(dir);
} else {
path = getActivity().getExternalFilesDir(null);
}
String message = null;
- final Context context= getActivity().getApplicationContext();
- if(!path.exists()) {
+ final Context context = getActivity().getApplicationContext();
+ if (!path.exists()) {
message = String.format(context.getString(R.string.folder_does_not_exist_error), dir);
- } else if(!path.canRead()) {
+ } else if (!path.canRead()) {
message = String.format(context.getString(R.string.folder_not_readable_error), dir);
- } else if(!path.canWrite()) {
+ } else if (!path.canWrite()) {
message = String.format(context.getString(R.string.folder_not_writable_error), dir);
}
- if(message == null) {
+ if (message == null) {
Log.d(TAG, "Setting data folder: " + dir);
UserPreferences.setDataFolder(dir);
setDataFolderText();
@@ -210,6 +248,16 @@ public class StoragePreferencesFragment extends PreferenceFragmentCompat {
ab.show();
}
}
+
+ if (resultCode == Activity.RESULT_OK && requestCode == CHOOSE_OPML_EXPORT_PATH) {
+ Uri uri = data.getData();
+ export(new OpmlWriter(), uri);
+ }
+
+ if (resultCode == Activity.RESULT_OK && requestCode == CHOOSE_HTML_EXPORT_PATH) {
+ Uri uri = data.getData();
+ export(new HtmlWriter(), uri);
+ }
}
private void setDataFolderText() {
@@ -231,6 +279,50 @@ public class StoragePreferencesFragment extends PreferenceFragmentCompat {
activity.startActivityForResult(intent, DirectoryChooserActivity.RESULT_CODE_DIR_SELECTED);
}
+ private void openOpmlExportPathPicker() {
+ if (Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN_MR2) {
+ Intent intentPickAction = new Intent(Intent.ACTION_CREATE_DOCUMENT)
+ .addCategory(Intent.CATEGORY_OPENABLE)
+ .setType(CONTENT_TYPE_OPML)
+ .putExtra(Intent.EXTRA_TITLE, DEFAULT_OPML_OUTPUT_NAME);
+
+ // Creates an implicit intent to launch a file manager which lets
+ // the user choose a specific directory to export to.
+ try {
+ startActivityForResult(intentPickAction, CHOOSE_OPML_EXPORT_PATH);
+ return;
+ } catch (ActivityNotFoundException e) {
+ Log.e(TAG, "No activity found. Should never happen...");
+ }
+ }
+
+ // If we are using a SDK lower than API 21 or the implicit intent failed
+ // fallback to the legacy export process
+ export(new OpmlWriter());
+ }
+
+ private void openHtmlExportPathPicker() {
+ if (Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN_MR2) {
+ Intent intentPickAction = new Intent(Intent.ACTION_CREATE_DOCUMENT)
+ .addCategory(Intent.CATEGORY_OPENABLE)
+ .setType(CONTENT_TYPE_HTML)
+ .putExtra(Intent.EXTRA_TITLE, DEFAULT_HTML_OUTPUT_NAME);
+
+ // Creates an implicit intent to launch a file manager which lets
+ // the user choose a specific directory to export to.
+ try {
+ startActivityForResult(intentPickAction, CHOOSE_HTML_EXPORT_PATH);
+ return;
+ } catch (ActivityNotFoundException e) {
+ Log.e(TAG, "No activity found. Should never happen...");
+ }
+ }
+
+ // If we are using a SDK lower than API 21 or the implicit intent failed
+ // fallback to the legacy export process
+ export(new HtmlWriter());
+ }
+
private void showChooseDataFolderDialog() {
ChooseDataFolderDialog.showDialog(
getActivity(), new ChooseDataFolderDialog.RunnableWithString() {
diff --git a/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedItemMenuHandler.java b/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedItemMenuHandler.java
index 156657a00..add62b480 100644
--- a/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedItemMenuHandler.java
+++ b/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedItemMenuHandler.java
@@ -3,7 +3,10 @@ package de.danoeh.antennapod.menuhandler;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
-import android.support.annotation.Nullable;
+import android.os.Handler;
+import android.support.annotation.NonNull;
+import android.support.design.widget.Snackbar;
+import android.support.v4.app.Fragment;
import android.util.Log;
import android.widget.Toast;
@@ -18,7 +21,6 @@ import de.danoeh.antennapod.core.service.playback.PlaybackService;
import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.util.FeedItemUtil;
import de.danoeh.antennapod.core.util.IntentUtils;
-import de.danoeh.antennapod.core.util.LongList;
import de.danoeh.antennapod.core.util.ShareUtils;
/**
@@ -51,35 +53,21 @@ public class FeedItemMenuHandler {
* @param mi An instance of MenuInterface that the method uses to change a
* MenuItem's visibility
* @param selectedItem The FeedItem for which the menu is supposed to be prepared
- * @param showExtendedMenu True if MenuItems that let the user share information about
- * the FeedItem and visit its website should be set visible. This
- * parameter should be set to false if the menu space is limited.
- * @param queueAccess Used for testing if the queue contains the selected item; only used for
- * move to top/bottom in the queue
* @return Returns true if selectedItem is not null.
*/
public static boolean onPrepareMenu(MenuInterface mi,
- FeedItem selectedItem,
- boolean showExtendedMenu,
- @Nullable LongList queueAccess) {
+ FeedItem selectedItem) {
if (selectedItem == null) {
return false;
}
boolean hasMedia = selectedItem.getMedia() != null;
boolean isPlaying = hasMedia && selectedItem.getState() == FeedItem.State.PLAYING;
- boolean keepSorted = UserPreferences.isQueueKeepSorted();
if (!isPlaying) {
mi.setItemVisibility(R.id.skip_episode_item, false);
}
boolean isInQueue = selectedItem.isTagged(FeedItem.TAG_QUEUE);
- if (queueAccess == null || queueAccess.size() == 0 || queueAccess.get(0) == selectedItem.getId() || keepSorted) {
- mi.setItemVisibility(R.id.move_to_top_item, false);
- }
- if (queueAccess == null || queueAccess.size() == 0 || queueAccess.get(queueAccess.size()-1) == selectedItem.getId() || keepSorted) {
- mi.setItemVisibility(R.id.move_to_bottom_item, false);
- }
if (!isInQueue) {
mi.setItemVisibility(R.id.remove_from_queue_item, false);
}
@@ -87,12 +75,12 @@ public class FeedItemMenuHandler {
mi.setItemVisibility(R.id.add_to_queue_item, false);
}
- if (!showExtendedMenu || !ShareUtils.hasLinkToShare(selectedItem)) {
+ if (!ShareUtils.hasLinkToShare(selectedItem)) {
mi.setItemVisibility(R.id.visit_website_item, false);
mi.setItemVisibility(R.id.share_link_item, false);
mi.setItemVisibility(R.id.share_link_with_position_item, false);
}
- if (!showExtendedMenu || !hasMedia || selectedItem.getMedia().getDownload_url() == null) {
+ if (!hasMedia || selectedItem.getMedia().getDownload_url() == null) {
mi.setItemVisibility(R.id.share_download_url_item, false);
mi.setItemVisibility(R.id.share_download_url_with_position_item, false);
}
@@ -104,6 +92,7 @@ public class FeedItemMenuHandler {
boolean fileDownloaded = hasMedia && selectedItem.getMedia().fileExists();
mi.setItemVisibility(R.id.share_file, fileDownloaded);
+ mi.setItemVisibility(R.id.remove_new_flag_item, selectedItem.isNew());
if (selectedItem.isPlayed()) {
mi.setItemVisibility(R.id.mark_read_item, false);
} else {
@@ -114,7 +103,7 @@ public class FeedItemMenuHandler {
mi.setItemVisibility(R.id.reset_position, false);
}
- if(!UserPreferences.isEnableAutodownload()) {
+ if(!UserPreferences.isEnableAutodownload() || fileDownloaded) {
mi.setItemVisibility(R.id.activate_auto_download, false);
mi.setItemVisibility(R.id.deactivate_auto_download, false);
} else if(selectedItem.getAutoDownload()) {
@@ -141,10 +130,8 @@ public class FeedItemMenuHandler {
*/
public static boolean onPrepareMenu(MenuInterface mi,
FeedItem selectedItem,
- boolean showExtendedMenu,
- LongList queueAccess,
int... excludeIds) {
- boolean rc = onPrepareMenu(mi, selectedItem, showExtendedMenu, queueAccess);
+ boolean rc = onPrepareMenu(mi, selectedItem);
if (rc && excludeIds != null) {
for (int id : excludeIds) {
mi.setItemVisibility(id, false);
@@ -153,8 +140,16 @@ public class FeedItemMenuHandler {
return rc;
}
- public static boolean onMenuItemClicked(Context context, int menuItemId,
- FeedItem selectedItem) {
+ /**
+ * Default menu handling for the given FeedItem.
+ *
+ * A Fragment instance, (rather than the more generic Context), is needed as a parameter
+ * to support some UI operations, e.g., creating a Snackbar.
+ */
+ public static boolean onMenuItemClicked(@NonNull Fragment fragment, int menuItemId,
+ @NonNull FeedItem selectedItem) {
+
+ @NonNull Context context = fragment.requireContext();
switch (menuItemId) {
case R.id.skip_episode_item:
IntentUtils.sendLocalBroadcast(context, PlaybackService.ACTION_SKIP_CURRENT_EPISODE);
@@ -162,6 +157,9 @@ public class FeedItemMenuHandler {
case R.id.remove_item:
DBWriter.deleteFeedMediaOfItem(context, selectedItem.getMedia().getId());
break;
+ case R.id.remove_new_flag_item:
+ removeNewFlagWithUndo(fragment, selectedItem);
+ break;
case R.id.mark_read_item:
selectedItem.setPlayed(true);
DBWriter.markItemPlayed(selectedItem, FeedItem.PLAYED, false);
@@ -216,14 +214,7 @@ public class FeedItemMenuHandler {
DBWriter.setFeedItemAutoDownload(selectedItem, false);
break;
case R.id.visit_website_item:
- Uri uri = Uri.parse(FeedItemUtil.getLinkWithFallback(selectedItem));
- Intent intent = new Intent(Intent.ACTION_VIEW, uri);
- if(IntentUtils.isCallable(context, intent)) {
- context.startActivity(intent);
- } else {
- Toast.makeText(context, context.getString(R.string.download_error_malformed_url),
- Toast.LENGTH_SHORT).show();
- }
+ IntentUtils.openInBrowser(context, FeedItemUtil.getLinkWithFallback(selectedItem));
break;
case R.id.share_link_item:
ShareUtils.shareFeedItemLink(context, selectedItem);
@@ -249,4 +240,39 @@ public class FeedItemMenuHandler {
return true;
}
+ /**
+ * Remove new flag with additional UI logic to allow undo with Snackbar.
+ *
+ * Undo is useful for Remove new flag, given there is no UI to undo it otherwise
+ * ,i.e., there is (context) menu item for add new flag
+ */
+ public static void removeNewFlagWithUndo(@NonNull Fragment fragment, FeedItem item) {
+ if (item == null) {
+ return;
+ }
+
+ Log.d(TAG, "removeNewFlagWithUndo(" + item.getId() + ")");
+ // we're marking it as unplayed since the user didn't actually play it
+ // but they don't want it considered 'NEW' anymore
+ DBWriter.markItemPlayed(FeedItem.UNPLAYED, item.getId());
+
+ final Handler h = new Handler(fragment.requireContext().getMainLooper());
+ final Runnable r = () -> {
+ FeedMedia media = item.getMedia();
+ if (media != null && media.hasAlmostEnded() && UserPreferences.isAutoDelete()) {
+ DBWriter.deleteFeedMediaOfItem(fragment.requireContext(), media.getId());
+ }
+ };
+
+ Snackbar snackbar = Snackbar.make(fragment.getView(), fragment.getString(R.string.removed_new_flag_label),
+ Snackbar.LENGTH_LONG);
+ snackbar.setAction(fragment.getString(R.string.undo), v -> {
+ DBWriter.markItemPlayed(FeedItem.NEW, item.getId());
+ // don't forget to cancel the thing that's going to remove the media
+ h.removeCallbacks(r);
+ });
+ snackbar.show();
+ h.postDelayed(r, (int) Math.ceil(snackbar.getDuration() * 1.05f));
+ }
+
}
diff --git a/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedMenuHandler.java b/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedMenuHandler.java
index 0928cfd62..dbb3b6e7b 100644
--- a/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedMenuHandler.java
+++ b/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedMenuHandler.java
@@ -20,11 +20,13 @@ import java.util.Set;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.dialog.ConfirmationDialog;
import de.danoeh.antennapod.core.feed.Feed;
+import de.danoeh.antennapod.core.feed.FeedItemFilter;
import de.danoeh.antennapod.core.storage.DBTasks;
import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.storage.DownloadRequestException;
import de.danoeh.antennapod.core.util.IntentUtils;
import de.danoeh.antennapod.core.util.ShareUtils;
+import de.danoeh.antennapod.dialog.FilterDialog;
/**
* Handles interactions with the FeedItemMenu.
@@ -84,14 +86,7 @@ public class FeedMenuHandler {
conDialog.createNewDialog().show();
break;
case R.id.visit_website_item:
- Uri uri = Uri.parse(selectedFeed.getLink());
- Intent intent = new Intent(Intent.ACTION_VIEW, uri);
- if(IntentUtils.isCallable(context, intent)) {
- context.startActivity(intent);
- } else {
- Toast.makeText(context, context.getString(R.string.download_error_malformed_url),
- Toast.LENGTH_SHORT).show();
- }
+ IntentUtils.openInBrowser(context, selectedFeed.getLink());
break;
case R.id.share_link_item:
ShareUtils.shareFeedlink(context, selectedFeed);
@@ -105,42 +100,15 @@ public class FeedMenuHandler {
return true;
}
- private static void showFilterDialog(final Context context, final Feed feed) {
- final String[] items = context.getResources().getStringArray(R.array.episode_filter_options);
- final String[] values = context.getResources().getStringArray(R.array.episode_filter_values);
- final boolean[] checkedItems = new boolean[items.length];
-
- final Set<String> filter = new HashSet<>(Arrays.asList(feed.getItemFilter().getValues()));
- Iterator<String> it = filter.iterator();
- while(it.hasNext()) {
- // make sure we have no empty strings in the filter list
- if(TextUtils.isEmpty(it.next())) {
- it.remove();
- }
- }
- for(int i=0; i < values.length; i++) {
- String value = values[i];
- if(filter.contains(value)) {
- checkedItems[i] = true;
+ private static void showFilterDialog(Context context, Feed selectedFeed) {
+ FilterDialog filterDialog = new FilterDialog(context, selectedFeed.getItemFilter()) {
+ @Override
+ protected void updateFilter(Set<String> filterValues) {
+ selectedFeed.setItemFilter(filterValues.toArray(new String[filterValues.size()]));
+ DBWriter.setFeedItemsFilter(selectedFeed.getId(), filterValues);
}
- }
-
- AlertDialog.Builder builder = new AlertDialog.Builder(context);
- builder.setTitle(R.string.filter);
- builder.setMultiChoiceItems(items, checkedItems, (dialog, which, isChecked) -> {
- if (isChecked) {
- filter.add(values[which]);
- } else {
- filter.remove(values[which]);
- }
- });
- builder.setPositiveButton(R.string.confirm_label, (dialog, which) -> {
- feed.setItemFilter(filter.toArray(new String[filter.size()]));
- DBWriter.setFeedItemsFilter(feed.getId(), filter);
- });
- builder.setNegativeButton(R.string.cancel_label, null);
- builder.create().show();
+ };
+ filterDialog.openDialog();
}
-
}
diff --git a/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceUpgrader.java b/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceUpgrader.java
index e56703598..6392d0535 100644
--- a/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceUpgrader.java
+++ b/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceUpgrader.java
@@ -3,27 +3,31 @@ package de.danoeh.antennapod.preferences;
import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
+
import de.danoeh.antennapod.BuildConfig;
+import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.preferences.UserPreferences;
+import de.danoeh.antennapod.core.util.download.AutoUpdateManager;
import de.danoeh.antennapod.core.util.gui.NotificationUtils;
public class PreferenceUpgrader {
- private static final String PREF_CONFIGURED_VERSION = "configuredVersion";
- private static final String PREF_NAME = "PreferenceUpgrader";
+ private static final String PREF_CONFIGURED_VERSION = "version_code";
+ private static final String PREF_NAME = "app_version";
private static SharedPreferences prefs;
public static void checkUpgrades(Context context) {
prefs = PreferenceManager.getDefaultSharedPreferences(context);
SharedPreferences upgraderPrefs = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);
- int oldVersion = upgraderPrefs.getInt(PREF_CONFIGURED_VERSION, 1070200);
+ int oldVersion = upgraderPrefs.getInt(PREF_CONFIGURED_VERSION, -1);
int newVersion = BuildConfig.VERSION_CODE;
if (oldVersion != newVersion) {
NotificationUtils.createChannels(context);
+ AutoUpdateManager.restartUpdateAlarm();
- upgraderPrefs.edit().putInt(PREF_CONFIGURED_VERSION, newVersion).apply();
upgrade(oldVersion);
+ upgraderPrefs.edit().putInt(PREF_CONFIGURED_VERSION, newVersion).apply();
}
}
@@ -41,12 +45,8 @@ public class PreferenceUpgrader {
}
}
if (oldVersion < 1070300) {
- UserPreferences.restartUpdateAlarm();
-
- if (UserPreferences.getMediaPlayer().equals("builtin")) {
- prefs.edit().putString(UserPreferences.PREF_MEDIA_PLAYER,
- UserPreferences.PREF_MEDIA_PLAYER_EXOPLAYER).apply();
- }
+ prefs.edit().putString(UserPreferences.PREF_MEDIA_PLAYER,
+ UserPreferences.PREF_MEDIA_PLAYER_EXOPLAYER).apply();
if (prefs.getBoolean("prefEnableAutoDownloadOnMobile", false)) {
UserPreferences.setAllowMobileAutoDownload(true);
@@ -65,5 +65,13 @@ public class PreferenceUpgrader {
break;
}
}
+ if (oldVersion < 1070400) {
+ int theme = UserPreferences.getTheme();
+ if (theme == R.style.Theme_AntennaPod_Light) {
+ prefs.edit().putString(UserPreferences.PREF_THEME, "system").apply();
+ }
+
+ UserPreferences.setQueueLocked(false);
+ }
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/spa/SPAUtil.java b/app/src/main/java/de/danoeh/antennapod/spa/SPAUtil.java
index 03958508d..5fa6588d9 100644
--- a/app/src/main/java/de/danoeh/antennapod/spa/SPAUtil.java
+++ b/app/src/main/java/de/danoeh/antennapod/spa/SPAUtil.java
@@ -46,7 +46,7 @@ public class SPAUtil {
SharedPreferences.Editor editor = prefs.edit();
editor.putBoolean(PREF_HAS_QUERIED_SP_APPS, true);
- editor.commit();
+ editor.apply();
return true;
} else {
@@ -63,7 +63,7 @@ public class SPAUtil {
SharedPreferences.Editor editor = PreferenceManager
.getDefaultSharedPreferences(c.getApplicationContext()).edit();
editor.putBoolean(PREF_HAS_QUERIED_SP_APPS, false);
- editor.commit();
+ editor.apply();
}
}
}
diff --git a/app/src/main/play/listings/de-DE/full-description.txt b/app/src/main/play/listings/de-DE/full-description.txt
index f312ea559..c49417c3e 100644
--- a/app/src/main/play/listings/de-DE/full-description.txt
+++ b/app/src/main/play/listings/de-DE/full-description.txt
@@ -1,12 +1,12 @@
AntennaPod ist ein Podcast-Manager und -Player, der dir unmittelbar Zugriff auf Millionen von freien und bezahlten Podcasts ermöglicht, angefangen von unabhängigen Podcastern zu großen Rundfunkanstalten oder Hörfunksendern wie BBC, NPR und CNN. Abonniere, importiere und exportiere deine Feeds mühelos mit Hilfe des iTunes-Verzeichnisses, OPML-Dateien oder einfachen RSS-URLs. Reduziere Aufwand, Stromverbrauch und Datenverbrauch durch die Kontrolle der Downloads (bestimmte Uhrzeiten, Intervalle, WiFi-Netze) und des Löschens (basierend auf deinen Favoriten und weiteren Einstellungen).
-Aber am wichtigsten: Downloade, streame oder füge Episoden zur Abspielliste hinzu und genieße sie mit einstellbarer Abspielgeschwindigkeit, Unterstützung von Kapiteln und Schlummerfunktion. Mit Flattr kannst du den Podcastern sogar deine Wertschätzung zeigen.
+Aber am wichtigsten: Downloade, streame oder füge Episoden zur Abspielliste hinzu und genieße sie mit einstellbarer Abspielgeschwindigkeit, Unterstützung von Kapiteln und Schlummerfunktion.
AntennaPod ist, von Podcast-Enthusiasten gemacht, frei im Sinne des Wortes: Open Source, keine Kosten, keine Werbung.
<b>Alle Funktionen:</b><br>
IMPORTIERE, ORGANISIERE UND HÖRE<br>
&#8226; Importiere oder füge Feeds über das iTunes und gPodder.net Verzeichnis, OMPL Dateien und RSS oder Atom Links hinzu.
-&#8226; Bediene die Wiedergabe von überall: Homescreen-Widget, Benachrichtigung und Koopfhörer- und Bluetooth-Bedienelementen<br>
+&#8226; Bediene die Wiedergabe von überall: Startbildschirm-Widget, Benachrichtigung und Kopfhörer- und Bluetooth-Bedienelemente<br>
&#8226; Genieße das Zuhören auf deine Art mit einstellbarer Abspielgeschwindigkeit, der Unterstützung von Kapiteln (MP3, OGG, Podlove) und ausgereifter Schlummerfunktion (durch Schütteln zurücksetzen, Lautstärke verringern und Geschwindigkeit verlangsamen)
&#8226; Greife auf Passwort-geschützte Feeds und Episoden zu<br>
&#8226; Nutze den Vorteil von Paged Feeds (http://www.podlove.org/paged-feeds)
@@ -15,9 +15,8 @@ ORDNE, TEILE & GENIEßE
&#8226; Bleib an den Besten der Besten dran, indem Du Episoden als Favoriten markierst<br>
&#8226; Finde Episoden durch die Liste zuletzt gespielter Episoden oder durch Suche in Titel und Shownotes
&#8226; Teile Episoden and Feeds über soziale Medien, E-Mail, den gPodder.net-Dienst oder als OPML-Export
-&#8226; Unterstütze die Autoren von Inhalten mit Flattr (inklusive automatischem Flattren)
-STEUER DAS SYSTEM<br>
+STEUERE DAS SYSTEM<br>
&#8226; Kontrolliere automatisches Herunterladen: Wähle Feeds aus, schließe mobile Netze aus, suche bestimmte WiFi-Netze aus, setze voraus, dass das Smartphone geladen wird und lege Zeitpunkte oder Intervalle fest<br>
&#8226; Verwalte deinen Speicherplatz durch das Festlegen der Anzahl gespeicherter Episoden, schlaues Löschen und durch Auswahl des Speicherortes<br>
&#8226; Benutze AntennaPod in deiner Sprache (EN, DE, CS, NL, NB, JA, PT, ES, SV, CA, UK, FR, KO, TR, ZH)<br>
diff --git a/app/src/main/play/listings/de-DE/title.txt b/app/src/main/play/listings/de-DE/title.txt
new file mode 100644
index 000000000..31552f353
--- /dev/null
+++ b/app/src/main/play/listings/de-DE/title.txt
@@ -0,0 +1 @@
+AntennaPod \ No newline at end of file
diff --git a/app/src/main/play/listings/en-US/graphics/icon/icon_play.png b/app/src/main/play/listings/en-US/graphics/icon/icon_play.png
new file mode 100644
index 000000000..824346d21
--- /dev/null
+++ b/app/src/main/play/listings/en-US/graphics/icon/icon_play.png
Binary files differ
diff --git a/app/src/main/play/listings/es-ES/full-description.txt b/app/src/main/play/listings/es-ES/full-description.txt
new file mode 100644
index 000000000..8c5cba745
--- /dev/null
+++ b/app/src/main/play/listings/es-ES/full-description.txt
@@ -0,0 +1,42 @@
+AntennaPod es un reproductor y administrador de pódcast que te da acceso instantáneo a millones de pódcast gratuitos y de pago, desde editores independientes a grandes editoriales como la BBC, NPR y CNN. Añade, importa y exporta tus fuentes sin complicaciones usando la base de datos de pódcast de iTunes, archivos OPML o las URL de tipo RSS. Ahorra esfuerzo, energía de la batería y uso de datos móviles con potentes controles de automatización para descargar episodios (especifica horarios, intervalos y redes wifi) y elimina episodios (según tus favoritos y la configuración de retardo).<br>
+Y lo que es más importante: Descarga, escucha en stream, o añade la cola episodios y disfrútalos como quieras con velocidad de reproducción ajustable, soporte para capítulos y temporizador de sueño.
+
+Creado por entusiastas del pódcast, AntennaPod es libre en todos los sentidos: código abierto, gratuito y sin publicidad.
+
+<b>Todas las características:</b><br>
+IMPORTAR, ORGANIZAR Y REPRODUCIR<br>
+&#8226; Añade e importa fuentes mediante los directorios de iTunes y gPodder.net, archivos OPML y enlaces RSS o Atom<br>
+&#8226; Administra la reproducción desde cualquier parte: control en pantalla de inicio, notificación del sistema y controles de auricular y bluetooth<br>
+&#8226; Disfruta escuchando a tu manera con velocidad de reproducción ajustable, soporte de capítulos (MP3, VorbisComment y Podlove), recordatorio del punto de reproducción y el temporizador de sueño avanzado (agita para restablecer, bajar el volumen y disminuir la velocidad de reproducción)<br>
+&#8226; Accede a fuentes y episodios protegidos con contraseña<br>
+&#8226; Aprovecha las fuentes paginadas (www.podlove.org/paged-feeds)
+
+MANTÉN UN SEGUIMIENTO, COMPARTE Y APRECIA
+&#8226; Haz un seguimiento de lo mejor de lo mejor marcando episodios como favoritos<br>
+&#8226; Encuentra ese episodio a través del historial de reproducción o por búsqueda (títulos y notas de episodios)<br>
+&#8226; Comparte episodios y fuentes a través de las avanzadas redes sociales y opciones de correo electrónico, los servicios de gPodder.net y la exportación OPML<br>
+
+CONTROLA EL SISTEMA<br>
+&#8226; Controla las descargas automáticas: elige las fuentes, excluye las redes móviles, selecciona redes wifi específicas, o solo cuando el teléfono se esté cargando y establece horarios o intervalos<br>
+&#8226; Administra el almacenamiento configurando la cantidad de episodios almacenados, el borrado inteligente (según tus favoritos y el estado de reproducción) y selecciona tu ubicación preferida<br>
+&#8226; Usa AntennaPod en tu idioma (EN, DE, CS, NL, NB, JA, PT, ES, SV, CA, UK, FR, KO, TR, ZH)<br>
+&#8226; Adáptate a tu entorno usando el tema claro u oscuro<br>
+&#8226; Haz una copia de seguridad de tus suscripciones con la integración de gPodder.net y la exportación OPML
+
+<b>¡Únete a la comunidad AntennaPod!</b><br>
+AntennaPod está en continuo desarrollo por voluntarios. ¡Tú también puedes contribuir, con tu código o con tus comentarios!
+
+GitHub es el sitio que debes visitar para solicitar características nuevas, reportar fallos y contribuir con código<br>
+https://www.github.com/AntennaPod/AntennaPod
+
+Nuestro Grupo de Google es el sitio para compartir tus ideas, momentos favoritos de tus pódcast y tu gratitud a los voluntarios:<br>
+https://groups.google.com/forum/#!forum/antennapod
+
+¿Tienes una pregunta o quieres darnos tu opinión?
+https://twitter.com/@AntennaPod
+
+Transifex es el sitio para ayudar con las traducciones:<br>
+https://www.transifex.com/antennapod/antennapod
+
+Echa un vistazo a nuestro programa de pruebas Beta y ser el primero en usar las nuevas características:<br>
+https://www.github.com/AntennaPod/AntennaPod/wiki/Help-test-AntennaPod \ No newline at end of file
diff --git a/app/src/main/play/listings/es-ES/short-description.txt b/app/src/main/play/listings/es-ES/short-description.txt
new file mode 100644
index 000000000..cc94d9c22
--- /dev/null
+++ b/app/src/main/play/listings/es-ES/short-description.txt
@@ -0,0 +1 @@
+Reproductor y gestor de pódcast fácil de usar, flexible y de código abierto \ No newline at end of file
diff --git a/app/src/main/play/listings/es-ES/title.txt b/app/src/main/play/listings/es-ES/title.txt
new file mode 100644
index 000000000..31552f353
--- /dev/null
+++ b/app/src/main/play/listings/es-ES/title.txt
@@ -0,0 +1 @@
+AntennaPod \ No newline at end of file
diff --git a/app/src/main/play/listings/fr-FR/full-description.txt b/app/src/main/play/listings/fr-FR/full-description.txt
index 372e1da60..5c69f3892 100644
--- a/app/src/main/play/listings/fr-FR/full-description.txt
+++ b/app/src/main/play/listings/fr-FR/full-description.txt
@@ -1,5 +1,5 @@
-AntennaPod est un lecteur et gestionnaire de podcast permettant l'accès à des millions de podcast gratuits ou payants produit aussi bien par des podcasters indépendants que de gros éditeurs comme la BBC, NPR ou CNN. Ajoutez, importez et exportez leurs flux facilement à partir d'ITunes, de fichiers OPML ou simplement à partir de liens RSS. Gagnez du temps, préservez votre batterie et consommation internet grâce à une automatisation puissante des téléchargements (date, fréquence, choix du réseau WiFi, etc...) et des suppressions d’épisodes écoutés (selon vos critères)<br>
-Avant tout : téléchargez, streamez ou ajoutez à la liste de lecture vos épisodes et écoutez les comme vous voulez grâce au réglage de vitesse de lecture, au support des chapitres et au minuteur d'arrêt. Vous pouvez même montrer votre appréciation aux créateurs de contenu avec notre intégration de Flattr.
+AntennaPod est un lecteur de podcast permettant l'accès à des millions de podcast gratuits ou payants produit aussi bien par des podcasters indépendants que des éditeurs professionnels comme la BBC, NPR ou CNN. Ajoutez, importez et exportez leurs flux facilement à partir d'ITunes, de fichiers OPML ou simplement à partir de liens RSS. Economisez votre temps, votre batterie et votre consommation de données grâce à l'automatisation des téléchargements (date, fréquence, choix du réseau WiFi, etc...) et de la suppression des épisodes (selon vos critères)<br>
+Avant tout : téléchargez, streamez ou ajoutez à la liste de lecture vos épisodes et écoutez les comme vous voulez grâce au réglage de vitesse de lecture, au support des chapitres et au minuteur d'arrêt.
Conçu par des fans de podcast, AntennaPod est gratuit dans tous les sens du terme : open source, gratuit et sans publicité.
@@ -15,7 +15,6 @@ SUIVEZ, PARTAGEZ & PROFITEZ<br>
&#8226; Marquer les meilleurs épisodes en tant que favoris<br>
&#8226; Retrouvez un épisode à partir de l'historique de lecture ou en recherchant parmi les titres et commentaires des épisodes précédents<br>
&#8226; Partagez vos épisodes et flux sur les réseaux sociaux, par email, sur gPodder.net ou en les exportant au format OPML<br>
-&#8226; Soutenez les créateurs de contenu avec l'intégration à Flattr et la possibilité de flatter automatiquement
CONTRÔLER LE SYSTÈME<br>
&#8226; Prenez le contrôle en automatisant vos téléchargements : choix des flux, restriction de la connexion mobile, sélection du réseau WIFI à utiliser, uniquement durant la recharge et spécifiez la fréquence de mise à jour vous-même<br>
diff --git a/app/src/main/play/listings/fr-FR/short-description.txt b/app/src/main/play/listings/fr-FR/short-description.txt
index 61c3c7e20..7143385e1 100644
--- a/app/src/main/play/listings/fr-FR/short-description.txt
+++ b/app/src/main/play/listings/fr-FR/short-description.txt
@@ -1 +1 @@
-Un lecteur et gestionnaire de podcast facile à utiliser et flexible \ No newline at end of file
+Un lecteur de podcast facile à utiliser et flexible \ No newline at end of file
diff --git a/app/src/main/play/listings/fr-FR/title.txt b/app/src/main/play/listings/fr-FR/title.txt
new file mode 100644
index 000000000..31552f353
--- /dev/null
+++ b/app/src/main/play/listings/fr-FR/title.txt
@@ -0,0 +1 @@
+AntennaPod \ No newline at end of file
diff --git a/app/src/main/play/listings/gl-ES/full-description.txt b/app/src/main/play/listings/gl-ES/full-description.txt
index c275c1fc2..2acaf65f7 100644
--- a/app/src/main/play/listings/gl-ES/full-description.txt
+++ b/app/src/main/play/listings/gl-ES/full-description.txt
@@ -1,5 +1,5 @@
AntenaPod é un xestor de podcast e reprodutor que lle da acceso a millóns de podcast tanto gratuítos como de pagamento, desde podcasters independentes a grandes productores como BBC, NPR e CNN. Engada, importe e exporte as súas fontes de xeito doado utilizando a base de datos de iTunes, ficheiros OPML ou URLs RSS. Aforre traballo, enerxía da batería e datos móbiles co sistema automatizado de control das descargas de episodios (indicando horario, intervalos e redes WiFi) e borrando episodios (baseado nos seus favoritos e axustes de retardo).<br>
-Mais o máis importante: Descargue, envíe ou poña na cola os episodios e disfrúteos do xeito en que máis lle conveña, axustando a velocidade de reprodución, xestión de capítulos e apagado automático. Poderá tamén mostrarlle aos creadores canto lle gustan os seus contidos gracias a integración con Flattr.
+O máis importante: Descarga, reproduce ou pon en cola os episodios e desfrútaos do xeito en que máis che conveña axustando a velocidade de reprodución, o soporte de capítulos e o apagado programable.
Escrito por namorados dos podcast, AntennaPod é gratuíto e libre: open source, sen custos, sen publicidade.
@@ -15,7 +15,6 @@ SIGA, COMPARTA E VALORE <br>
&#8226; Garde o melloriño de cada casa marcando episodios como favoritos<br>
&#8226; Atope ese episodio especial no historial de reprodución ou buscando (títulos e notas do episodio)<br>
&#8226; Comparta episodios e fontes a través das opcións de redes sociais e correro electrónico, os servizos de gPodder.net e exportando a OPML<br>
-&#8226; Axude aos creadores de contido coa integración con Flattr incluíndo o flattring automático
CONTROL DO SISTEMA <br>
&#8226; Tome control sobre as descargas automáticas: escolla fontes, exclúa redes móbiles, redes WIFI concretas, requerir que o móbil esté a cargar e horarios e intervalos<br>
diff --git a/app/src/main/play/listings/gl-ES/title.txt b/app/src/main/play/listings/gl-ES/title.txt
new file mode 100644
index 000000000..31552f353
--- /dev/null
+++ b/app/src/main/play/listings/gl-ES/title.txt
@@ -0,0 +1 @@
+AntennaPod \ No newline at end of file
diff --git a/app/src/main/play/listings/it-IT/title.txt b/app/src/main/play/listings/it-IT/title.txt
new file mode 100644
index 000000000..31552f353
--- /dev/null
+++ b/app/src/main/play/listings/it-IT/title.txt
@@ -0,0 +1 @@
+AntennaPod \ No newline at end of file
diff --git a/app/src/main/play/listings/iw-IL/full-description.txt b/app/src/main/play/listings/iw-IL/full-description.txt
new file mode 100644
index 000000000..58895c50e
--- /dev/null
+++ b/app/src/main/play/listings/iw-IL/full-description.txt
@@ -0,0 +1,42 @@
+היישומון ×נטנה־פּוֹד ×”×•× × ×’×Ÿ ומנהל פודק××¡×˜×™× ×©×ž×¢× ×™×§ לך גישה ישירה ×œ×ž×™×œ×™×•× ×™× ×©×œ פודק××¡×˜×™× ×‘×—×™× × ×•×‘×ª×©×œ×•×, החל ממגישי פודק××¡×˜×™× ×¢×¦×ž××™×™× ×•×¢×“ ×œ×ž×¤×™×¦×™× ×’×“×•×œ×™× ×›×’×•×Ÿ BBC,†NPR ו־CNN. ניתן להוסיף, ×œ×™×™×‘× ×•×œ×™×™×¦× ×ת ההזנות ×©×œ×”× ×‘×§×œ×•×ª יחסית ב×מצעות מסד נתוני הפודק××¡×˜×™× ×©×œ iTunes, קובצי OPML ×ו כתובות של RSS. מ×פשר לך לחסוך במ×מץ, סוללה ותקשורת × ×ª×•× ×™× ×¢× ×¤×§×“×™ ×וטומציה להורדה של ×¤×¨×§×™× (לפי זמני×, הפרשי זמן ורשתות ×לחוטיות) ומחיקה של ×¤×¨×§×™× (על בסיס הגדרות ×”×ž×•×¢×“×¤×™× ×•×”×”×©×”×™×” שלך).<br>
+×בל ×”×›×™ חשוב: ניתן להוריד, ×œ×”×–×¨×™× ×ו לסדר רשימות של ×¤×¨×§×™× ×•×œ×™×”× ×•×ª ×ž×”× ×‘×›×œ דרך שמת×ימה לך ×¢× ×ž×”×™×¨×•×™×•×ª × ×’×™× ×” משתנות, תמיכה ×‘×ž×§×˜×¢×™× ×•×ž×ª×–×ž×Ÿ שינה
+
+מיוצרת על ידי חובבי פודק×סטי×, ×נטנהפוד ×”×™× ×” תוכנה חינמית בכל מובן המילה: קוד פתוח, ×œ×œ× ×¢×œ×•×ª ×•×œ×œ× ×¤×¨×¡×•×ž×•×ª.
+
+<b>כל התכונות:</b><br>
+ייבו×, ×רגון ונגינה<br>
+&#8226; ניתן להוסיף ×•×œ×™×™×‘× ×”×–× ×•×ª דרך הספריות של iTunes ושל gPodder.net, קובצי OPML וקישורי RSS ×ו Atom<br>
+&#8226; ניתן לנהל ×ת ×”× ×’×™× ×” מכל מקו×: וידג׳ט על מסך הבית, התרעות המערכת ופקדי שקע ×וזניות ובלוטות׳<br>
+&#8226; פשוט ליהנות בדרך שלך ×¢× ×ž×”×™×¨×•×ª × ×’×™× ×” משתנה, תמיכה ×‘×ž×§×˜×¢×™× (MP3, VorbisComment ו־Podlove), שמירת ×ž×™×§×•× ×”× ×’×™× ×” ומתזמן שינה ×ž×ª×§×“× (ניתן לנער כדי ל×פס, להנמיך ×ת עצמך השמע ולה×ט ×ת מהירות ×”× ×’×™× ×”)<br>
+&#8226; גישה להזנות ×•×œ×¤×¨×§×™× ×”×ž×•×’× ×™× ×‘×¡×¡×ž×”<br>
+&#8226; ניתן להשתמש בעימודי ההזנות שלנו (www.podlove.org/paged-feeds)
+
+מעקב, שיתוף והבעת הערכה<br>
+&#8226; מעקב ×חר ×”×˜×•×‘×™× ×©×‘×˜×•×‘×™× ×¢×œ ידי סימון ×¤×¨×§×™× ×›×ž×•×¢×“×¤×™×<br>
+&#8226; ניתן ל×תר פרק ×חד דרך היסטוריית ×”× ×’×™× ×” ×ו על ידי חיפוש (כותרות והערות פרק)<br>
+&#8226; ניתן לשתף ×¤×¨×§×™× ×•×”×–× ×•×ª דרך ×פשרויות מתקדמות ברשתות חברתיות ודו×״ל, שירותי gPodder.net ודרך ×™×™×¦×•× OPML<br>
+
+שליטה במערכת<br>
+&#8226; ניתן לשלוט על הורדה ×וטומטית: לבחור הזנות, להחריג רשתות סלולריות, לבחור רשתות ×לחוטיות מסוימות, לדרוש מהטלפון להיות בטעינה ולהגדיר ×ž×•×¢×“×™× ×ו מרווחי זמן<br>
+&#8226; ניתן לנהל ×ת ×”×חסון על ידי הגדרת כמות ×”×¤×¨×§×™× ×©× ×©×ž×¨×™× ×‘×ž×˜×ž×•×Ÿ, מחיקה חכמה (בהתבסס על ×”×ž×•×¢×“×¤×™× ×•×ž×¦×‘ ×”× ×’×™× ×” שלך) ובחירת ×”×ž×™×§×•× ×”×ž×•×¢×“×£ עליך<br>
+&#8226; ניתן להשתמש ב×נטנה־פּוֹד בשפה שלך (HE, EN, DE, CS, NL, NB, JA, PT, ES, SV, CA, UK, FR, KO, TR, ZH)<br>
+&#8226; הת×מה לסביבה שלך ב×מצעות ערכות עיצוב בהירה וכהה<br>
+&#8226; גיבוי התמיכה שלך ×¢× ×©×™×œ×•×‘ מול gPodder.net ×•×™×™×¦×•× ×©×œ OPML
+
+<b>×ž×–×ž×™× ×™× ×ותך להצטרף לקהילת ×נטנה־פּוֹד!</b><br>
+×ת תהליכי הפיתוח ×”×¤×¢×™×œ×™× ×©×œ ×נטנה־פּוֹד ×ž×•×‘×™×œ×™× ×ž×ª× ×“×‘×™×. ניתן ×œ×ª×¨×•× ×’× ×›×Ÿ, ×¢× ×§×•×“ ×ו ×¢× ×”×¢×¨×”!
+
+GitHub ×”×•× ×”×ž×§×•× ×‘×• ×נו ×ž×¨×›×–×™× ×ת בקשות התכונות, דיווחי התקלות ותרומות הקוד:<br>
+https://www.github.com/AntennaPod/AntennaPod
+
+הקבוצה שלנו ב־Google ×”×™× ×”×ž×§×•× ×œ×©×ª×£ ×ת הרעיונות שלך, רגעי הפודק×סט ×”×ž×•×¢×“×¤×™× ×¢×œ×™×š ו×ת הערכתך לכל המתנדבי×:<br>
+https://groups.google.com/forum/#!forum/antennapod
+
+יש לך ש×לה ×ו שמעניין ×ותך לתת לנו משוב?
+https://twitter.com/@AntennaPod
+
+Transifex ×”×•× ×”×ž×§×•× ×œ×¡×™×™×¢ בתרגומי×:<br>
+https://www.transifex.com/antennapod/antennapod
+
+×ž×–×ž×™× ×™× ×ותך לחקור ×ת תכנית הבדיקות שלנו כדי לקבל ×ת התכונות העדכניות ביותר לפני כול×:<br>
+https://www.github.com/AntennaPod/AntennaPod/wiki/Help-test-AntennaPod \ No newline at end of file
diff --git a/app/src/main/play/listings/iw-IL/short-description.txt b/app/src/main/play/listings/iw-IL/short-description.txt
new file mode 100644
index 000000000..34ffefafc
--- /dev/null
+++ b/app/src/main/play/listings/iw-IL/short-description.txt
@@ -0,0 +1 @@
+מנהל פודק××¡×˜×™× ×•× ×’×Ÿ קל לשימוש, גמיש ובקוד פתוח \ No newline at end of file
diff --git a/app/src/main/play/listings/iw-IL/title.txt b/app/src/main/play/listings/iw-IL/title.txt
new file mode 100644
index 000000000..31552f353
--- /dev/null
+++ b/app/src/main/play/listings/iw-IL/title.txt
@@ -0,0 +1 @@
+AntennaPod \ No newline at end of file
diff --git a/app/src/main/play/listings/ja-JP/full-description.txt b/app/src/main/play/listings/ja-JP/full-description.txt
index b85f6787e..4dc50f57e 100644
--- a/app/src/main/play/listings/ja-JP/full-description.txt
+++ b/app/src/main/play/listings/ja-JP/full-description.txt
@@ -1,5 +1,5 @@
AntennaPodã¯ã€ç‹¬è‡ªã®ãƒãƒƒãƒ‰ã‚­ãƒ£ã‚¹ã‚¿ãƒ¼ã‹ã‚‰ã€BBCã€NPRã€CNNãªã©ã®å¤§è¦æ¨¡ãªæ”¾é€ã¾ã§ã€æ•°ç™¾ä¸‡ã®ç„¡æ–™ã‚„有料ãƒãƒƒãƒ‰ã‚­ãƒ£ã‚¹ãƒˆã«çž¬æ™‚ã«ã‚¢ã‚¯ã‚»ã‚¹ã™ã‚‹ã“ã¨ãŒã§ãã‚‹ã€ãƒãƒƒãƒ‰ã‚­ãƒ£ã‚¹ãƒˆãƒžãƒãƒ¼ã‚¸ãƒ£ãƒ¼ãŠã‚ˆã³ãƒ—レーヤーã§ã™ã€‚フィードã¯æ‰‹é–“ã®ã‹ã‹ã‚‰ãªã„iTunesã®Podcastã®ãƒ‡ãƒ¼ã‚¿ãƒ™ãƒ¼ã‚¹ã€OPMLファイルや簡å˜ãªRSSã®URLを使用ã—ã¦è¿½åŠ ã€ã‚¤ãƒ³ãƒãƒ¼ãƒˆã€ã‚¨ã‚¯ã‚¹ãƒãƒ¼ãƒˆã—ã¾ã™ã€‚エピソードã®ãƒ€ã‚¦ãƒ³ãƒ­ãƒ¼ãƒ‰ (時間ã€é–“éš”ãŠã‚ˆã³WiFiãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã‚’指定) ã¨ã‚¨ãƒ”ソードã®å‰Šé™¤ (ãŠæ°—ã«å…¥ã‚Šã¨é…延設定ã«åŸºã¥ã„ã¦) ã‚’ã™ã‚‹ãŸã‚ã«å¼·åŠ›ãªè‡ªå‹•ã‚³ãƒ³ãƒˆãƒ­ãƒ¼ãƒ«ã§ã€æ‰‹é–“ã€ãƒãƒƒãƒ†ãƒªæ¶ˆè²»ã€ãƒ¢ãƒã‚¤ãƒ«ãƒ‡ãƒ¼ã‚¿ä½¿ç”¨é‡ã‚’節約ã—ã¾ã™ã€‚<br>
-ã—ã‹ã—最もé‡è¦ãªã“ã¨: エピソードをダウンロードã€ã‚¹ãƒˆãƒªãƒ¼ãƒ å†ç”Ÿã€ã¾ãŸã¯ã‚­ãƒ¥ãƒ¼ã«å…¥ã‚Œã¦ã€ãã—ã¦å†ç”Ÿé€Ÿåº¦ã®èª¿æ•´ã€ãƒãƒ£ãƒ—ターã®ã‚µãƒãƒ¼ãƒˆã€ã‚¹ãƒªãƒ¼ãƒ—タイマーã§å¥½ããªã‚ˆã†ã«æ¥½ã—ã‚“ã§ãã ã•ã„。Flattrçµ±åˆã§ã‚³ãƒ³ãƒ†ãƒ³ãƒ„作æˆè€…ã«ã‚ãªãŸã®æ„›ã‚’示ã™ã“ã¨ãŒã§ãã¾ã™ã€‚
+ã—ã‹ã—最もé‡è¦ãªã“ã¨: エピソードをダウンロードã€ã‚¹ãƒˆãƒªãƒ¼ãƒ å†ç”Ÿã€ã¾ãŸã¯ã‚­ãƒ¥ãƒ¼ã«å…¥ã‚Œã¦ã€ãã—ã¦å†ç”Ÿé€Ÿåº¦ã®èª¿æ•´ã€ãƒãƒ£ãƒ—ターã®ã‚µãƒãƒ¼ãƒˆã€ã‚¹ãƒªãƒ¼ãƒ—タイマーã§å¥½ããªã‚ˆã†ã«æ¥½ã—ã‚“ã§ãã ã•ã„。
ãƒãƒƒãƒ‰ã‚­ãƒ£ã‚¹ãƒˆæ„›å¥½å®¶ãŒä½œæˆã—㟠AntennaPod ã¯ã™ã¹ã¦ã®æ„味ã§ãƒ•ãƒªãƒ¼è‡ªç”±ã§ã™: オープンソースã€ã‚³ã‚¹ãƒˆä¸è¦ã€åºƒå‘Šã¯ã‚ã‚Šã¾ã›ã‚“。
@@ -15,7 +15,6 @@ AntennaPodã¯ã€ç‹¬è‡ªã®ãƒãƒƒãƒ‰ã‚­ãƒ£ã‚¹ã‚¿ãƒ¼ã‹ã‚‰ã€BBCã€NPRã€CNNãªã©ã
&#8226; エピソードをãŠæ°—ã«å…¥ã‚Šã¨ã—ã¦ãƒžãƒ¼ã‚¯ã—ã¦ã€ä¸€ç•ªã®ä¸­ã®ä¸€ç•ªã‚’ä¿å­˜ã—ã¦ãã ã•ã„<br>
&#8226; å†ç”Ÿå±¥æ­´ã‹ã‚‰ã€ã¾ãŸã¯æ¤œç´¢ (タイトルã¨ã‚·ãƒ§ãƒ¼ãƒŽãƒ¼ãƒˆ) ã—ã¦ç›®çš„ã®ã‚¨ãƒ”ソードを見ã¤ã‘ã¦ãã ã•ã„<br>
&#8226; 高度ãªã‚½ãƒ¼ã‚·ãƒ£ãƒ«ãƒ¡ãƒ‡ã‚£ã‚¢ã¨ãƒ¡ãƒ¼ãƒ«ã‚ªãƒ—ションã€gPodder.netサービスã€OPMLエクスãƒãƒ¼ãƒˆã‹ã‚‰ã‚¨ãƒ”ソードやフィードを共有ã—ã¦ãã ã•ã„<br>
-&#8226; 自動Flattrã‚’å«ã‚€Flattrã®çµ±åˆã§ã‚³ãƒ³ãƒ†ãƒ³ãƒ„クリエイターをサãƒãƒ¼ãƒˆã—ã¾ã™
システムã®ã‚³ãƒ³ãƒˆãƒ­ãƒ¼ãƒ«
&#8226; 自動ダウンロードã®åˆ¶å¾¡: フィードをé¸æŠžã€ãƒ¢ãƒã‚¤ãƒ«ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã‚’除外ã€ç‰¹å®šã®WiFiãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã‚’é¸æŠžã€é›»è©±ã‚’å……é›»ã™ã‚‹å¿…è¦ã€æ™‚間や間隔を設定<br>
diff --git a/app/src/main/play/listings/ja-JP/title.txt b/app/src/main/play/listings/ja-JP/title.txt
new file mode 100644
index 000000000..31552f353
--- /dev/null
+++ b/app/src/main/play/listings/ja-JP/title.txt
@@ -0,0 +1 @@
+AntennaPod \ No newline at end of file
diff --git a/app/src/main/play/listings/nl-NL/full-description.txt b/app/src/main/play/listings/nl-NL/full-description.txt
index 6c6ef1e8a..518745f17 100644
--- a/app/src/main/play/listings/nl-NL/full-description.txt
+++ b/app/src/main/play/listings/nl-NL/full-description.txt
@@ -1,5 +1,5 @@
Met AntennaPod speel en beheer je al je podcasts en krijg je directe toegang tot duizenden gratis en betaalde podcasts - van onafhankelijke makers tot grote merken zoals BBC, CNN en NPO. Via de iTunes-databank, OPML-bestanden en simpele RSS-linkjes voeg je deze podcasts gemakkelijk toe. Dankzij simpele maar slimme automatische controle van het downloaden en verwijderen van afleveringen bespaar je de accu, hoef je je favoriete podcast niet meer handmatig te volgen en verbruik je geen onnodige mobiele gegevens.<br>
-Maar belangrijker: Met een handige wachtrij, aanpasbare afspeelsnelheden, ondersteuning van hoofdstukken en een slaap timer luister je naar podcasts op de manier die jij prettig vindt. Je kunt zelfs je liefde voor de makers uiten met onze Flattr-integratie.
+Maar belangrijker: download, stream of voeg afleveringen toe aan de wachtrij en geniet ervan! Je hebt beschikking over afspeelsnelheden, hoofdstukondersteuning en een slaaptimer.
Gemaakt door podcast-enthousiastelingen, AntennaPod is vrij in de breedste zin van het woord: vrij van advertenties, open source en gratis.
@@ -15,7 +15,6 @@ DEEL & WAARDEER<br>
&#8226; Hou het beste van het beste bij door afleveringen als favoriet te markeren<br>
&#8226; Vindt die ene aflevering terug in de afspeelgeschiedenis of door te zoeken (in titels, shownotes en makers)<br>
&#8226; Deel podcasts en afleveringen via uitgebreide opties voor sociale media, WhatsApp, email en gPodder.net<br>
-&#8226; Steun makers met Flattr-integratie, inclusief auto-Flattr
HOUD DE CONTROLE
&#8226; Beheer automatische downloads: kies je podcasts, sluit mobiele netwerken uit, selecteer specifieke WiFi-verbindingen, eis dat de telefoon wordt opgeladen and bepaal tijden of intervals<br>
diff --git a/app/src/main/play/listings/nl-NL/title.txt b/app/src/main/play/listings/nl-NL/title.txt
new file mode 100644
index 000000000..31552f353
--- /dev/null
+++ b/app/src/main/play/listings/nl-NL/title.txt
@@ -0,0 +1 @@
+AntennaPod \ No newline at end of file
diff --git a/app/src/main/play/listings/sv-SE/full-description.txt b/app/src/main/play/listings/sv-SE/full-description.txt
new file mode 100644
index 000000000..d46fdafdc
--- /dev/null
+++ b/app/src/main/play/listings/sv-SE/full-description.txt
@@ -0,0 +1,42 @@
+AntennaPod är en podcasthanterare och spelare som ger dig omedelbar tillgång till miljoner av gratis och betalda podcasts, från oberoende podcastare till stora publiceringshus så som BBC, NPR och CNN. Lägg till, importera och exportera enkelt deras flöden med iTunes podcastdatabas, OPML-filer eller vanliga RSS URL:er. Spara möda, batterikraft och mobildata med kraftfulla automatiseringskontroller för nedladdning (välj tider, intervall och WiFi-nätverk) och borttagning av episoder (baserat på dina favoriter och fördröjningsinställningar).
+Men viktigast av allt: Ladda ner, strömma eller köa episoder och avnjut dem på det sätt du gillar med justerbar uppspelningshastighet, kapitelstöd och insomningstimer.
+
+Gjord av podcastenthusiaster, AntennaPod är fri i alla ordets bemärkelser: öppen källkod, inga kostnader, ingen reklam.
+
+<b>Alla funktioner:</b><br>
+IMPORTERA, ORGANISERA OCH SPELA<br>
+&#8226; Lägg till och importera flöden via iTunes och gPodder.net, OPML filer och RSS eller Atom länkar<br>
+&#8226; Hantera uppspelningen från vartsomhelst: hemskärmswidget, aviseringsfältet och hörlurs/bluetoth-kontroller<br>
+&#8226; Njut av att lyssna på ditt sätt med justerbar uppspelningshastighet, kapitelstöd (MP4, VorbisComment och Podlove), ihågkommen uppspelningsposition och en avancerad sömntimer (skaka för återställaning, sänk volymen och sänk hastigheten)<br>
+&#8226; Kom åt lösenordsskyddade flöden och episoder<br>
+&#8226; Dra nytta av siduppdelade flöden (www.podlove.ord/paged-feeds)
+
+SPÃ…RA, DELA & UPPSKATTA<br>
+&#8226; Håll ordning på de bästa av de bästa med favoritmarkering av episoder<br>
+&#8226; Hitta just den där episoden i uppspelningshistoriken eller genom sökning (titel och shownotes)<br>
+&#8226; Dela episoder och flöden med avancerade vald för social media och och email, tjänsten gPodder.net och via OPML export<br>
+
+KONTROLLERA SYSTEMET<br>
+&#8226; Ta kontroll över automatisk nedladdning: välj flöden, exkludera mobilnätverk, välj specifika WiFi nätverk, kräv att telefonen är inkopplad för laddning och sätt tider eller intervall för körning<br>
+&#8226; Hantera lagringsutrymme genom att välja antalet cachade episoder, smart borttagning (baserat på dina favoriter och uppspelningsstatus) och välj den lagringsplats du föredrar<br>
+&#8226; Använd AntennaPod på ditt språk (EN, DE, CS, NL, NB, JA, PT, ES, SV, CA, UK, FR, KO, TR, ZH)<br>
+&#8226; Anpassa till din omgivning med det ljusa och mörka temat<br>
+&#8226; Ta backup av dina prenumerationer med integreringen av gPodder.net och OPML exportering
+
+<b>GÃ¥ med i AntennaPods gemenskap!</b><br>
+AntennaPod är under aktiv utveckling av volontärer. Du kan också bidra, med kod eller kommentarer!
+
+GitHub är platsen att gå till för att be om funktioner, skapa buggrapporter eller bidra med kod:<br>
+https://www.github.com/AntennaPod/AntennaPod
+
+Vår Google Group är platsen för att dela idéer, dina favoritögonblick med podcasting och din uppskattning till volontärerna:<br>
+https://groups.google.com/forum/#!forum/antennapod
+
+Har du frågor eller vill ge feedback?
+https://twitter.com/@AntennaPod
+
+Transifex är platsen att gå till för att hjälpa till med översättningen:<br>
+https://www.transifex.com/antennapod/antennapod
+
+Kolla in vårat Beta Testing program för att få de senaste funktionerna först:<br>
+https://www.github.com/AntennaPod/AntennaPod/wiki/Help-test-AntennaPod \ No newline at end of file
diff --git a/app/src/main/play/listings/sv-SE/short-description.txt b/app/src/main/play/listings/sv-SE/short-description.txt
new file mode 100644
index 000000000..2273fc5e4
--- /dev/null
+++ b/app/src/main/play/listings/sv-SE/short-description.txt
@@ -0,0 +1 @@
+Användarvänlig och flexibel podcasthanterare och spelare med öppen källkod \ No newline at end of file
diff --git a/app/src/main/play/listings/sv-SE/title.txt b/app/src/main/play/listings/sv-SE/title.txt
new file mode 100644
index 000000000..31552f353
--- /dev/null
+++ b/app/src/main/play/listings/sv-SE/title.txt
@@ -0,0 +1 @@
+AntennaPod \ No newline at end of file
diff --git a/app/src/main/play/listings/zh-CN/full-description.txt b/app/src/main/play/listings/zh-CN/full-description.txt
new file mode 100644
index 000000000..a39b43eee
--- /dev/null
+++ b/app/src/main/play/listings/zh-CN/full-description.txt
@@ -0,0 +1,42 @@
+AntennaPod 是一款播客管ç†å™¨å’Œæ’­æ”¾å™¨ï¼Œå¯è®©æ‚¨å³æ—¶æ”¶å¬æ•°ç™¾ä¸‡å…费和付费的播客,从独立播客到大型出版商如BBC,NPRå’ŒCNN;使用 iTunes 播客数æ®åº“,OPML文件或简å•çš„RSS URLè½»æ¾æ·»åŠ ï¼Œå¯¼å…¥å’Œå¯¼å‡º feeds ;通过强大的自动化控制功能节çœå·¥ä½œé‡ï¼Œç”µæ± ç”µé‡å’Œç§»åŠ¨æ•°æ®ä½¿ç”¨æƒ…况,以便下载剧集(指定时间,间隔和 WiFi 网络)和删除剧集(根æ®æ‚¨çš„收è—和延迟设置)。
+但最é‡è¦çš„是:下载,串æµæˆ–安排节目并用å¯è°ƒèŠ‚回放速度,章节支æŒå’Œç¡çœ å®šæ—¶å™¨ä»¥æ‚¨å–œæ¬¢çš„æ–¹å¼äº«å—它们。
+
+AntennaPod由播客爱好者开å‘,在任æ„æ–¹é¢ä¸Šéƒ½æ˜¯è‡ªç”±çš„:开æºï¼Œå…费,无广告。
+
+<b>所有功能:</b><br>
+导入ã€æ•´ç†ä¸Žæ’­æ”¾<br>
+&#8226; 通过 iTunesã€gPodder.netã€OPML 文件ã€RSS 或 Atom 链接添加和导入订阅
+&#8226;从任æ„地点管ç†å›žæ”¾ï¼šä¸»å±widget,系统通知,耳塞和è“牙控制装置
+&#8226;借助å¯è°ƒèŠ‚回放速度,章节支æŒï¼ˆMp3ã€Vorbis注释和Podlove),记忆的回放ä½ç½®å’Œä¸€ä¸ªé«˜çº§çš„ç¡çœ å®šæ—¶å™¨ (摇动设备æ¥é‡ç½®ã€è°ƒä½ŽéŸ³é‡å¹¶å‡æ…¢å›žæ”¾ï¼‰ä»¥æ‚¨è‡ªå·±çš„æ–¹å¼äº«å—收å¬æ’­å®¢èŠ‚ç›®
+#8226; 访问å—密ç ä¿æŠ¤çš„ feeds 和剧集
+&#8226; 充分利用分页 feeds (www.podlove.org/paged-feeds)
+
+ä¿æŒæ›´æ–°ï¼Œåˆ†äº«&欣èµ
+&#8226; 通过将剧集标记为收è—æ¥è¿½è¸ªæœ€ä½³çš„剧集
+&#8226; 通过回放历å²æˆ–æœç´¢ï¼ˆæ ‡é¢˜å’ŒèŠ‚目笔记)找到您需è¦çš„那期节目
+&#8226; 通过高级社交媒体和电邮选项,gPodder.netæœåŠ¡å’Œå¯¼å‡ºOPML文件æ¥åˆ†äº«èŠ‚目和æº
+
+一切尽在掌控<br>
+&#8226; 控制自动下载:选择æºï¼ŒæŽ’除移动网络,选择特定无线网络,è¦æ±‚手机充电并设定次数或间隔
+&#8226; 通过设定缓存节目的数é‡ç®¡ç†å­˜å‚¨ï¼Œæ™ºèƒ½åˆ é™¤ï¼ˆåŸºäºŽæ‚¨çš„喜好和播放状æ€)并选择您å好的存储ä½ç½®
+&#8226; 使用对应您的语言的 AntennaPod (EN, DE, CS, NL, NB, JA, PT, ES, SV, CA, UK, FR, KO, TR, ZH)<br>
+&#8226; 使用浅色和深色主题以适应您的环境
+&#8226; 使用 gPodder.net 集æˆåŠŸèƒ½å’Œ OPML 导出以备份您的订阅
+
+<b>加入 AntennaPod 社区ï¼</b><br>
+志愿者正在积æžå¼€å‘ AntennaPod 。您也å¯ä»¥é€šè¿‡ä»£ç æˆ–评论åšå‡ºè´¡çŒ®ï¼
+
+GitHub 是申请增加功能,报告错误和贡献代ç çš„地方:
+https://www.github.com/AntennaPod/AntennaPod
+
+对于所有志愿者æ¥è¯´ï¼Œæˆ‘们的谷歌社群是分享你的创æ„,收è—的播客瞬间和表达感激的地方:
+https://groups.google.com/forum/#!forum/antennapod
+
+抱有疑问或者想è¦å‘我们æä¾›å馈?
+https://twitter.com/@AntennaPod
+
+Transifex 是与翻译者帮助项目的网站:
+https://www.transifex.com/antennapod/antennapod
+
+查看我们的测试程åºä»¥èŽ·å¾—最新的功能列表:
+https://www.github.com/AntennaPod/AntennaPod/wiki/Help-test-AntennaPod \ No newline at end of file
diff --git a/app/src/main/play/listings/zh-CN/short-description.txt b/app/src/main/play/listings/zh-CN/short-description.txt
index ab28b4df7..e6072fcf3 100644
--- a/app/src/main/play/listings/zh-CN/short-description.txt
+++ b/app/src/main/play/listings/zh-CN/short-description.txt
@@ -1 +1 @@
-简å•æ˜“用ã€çµæ´»çš„å¼€æºæ’­å®¢ç®¡ç†å·¥å…·ä¸Žæ’­æ”¾å™¨ \ No newline at end of file
+易用ã€çµæ´»çš„å¼€æºæ’­å®¢ç®¡ç†å·¥å…·ä¸Žæ’­æ”¾å™¨ \ No newline at end of file
diff --git a/app/src/main/play/listings/zh-CN/title.txt b/app/src/main/play/listings/zh-CN/title.txt
new file mode 100644
index 000000000..31552f353
--- /dev/null
+++ b/app/src/main/play/listings/zh-CN/title.txt
@@ -0,0 +1 @@
+AntennaPod \ No newline at end of file
diff --git a/app/src/main/play/release-notes/en-US/default.txt b/app/src/main/play/release-notes/en-US/default.txt
index 7c1b36539..11371852d 100644
--- a/app/src/main/play/release-notes/en-US/default.txt
+++ b/app/src/main/play/release-notes/en-US/default.txt
@@ -4,4 +4,5 @@
- Added batch editing to the queue (by @ByteHamster)
- Added option to adapt remaining time to playback speed (by @CedricCabessa)
- Removed broken Flattr integration (by @ByteHamster)
-- Tons of bug fixes and performance improvements (by @andersonvom, @archibishop, @ByteHamster, @gaul, @jas14)
+- Added filter to "All episodes" list (by @jhunnius)
+- Tons of bug fixes and performance improvements
diff --git a/app/src/main/res/layout/addfeed.xml b/app/src/main/res/layout/addfeed.xml
index 011aa4c8b..a7f7d9f12 100644
--- a/app/src/main/res/layout/addfeed.xml
+++ b/app/src/main/res/layout/addfeed.xml
@@ -10,6 +10,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
+ android:focusableInTouchMode="true"
android:padding="8dp">
<android.support.v7.widget.CardView
@@ -27,6 +28,8 @@
<ImageView
android:layout_width="40dp"
android:layout_height="match_parent"
+ android:layout_marginLeft="8dp"
+ android:layout_marginRight="8dp"
android:contentDescription="@string/search_podcast_hint"
app:srcCompat="?attr/action_search"
android:scaleType="center"/>
@@ -39,8 +42,10 @@
android:inputType="text"
android:imeOptions="actionSearch"
android:importantForAutofill="no"
- android:layout_marginLeft="8dp"
+ android:layout_marginStart="0dp"
+ android:layout_marginLeft="0dp"
android:layout_marginRight="8dp"
+ android:layout_marginEnd="8dp"
android:paddingTop="16dp"
android:paddingBottom="16dp"
android:hint="@string/search_podcast_hint"
@@ -109,16 +114,17 @@
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:padding="8dp"
android:layout_gravity="center_horizontal"
android:orientation="horizontal">
<LinearLayout
android:id="@+id/advanced_search"
- android:layout_width="96dp"
+ android:layout_width="120dp"
android:layout_height="wrap_content"
android:orientation="vertical"
android:gravity="center_horizontal"
- android:padding="16dp"
+ android:padding="8dp"
android:background="?android:attr/selectableItemBackground">
<ImageView
@@ -127,8 +133,7 @@
android:contentDescription="@string/advanced_search"
app:srcCompat="?attr/action_search"
android:scaleType="center"
- android:layout_marginBottom="4dp"
- android:tint="?android:attr/textColorPrimary"/>
+ android:layout_marginBottom="4dp"/>
<TextView
android:layout_width="match_parent"
@@ -140,11 +145,11 @@
<LinearLayout
android:id="@+id/btn_opml_import"
- android:layout_width="96dp"
+ android:layout_width="120dp"
android:layout_height="wrap_content"
android:orientation="vertical"
android:gravity="center_horizontal"
- android:padding="16dp"
+ android:padding="8dp"
android:background="?android:attr/selectableItemBackground">
<ImageView
@@ -153,8 +158,7 @@
android:contentDescription="@string/opml_import_label"
app:srcCompat="?attr/av_download"
android:scaleType="center"
- android:layout_marginBottom="4dp"
- android:tint="?android:attr/textColorPrimary"/>
+ android:layout_marginBottom="4dp"/>
<TextView
android:layout_width="match_parent"
diff --git a/app/src/main/res/layout/all_episodes_fragment.xml b/app/src/main/res/layout/all_episodes_fragment.xml
index 89f900a1f..53636c2b6 100644
--- a/app/src/main/res/layout/all_episodes_fragment.xml
+++ b/app/src/main/res/layout/all_episodes_fragment.xml
@@ -1,19 +1,37 @@
<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- android:orientation="vertical"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
+ <TextView
+ android:id="@+id/txtvInformation"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:paddingTop="2dp"
+ android:paddingBottom="2dp"
+ android:visibility="gone"
+ tools:text="(i) Information" />
<android.support.v7.widget.RecyclerView
android:id="@android:id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
+ android:layout_below="@+id/txtvInformation"
+ android:layout_marginTop="0dp"
+ android:layout_marginBottom="0dp"
android:clipToPadding="false"
android:paddingTop="@dimen/list_vertical_padding"
android:paddingBottom="@dimen/list_vertical_padding"
- android:scrollbarStyle="outsideOverlay"
+ app:fastScrollEnabled="true"
+ app:fastScrollHorizontalThumbDrawable="@drawable/thumb_drawable"
+ app:fastScrollHorizontalTrackDrawable="@drawable/line_drawable"
+ app:fastScrollVerticalThumbDrawable="@drawable/thumb_drawable"
+ app:fastScrollVerticalTrackDrawable="@drawable/line_drawable"
tools:itemCount="13"
tools:listitem="@layout/new_episodes_listitem" />
@@ -21,12 +39,13 @@
android:id="@+id/progLoading"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_gravity="center"
+ android:gravity="center"
android:indeterminateOnly="true"
android:visibility="gone"
+ android:layout_centerInParent="true"
tools:visibility="gone"
tools:layout_width="match_parent"
tools:layout_height="64dp"
tools:background="@android:color/holo_red_light"/>
-</FrameLayout> \ No newline at end of file
+</RelativeLayout> \ No newline at end of file
diff --git a/app/src/main/res/layout/bug_report.xml b/app/src/main/res/layout/bug_report.xml
new file mode 100644
index 000000000..e97e85265
--- /dev/null
+++ b/app/src/main/res/layout/bug_report.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:padding="16dp">
+ <Button
+ android:id="@+id/btn_open_bug_tracker"
+ android:text="@string/open_bug_tracker"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"/>
+
+ <Button
+ android:id="@+id/btn_copy_log"
+ android:text="@string/copy_to_clipboard"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"/>
+
+ <TextView
+ android:layout_marginTop="8dp"
+ android:id="@+id/crash_report_logs"
+ android:textIsSelectable="true"
+ android:textSize="12sp"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1"/>
+
+</LinearLayout>
diff --git a/app/src/main/res/layout/checkbox_do_not_show_again.xml b/app/src/main/res/layout/checkbox_do_not_show_again.xml
new file mode 100644
index 000000000..15f26e8b4
--- /dev/null
+++ b/app/src/main/res/layout/checkbox_do_not_show_again.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:paddingLeft="16dp"
+ android:paddingRight="16dp"
+ android:paddingTop="8dp"
+ android:paddingBottom="8dp">
+
+ <CheckBox
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:id="@+id/checkbox_do_not_show_again"
+ android:text="@string/checkbox_do_not_show_again"/>
+
+</LinearLayout> \ No newline at end of file
diff --git a/app/src/main/res/layout/downloaded_episodeslist_item.xml b/app/src/main/res/layout/downloaded_episodeslist_item.xml
index 65a08251f..3f8065466 100644
--- a/app/src/main/res/layout/downloaded_episodeslist_item.xml
+++ b/app/src/main/res/layout/downloaded_episodeslist_item.xml
@@ -17,7 +17,7 @@
android:layout_marginTop="@dimen/listitem_threeline_verticalpadding"
android:contentDescription="@string/cover_label"
android:scaleType="centerCrop"
- tools:src="@drawable/ic_stat_antenna_default"
+ tools:src="@drawable/ic_antenna"
tools:background="@android:color/holo_green_dark"/>
diff --git a/app/src/main/res/layout/episodes_apply_action_fragment.xml b/app/src/main/res/layout/episodes_apply_action_fragment.xml
index 984e960d8..ad453afbe 100644
--- a/app/src/main/res/layout/episodes_apply_action_fragment.xml
+++ b/app/src/main/res/layout/episodes_apply_action_fragment.xml
@@ -29,17 +29,14 @@
android:layout_alignParentBottom="true"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
- android:elevation="@dimen/sd_close_elevation"
- tools:ignore="UnusedAttribute">
- <!-- android:elevation:
- 1. Needs to match the speed dial's minimal elevation,
- or the speed dial can't be clicked at all
- -->
+ android:elevation="@dimen/sd_open_elevation"
+ tools:ignore="UnusedAttribute" >
+
<com.leinardi.android.speeddial.SpeedDialView
android:id="@+id/fabSD"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- app:sdMainFabClosedSrc="@drawable/ic_fab_edit"
+ app:sdMainFabClosedSrc="?attr/batch_edit_fab_icon"
app:sdOverlayLayout="@id/fabSDOverlay"
android:layout_marginEnd="16dp"
android:layout_marginRight="16dp"
diff --git a/app/src/main/res/layout/feeditem_fragment.xml b/app/src/main/res/layout/feeditem_fragment.xml
index 78c0b3b16..b047b3da0 100644
--- a/app/src/main/res/layout/feeditem_fragment.xml
+++ b/app/src/main/res/layout/feeditem_fragment.xml
@@ -36,7 +36,8 @@
android:layout_marginBottom="16dp"
android:contentDescription="@string/cover_label"
android:gravity="center_vertical"
- tools:src="@drawable/ic_stat_antenna_default"
+ android:foreground="?attr/selectableItemBackground"
+ tools:src="@drawable/ic_antenna"
tools:background="@android:color/holo_green_dark" />
<TextView
@@ -47,6 +48,7 @@
android:layout_alignTop="@id/imgvCover"
android:layout_toRightOf="@id/imgvCover"
android:layout_toEndOf="@id/imgvCover"
+ android:foreground="?attr/selectableItemBackground"
tools:text="Podcast title"
tools:background="@android:color/holo_green_dark" />
diff --git a/app/src/main/res/layout/feeditemlist_header.xml b/app/src/main/res/layout/feeditemlist_header.xml
index e1f451e9e..596135d88 100644
--- a/app/src/main/res/layout/feeditemlist_header.xml
+++ b/app/src/main/res/layout/feeditemlist_header.xml
@@ -27,7 +27,7 @@
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:contentDescription="@string/cover_label"
- tools:src="@drawable/ic_stat_antenna_default"
+ tools:src="@drawable/ic_antenna"
tools:background="@android:color/holo_green_dark"/>
<ImageButton
diff --git a/app/src/main/res/layout/gpodnet_podcast_listitem.xml b/app/src/main/res/layout/gpodnet_podcast_listitem.xml
index 27a8bbdca..6e02fa090 100644
--- a/app/src/main/res/layout/gpodnet_podcast_listitem.xml
+++ b/app/src/main/res/layout/gpodnet_podcast_listitem.xml
@@ -23,7 +23,7 @@
android:contentDescription="@string/cover_label"
android:cropToPadding="true"
android:scaleType="fitXY"
- tools:src="@drawable/ic_stat_antenna_default"
+ tools:src="@drawable/ic_antenna"
tools:background="@android:color/holo_green_dark" />
<LinearLayout
diff --git a/app/src/main/res/layout/itunes_podcast_listitem.xml b/app/src/main/res/layout/itunes_podcast_listitem.xml
index 4848563b1..b2411c5df 100644
--- a/app/src/main/res/layout/itunes_podcast_listitem.xml
+++ b/app/src/main/res/layout/itunes_podcast_listitem.xml
@@ -25,7 +25,7 @@
android:cropToPadding="true"
android:scaleType="fitXY"
tools:background="@android:color/holo_green_dark"
- tools:src="@drawable/ic_stat_antenna_default" />
+ tools:src="@drawable/ic_antenna" />
<LinearLayout
android:layout_width="match_parent"
diff --git a/app/src/main/res/layout/mediaplayerinfo_activity.xml b/app/src/main/res/layout/mediaplayerinfo_activity.xml
index a6427e985..c4217db54 100644
--- a/app/src/main/res/layout/mediaplayerinfo_activity.xml
+++ b/app/src/main/res/layout/mediaplayerinfo_activity.xml
@@ -155,11 +155,11 @@
android:layout_marginTop="-8dp"
android:gravity="center"
android:text="30"
- android:textSize="10sp"
+ android:textSize="12sp"
android:textColor="?android:attr/textColorSecondary"
android:clickable="false"/>
- <Button
+ <ImageButton
android:id="@+id/butPlaybackSpeed"
android:layout_width="@dimen/audioplayer_playercontrols_length"
android:layout_height="@dimen/audioplayer_playercontrols_length"
@@ -167,13 +167,28 @@
android:layout_toStartOf="@id/butRev"
android:background="?attr/selectableItemBackground"
android:contentDescription="@string/set_playback_speed_label"
- android:src="?attr/av_fast_forward"
- android:textSize="@dimen/text_size_medium"
- android:textAllCaps="false"
- android:maxLines="1"
+ android:src="?attr/av_speed"
+ android:scaleType="fitCenter"
+ tools:src="@drawable/ic_playback_speed_white"
tools:visibility="gone"
tools:background="@android:color/holo_green_dark" />
+ <TextView
+ android:id="@+id/txtvPlaybackSpeed"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/butPlaybackSpeed"
+ android:layout_alignLeft="@id/butPlaybackSpeed"
+ android:layout_alignStart="@id/butPlaybackSpeed"
+ android:layout_alignRight="@id/butPlaybackSpeed"
+ android:layout_alignEnd="@id/butPlaybackSpeed"
+ android:layout_marginTop="-8dp"
+ android:gravity="center"
+ android:text="1.00"
+ android:textSize="12sp"
+ android:textColor="?android:attr/textColorSecondary"
+ android:clickable="false"/>
+
<ImageButton
android:id="@+id/butCastDisconnect"
android:layout_width="@dimen/audioplayer_playercontrols_length"
@@ -216,7 +231,7 @@
android:layout_marginTop="-8dp"
android:gravity="center"
android:text="30"
- android:textSize="10sp"
+ android:textSize="12sp"
android:textColor="?android:attr/textColorSecondary"
android:clickable="false"/>
diff --git a/app/src/main/res/layout/nav_feedlistitem.xml b/app/src/main/res/layout/nav_feedlistitem.xml
index 816870d1c..52833b3cd 100644
--- a/app/src/main/res/layout/nav_feedlistitem.xml
+++ b/app/src/main/res/layout/nav_feedlistitem.xml
@@ -25,7 +25,7 @@
android:scaleType="centerCrop"
android:layout_marginTop="4dp"
android:layout_marginBottom="4dp"
- tools:src="@drawable/ic_stat_antenna_default"
+ tools:src="@drawable/ic_antenna"
tools:background="@android:color/holo_green_dark"/>
<TextView
diff --git a/app/src/main/res/layout/queue_listitem.xml b/app/src/main/res/layout/queue_listitem.xml
index 6b41b68d5..1dcc34bce 100644
--- a/app/src/main/res/layout/queue_listitem.xml
+++ b/app/src/main/res/layout/queue_listitem.xml
@@ -51,7 +51,7 @@
android:layout_height="@dimen/thumbnail_length_queue_item"
android:layout_centerVertical="true"
android:contentDescription="@string/cover_label"
- tools:src="@drawable/ic_stat_antenna_default"
+ tools:src="@drawable/ic_antenna"
tools:background="@android:color/holo_green_dark"/>
</RelativeLayout>
diff --git a/app/src/main/res/layout/searchlist_item.xml b/app/src/main/res/layout/searchlist_item.xml
index 50374c737..4a055fea9 100644
--- a/app/src/main/res/layout/searchlist_item.xml
+++ b/app/src/main/res/layout/searchlist_item.xml
@@ -18,7 +18,7 @@
android:layout_marginStart="@dimen/listitem_threeline_horizontalpadding"
android:contentDescription="@string/cover_label"
android:scaleType="centerCrop"
- tools:src="@drawable/ic_stat_antenna_default"
+ tools:src="@drawable/ic_antenna"
tools:background="@android:color/holo_green_dark"/>
<LinearLayout
diff --git a/app/src/main/res/layout/statistics_listitem.xml b/app/src/main/res/layout/statistics_listitem.xml
index b186add9e..f52aa73e0 100644
--- a/app/src/main/res/layout/statistics_listitem.xml
+++ b/app/src/main/res/layout/statistics_listitem.xml
@@ -23,7 +23,7 @@
android:scaleType="centerCrop"
android:layout_marginTop="4dp"
android:layout_marginBottom="4dp"
- tools:src="@drawable/ic_stat_antenna_default"
+ tools:src="@drawable/ic_antenna"
tools:background="@android:color/holo_green_dark"/>
<TextView
diff --git a/app/src/main/res/menu/allepisodes_context.xml b/app/src/main/res/menu/allepisodes_context.xml
deleted file mode 100644
index 907bc9334..000000000
--- a/app/src/main/res/menu/allepisodes_context.xml
+++ /dev/null
@@ -1,86 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<menu xmlns:android="http://schemas.android.com/apk/res/android">
-
- <item
- android:id="@id/skip_episode_item"
- android:menuCategory="container"
- android:title="@string/skip_episode_label" />
-
-
- <item
- android:id="@+id/remove_new_flag_item"
- android:menuCategory="container"
- android:title="@string/remove_new_flag_label" />
-
- <item
- android:id="@+id/mark_read_item"
- android:menuCategory="container"
- android:title="@string/mark_read_label" />
- <item
- android:id="@+id/mark_unread_item"
- android:menuCategory="container"
- android:title="@string/mark_unread_label" />
-
- <item
- android:id="@+id/add_to_queue_item"
- android:menuCategory="container"
- android:title="@string/add_to_queue_label" />
- <item
- android:id="@+id/remove_from_queue_item"
- android:menuCategory="container"
- android:title="@string/remove_from_queue_label" />
- <item
- android:id="@+id/add_to_favorites_item"
- android:menuCategory="container"
- android:title="@string/add_to_favorite_label" />
- <item
- android:id="@+id/remove_from_favorites_item"
- android:menuCategory="container"
- android:title="@string/remove_from_favorite_label" />
- <item
- android:id="@+id/reset_position"
- android:menuCategory="container"
- android:title="@string/reset_position" />
-
- <item
- android:id="@+id/activate_auto_download"
- android:menuCategory="container"
- android:title="@string/activate_auto_download" />
- <item
- android:id="@+id/deactivate_auto_download"
- android:menuCategory="container"
- android:title="@string/deactivate_auto_download" />
-
- <item
- android:id="@+id/visit_website_item"
- android:menuCategory="container"
- android:title="@string/visit_website_label" />
- <item
- android:id="@+id/share_item"
- android:menuCategory="container"
- android:title="@string/share_label">
- <menu>
- <item
- android:id="@+id/share_link_item"
- android:menuCategory="container"
- android:title="@string/share_link_label" />
- <item
- android:id="@+id/share_link_with_position_item"
- android:menuCategory="container"
- android:title="@string/share_link_with_position_label" />
- <item
- android:id="@+id/share_download_url_item"
- android:menuCategory="container"
- android:title="@string/share_item_url_label" />
- <item
- android:id="@+id/share_download_url_with_position_item"
- android:menuCategory="container"
- android:title="@string/share_item_url_with_position_label" />
- <item
- android:id="@+id/share_file"
- android:menuCategory="container"
- android:title="@string/share_file_label" />
- </menu>
- </item>
-</menu> \ No newline at end of file
diff --git a/app/src/main/res/menu/episodes.xml b/app/src/main/res/menu/episodes.xml
index 23c8f862a..1e1aa8f56 100644
--- a/app/src/main/res/menu/episodes.xml
+++ b/app/src/main/res/menu/episodes.xml
@@ -18,10 +18,19 @@
android:icon="?attr/navigation_refresh"/>
<item
+ android:id="@+id/filter_items"
+ android:icon="?attr/ic_filter"
+ android:menuCategory="container"
+ android:title="@string/filter"
+ android:visible="false"
+ custom:showAsAction="ifRoom"/>
+
+ <item
android:id="@+id/mark_all_read_item"
android:title="@string/mark_all_read_label"
android:menuCategory="container"
custom:showAsAction="collapseActionView"
+ android:visible="false"
android:icon="?attr/navigation_accept"/>
<item
@@ -29,6 +38,7 @@
android:title="@string/remove_all_new_flags_label"
android:menuCategory="container"
custom:showAsAction="collapseActionView"
+ android:visible="false"
android:icon="?attr/navigation_accept"/>
</menu>
diff --git a/app/src/main/res/menu/feedinfo.xml b/app/src/main/res/menu/feedinfo.xml
index 300068007..f20d679a5 100644
--- a/app/src/main/res/menu/feedinfo.xml
+++ b/app/src/main/res/menu/feedinfo.xml
@@ -11,7 +11,7 @@
<item
android:id="@+id/share_link_item"
custom:showAsAction="collapseActionView"
- android:title="@string/share_link_label">
+ android:title="@string/share_website_url_label">
</item>
<item
android:id="@+id/share_download_url_item"
diff --git a/app/src/main/res/menu/feeditem_options.xml b/app/src/main/res/menu/feeditem_options.xml
index 0801b79a1..e415ff85a 100644
--- a/app/src/main/res/menu/feeditem_options.xml
+++ b/app/src/main/res/menu/feeditem_options.xml
@@ -9,6 +9,11 @@
</item>
<item
+ android:id="@+id/remove_new_flag_item"
+ custom:showAsAction="collapseActionView"
+ android:title="@string/remove_new_flag_label" />
+
+ <item
android:id="@+id/mark_read_item"
custom:showAsAction="collapseActionView"
android:title="@string/mark_read_label">
diff --git a/app/src/main/res/menu/feeditemlist_context.xml b/app/src/main/res/menu/feeditemlist_context.xml
index df13cb027..6e4966206 100644
--- a/app/src/main/res/menu/feeditemlist_context.xml
+++ b/app/src/main/res/menu/feeditemlist_context.xml
@@ -8,6 +8,11 @@
android:title="@string/skip_episode_label" />
<item
+ android:id="@+id/remove_new_flag_item"
+ android:menuCategory="container"
+ android:title="@string/remove_new_flag_label" />
+
+ <item
android:id="@+id/mark_read_item"
android:menuCategory="container"
android:title="@string/mark_read_label" />
diff --git a/app/src/main/res/menu/feedlist.xml b/app/src/main/res/menu/feedlist.xml
index e62fc9d36..4144c392f 100644
--- a/app/src/main/res/menu/feedlist.xml
+++ b/app/src/main/res/menu/feedlist.xml
@@ -33,7 +33,7 @@
<item
android:id="@+id/action_search"
android:icon="?attr/action_search"
- custom:showAsAction="always"
+ custom:showAsAction="always|collapseActionView"
custom:actionViewClass="android.support.v7.widget.SearchView"
android:title="@string/search_label"/>
@@ -49,7 +49,7 @@
android:id="@+id/share_link_item"
android:menuCategory="container"
custom:showAsAction="collapseActionView"
- android:title="@string/share_link_label">
+ android:title="@string/share_website_url_label">
</item>
<item
android:id="@+id/share_download_url_item"
diff --git a/app/src/main/res/menu/mediaplayer.xml b/app/src/main/res/menu/mediaplayer.xml
index 44d511ee4..055951760 100644
--- a/app/src/main/res/menu/mediaplayer.xml
+++ b/app/src/main/res/menu/mediaplayer.xml
@@ -35,6 +35,14 @@
</item>
<item
+ android:id="@+id/open_feed_item"
+ android:icon="?attr/feed"
+ custom:showAsAction="collapseActionView"
+ android:title="@string/open_podcast"
+ android:visible="false">
+ </item>
+
+ <item
android:id="@+id/visit_website_item"
android:icon="?attr/location_web_site"
custom:showAsAction="collapseActionView"
diff --git a/app/src/main/res/menu/queue_context.xml b/app/src/main/res/menu/queue_context.xml
index e1c3e6216..522e712e2 100644
--- a/app/src/main/res/menu/queue_context.xml
+++ b/app/src/main/res/menu/queue_context.xml
@@ -11,77 +11,6 @@
android:id="@+id/move_to_bottom_item"
android:menuCategory="container"
android:title="@string/move_to_bottom_label" />
+ <!-- rest of the menu items can be found in the generic feeditemlist_context.xml -->
- <item
- android:id="@+id/mark_read_item"
- android:menuCategory="container"
- android:title="@string/mark_read_label" />
-
- <item
- android:id="@+id/mark_unread_item"
- android:menuCategory="container"
- android:title="@string/mark_unread_label" />
-
- <item
- android:id="@+id/remove_from_queue_item"
- android:menuCategory="container"
- android:title="@string/remove_from_queue_label" />
- <item
- android:id="@+id/remove_item"
- android:menuCategory="container"
- android:title="@string/delete_label" />
-
- <item
- android:id="@+id/add_to_favorites_item"
- android:menuCategory="container"
- android:title="@string/add_to_favorite_label" />
- <item
- android:id="@+id/remove_from_favorites_item"
- android:menuCategory="container"
- android:title="@string/remove_from_favorite_label" />
- <item
- android:id="@+id/reset_position"
- android:menuCategory="container"
- android:title="@string/reset_position" />
-
- <item
- android:id="@+id/activate_auto_download"
- android:menuCategory="container"
- android:title="@string/activate_auto_download" />
- <item
- android:id="@+id/deactivate_auto_download"
- android:menuCategory="container"
- android:title="@string/deactivate_auto_download" />
-
- <item
- android:id="@+id/visit_website_item"
- android:menuCategory="container"
- android:title="@string/visit_website_label" />
- <item
- android:id="@+id/share_item"
- android:menuCategory="container"
- android:title="@string/share_label">
- <menu>
- <item
- android:id="@+id/share_link_item"
- android:menuCategory="container"
- android:title="@string/share_link_label" />
- <item
- android:id="@+id/share_link_with_position_item"
- android:menuCategory="container"
- android:title="@string/share_link_with_position_label" />
- <item
- android:id="@+id/share_download_url_item"
- android:menuCategory="container"
- android:title="@string/share_item_url_label" />
- <item
- android:id="@+id/share_download_url_with_position_item"
- android:menuCategory="container"
- android:title="@string/share_item_url_with_position_label" />
- <item
- android:id="@+id/share_file"
- android:menuCategory="container"
- android:title="@string/share_file_label" />
- </menu>
- </item>
</menu> \ No newline at end of file
diff --git a/app/src/main/res/menu/subscriptions.xml b/app/src/main/res/menu/subscriptions.xml
index f39e0ac97..1780592d5 100644
--- a/app/src/main/res/menu/subscriptions.xml
+++ b/app/src/main/res/menu/subscriptions.xml
@@ -3,6 +3,13 @@
xmlns:custom="http://schemas.android.com/apk/res-auto">
<item
+ android:id="@+id/refresh_item"
+ android:title="@string/refresh_label"
+ android:menuCategory="container"
+ custom:showAsAction="always"
+ android:icon="?attr/navigation_refresh"/>
+
+ <item
android:id="@+id/subscription_num_columns"
android:title="@string/subscription_num_columns"
custom:showAsAction="never">
diff --git a/app/src/main/res/values-w300dp/dimens-fabspeeddial.xml b/app/src/main/res/values-w300dp/dimens-fabspeeddial.xml
index e531395c0..1b90da786 100644
--- a/app/src/main/res/values-w300dp/dimens-fabspeeddial.xml
+++ b/app/src/main/res/values-w300dp/dimens-fabspeeddial.xml
@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
-<resources>
+<resources xmlns:tools="http://schemas.android.com/tools">
<!-- increase FAB speed dial label's max width if the screen is wide enough
(300dp ~ 1.875 inch, devices with 3.5-inch screens have a width of ~ 1.9in
so the setup is applicable for most phones)
-->
- <dimen name="sd_label_max_width">240dp</dimen>
+ <dimen name="sd_label_max_width" tools:ignore="MissingDefaultResource">240dp</dimen>
</resources>
diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml
index f45847e54..37707ead6 100644
--- a/app/src/main/res/xml/preferences.xml
+++ b/app/src/main/res/xml/preferences.xml
@@ -1,9 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen
- xmlns:android="http://schemas.android.com/apk/res/android">
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:search="http://schemas.android.com/apk/res-auto">
<com.bytehamster.lib.preferencesearch.SearchPreference
- android:key="searchPreference" />
+ android:key="searchPreference"
+ search:textHint="@string/preference_search_hint"
+ search:textNoResults="@string/preference_search_no_results"
+ search:textClearHistory="@string/preference_search_clear_history" />
<Preference
android:key="prefScreenInterface"
@@ -39,16 +43,14 @@
<Preference
android:key="prefFaq"
android:title="@string/pref_faq"
- android:icon="?attr/ic_question_answer" />
-
+ android:icon="?attr/ic_questionmark" />
<Preference
- android:key="prefKnownIssues"
- android:title="@string/pref_known_issues"
- android:icon="?attr/ic_known_issues" />
+ android:key="prefViewMailingList"
+ android:title="@string/view_mailing_list"
+ android:icon="?attr/ic_chat" />
<Preference
- android:key="prefSendCrashReport"
- android:title="@string/crash_report_title"
- android:summary="@string/crash_report_sum"
+ android:key="prefSendBugReport"
+ android:title="@string/bug_report_title"
android:icon="?attr/ic_bug" />
<Preference
android:key="prefAbout"
diff --git a/app/src/main/res/xml/preferences_user_interface.xml b/app/src/main/res/xml/preferences_user_interface.xml
index 1d970a5f7..c48e9adc8 100644
--- a/app/src/main/res/xml/preferences_user_interface.xml
+++ b/app/src/main/res/xml/preferences_user_interface.xml
@@ -10,7 +10,7 @@
android:title="@string/pref_set_theme_title"
android:key="prefTheme"
android:summary="@string/pref_set_theme_sum"
- android:defaultValue="0"
+ android:defaultValue="system"
app:useStockLayout="true"/>
<Preference
android:key="prefHiddenDrawerItems"
diff --git a/app/src/main/templates/about.html b/app/src/main/templates/about.html
index 05d1b6e28..fd70ab549 100644
--- a/app/src/main/templates/about.html
+++ b/app/src/main/templates/about.html
@@ -83,7 +83,8 @@
Created by Daniel Oeh<br />
Copyright &copy; 2012-@year@<br />
AntennaPod Contributors <a href="CONTRIBUTORS.txt">(View)</a><br />
-Licensed under the MIT License <a href="LICENSE.txt">(View)</a>
+Licensed under the MIT License <a href="LICENSE.txt">(View)</a><br />
+Privacy Policy <a href="https://antennapod.org/privacy.html">(View)</a>
</div>
<h1>Used libraries</h1>
@@ -104,6 +105,11 @@ by Google, licensed under the Apache 2.0 license <a href="LICENSE_APACHE-2.0.txt
</div>
<div class="card">
+ <h2>Floating Action Button Speed Dial <a href="https://github.com/leinardi/FloatingActionButtonSpeedDial">(Link)</a></h2>
+ by Roberto Leinardi, licensed under the Apache 2.0 license <a href="LICENSE_APACHE-2.0.txt">(View)</a>
+</div>
+
+<div class="card">
<h2>Glide <a href="https://github.com/bumptech/glide/">(Link)</a></h2>
licensed under the Simplified BSD license <a href="LICENSE_GLIDE.txt">(View)</a>
</div>
diff --git a/app/src/play/java/de/danoeh/antennapod/activity/CastEnabledActivity.java b/app/src/play/java/de/danoeh/antennapod/activity/CastEnabledActivity.java
index 87304b3d6..caca8a6e3 100644
--- a/app/src/play/java/de/danoeh/antennapod/activity/CastEnabledActivity.java
+++ b/app/src/play/java/de/danoeh/antennapod/activity/CastEnabledActivity.java
@@ -29,17 +29,34 @@ public abstract class CastEnabledActivity extends AppCompatActivity
implements SharedPreferences.OnSharedPreferenceChangeListener {
public static final String TAG = "CastEnabledActivity";
- protected CastManager castManager;
- protected SwitchableMediaRouteActionProvider mediaRouteActionProvider;
+ private CastConsumer castConsumer;
+ private CastManager castManager;
+
+ private SwitchableMediaRouteActionProvider mediaRouteActionProvider;
private final CastButtonVisibilityManager castButtonVisibilityManager = new CastButtonVisibilityManager();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ if (!CastManager.isInitialized()) {
+ return;
+ }
+
PreferenceManager.getDefaultSharedPreferences(getApplicationContext()).
registerOnSharedPreferenceChangeListener(this);
+ castConsumer = new DefaultCastConsumer() {
+ @Override
+ public void onApplicationConnected(ApplicationMetadata appMetadata, String sessionId, boolean wasLaunched) {
+ onCastConnectionChanged(true);
+ }
+
+ @Override
+ public void onDisconnected() {
+ onCastConnectionChanged(false);
+ }
+ };
castManager = CastManager.getInstance();
castManager.addCastConsumer(castConsumer);
castButtonVisibilityManager.setPrefEnabled(UserPreferences.isCastEnabled());
@@ -48,6 +65,10 @@ public abstract class CastEnabledActivity extends AppCompatActivity
@Override
protected void onDestroy() {
+ if (!CastManager.isInitialized()) {
+ super.onDestroy();
+ return;
+ }
PreferenceManager.getDefaultSharedPreferences(getApplicationContext())
.unregisterOnSharedPreferenceChangeListener(this);
castManager.removeCastConsumer(castConsumer);
@@ -58,6 +79,9 @@ public abstract class CastEnabledActivity extends AppCompatActivity
@CallSuper
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
+ if (!CastManager.isInitialized()) {
+ return true;
+ }
getMenuInflater().inflate(R.menu.cast_enabled, menu);
castButtonVisibilityManager.setMenu(menu);
return true;
@@ -67,6 +91,10 @@ public abstract class CastEnabledActivity extends AppCompatActivity
@CallSuper
public boolean onPrepareOptionsMenu(Menu menu) {
super.onPrepareOptionsMenu(menu);
+ if (!CastManager.isInitialized()) {
+ return true;
+ }
+
MenuItem mediaRouteButton = menu.findItem(R.id.media_route_menu_item);
if (mediaRouteButton == null) {
Log.wtf(TAG, "MediaRoute item could not be found on the menu!", new Exception());
@@ -83,15 +111,22 @@ public abstract class CastEnabledActivity extends AppCompatActivity
@Override
protected void onResume() {
super.onResume();
+ if (!CastManager.isInitialized()) {
+ return;
+ }
castButtonVisibilityManager.setResumed(true);
}
@Override
protected void onPause() {
super.onPause();
+ if (!CastManager.isInitialized()) {
+ return;
+ }
castButtonVisibilityManager.setResumed(false);
}
+
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
if (UserPreferences.PREF_CAST_ENABLED.equals(key)) {
@@ -105,18 +140,6 @@ public abstract class CastEnabledActivity extends AppCompatActivity
}
}
- CastConsumer castConsumer = new DefaultCastConsumer() {
- @Override
- public void onApplicationConnected(ApplicationMetadata appMetadata, String sessionId, boolean wasLaunched) {
- onCastConnectionChanged(true);
- }
-
- @Override
- public void onDisconnected() {
- onCastConnectionChanged(false);
- }
- };
-
private void onCastConnectionChanged(boolean connected) {
if (connected) {
castButtonVisibilityManager.onConnected();
@@ -133,6 +156,9 @@ public abstract class CastEnabledActivity extends AppCompatActivity
* @param showAsAction refer to {@link MenuItem#setShowAsAction(int)}
*/
public final void requestCastButton(int showAsAction) {
+ if (!CastManager.isInitialized()) {
+ return;
+ }
castButtonVisibilityManager.requestCastButton(showAsAction);
}
diff --git a/app/src/play/java/de/danoeh/antennapod/preferences/PreferenceControllerFlavorHelper.java b/app/src/play/java/de/danoeh/antennapod/preferences/PreferenceControllerFlavorHelper.java
index c9d52df0c..0e69da61e 100644
--- a/app/src/play/java/de/danoeh/antennapod/preferences/PreferenceControllerFlavorHelper.java
+++ b/app/src/play/java/de/danoeh/antennapod/preferences/PreferenceControllerFlavorHelper.java
@@ -1,8 +1,13 @@
package de.danoeh.antennapod.preferences;
+import android.content.Context;
+import android.support.annotation.NonNull;
+import android.support.v7.app.AlertDialog;
+
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GoogleApiAvailability;
+import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.fragment.preferences.PlaybackPreferencesFragment;
@@ -18,6 +23,7 @@ public class PreferenceControllerFlavorHelper {
final int googlePlayServicesCheck = GoogleApiAvailability.getInstance()
.isGooglePlayServicesAvailable(ui.getActivity());
if (googlePlayServicesCheck == ConnectionResult.SUCCESS) {
+ displayRestartRequiredDialog(ui.requireContext());
return true;
} else {
GoogleApiAvailability.getInstance()
@@ -29,4 +35,13 @@ public class PreferenceControllerFlavorHelper {
return true;
});
}
+
+ private static void displayRestartRequiredDialog(@NonNull Context context) {
+ AlertDialog.Builder dialog = new AlertDialog.Builder(context);
+ dialog.setTitle(android.R.string.dialog_alert_title);
+ dialog.setMessage(R.string.pref_restart_required);
+ dialog.setPositiveButton(android.R.string.ok, null);
+ dialog.setCancelable(false);
+ dialog.show();
+ }
}