summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorByteHamster <info@bytehamster.com>2019-09-27 14:03:55 +0200
committerByteHamster <info@bytehamster.com>2019-09-27 14:03:55 +0200
commit466e6a9b7ee56419332e4860b4824b8b873958cc (patch)
treeb574fdeb357b696f2ad52d59ade382253eb0c38c
parent6e5c4dd9d21ff5c5b7272fc97944fc484dbfb642 (diff)
parent3e01d66cbdd14f141a4e75799731d0dc0de33b81 (diff)
downloadAntennaPod-466e6a9b7ee56419332e4860b4824b8b873958cc.zip
Merge branch 'develop' into feedinfo-fragment
-rw-r--r--.circleci/config.yml74
-rw-r--r--app/build.gradle6
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/AboutActivity.java4
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/BugReportActivity.java13
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java3
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/RatingDialog.java9
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java10
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/FeedItemlistFragment.java4
-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.java11
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java12
-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/MainPreferencesFragment.java15
-rw-r--r--app/src/main/java/de/danoeh/antennapod/menuhandler/FeedItemMenuHandler.java9
-rw-r--r--app/src/main/java/de/danoeh/antennapod/menuhandler/FeedMenuHandler.java9
-rw-r--r--app/src/main/java/de/danoeh/antennapod/preferences/PreferenceUpgrader.java4
-rw-r--r--app/src/main/res/menu/subscriptions.xml7
-rw-r--r--core/build.gradle2
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/event/DownloadEvent.java4
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/preferences/UserPreferences.java17
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/receiver/FeedUpdateReceiver.java6
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/FeedUpdateWorker.java28
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/playback/ExoPlayerWrapper.java39
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java2
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceNotificationBuilder.java12
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/DBTasks.java55
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/FeedUpdateUtils.java36
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/IntentUtils.java16
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/download/AutoUpdateManager.java70
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/exception/RxJavaErrorHandlerSetup.java18
-rw-r--r--core/src/main/res/drawable-hdpi/ic_media_cast_disconnect.pngbin2355 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-mdpi/ic_media_cast_disconnect.pngbin1467 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-xhdpi/ic_media_cast_disconnect.pngbin4496 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-xxhdpi/ic_media_cast_disconnect.pngbin7096 -> 0 bytes
-rw-r--r--core/src/main/res/drawable/ic_notification_cast_off.xml5
-rw-r--r--core/src/main/res/drawable/ic_notification_fast_forward.xml5
-rw-r--r--core/src/main/res/drawable/ic_notification_fast_rewind.xml5
-rw-r--r--core/src/main/res/drawable/ic_notification_key.xml9
-rw-r--r--core/src/main/res/drawable/ic_notification_pause.xml5
-rw-r--r--core/src/main/res/drawable/ic_notification_play.xml5
-rw-r--r--core/src/main/res/drawable/ic_notification_skip.xml5
41 files changed, 315 insertions, 261 deletions
diff --git a/.circleci/config.yml b/.circleci/config.yml
index a4db1befd..b6a6ba78e 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -1,49 +1,77 @@
version: 2
jobs:
- test:
+ test-debug:
docker:
- image: circleci/android:api-28
-
working_directory: ~/AntennaPod
-
environment:
GRADLE_OPTS: '-Dorg.gradle.jvmargs="-Xmx1536m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError"'
_JAVA_OPTIONS: "-Xms256m -Xmx1280m"
-
steps:
- checkout
-
- restore_cache:
keys:
- v1-android-{{ checksum "build.gradle" }}
- # fallback to using the latest cache if no exact match is found
- v1-android-
-
- - run:
- name: Create temporary release keystore
- command: keytool -noprompt -genkey -v -keystore "app/keystore" -alias alias -storepass password -keypass password -keyalg RSA -validity 10 -dname "CN=antennapod.org, OU=dummy, O=dummy, L=dummy, S=dummy, C=US"
-
- run:
name: Build debug
command: ./gradlew assembleDebug -PdisablePreDex
+ - run:
+ name: Execute debug unit tests
+ command: ./gradlew :core:testPlayDebugUnitTest -PdisablePreDex
+ - save_cache:
+ paths:
+ - ~/.android
+ - ~/.gradle
+ - ~/android
+ key: v1-android-{{ checksum "build.gradle" }}
+ test-release:
+ docker:
+ - image: circleci/android:api-28
+ working_directory: ~/AntennaPod
+ environment:
+ GRADLE_OPTS: '-Dorg.gradle.jvmargs="-Xmx1536m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError"'
+ _JAVA_OPTIONS: "-Xms256m -Xmx1280m"
+ steps:
+ - checkout
+ - restore_cache:
+ keys:
+ - v1-android-{{ checksum "build.gradle" }}
+ - v1-android-
+ - run:
+ name: Create temporary release keystore
+ command: keytool -noprompt -genkey -v -keystore "app/keystore" -alias alias -storepass password -keypass password -keyalg RSA -validity 10 -dname "CN=antennapod.org, OU=dummy, O=dummy, L=dummy, S=dummy, C=US"
- run:
name: Build release
command: ./gradlew assembleRelease -PdisablePreDex
-
- run:
- name: Execute unit tests
+ name: Execute release unit tests
command: ./gradlew :core:testPlayReleaseUnitTest -PdisablePreDex
+ - save_cache:
+ paths:
+ - ~/.android
+ - ~/.gradle
+ - ~/android
+ key: v1-android-{{ checksum "build.gradle" }}
+ build-androidtest:
+ docker:
+ - image: circleci/android:api-28
+ working_directory: ~/AntennaPod
+ environment:
+ GRADLE_OPTS: '-Dorg.gradle.jvmargs="-Xmx1536m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError"'
+ _JAVA_OPTIONS: "-Xms256m -Xmx1280m"
+ steps:
+ - checkout
+ - restore_cache:
+ keys:
+ - v1-android-{{ checksum "build.gradle" }}
+ - v1-android-
- run:
name: Build integration tests
command: ./gradlew :app:assemblePlayDebugAndroidTest -PdisablePreDex
-
- - store_artifacts:
- path: app/build/outputs/apk
- destination: apks
-
- save_cache:
paths:
- ~/.android
@@ -55,10 +83,8 @@ jobs:
docker:
- image: circleci/android:api-28
working_directory: ~/AntennaPod
-
steps:
- checkout
-
- run:
name: Checkstyle
command: ./gradlew checkstyle
@@ -66,10 +92,12 @@ jobs:
workflows:
version: 2
- test:
+ unit-tests:
jobs:
- - test
+ - test-debug
+ - test-release
+ - build-androidtest
- checkstyle:
+ static-analysis:
jobs:
- checkstyle
diff --git a/app/build.gradle b/app/build.gradle
index f9ef8a7ef..3996d7ba5 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -18,8 +18,8 @@ android {
// "1.2.3-SNAPSHOT" -> 1020300
// "1.2.3-RC4" -> 1020304
// "1.2.3" -> 1020395
- versionCode 1070395
- versionName "1.7.3"
+ versionCode 1070396
+ versionName "1.7.3b"
testApplicationId "de.test.antennapod"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
generatedDensities = []
@@ -173,8 +173,8 @@ dependencies {
implementation 'com.github.mfietz:fyydlin:v0.4.2'
implementation 'com.github.ByteHamster:SearchPreference:v1.3.0'
- implementation "org.awaitility:awaitility:$awaitilityVersion"
+ 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'
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/BugReportActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/BugReportActivity.java
index 32694a74e..7df973f9b 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/BugReportActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/BugReportActivity.java
@@ -1,19 +1,16 @@
package de.danoeh.antennapod.activity;
-import android.content.ActivityNotFoundException;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
-import android.content.Intent;
-import android.net.Uri;
import android.os.Bundle;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.widget.TextView;
-import android.widget.Toast;
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;
@@ -45,13 +42,7 @@ public class BugReportActivity extends AppCompatActivity {
}
findViewById(R.id.btn_open_bug_tracker).setOnClickListener(v -> {
- try {
- Intent myIntent = new Intent(Intent.ACTION_VIEW,
- Uri.parse("https://github.com/AntennaPod/AntennaPod/issues"));
- startActivity(myIntent);
- } catch (ActivityNotFoundException e) {
- Toast.makeText(this, R.string.pref_no_browser_found, Toast.LENGTH_LONG).show();
- }
+ IntentUtils.openInBrowser(BugReportActivity.this, "https://github.com/AntennaPod/AntennaPod/issues");
});
findViewById(R.id.btn_copy_log).setOnClickListener(v -> {
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 eefa35500..91c3796af 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java
@@ -452,8 +452,7 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
}
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 (feedItem != null) {
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/fragment/EpisodesListFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java
index 799f45eba..c0c23685a 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java
@@ -39,17 +39,16 @@ 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.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.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.core.util.download.AutoUpdateManager;
import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler;
import de.danoeh.antennapod.menuhandler.MenuItemUtils;
import de.danoeh.antennapod.view.EmptyViewHandler;
@@ -197,10 +196,7 @@ public abstract class EpisodesListFragment extends Fragment {
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);
- }
+ AutoUpdateManager.runImmediate(requireContext());
return true;
case R.id.mark_all_read_item:
ConfirmationDialog markAllReadConfirmationDialog = new ConfirmationDialog(getActivity(),
@@ -397,7 +393,7 @@ public abstract class EpisodesListFragment extends Fragment {
Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
DownloaderUpdate update = event.update;
downloaderList = update.downloaders;
- if (isMenuInvalidationAllowed && isUpdatingFeeds != update.feedIds.length > 0) {
+ if (isMenuInvalidationAllowed && event.hasChangedFeedUpdateStatus(isUpdatingFeeds)) {
requireActivity().invalidateOptionsMenu();
}
if (update.mediaIds.length > 0) {
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/FeedItemlistFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/FeedItemlistFragment.java
index 81b7dda8a..f5290bd85 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/FeedItemlistFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/FeedItemlistFragment.java
@@ -378,10 +378,10 @@ public class FeedItemlistFragment 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();
}
}
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 04bf32fd0..9395b0994 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java
@@ -210,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;
}
});
@@ -484,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);
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 d461dbc5d..a1f5dca18 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java
@@ -41,14 +41,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;
@@ -56,6 +54,7 @@ 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;
@@ -201,8 +200,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) {
@@ -305,10 +304,7 @@ public class QueueFragment extends Fragment {
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
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/MainPreferencesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/MainPreferencesFragment.java
index 31fb7ff8b..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
@@ -15,6 +15,7 @@ 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 de.danoeh.antennapod.core.util.IntentUtils;
public class MainPreferencesFragment extends PreferenceFragmentCompat {
private static final String TAG = "MainPreferencesFragment";
@@ -72,11 +73,11 @@ public class MainPreferencesFragment extends PreferenceFragmentCompat {
}
);
findPreference(PREF_FAQ).setOnPreferenceClickListener(preference -> {
- openInBrowser("https://antennapod.org/faq.html");
+ IntentUtils.openInBrowser(getContext(), "https://antennapod.org/faq.html");
return true;
});
findPreference(PREF_VIEW_MAILING_LIST).setOnPreferenceClickListener(preference -> {
- openInBrowser("https://groups.google.com/forum/#!forum/antennapod");
+ IntentUtils.openInBrowser(getContext(), "https://groups.google.com/forum/#!forum/antennapod");
return true;
});
findPreference(PREF_SEND_BUG_REPORT).setOnPreferenceClickListener(preference -> {
@@ -85,16 +86,6 @@ public class MainPreferencesFragment extends PreferenceFragmentCompat {
});
}
- 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/menuhandler/FeedItemMenuHandler.java b/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedItemMenuHandler.java
index f0b6330b3..add62b480 100644
--- a/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedItemMenuHandler.java
+++ b/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedItemMenuHandler.java
@@ -214,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);
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 3949119fc..dbb3b6e7b 100644
--- a/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedMenuHandler.java
+++ b/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedMenuHandler.java
@@ -86,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);
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 195d062f3..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,9 +3,11 @@ 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 {
@@ -22,7 +24,7 @@ public class PreferenceUpgrader {
if (oldVersion != newVersion) {
NotificationUtils.createChannels(context);
- UserPreferences.restartUpdateAlarm();
+ AutoUpdateManager.restartUpdateAlarm();
upgrade(oldVersion);
upgraderPrefs.edit().putInt(PREF_CONFIGURED_VERSION, newVersion).apply();
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/core/build.gradle b/core/build.gradle
index 133f1b262..8614d5589 100644
--- a/core/build.gradle
+++ b/core/build.gradle
@@ -76,7 +76,6 @@ dependencies {
annotationProcessor "org.greenrobot:eventbus-annotation-processor:$eventbusVersion"
implementation "io.reactivex.rxjava2:rxandroid:$rxAndroidVersion"
implementation "io.reactivex.rxjava2:rxjava:$rxJavaVersion"
- implementation "org.awaitility:awaitility:$awaitilityVersion"
implementation "com.google.android.exoplayer:exoplayer:$exoPlayerVersion"
implementation "com.github.AntennaPod:AntennaPod-AudioPlayer:$audioPlayerVersion"
@@ -92,6 +91,7 @@ dependencies {
System.out.println("core: free build hack, skipping some dependencies")
}
+ testImplementation "org.awaitility:awaitility:$awaitilityVersion"
testImplementation 'junit:junit:4.12'
testImplementation 'org.mockito:mockito-core:1.10.19'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
diff --git a/core/src/main/java/de/danoeh/antennapod/core/event/DownloadEvent.java b/core/src/main/java/de/danoeh/antennapod/core/event/DownloadEvent.java
index 124fd3e64..24a71ec96 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/event/DownloadEvent.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/event/DownloadEvent.java
@@ -25,4 +25,8 @@ public class DownloadEvent {
"update=" + update +
'}';
}
+
+ public boolean hasChangedFeedUpdateStatus(boolean oldStatus) {
+ return oldStatus != update.feedIds.length > 0;
+ }
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/preferences/UserPreferences.java b/core/src/main/java/de/danoeh/antennapod/core/preferences/UserPreferences.java
index a06047229..787e32ccc 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/preferences/UserPreferences.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/preferences/UserPreferences.java
@@ -622,7 +622,7 @@ public class UserPreferences {
.apply();
// when updating with an interval, we assume the user wants
// to update *now* and then every 'hours' interval thereafter.
- restartUpdateAlarm();
+ AutoUpdateManager.restartUpdateAlarm();
}
/**
@@ -632,7 +632,7 @@ public class UserPreferences {
prefs.edit()
.putString(PREF_UPDATE_INTERVAL, hourOfDay + ":" + minute)
.apply();
- restartUpdateAlarm();
+ AutoUpdateManager.restartUpdateAlarm();
}
public static void disableAutoUpdate() {
@@ -882,19 +882,6 @@ public class UserPreferences {
return getUpdateTimeOfDay().length == 2;
}
- public static void restartUpdateAlarm() {
- if (isAutoUpdateDisabled()) {
- AutoUpdateManager.disableAutoUpdate();
- } else if (isAutoUpdateTimeOfDay()) {
- int[] timeOfDay = getUpdateTimeOfDay();
- Log.d(TAG, "timeOfDay: " + Arrays.toString(timeOfDay));
- AutoUpdateManager.restartUpdateTimeOfDayAlarm(timeOfDay[0], timeOfDay[1]);
- } else {
- long milliseconds = getUpdateInterval();
- AutoUpdateManager.restartUpdateIntervalAlarm(milliseconds);
- }
- }
-
/**
* Evaluates whether Cast support (Chromecast, Audio Cast, etc) is enabled on the preferences.
*/
diff --git a/core/src/main/java/de/danoeh/antennapod/core/receiver/FeedUpdateReceiver.java b/core/src/main/java/de/danoeh/antennapod/core/receiver/FeedUpdateReceiver.java
index 126f12247..af0a0d426 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/receiver/FeedUpdateReceiver.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/receiver/FeedUpdateReceiver.java
@@ -6,8 +6,7 @@ import android.content.Intent;
import android.util.Log;
import de.danoeh.antennapod.core.ClientConfig;
-import de.danoeh.antennapod.core.preferences.UserPreferences;
-import de.danoeh.antennapod.core.util.FeedUpdateUtils;
+import de.danoeh.antennapod.core.util.download.AutoUpdateManager;
/**
* Refreshes all feeds when it receives an intent
@@ -20,7 +19,8 @@ public class FeedUpdateReceiver extends BroadcastReceiver {
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "Received intent");
ClientConfig.initialize(context);
- FeedUpdateUtils.startAutoUpdate(context, null);
+
+ AutoUpdateManager.runOnce();
}
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/FeedUpdateWorker.java b/core/src/main/java/de/danoeh/antennapod/core/service/FeedUpdateWorker.java
index efdb96dc1..87c18227b 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/FeedUpdateWorker.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/FeedUpdateWorker.java
@@ -2,17 +2,23 @@ package de.danoeh.antennapod.core.service;
import android.content.Context;
import android.support.annotation.NonNull;
+import android.util.Log;
+
import androidx.work.Worker;
import androidx.work.WorkerParameters;
+
import de.danoeh.antennapod.core.ClientConfig;
import de.danoeh.antennapod.core.preferences.UserPreferences;
-import de.danoeh.antennapod.core.util.FeedUpdateUtils;
-import org.awaitility.Awaitility;
-
-import java.util.concurrent.atomic.AtomicBoolean;
+import de.danoeh.antennapod.core.storage.DBTasks;
+import de.danoeh.antennapod.core.util.NetworkUtils;
+import de.danoeh.antennapod.core.util.download.AutoUpdateManager;
public class FeedUpdateWorker extends Worker {
+ private static final String TAG = "FeedUpdateWorker";
+
+ public static final String PARAM_RUN_ONCE = "runOnce";
+
public FeedUpdateWorker(@NonNull Context context, @NonNull WorkerParameters params) {
super(context, params);
}
@@ -20,16 +26,20 @@ public class FeedUpdateWorker extends Worker {
@Override
@NonNull
public Result doWork() {
+ final boolean isRunOnce = getInputData().getBoolean(PARAM_RUN_ONCE, false);
+ Log.d(TAG, "doWork() : isRunOnce = " + isRunOnce);
ClientConfig.initialize(getApplicationContext());
- AtomicBoolean finished = new AtomicBoolean(false);
- FeedUpdateUtils.startAutoUpdate(getApplicationContext(), () -> finished.set(true));
- Awaitility.await().until(finished::get);
+ if (NetworkUtils.networkAvailable() && NetworkUtils.isFeedRefreshAllowed()) {
+ DBTasks.refreshAllFeeds(getApplicationContext());
+ } else {
+ Log.d(TAG, "Blocking automatic update: no wifi available / no mobile updates allowed");
+ }
- if (UserPreferences.isAutoUpdateTimeOfDay()) {
+ if (!isRunOnce && UserPreferences.isAutoUpdateTimeOfDay()) {
// WorkManager does not allow to set specific time for repeated tasks.
// We repeatedly schedule a OneTimeWorkRequest instead.
- UserPreferences.restartUpdateAlarm();
+ AutoUpdateManager.restartUpdateAlarm();
}
return Result.success();
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/playback/ExoPlayerWrapper.java b/core/src/main/java/de/danoeh/antennapod/core/service/playback/ExoPlayerWrapper.java
index 7af33f8f9..c3b412012 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/playback/ExoPlayerWrapper.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/playback/ExoPlayerWrapper.java
@@ -2,7 +2,6 @@ package de.danoeh.antennapod.core.service.playback;
import android.content.Context;
import android.net.Uri;
-import android.os.Handler;
import android.view.SurfaceHolder;
import com.google.android.exoplayer2.C;
@@ -23,38 +22,40 @@ import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
+import com.google.android.exoplayer2.upstream.DefaultHttpDataSource;
+import com.google.android.exoplayer2.upstream.DefaultHttpDataSourceFactory;
import com.google.android.exoplayer2.util.Util;
+import io.reactivex.Observable;
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.disposables.Disposable;
import org.antennapod.audio.MediaPlayer;
import de.danoeh.antennapod.core.util.playback.IPlayer;
+import java.util.concurrent.TimeUnit;
+
public class ExoPlayerWrapper implements IPlayer {
private final Context mContext;
+ private final Disposable bufferingUpdateDisposable;
private SimpleExoPlayer mExoPlayer;
private MediaSource mediaSource;
private MediaPlayer.OnSeekCompleteListener audioSeekCompleteListener;
private MediaPlayer.OnCompletionListener audioCompletionListener;
private MediaPlayer.OnErrorListener audioErrorListener;
private MediaPlayer.OnBufferingUpdateListener bufferingUpdateListener;
- private boolean shouldCheckBufferingUpdates = true;
ExoPlayerWrapper(Context context) {
mContext = context;
mExoPlayer = createPlayer();
- Handler handler = new Handler(); // Main thread
- handler.postDelayed(new Runnable() {
- @Override
- public void run() {
- if (bufferingUpdateListener != null) {
- bufferingUpdateListener.onBufferingUpdate(null, mExoPlayer.getBufferedPercentage());
- }
- if (shouldCheckBufferingUpdates) {
- handler.postDelayed(this, 2000);
- }
- }
- }, 2000);
+ bufferingUpdateDisposable = Observable.interval(2, TimeUnit.SECONDS)
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(aLong -> {
+ if (bufferingUpdateListener != null) {
+ bufferingUpdateListener.onBufferingUpdate(null, mExoPlayer.getBufferedPercentage());
+ }
+ });
}
private SimpleExoPlayer createPlayer() {
@@ -168,7 +169,7 @@ public class ExoPlayerWrapper implements IPlayer {
@Override
public void release() {
- shouldCheckBufferingUpdates = false;
+ bufferingUpdateDisposable.dispose();
if (mExoPlayer != null) {
mExoPlayer.release();
}
@@ -202,8 +203,12 @@ public class ExoPlayerWrapper implements IPlayer {
@Override
public void setDataSource(String s) throws IllegalArgumentException, IllegalStateException {
- DataSource.Factory dataSourceFactory =
- new DefaultDataSourceFactory(mContext, Util.getUserAgent(mContext, mContext.getPackageName()), null);
+ DefaultHttpDataSourceFactory httpDataSourceFactory = new DefaultHttpDataSourceFactory(
+ Util.getUserAgent(mContext, mContext.getPackageName()), null,
+ DefaultHttpDataSource.DEFAULT_CONNECT_TIMEOUT_MILLIS,
+ DefaultHttpDataSource.DEFAULT_READ_TIMEOUT_MILLIS,
+ true);
+ DataSource.Factory dataSourceFactory = new DefaultDataSourceFactory(mContext, null, httpDataSourceFactory);
ExtractorMediaSource.Factory f = new ExtractorMediaSource.Factory(dataSourceFactory);
mediaSource = f.createMediaSource(Uri.parse(s));
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java
index ace89e40a..a3535616e 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java
@@ -451,7 +451,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
notificationBuilder.loadIcon(getPlayable());
}
}
- startForeground(NOTIFICATION_ID, notificationBuilder.build());
+ stateManager.startForeground(NOTIFICATION_ID, notificationBuilder.build());
}
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(this);
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceNotificationBuilder.java b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceNotificationBuilder.java
index 0e7339ac4..1a13fe5a7 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceNotificationBuilder.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceNotificationBuilder.java
@@ -115,7 +115,7 @@ public class PlaybackServiceNotificationBuilder extends NotificationCompat.Build
stopCastingIntent.putExtra(PlaybackService.EXTRA_CAST_DISCONNECT, true);
PendingIntent stopCastingPendingIntent = PendingIntent.getService(context,
numActions, stopCastingIntent, PendingIntent.FLAG_UPDATE_CURRENT);
- addAction(R.drawable.ic_media_cast_disconnect,
+ addAction(R.drawable.ic_notification_cast_off,
context.getString(R.string.cast_disconnect_label),
stopCastingPendingIntent);
numActions++;
@@ -124,7 +124,7 @@ public class PlaybackServiceNotificationBuilder extends NotificationCompat.Build
// always let them rewind
PendingIntent rewindButtonPendingIntent = getPendingIntentForMediaAction(
KeyEvent.KEYCODE_MEDIA_REWIND, numActions);
- addAction(android.R.drawable.ic_media_rew, context.getString(R.string.rewind_label), rewindButtonPendingIntent);
+ addAction(R.drawable.ic_notification_fast_rewind, context.getString(R.string.rewind_label), rewindButtonPendingIntent);
if (UserPreferences.showRewindOnCompactNotification()) {
compactActionList.add(numActions);
}
@@ -133,14 +133,14 @@ public class PlaybackServiceNotificationBuilder extends NotificationCompat.Build
if (playerStatus == PlayerStatus.PLAYING) {
PendingIntent pauseButtonPendingIntent = getPendingIntentForMediaAction(
KeyEvent.KEYCODE_MEDIA_PAUSE, numActions);
- addAction(android.R.drawable.ic_media_pause, //pause action
+ addAction(R.drawable.ic_notification_pause, //pause action
context.getString(R.string.pause_label),
pauseButtonPendingIntent);
compactActionList.add(numActions++);
} else {
PendingIntent playButtonPendingIntent = getPendingIntentForMediaAction(
KeyEvent.KEYCODE_MEDIA_PLAY, numActions);
- addAction(android.R.drawable.ic_media_play, //play action
+ addAction(R.drawable.ic_notification_play, //play action
context.getString(R.string.play_label),
playButtonPendingIntent);
compactActionList.add(numActions++);
@@ -149,7 +149,7 @@ public class PlaybackServiceNotificationBuilder extends NotificationCompat.Build
// ff follows play, then we have skip (if it's present)
PendingIntent ffButtonPendingIntent = getPendingIntentForMediaAction(
KeyEvent.KEYCODE_MEDIA_FAST_FORWARD, numActions);
- addAction(android.R.drawable.ic_media_ff, context.getString(R.string.fast_forward_label), ffButtonPendingIntent);
+ addAction(R.drawable.ic_notification_fast_forward, context.getString(R.string.fast_forward_label), ffButtonPendingIntent);
if (UserPreferences.showFastForwardOnCompactNotification()) {
compactActionList.add(numActions);
}
@@ -158,7 +158,7 @@ public class PlaybackServiceNotificationBuilder extends NotificationCompat.Build
if (UserPreferences.isFollowQueue()) {
PendingIntent skipButtonPendingIntent = getPendingIntentForMediaAction(
KeyEvent.KEYCODE_MEDIA_NEXT, numActions);
- addAction(android.R.drawable.ic_media_next,
+ addAction(R.drawable.ic_notification_skip,
context.getString(R.string.skip_episode_label),
skipButtonPendingIntent);
if (UserPreferences.showSkipOnCompactNotification()) {
diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/DBTasks.java b/core/src/main/java/de/danoeh/antennapod/core/storage/DBTasks.java
index 0fb181299..4c15f5f00 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/storage/DBTasks.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/storage/DBTasks.java
@@ -3,7 +3,7 @@ package de.danoeh.antennapod.core.storage;
import android.content.Context;
import android.content.SharedPreferences;
import android.database.Cursor;
-import android.support.annotation.Nullable;
+import android.os.Looper;
import android.util.Log;
import java.util.ArrayList;
@@ -144,53 +144,36 @@ public final class DBTasks {
private static final AtomicBoolean isRefreshing = new AtomicBoolean(false);
/**
- * Refreshes a given list of Feeds in a separate Thread. This method might ignore subsequent calls if it is still
+ * Refreshes all feeds.
+ * It must not be from the main thread.
+ * This method might ignore subsequent calls if it is still
* enqueuing Feeds for download from a previous call
*
* @param context Might be used for accessing the database
- * @param feeds List of Feeds that should be refreshed.
*/
- public static void refreshAllFeeds(final Context context, final List<Feed> feeds) {
- refreshAllFeeds(context, feeds, null);
- }
-
- /**
- * Refreshes a given list of Feeds in a separate Thread. This method might ignore subsequent calls if it is still
- * enqueuing Feeds for download from a previous call
- *
- * @param context Might be used for accessing the database
- * @param feeds List of Feeds that should be refreshed.
- * @param callback Called after everything was added enqueued for download. Might be null.
- */
- public static void refreshAllFeeds(final Context context, final List<Feed> feeds, @Nullable Runnable callback) {
+ public static void refreshAllFeeds(final Context context) {
if (!isRefreshing.compareAndSet(false, true)) {
Log.d(TAG, "Ignoring request to refresh all feeds: Refresh lock is locked");
return;
}
- new Thread(() -> {
- if (feeds != null) {
- refreshFeeds(context, feeds);
- } else {
- refreshFeeds(context, DBReader.getFeedList());
- }
- isRefreshing.set(false);
+ if (Looper.myLooper() == Looper.getMainLooper()) {
+ throw new IllegalStateException("DBTasks.refreshAllFeeds() must not be called from the main thread.");
+ }
- SharedPreferences prefs = context.getSharedPreferences(PREF_NAME, MODE_PRIVATE);
- prefs.edit().putLong(PREF_LAST_REFRESH, System.currentTimeMillis()).apply();
+ refreshFeeds(context, DBReader.getFeedList());
+ isRefreshing.set(false);
- if (ClientConfig.gpodnetCallbacks.gpodnetEnabled()) {
- GpodnetSyncService.sendSyncIntent(context);
- }
- // Note: automatic download of episodes will be done but not here.
- // Instead it is done after all feeds have been refreshed (asynchronously),
- // in DownloadService.onDestroy()
- // See Issue #2577 for the details of the rationale
+ SharedPreferences prefs = context.getSharedPreferences(PREF_NAME, MODE_PRIVATE);
+ prefs.edit().putLong(PREF_LAST_REFRESH, System.currentTimeMillis()).apply();
- if (callback != null) {
- callback.run();
- }
- }).start();
+ if (ClientConfig.gpodnetCallbacks.gpodnetEnabled()) {
+ GpodnetSyncService.sendSyncIntent(context);
+ }
+ // Note: automatic download of episodes will be done but not here.
+ // Instead it is done after all feeds have been refreshed (asynchronously),
+ // in DownloadService.onDestroy()
+ // See Issue #2577 for the details of the rationale
}
/**
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/FeedUpdateUtils.java b/core/src/main/java/de/danoeh/antennapod/core/util/FeedUpdateUtils.java
deleted file mode 100644
index b425687ae..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/util/FeedUpdateUtils.java
+++ /dev/null
@@ -1,36 +0,0 @@
-package de.danoeh.antennapod.core.util;
-
-import android.content.Context;
-import android.util.Log;
-
-import org.awaitility.core.ConditionTimeoutException;
-
-import java.util.concurrent.TimeUnit;
-
-import de.danoeh.antennapod.core.storage.DBTasks;
-
-import static org.awaitility.Awaitility.with;
-
-public class FeedUpdateUtils {
- private static final String TAG = "FeedUpdateUtils";
-
- private FeedUpdateUtils() {}
-
- public static void startAutoUpdate(Context context, Runnable callback) {
- // the network check is blocking for possibly a long time: so run the logic
- // in a separate thread to prevent the code blocking the callers
- final Runnable runnable = () -> {
- try {
- with().pollInterval(1, TimeUnit.SECONDS)
- .await()
- .atMost(10, TimeUnit.SECONDS)
- .until(() -> NetworkUtils.networkAvailable() && NetworkUtils.isFeedRefreshAllowed());
- DBTasks.refreshAllFeeds(context, null, callback);
- } catch (ConditionTimeoutException ignore) {
- Log.d(TAG, "Blocking automatic update: no wifi available / no mobile updates allowed");
- }
- };
- new Thread(runnable).start();
- }
-
-}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/IntentUtils.java b/core/src/main/java/de/danoeh/antennapod/core/util/IntentUtils.java
index e81ab47ed..656b518bf 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/IntentUtils.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/IntentUtils.java
@@ -1,13 +1,20 @@
package de.danoeh.antennapod.core.util;
+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.util.Log;
+import android.widget.Toast;
+import de.danoeh.antennapod.core.R;
import java.util.List;
public class IntentUtils {
+ private static final String TAG = "IntentUtils";
+
private IntentUtils(){}
/*
@@ -28,4 +35,13 @@ public class IntentUtils {
context.sendBroadcast(new Intent(action).setPackage(context.getPackageName()));
}
+ public static void openInBrowser(Context context, String url) {
+ try {
+ Intent myIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
+ context.startActivity(myIntent);
+ } catch (ActivityNotFoundException e) {
+ Toast.makeText(context, R.string.pref_no_browser_found, Toast.LENGTH_LONG).show();
+ Log.e(TAG, Log.getStackTraceString(e));
+ }
+ }
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/download/AutoUpdateManager.java b/core/src/main/java/de/danoeh/antennapod/core/util/download/AutoUpdateManager.java
index 412b150fa..ebeec058d 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/download/AutoUpdateManager.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/download/AutoUpdateManager.java
@@ -1,21 +1,29 @@
package de.danoeh.antennapod.core.util.download;
+import android.content.Context;
+import android.support.annotation.NonNull;
import android.util.Log;
+
import androidx.work.Constraints;
+import androidx.work.Data;
import androidx.work.ExistingPeriodicWorkPolicy;
import androidx.work.ExistingWorkPolicy;
import androidx.work.NetworkType;
import androidx.work.OneTimeWorkRequest;
import androidx.work.PeriodicWorkRequest;
import androidx.work.WorkManager;
-import de.danoeh.antennapod.core.preferences.UserPreferences;
-import de.danoeh.antennapod.core.service.FeedUpdateWorker;
+import java.util.Arrays;
import java.util.Calendar;
import java.util.concurrent.TimeUnit;
+import de.danoeh.antennapod.core.preferences.UserPreferences;
+import de.danoeh.antennapod.core.service.FeedUpdateWorker;
+import de.danoeh.antennapod.core.storage.DBTasks;
+
public class AutoUpdateManager {
- private static final String WORK_ID_FEED_UPDATE = FeedUpdateWorker.class.getName();
+ private static final String WORK_ID_FEED_UPDATE = "de.danoeh.antennapod.core.service.FeedUpdateWorker";
+ private static final String WORK_ID_FEED_UPDATE_ONCE = WORK_ID_FEED_UPDATE + "Once";
private static final String TAG = "AutoUpdateManager";
private AutoUpdateManager() {
@@ -23,9 +31,25 @@ public class AutoUpdateManager {
}
/**
+ * Start / restart periodic auto feed refresh
+ */
+ public static void restartUpdateAlarm() {
+ if (UserPreferences.isAutoUpdateDisabled()) {
+ disableAutoUpdate();
+ } else if (UserPreferences.isAutoUpdateTimeOfDay()) {
+ int[] timeOfDay = UserPreferences.getUpdateTimeOfDay();
+ Log.d(TAG, "timeOfDay: " + Arrays.toString(timeOfDay));
+ restartUpdateTimeOfDayAlarm(timeOfDay[0], timeOfDay[1]);
+ } else {
+ long milliseconds = UserPreferences.getUpdateInterval();
+ restartUpdateIntervalAlarm(milliseconds);
+ }
+ }
+
+ /**
* Sets the interval in which the feeds are refreshed automatically
*/
- public static void restartUpdateIntervalAlarm(long intervalMillis) {
+ private static void restartUpdateIntervalAlarm(long intervalMillis) {
Log.d(TAG, "Restarting update alarm.");
PeriodicWorkRequest workRequest = new PeriodicWorkRequest.Builder(FeedUpdateWorker.class,
@@ -40,7 +64,7 @@ public class AutoUpdateManager {
/**
* Sets time of day the feeds are refreshed automatically
*/
- public static void restartUpdateTimeOfDayAlarm(int hoursOfDay, int minute) {
+ private static void restartUpdateTimeOfDayAlarm(int hoursOfDay, int minute) {
Log.d(TAG, "Restarting update alarm.");
Calendar now = Calendar.getInstance();
@@ -60,6 +84,41 @@ public class AutoUpdateManager {
WorkManager.getInstance().enqueueUniqueWork(WORK_ID_FEED_UPDATE, ExistingWorkPolicy.REPLACE, workRequest);
}
+ /**
+ * Run auto feed refresh once in background, as soon as what OS scheduling allows.
+ *
+ * Callers from UI should use {@link #runImmediate(Context)}, as it will guarantee
+ * the refresh be run immediately.
+ */
+ public static void runOnce() {
+ Log.d(TAG, "Run auto update once, as soon as OS allows.");
+
+ OneTimeWorkRequest workRequest = new OneTimeWorkRequest.Builder(FeedUpdateWorker.class)
+ .setConstraints(getConstraints())
+ .setInitialDelay(0L, TimeUnit.MILLISECONDS)
+ .setInputData(new Data.Builder()
+ .putBoolean(FeedUpdateWorker.PARAM_RUN_ONCE, true)
+ .build()
+ )
+ .build();
+
+ WorkManager.getInstance().enqueueUniqueWork(WORK_ID_FEED_UPDATE_ONCE, ExistingWorkPolicy.REPLACE, workRequest);
+
+ }
+
+ /**
+ /**
+ * Run auto feed refresh once in background immediately, using its own thread.
+ *
+ * Callers where the additional threads is not suitable should use {@link #runOnce()}
+ */
+ public static void runImmediate(@NonNull Context context) {
+ Log.d(TAG, "Run auto update immediately in background.");
+ new Thread(() -> {
+ DBTasks.refreshAllFeeds(context.getApplicationContext());
+ }, "ManualRefreshAllFeeds").start();
+ }
+
public static void disableAutoUpdate() {
WorkManager.getInstance().cancelUniqueWork(WORK_ID_FEED_UPDATE);
}
@@ -74,4 +133,5 @@ public class AutoUpdateManager {
}
return constraints.build();
}
+
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/exception/RxJavaErrorHandlerSetup.java b/core/src/main/java/de/danoeh/antennapod/core/util/exception/RxJavaErrorHandlerSetup.java
index 12f0c1c6e..223104d2e 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/exception/RxJavaErrorHandlerSetup.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/exception/RxJavaErrorHandlerSetup.java
@@ -1,12 +1,9 @@
package de.danoeh.antennapod.core.util.exception;
import android.util.Log;
-import de.danoeh.antennapod.core.gpoddernet.GpodnetServiceException;
import io.reactivex.exceptions.UndeliverableException;
import io.reactivex.plugins.RxJavaPlugins;
-import java.io.InterruptedIOException;
-
public class RxJavaErrorHandlerSetup {
private RxJavaErrorHandlerSetup() {
@@ -14,21 +11,14 @@ public class RxJavaErrorHandlerSetup {
}
public static void setupRxJavaErrorHandler() {
- RxJavaPlugins.setErrorHandler(originalCause -> {
- Throwable e = originalCause;
+ RxJavaPlugins.setErrorHandler(e -> {
if (e instanceof UndeliverableException) {
- e = e.getCause();
- }
- if (e instanceof GpodnetServiceException) {
- e = e.getCause();
- }
- if (e instanceof InterruptedException || e instanceof InterruptedIOException) {
- // fine, some blocking code was interrupted by a dispose call
- Log.d("RxJavaErrorHandler", "Ignored exception: " + Log.getStackTraceString(originalCause));
+ // Probably just disposed because the fragment was left
+ Log.d("RxJavaErrorHandler", "Ignored exception: " + Log.getStackTraceString(e));
return;
}
Thread.currentThread().getUncaughtExceptionHandler()
- .uncaughtException(Thread.currentThread(), originalCause);
+ .uncaughtException(Thread.currentThread(), e);
});
}
}
diff --git a/core/src/main/res/drawable-hdpi/ic_media_cast_disconnect.png b/core/src/main/res/drawable-hdpi/ic_media_cast_disconnect.png
deleted file mode 100644
index 700c116e5..000000000
--- a/core/src/main/res/drawable-hdpi/ic_media_cast_disconnect.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-mdpi/ic_media_cast_disconnect.png b/core/src/main/res/drawable-mdpi/ic_media_cast_disconnect.png
deleted file mode 100644
index 767f420df..000000000
--- a/core/src/main/res/drawable-mdpi/ic_media_cast_disconnect.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xhdpi/ic_media_cast_disconnect.png b/core/src/main/res/drawable-xhdpi/ic_media_cast_disconnect.png
deleted file mode 100644
index 740867129..000000000
--- a/core/src/main/res/drawable-xhdpi/ic_media_cast_disconnect.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xxhdpi/ic_media_cast_disconnect.png b/core/src/main/res/drawable-xxhdpi/ic_media_cast_disconnect.png
deleted file mode 100644
index 2d2ec9035..000000000
--- a/core/src/main/res/drawable-xxhdpi/ic_media_cast_disconnect.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable/ic_notification_cast_off.xml b/core/src/main/res/drawable/ic_notification_cast_off.xml
new file mode 100644
index 000000000..63a21fbe2
--- /dev/null
+++ b/core/src/main/res/drawable/ic_notification_cast_off.xml
@@ -0,0 +1,5 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="24dp" android:viewportHeight="24.0"
+ android:viewportWidth="24.0" android:width="24dp">
+ <path android:fillColor="#FFFFFFFF" android:pathData="M1.6,1.27L0.25,2.75L1.41,3.8C1.16,4.13 1,4.55 1,5V8H3V5.23L18.2,19H14V21H20.41L22.31,22.72L23.65,21.24M6.5,3L8.7,5H21V16.14L23,17.95V5C23,3.89 22.1,3 21,3M1,10V12A9,9 0 0,1 10,21H12C12,14.92 7.08,10 1,10M1,14V16A5,5 0 0,1 6,21H8A7,7 0 0,0 1,14M1,18V21H4A3,3 0 0,0 1,18Z" />
+</vector> \ No newline at end of file
diff --git a/core/src/main/res/drawable/ic_notification_fast_forward.xml b/core/src/main/res/drawable/ic_notification_fast_forward.xml
new file mode 100644
index 000000000..bf564977c
--- /dev/null
+++ b/core/src/main/res/drawable/ic_notification_fast_forward.xml
@@ -0,0 +1,5 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="24dp" android:viewportHeight="24.0"
+ android:viewportWidth="24.0" android:width="24dp">
+ <path android:fillColor="#FFFFFFFF" android:pathData="M4,18l8.5,-6L4,6v12zM13,6v12l8.5,-6L13,6z"/>
+</vector>
diff --git a/core/src/main/res/drawable/ic_notification_fast_rewind.xml b/core/src/main/res/drawable/ic_notification_fast_rewind.xml
new file mode 100644
index 000000000..847159cc5
--- /dev/null
+++ b/core/src/main/res/drawable/ic_notification_fast_rewind.xml
@@ -0,0 +1,5 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="24dp" android:viewportHeight="24.0"
+ android:viewportWidth="24.0" android:width="24dp">
+ <path android:fillColor="#FFFFFFFF" android:pathData="M11,18L11,6l-8.5,6 8.5,6zM11.5,12l8.5,6L20,6l-8.5,6z"/>
+</vector>
diff --git a/core/src/main/res/drawable/ic_notification_key.xml b/core/src/main/res/drawable/ic_notification_key.xml
index b1e2f9b8c..c8a817eeb 100644
--- a/core/src/main/res/drawable/ic_notification_key.xml
+++ b/core/src/main/res/drawable/ic_notification_key.xml
@@ -1,5 +1,6 @@
-<vector android:height="24dp"
- android:viewportHeight="24.0" android:viewportWidth="24.0"
- android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="#FFFFFFFF" android:pathData="M12.65,10C11.83,7.67 9.61,6 7,6c-3.31,0 -6,2.69 -6,6s2.69,6 6,6c2.61,0 4.83,-1.67 5.65,-4H17v4h4v-4h2v-4H12.65zM7,14c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2 2,0.9 2,2 -0.9,2 -2,2z"/>
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="24dp" android:viewportHeight="24.0"
+ android:viewportWidth="24.0" android:width="24dp">
+ <path android:fillColor="#FFFFFFFF"
+ android:pathData="M12.65,10C11.83,7.67 9.61,6 7,6c-3.31,0 -6,2.69 -6,6s2.69,6 6,6c2.61,0 4.83,-1.67 5.65,-4H17v4h4v-4h2v-4H12.65zM7,14c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2 2,0.9 2,2 -0.9,2 -2,2z"/>
</vector>
diff --git a/core/src/main/res/drawable/ic_notification_pause.xml b/core/src/main/res/drawable/ic_notification_pause.xml
new file mode 100644
index 000000000..d46efb2f5
--- /dev/null
+++ b/core/src/main/res/drawable/ic_notification_pause.xml
@@ -0,0 +1,5 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="24dp" android:viewportHeight="24.0"
+ android:viewportWidth="24.0" android:width="24dp">
+ <path android:fillColor="#FFFFFFFF" android:pathData="M6,19h4L10,5L6,5v14zM14,5v14h4L18,5h-4z"/>
+</vector>
diff --git a/core/src/main/res/drawable/ic_notification_play.xml b/core/src/main/res/drawable/ic_notification_play.xml
new file mode 100644
index 000000000..d571460c6
--- /dev/null
+++ b/core/src/main/res/drawable/ic_notification_play.xml
@@ -0,0 +1,5 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="24dp" android:viewportHeight="24.0"
+ android:viewportWidth="24.0" android:width="24dp">
+ <path android:fillColor="#FFFFFFFF" android:pathData="M8,5v14l11,-7z"/>
+</vector>
diff --git a/core/src/main/res/drawable/ic_notification_skip.xml b/core/src/main/res/drawable/ic_notification_skip.xml
new file mode 100644
index 000000000..0c65448cc
--- /dev/null
+++ b/core/src/main/res/drawable/ic_notification_skip.xml
@@ -0,0 +1,5 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="24dp" android:viewportHeight="24.0"
+ android:viewportWidth="24.0" android:width="24dp">
+ <path android:fillColor="#FFFFFFFF" android:pathData="M6,18l8.5,-6L6,6v12zM16,6v12h2V6h-2z"/>
+</vector>