Merge branch 'develop' into feedinfo-fragment
This commit is contained in:
commit
466e6a9b7e
|
@ -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
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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/", "");
|
||||
|
|
|
@ -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 -> {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -2,6 +2,13 @@
|
|||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
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"
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -25,4 +25,8 @@ public class DownloadEvent {
|
|||
"update=" + update +
|
||||
'}';
|
||||
}
|
||||
|
||||
public boolean hasChangedFeedUpdateStatus(boolean oldStatus) {
|
||||
return oldStatus != update.feedIds.length > 0;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,31 +1,55 @@
|
|||
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() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 2.3 KiB |
Binary file not shown.
Before Width: | Height: | Size: 1.4 KiB |
Binary file not shown.
Before Width: | Height: | Size: 4.4 KiB |
Binary file not shown.
Before Width: | Height: | Size: 6.9 KiB |
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
Loading…
Reference in New Issue