summaryrefslogtreecommitdiff
path: root/app/src/main/java/de/danoeh/antennapod
diff options
context:
space:
mode:
authorH. Lehmann <ByteHamster@users.noreply.github.com>2019-08-11 14:57:44 +0200
committerGitHub <noreply@github.com>2019-08-11 14:57:44 +0200
commit1315c9e20b30e62b8022f831a0bcf5779b5bb2cb (patch)
tree65dd884ea83389d366d971ca5635e6e9f6c95dc4 /app/src/main/java/de/danoeh/antennapod
parentab864cd171a563177a8f565fc2744e88ba669516 (diff)
parentce64c412ac02041d877e4770381f29710a06ba17 (diff)
downloadAntennaPod-1315c9e20b30e62b8022f831a0bcf5779b5bb2cb.zip
Merge branch 'develop' into make_multidex_on_debug_build_only
Diffstat (limited to 'app/src/main/java/de/danoeh/antennapod')
-rw-r--r--app/src/main/java/de/danoeh/antennapod/PodcastApp.java6
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/AboutActivity.java17
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/AudioplayerActivity.java1
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/FeedInfoActivity.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/FeedSettingsActivity.java332
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/FlattrAuthActivity.java120
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/ImportExportActivity.java30
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java35
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java88
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/MediaplayerInfoActivity.java126
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java147
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/OpmlImportFromPathActivity.java59
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/PreferenceActivity.java188
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/gpoddernet/GpodnetAuthenticationActivity.java17
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/ActionButtonCallback.java9
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/ActionButtonUtils.java97
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/AdapterUtils.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/AllEpisodesRecycleAdapter.java23
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/CoverLoader.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/DataFolderAdapter.java148
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/DefaultActionButtonCallback.java139
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/FeedDiscoverAdapter.java76
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistAdapter.java20
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/NavListAdapter.java8
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/QueueRecyclerAdapter.java26
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/AddToQueueActionButton.java32
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/CancelDownloadActionButton.java44
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/DownloadActionButton.java74
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/ItemActionButton.java63
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/MarkAsPlayedActionButton.java41
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/MobileDownloadHelper.java60
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/PlayActionButton.java63
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/itunes/ItunesAdapter.java84
-rw-r--r--app/src/main/java/de/danoeh/antennapod/asynctask/ExportWorker.java8
-rw-r--r--app/src/main/java/de/danoeh/antennapod/config/ClientConfigurator.java1
-rw-r--r--app/src/main/java/de/danoeh/antennapod/config/FlattrCallbacksImpl.java53
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/AutoFlattrPreferenceDialog.java97
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/ChooseDataFolderDialog.java64
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/EpisodeFilterDialog.java71
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/EpisodesApplyActionFragment.java226
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/ProxyDialog.java17
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/RatingDialog.java4
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/SleepTimerDialog.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/discovery/CombinedSearcher.java96
-rw-r--r--app/src/main/java/de/danoeh/antennapod/discovery/FyydPodcastSearcher.java38
-rw-r--r--app/src/main/java/de/danoeh/antennapod/discovery/GpodnetPodcastSearcher.java38
-rw-r--r--app/src/main/java/de/danoeh/antennapod/discovery/ItunesPodcastSearcher.java74
-rw-r--r--app/src/main/java/de/danoeh/antennapod/discovery/ItunesTopListLoader.java110
-rw-r--r--app/src/main/java/de/danoeh/antennapod/discovery/PodcastSearchResult.java81
-rw-r--r--app/src/main/java/de/danoeh/antennapod/discovery/PodcastSearcher.java10
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/AddFeedFragment.java99
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java299
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ChaptersFragment.java125
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/CombinedSearchFragment.java173
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java135
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/CoverFragment.java115
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/DownloadLogFragment.java77
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java44
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/FavoriteEpisodesFragment.java39
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/FeedSettingsFragment.java177
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/FyydSearchFragment.java77
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java190
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java58
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ItemlistFragment.java108
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ItunesSearchFragment.java202
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/NewEpisodesFragment.java47
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java113
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java154
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/QuickFeedDiscoveryFragment.java119
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/RunningDownloadsFragment.java36
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java103
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/SubscriptionFragment.java195
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/AutoDownloadPreferencesFragment.java184
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/GpodderPreferencesFragment.java144
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/IntegrationsPreferencesFragment.java23
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/MainPreferencesFragment.java147
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/NetworkPreferencesFragment.java175
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/PlaybackPreferencesFragment.java92
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/StoragePreferencesFragment.java244
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/UserInterfacePreferencesFragment.java165
-rw-r--r--app/src/main/java/de/danoeh/antennapod/menuhandler/FeedItemMenuHandler.java20
-rw-r--r--app/src/main/java/de/danoeh/antennapod/menuhandler/FeedMenuHandler.java8
-rw-r--r--app/src/main/java/de/danoeh/antennapod/preferences/PreferenceController.java1218
-rw-r--r--app/src/main/java/de/danoeh/antennapod/preferences/PreferenceUpgrader.java69
-rw-r--r--app/src/main/java/de/danoeh/antennapod/view/EmptyViewHandler.java112
-rw-r--r--app/src/main/java/de/danoeh/antennapod/view/SimpleAdapterDataObserver.java41
-rw-r--r--app/src/main/java/de/danoeh/antennapod/view/WrappingGridView.java35
-rw-r--r--app/src/main/java/de/danoeh/antennapod/viewmodel/FeedSettingsViewModel.java30
88 files changed, 4688 insertions, 3873 deletions
diff --git a/app/src/main/java/de/danoeh/antennapod/PodcastApp.java b/app/src/main/java/de/danoeh/antennapod/PodcastApp.java
index fde9af16f..cb2f597d6 100644
--- a/app/src/main/java/de/danoeh/antennapod/PodcastApp.java
+++ b/app/src/main/java/de/danoeh/antennapod/PodcastApp.java
@@ -8,9 +8,11 @@ import com.joanzapata.iconify.Iconify;
import com.joanzapata.iconify.fonts.FontAwesomeModule;
import com.joanzapata.iconify.fonts.MaterialModule;
+import de.danoeh.antennapod.core.ApCoreEventBusIndex;
import de.danoeh.antennapod.core.ClientConfig;
import de.danoeh.antennapod.core.feed.EventDistributor;
import de.danoeh.antennapod.spa.SPAUtil;
+import org.greenrobot.eventbus.EventBus;
/** Main application class. */
public class PodcastApp extends Application {
@@ -58,6 +60,10 @@ public class PodcastApp extends Application {
Iconify.with(new MaterialModule());
SPAUtil.sendSPAppsQueryFeedsIntent(this);
+ EventBus.builder()
+ .addIndex(new ApEventBusIndex())
+ .addIndex(new ApCoreEventBusIndex())
+ .installDefaultEventBus();
}
}
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 ecfdf24b0..1bcdada44 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/AboutActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/AboutActivity.java
@@ -1,7 +1,9 @@
package de.danoeh.antennapod.activity;
+import android.content.Intent;
import android.content.res.TypedArray;
import android.graphics.Color;
+import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
@@ -46,22 +48,23 @@ public class AboutActivity extends AppCompatActivity {
webViewContainer = findViewById(R.id.webViewContainer);
webView = findViewById(R.id.webViewAbout);
webView.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE);
- if (UserPreferences.getTheme() == R.style.Theme_AntennaPod_Dark) {
- if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
- webView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
- }
- webView.setBackgroundColor(Color.TRANSPARENT);
+ if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
+ webView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
}
+ webView.setBackgroundColor(Color.TRANSPARENT);
webView.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
- if (!url.startsWith("http")) {
+ if (url.startsWith("http")) {
+ Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
+ startActivity(browserIntent);
+ return true;
+ } else {
url = url.replace("file:///android_asset/", "");
loadAsset(url);
return true;
}
- return false;
}
});
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/AudioplayerActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/AudioplayerActivity.java
index 67dda01cf..2bcd7a461 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/AudioplayerActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/AudioplayerActivity.java
@@ -129,6 +129,7 @@ public class AudioplayerActivity extends MediaplayerInfoActivity {
}
UserPreferences.setPlaybackSpeed(newSpeed);
controller.setPlaybackSpeed(Float.parseFloat(newSpeed));
+ onPositionObserverUpdate();
} else {
VariableSpeedDialog.showGetPluginDialog(this);
}
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/FeedInfoActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/FeedInfoActivity.java
index bfa694e5c..26e360bd3 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/FeedInfoActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/FeedInfoActivity.java
@@ -199,8 +199,6 @@ public class FeedInfoActivity extends AppCompatActivity {
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
super.onPrepareOptionsMenu(menu);
- menu.findItem(R.id.support_item).setVisible(
- feed != null && feed.getPaymentLink() != null);
menu.findItem(R.id.share_link_item).setVisible(feed != null && feed.getLink() != null);
menu.findItem(R.id.visit_website_item).setVisible(feed != null && feed.getLink() != null &&
IntentUtils.isCallable(this, new Intent(Intent.ACTION_VIEW, Uri.parse(feed.getLink()))));
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/FeedSettingsActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/FeedSettingsActivity.java
index 4698ed90e..fbd19f88a 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/FeedSettingsActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/FeedSettingsActivity.java
@@ -1,48 +1,26 @@
package de.danoeh.antennapod.activity;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.Intent;
+import android.arch.lifecycle.ViewModelProviders;
import android.graphics.LightingColorFilter;
-import android.net.Uri;
import android.os.Bundle;
+import android.support.v4.app.FragmentManager;
+import android.support.v4.app.FragmentTransaction;
import android.support.v7.app.AppCompatActivity;
-import android.text.Editable;
import android.text.TextUtils;
-import android.text.TextWatcher;
import android.util.Log;
-import android.view.Menu;
-import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
-import android.widget.AdapterView;
-import android.widget.AdapterView.OnItemSelectedListener;
-import android.widget.CheckBox;
-import android.widget.EditText;
import android.widget.ImageView;
-import android.widget.RadioButton;
-import android.widget.Spinner;
import android.widget.TextView;
-
import com.bumptech.glide.Glide;
import com.bumptech.glide.request.RequestOptions;
-
import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.core.dialog.ConfirmationDialog;
-import de.danoeh.antennapod.core.dialog.DownloadRequestErrorDialogCreator;
import de.danoeh.antennapod.core.feed.Feed;
-import de.danoeh.antennapod.core.feed.FeedFilter;
-import de.danoeh.antennapod.core.feed.FeedPreferences;
import de.danoeh.antennapod.core.glide.ApGlideSettings;
import de.danoeh.antennapod.core.glide.FastBlurTransformation;
import de.danoeh.antennapod.core.preferences.UserPreferences;
-import de.danoeh.antennapod.core.storage.DBReader;
-import de.danoeh.antennapod.core.storage.DBWriter;
-import de.danoeh.antennapod.core.storage.DownloadRequestException;
-import de.danoeh.antennapod.core.util.IntentUtils;
-import de.danoeh.antennapod.menuhandler.FeedMenuHandler;
-import io.reactivex.Maybe;
-import io.reactivex.MaybeOnSubscribe;
+import de.danoeh.antennapod.fragment.FeedSettingsFragment;
+import de.danoeh.antennapod.viewmodel.FeedSettingsViewModel;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
@@ -51,59 +29,14 @@ import io.reactivex.schedulers.Schedulers;
* Displays information about a feed.
*/
public class FeedSettingsActivity extends AppCompatActivity {
-
public static final String EXTRA_FEED_ID = "de.danoeh.antennapod.extra.feedId";
private static final String TAG = "FeedSettingsActivity";
- private boolean autoDeleteChanged = false;
private Feed feed;
-
+ private Disposable disposable;
private ImageView imgvCover;
private TextView txtvTitle;
- private EditText etxtUsername;
- private EditText etxtPassword;
- private EditText etxtFilterText;
- private RadioButton rdoFilterInclude;
- private RadioButton rdoFilterExclude;
- private CheckBox cbxAutoDownload;
- private CheckBox cbxKeepUpdated;
- private Spinner spnAutoDelete;
- private boolean filterInclude = true;
-
- private Disposable disposable;
-
- private boolean authInfoChanged = false;
-
- private final TextWatcher authTextWatcher = new TextWatcher() {
- @Override
- public void beforeTextChanged(CharSequence s, int start, int count, int after) {
- }
-
- @Override
- public void onTextChanged(CharSequence s, int start, int before, int count) {
- }
-
- @Override
- public void afterTextChanged(Editable s) {
- authInfoChanged = true;
- }
- };
-
- private boolean filterTextChanged = false;
-
- private final TextWatcher filterTextWatcher = new TextWatcher() {
- @Override
- public void beforeTextChanged(CharSequence s, int start, int count, int after) {
- }
-
- @Override
- public void onTextChanged(CharSequence s, int start, int before, int count) {
- }
-
- @Override
- public void afterTextChanged(Editable s) {
- filterTextChanged = true;
- }
- };
+ private ImageView imgvBackground;
+ private TextView txtvAuthorHeader;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -111,142 +44,24 @@ public class FeedSettingsActivity extends AppCompatActivity {
super.onCreate(savedInstanceState);
setContentView(R.layout.feedsettings);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
- long feedId = getIntent().getLongExtra(EXTRA_FEED_ID, -1);
imgvCover = findViewById(R.id.imgvCover);
txtvTitle = findViewById(R.id.txtvTitle);
- TextView txtvAuthorHeader = findViewById(R.id.txtvAuthor);
- ImageView imgvBackground = findViewById(R.id.imgvBackground);
+ txtvAuthorHeader = findViewById(R.id.txtvAuthor);
+ imgvBackground = findViewById(R.id.imgvBackground);
findViewById(R.id.butShowInfo).setVisibility(View.INVISIBLE);
findViewById(R.id.butShowSettings).setVisibility(View.INVISIBLE);
// https://github.com/bumptech/glide/issues/529
imgvBackground.setColorFilter(new LightingColorFilter(0xff828282, 0x000000));
- cbxAutoDownload = findViewById(R.id.cbxAutoDownload);
- cbxKeepUpdated = findViewById(R.id.cbxKeepUpdated);
- spnAutoDelete = findViewById(R.id.spnAutoDelete);
- etxtUsername = findViewById(R.id.etxtUsername);
- etxtPassword = findViewById(R.id.etxtPassword);
- etxtFilterText = findViewById(R.id.etxtEpisodeFilterText);
- rdoFilterInclude = findViewById(R.id.radio_filter_include);
- rdoFilterInclude.setOnClickListener(v -> {
- filterInclude = true;
- filterTextChanged = true;
- });
- rdoFilterExclude = findViewById(R.id.radio_filter_exclude);
- rdoFilterExclude.setOnClickListener(v -> {
- filterInclude = false;
- filterTextChanged = true;
- });
-
- disposable = Maybe.create((MaybeOnSubscribe<Feed>) emitter -> {
- Feed feed = DBReader.getFeed(feedId);
- if (feed != null) {
- emitter.onSuccess(feed);
- } else {
- emitter.onComplete();
- }
- })
+ long feedId = getIntent().getLongExtra(EXTRA_FEED_ID, -1);
+ disposable = ViewModelProviders.of(this).get(FeedSettingsViewModel.class).getFeed(feedId)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(result -> {
feed = result;
- FeedPreferences prefs = feed.getPreferences();
- Glide.with(FeedSettingsActivity.this)
- .load(feed.getImageLocation())
- .apply(new RequestOptions()
- .placeholder(R.color.light_gray)
- .error(R.color.light_gray)
- .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
- .fitCenter()
- .dontAnimate())
- .into(imgvCover);
- Glide.with(FeedSettingsActivity.this)
- .load(feed.getImageLocation())
- .apply(new RequestOptions()
- .placeholder(R.color.image_readability_tint)
- .error(R.color.image_readability_tint)
- .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
- .transform(new FastBlurTransformation())
- .dontAnimate())
- .into(imgvBackground);
-
- txtvTitle.setText(feed.getTitle());
-
- if (!TextUtils.isEmpty(feed.getAuthor())) {
- txtvAuthorHeader.setText(feed.getAuthor());
- }
-
- cbxAutoDownload.setEnabled(UserPreferences.isEnableAutodownload());
- cbxAutoDownload.setChecked(prefs.getAutoDownload());
- cbxAutoDownload.setOnCheckedChangeListener((compoundButton, checked) -> {
- feed.getPreferences().setAutoDownload(checked);
- feed.savePreferences();
- updateAutoDownloadSettings();
- ApplyToEpisodesDialog dialog = new ApplyToEpisodesDialog(FeedSettingsActivity.this,
- feed, checked);
- dialog.createNewDialog().show();
- });
- cbxKeepUpdated.setChecked(prefs.getKeepUpdated());
- cbxKeepUpdated.setOnCheckedChangeListener((compoundButton, checked) -> {
- feed.getPreferences().setKeepUpdated(checked);
- feed.savePreferences();
- });
- spnAutoDelete.setOnItemSelectedListener(new OnItemSelectedListener() {
- @Override
- public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
- FeedPreferences.AutoDeleteAction auto_delete_action;
- switch (parent.getSelectedItemPosition()) {
- case 0:
- auto_delete_action = FeedPreferences.AutoDeleteAction.GLOBAL;
- break;
- case 1:
- auto_delete_action = FeedPreferences.AutoDeleteAction.YES;
- break;
- case 2:
- auto_delete_action = FeedPreferences.AutoDeleteAction.NO;
- break;
- default: // TODO - add exceptions here
- return;
- }
- feed.getPreferences().setAutoDeleteAction(auto_delete_action);// p
- autoDeleteChanged = true;
- }
-
- @Override
- public void onNothingSelected(AdapterView<?> parent) {
- // Another interface callback
- }
- });
- spnAutoDelete.setSelection(prefs.getAutoDeleteAction().ordinal());
-
- etxtUsername.setText(prefs.getUsername());
- etxtPassword.setText(prefs.getPassword());
-
- etxtUsername.addTextChangedListener(authTextWatcher);
- etxtPassword.addTextChangedListener(authTextWatcher);
-
- FeedFilter filter = prefs.getFilter();
- if (filter.includeOnly()) {
- etxtFilterText.setText(filter.getIncludeFilter());
- rdoFilterInclude.setChecked(true);
- rdoFilterExclude.setChecked(false);
- filterInclude = true;
- } else if (filter.excludeOnly()) {
- etxtFilterText.setText(filter.getExcludeFilter());
- rdoFilterInclude.setChecked(false);
- rdoFilterExclude.setChecked(true);
- filterInclude = false;
- } else {
- Log.d(TAG, "No filter set");
- rdoFilterInclude.setChecked(false);
- rdoFilterExclude.setChecked(false);
- etxtFilterText.setText("");
- }
- etxtFilterText.addTextChangedListener(filterTextWatcher);
-
- supportInvalidateOptionsMenu();
- updateAutoDownloadSettings();
+ showFragment();
+ showHeader();
}, error -> {
Log.d(TAG, Log.getStackTraceString(error));
finish();
@@ -256,35 +71,42 @@ public class FeedSettingsActivity extends AppCompatActivity {
});
}
- @Override
- protected void onPause() {
- super.onPause();
- if (feed != null) {
- FeedPreferences prefs = feed.getPreferences();
- if (authInfoChanged) {
- Log.d(TAG, "Auth info changed, saving credentials");
- prefs.setUsername(etxtUsername.getText().toString());
- prefs.setPassword(etxtPassword.getText().toString());
- }
- if (filterTextChanged) {
- Log.d(TAG, "Filter info changed, saving...");
- String filterText = etxtFilterText.getText().toString();
- String includeString = "";
- String excludeString = "";
- if (filterInclude) {
- includeString = filterText;
- } else {
- excludeString = filterText;
- }
- prefs.setFilter(new FeedFilter(includeString, excludeString));
- }
- if (authInfoChanged || autoDeleteChanged || filterTextChanged) {
- DBWriter.setFeedPreferences(prefs);
- }
- authInfoChanged = false;
- autoDeleteChanged = false;
- filterTextChanged = false;
+ private void showFragment() {
+ FeedSettingsFragment fragment = new FeedSettingsFragment();
+ fragment.setArguments(getIntent().getExtras());
+
+ FragmentManager fragmentManager = getSupportFragmentManager();
+ FragmentTransaction fragmentTransaction =
+ fragmentManager.beginTransaction();
+ fragmentTransaction.replace(R.id.settings_fragment_container, fragment);
+ fragmentTransaction.commit();
+ }
+
+ private void showHeader() {
+ txtvTitle.setText(feed.getTitle());
+
+ if (!TextUtils.isEmpty(feed.getAuthor())) {
+ txtvAuthorHeader.setText(feed.getAuthor());
}
+
+ Glide.with(FeedSettingsActivity.this)
+ .load(feed.getImageLocation())
+ .apply(new RequestOptions()
+ .placeholder(R.color.light_gray)
+ .error(R.color.light_gray)
+ .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
+ .fitCenter()
+ .dontAnimate())
+ .into(imgvCover);
+ Glide.with(FeedSettingsActivity.this)
+ .load(feed.getImageLocation())
+ .apply(new RequestOptions()
+ .placeholder(R.color.image_readability_tint)
+ .error(R.color.image_readability_tint)
+ .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
+ .transform(new FastBlurTransformation())
+ .dontAnimate())
+ .into(imgvBackground);
}
@Override
@@ -296,69 +118,13 @@ public class FeedSettingsActivity extends AppCompatActivity {
}
@Override
- public boolean onCreateOptionsMenu(Menu menu) {
- super.onCreateOptionsMenu(menu);
- MenuInflater inflater = getMenuInflater();
- inflater.inflate(R.menu.feedinfo, menu);
- return true;
- }
-
- @Override
- public boolean onPrepareOptionsMenu(Menu menu) {
- super.onPrepareOptionsMenu(menu);
- menu.findItem(R.id.support_item).setVisible(
- feed != null && feed.getPaymentLink() != null);
- menu.findItem(R.id.share_link_item).setVisible(feed != null && feed.getLink() != null);
- menu.findItem(R.id.visit_website_item).setVisible(feed != null && feed.getLink() != null &&
- IntentUtils.isCallable(this, new Intent(Intent.ACTION_VIEW, Uri.parse(feed.getLink()))));
- return true;
- }
-
- @Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
finish();
return true;
default:
- try {
- return FeedMenuHandler.onOptionsItemClicked(this, item, feed);
- } catch (DownloadRequestException e) {
- e.printStackTrace();
- DownloadRequestErrorDialogCreator.newRequestErrorDialog(this,
- e.getMessage());
- }
return super.onOptionsItemSelected(item);
}
}
-
- private void updateAutoDownloadSettings() {
- if (feed != null && feed.getPreferences() != null) {
- boolean enabled = feed.getPreferences().getAutoDownload() && UserPreferences.isEnableAutodownload();
- rdoFilterInclude.setEnabled(enabled);
- rdoFilterExclude.setEnabled(enabled);
- etxtFilterText.setEnabled(enabled);
- }
- }
-
- private static class ApplyToEpisodesDialog extends ConfirmationDialog {
-
- private final Feed feed;
- private final boolean autoDownload;
-
- ApplyToEpisodesDialog(Context context, Feed feed, boolean autoDownload) {
- super(context, R.string.auto_download_apply_to_items_title,
- R.string.auto_download_apply_to_items_message);
- this.feed = feed;
- this.autoDownload = autoDownload;
- setPositiveText(R.string.yes);
- setNegativeText(R.string.no);
- }
-
- @Override
- public void onConfirmButtonPressed(DialogInterface dialog) {
- DBWriter.setFeedsItemsAutoDownload(feed, autoDownload);
- }
- }
-
}
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/FlattrAuthActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/FlattrAuthActivity.java
deleted file mode 100644
index 2b4384a02..000000000
--- a/app/src/main/java/de/danoeh/antennapod/activity/FlattrAuthActivity.java
+++ /dev/null
@@ -1,120 +0,0 @@
-package de.danoeh.antennapod.activity;
-
-
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Bundle;
-import android.support.v7.app.AppCompatActivity;
-import android.util.Log;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.View;
-import android.widget.Button;
-import android.widget.TextView;
-
-import org.shredzone.flattr4j.exception.FlattrException;
-
-import de.danoeh.antennapod.BuildConfig;
-import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.core.preferences.UserPreferences;
-import de.danoeh.antennapod.core.util.flattr.FlattrUtils;
-
-/** Guides the user through the authentication process */
-
-public class FlattrAuthActivity extends AppCompatActivity {
- private static final String TAG = "FlattrAuthActivity";
-
- private TextView txtvExplanation;
- private Button butAuthenticate;
- private Button butReturn;
-
- private boolean authSuccessful;
-
- private static FlattrAuthActivity singleton;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- setTheme(UserPreferences.getTheme());
- super.onCreate(savedInstanceState);
- singleton = this;
- authSuccessful = false;
- if (BuildConfig.DEBUG) Log.d(TAG, "Activity created");
- getSupportActionBar().setDisplayHomeAsUpEnabled(true);
- setContentView(R.layout.flattr_auth);
- txtvExplanation = findViewById(R.id.txtvExplanation);
- butAuthenticate = findViewById(R.id.but_authenticate);
- butReturn = findViewById(R.id.but_return_home);
-
- butReturn.setOnClickListener(v -> {
- Intent intent = new Intent(FlattrAuthActivity.this, MainActivity.class);
- intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
- startActivity(intent);
- });
-
- butAuthenticate.setOnClickListener(v -> {
- try {
- FlattrUtils.startAuthProcess(FlattrAuthActivity.this);
- } catch (FlattrException e) {
- e.printStackTrace();
- }
- });
- }
-
- public static FlattrAuthActivity getInstance() {
- return singleton;
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- if (BuildConfig.DEBUG) Log.d(TAG, "Activity resumed");
- Uri uri = getIntent().getData();
- if (uri != null) {
- if (BuildConfig.DEBUG) Log.d(TAG, "Received uri");
- FlattrUtils.handleCallback(this, uri);
- }
- }
-
- public void handleAuthenticationSuccess() {
- authSuccessful = true;
- txtvExplanation.setText(R.string.flattr_auth_success);
- butAuthenticate.setEnabled(false);
- butReturn.setVisibility(View.VISIBLE);
- }
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- super.onCreateOptionsMenu(menu);
- return true;
- }
-
-
-
- @Override
- protected void onPause() {
- super.onPause();
- if (authSuccessful) {
- finish();
- }
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case android.R.id.home:
- if (authSuccessful) {
- Intent intent = new Intent(this, PreferenceActivity.class);
- intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
- startActivity(intent);
- } else {
- finish();
- }
- break;
- default:
- return false;
- }
- return true;
- }
-
-
-}
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/ImportExportActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/ImportExportActivity.java
index e6c9c37cc..9795c1240 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/ImportExportActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/ImportExportActivity.java
@@ -23,6 +23,7 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.channels.FileChannel;
+import java.util.Arrays;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.preferences.UserPreferences;
@@ -109,9 +110,14 @@ public class ImportExportActivity extends AppCompatActivity {
}
private void restoreFrom(Uri inputUri) {
- File currentDB = getDatabasePath(PodDBAdapter.DATABASE_NAME);
InputStream inputStream = null;
try {
+ if (!validateDB(inputUri)) {
+ displayBadFileDialog();
+ return;
+ }
+
+ File currentDB = getDatabasePath(PodDBAdapter.DATABASE_NAME);
inputStream = getContentResolver().openInputStream(inputUri);
FileUtils.copyInputStreamToFile(inputStream, currentDB);
displayImportSuccessDialog();
@@ -123,6 +129,28 @@ public class ImportExportActivity extends AppCompatActivity {
}
}
+ private static final byte[] SQLITE3_MAGIC = "SQLite format 3\0".getBytes();
+ private boolean validateDB(Uri inputUri) throws IOException {
+ try (InputStream inputStream = getContentResolver().openInputStream(inputUri)) {
+ byte[] magicBuf = new byte[SQLITE3_MAGIC.length];
+ if (inputStream.read(magicBuf) == magicBuf.length) {
+ return Arrays.equals(SQLITE3_MAGIC, magicBuf);
+ }
+ }
+
+ return false;
+ }
+
+ private void displayBadFileDialog() {
+ AlertDialog.Builder d = new AlertDialog.Builder(ImportExportActivity.this);
+ d.setMessage(R.string.import_bad_file)
+ .setCancelable(false)
+ .setPositiveButton(android.R.string.ok, ((dialogInterface, i) -> {
+ // do nothing
+ }))
+ .show();
+ }
+
private void displayImportSuccessDialog() {
AlertDialog.Builder d = new AlertDialog.Builder(ImportExportActivity.this);
d.setMessage(R.string.import_ok);
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java
index 9f4fbe271..728019196 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java
@@ -31,6 +31,7 @@ import android.widget.Toast;
import com.bumptech.glide.Glide;
+import de.danoeh.antennapod.preferences.PreferenceUpgrader;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.Validate;
@@ -43,7 +44,6 @@ import de.danoeh.antennapod.core.dialog.ConfirmationDialog;
import de.danoeh.antennapod.core.event.MessageEvent;
import de.danoeh.antennapod.core.event.ProgressEvent;
import de.danoeh.antennapod.core.event.QueueEvent;
-import de.danoeh.antennapod.core.event.ServiceEvent;
import de.danoeh.antennapod.core.feed.EventDistributor;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.preferences.PlaybackPreferences;
@@ -55,7 +55,6 @@ import de.danoeh.antennapod.core.util.FeedItemUtil;
import de.danoeh.antennapod.core.util.Flavors;
import de.danoeh.antennapod.core.util.IntentUtils;
import de.danoeh.antennapod.core.util.StorageUtils;
-import de.danoeh.antennapod.core.util.download.AutoUpdateManager;
import de.danoeh.antennapod.core.util.gui.NotificationUtils;
import de.danoeh.antennapod.dialog.RatingDialog;
import de.danoeh.antennapod.dialog.RenameFeedDialog;
@@ -68,11 +67,13 @@ import de.danoeh.antennapod.fragment.PlaybackHistoryFragment;
import de.danoeh.antennapod.fragment.QueueFragment;
import de.danoeh.antennapod.fragment.SubscriptionFragment;
import de.danoeh.antennapod.menuhandler.NavDrawerActivity;
-import de.greenrobot.event.EventBus;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
+import org.greenrobot.eventbus.EventBus;
+import org.greenrobot.eventbus.Subscribe;
+import org.greenrobot.eventbus.ThreadMode;
/**
* The activity that is shown when the user launches the app.
@@ -206,8 +207,7 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
transaction.commit();
checkFirstLaunch();
- NotificationUtils.createChannels(this);
- UserPreferences.restartUpdateAlarm(false);
+ PreferenceUpgrader.checkUpgrades(this);
}
private void saveLastNavFragment(String tag) {
@@ -474,7 +474,6 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
protected void onResume() {
super.onResume();
StorageUtils.checkStorageAvailability(this);
- AutoUpdateManager.checkShouldRefreshFeeds(getApplicationContext());
Intent intent = getIntent();
if (intent.hasExtra(EXTRA_FEED_ID) ||
@@ -579,17 +578,17 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
}
Feed feed = navDrawerData.feeds.get(position - navAdapter.getSubscriptionOffset());
switch(item.getItemId()) {
- case R.id.mark_all_seen_item:
- ConfirmationDialog markAllSeenConfirmationDialog = new ConfirmationDialog(this,
- R.string.mark_all_seen_label,
- R.string.mark_all_seen_confirmation_msg) {
+ case R.id.remove_all_new_flags_item:
+ ConfirmationDialog removeAllNewFlagsConfirmationDialog = new ConfirmationDialog(this,
+ R.string.remove_all_new_flags_label,
+ R.string.remove_all_new_flags_confirmation_msg) {
@Override
public void onConfirmButtonPressed(DialogInterface dialog) {
dialog.dismiss();
- DBWriter.markFeedSeen(feed.getId());
+ DBWriter.removeFeedNewFlag(feed.getId());
}
};
- markAllSeenConfirmationDialog.createNewDialog().show();
+ removeAllNewFlagsConfirmationDialog.createNewDialog().show();
return true;
case R.id.mark_all_read_item:
ConfirmationDialog markAllReadConfirmationDialog = new ConfirmationDialog(this,
@@ -765,6 +764,7 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
}, error -> Log.e(TAG, Log.getStackTraceString(error)));
}
+ @Subscribe
public void onEvent(QueueEvent event) {
Log.d(TAG, "onEvent(" + event + ")");
// we are only interested in the number of queue items, not download status or position
@@ -776,15 +776,7 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
loadData();
}
- public void onEventMainThread(ServiceEvent event) {
- Log.d(TAG, "onEvent(" + event + ")");
- switch(event.action) {
- case SERVICE_STARTED:
- externalPlayerFragment.connectToPlaybackService();
- break;
- }
- }
-
+ @Subscribe(threadMode = ThreadMode.MAIN)
public void onEventMainThread(ProgressEvent event) {
Log.d(TAG, "onEvent(" + event + ")");
switch(event.action) {
@@ -803,6 +795,7 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
}
}
+ @Subscribe(threadMode = ThreadMode.MAIN)
public void onEventMainThread(MessageEvent event) {
Log.d(TAG, "onEvent(" + event + ")");
View parentLayout = findViewById(R.id.drawer_layout);
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java
index 1cddaa655..3946400a4 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java
@@ -38,7 +38,6 @@ import com.joanzapata.iconify.fonts.FontAwesomeIcons;
import java.util.Locale;
import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.core.event.ServiceEvent;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.feed.MediaType;
@@ -56,6 +55,7 @@ import de.danoeh.antennapod.core.util.IntentUtils;
import de.danoeh.antennapod.core.util.ShareUtils;
import de.danoeh.antennapod.core.util.StorageUtils;
import de.danoeh.antennapod.core.util.Supplier;
+import de.danoeh.antennapod.core.util.TimeSpeedConverter;
import de.danoeh.antennapod.core.util.gui.PictureInPictureUtil;
import de.danoeh.antennapod.core.util.playback.ExternalMedia;
import de.danoeh.antennapod.core.util.playback.MediaPlayerError;
@@ -79,6 +79,9 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
private static final String PREFS = "MediaPlayerActivityPreferences";
private static final String PREF_SHOW_TIME_LEFT = "showTimeLeft";
private static final int REQUEST_CODE_STORAGE = 42;
+ private static final float PLAYBACK_SPEED_STEP = 0.05f;
+ private static final float DEFAULT_MIN_PLAYBACK_SPEED = 0.5f;
+ private static final float DEFAULT_MAX_PLAYBACK_SPEED = 2.5f;
PlaybackController controller;
@@ -235,6 +238,7 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
StorageUtils.checkStorageAvailability(this);
getWindow().setFormat(PixelFormat.TRANSPARENT);
+ setupGUI();
}
@Override
@@ -274,11 +278,8 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
@Override
protected void onStart() {
super.onStart();
- if (controller != null) {
- controller.release();
- }
controller = newPlaybackController();
- setupGUI();
+ controller.init();
loadMediaInfo();
onPositionObserverUpdate();
}
@@ -329,11 +330,6 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
Playable media = controller.getMedia();
boolean isFeedMedia = media != null && (media instanceof FeedMedia);
- menu.findItem(R.id.support_item).setVisible(isFeedMedia && media.getPaymentLink() != null &&
- ((FeedMedia) media).getItem() != null &&
- ((FeedMedia) media).getItem().getFlattrStatus().flattrable()
- );
-
boolean hasWebsiteLink = ( getWebsiteLinkWithFallback(media) != null );
menu.findItem(R.id.visit_website_item).setVisible(hasWebsiteLink);
@@ -469,7 +465,7 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
final Button butDecSpeed = (Button) dialog.findViewById(R.id.butDecSpeed);
butDecSpeed.setOnClickListener(v -> {
if(controller != null && controller.canSetPlaybackSpeed()) {
- barPlaybackSpeed.setProgress(barPlaybackSpeed.getProgress() - 2);
+ barPlaybackSpeed.setProgress(barPlaybackSpeed.getProgress() - 1);
} else {
VariableSpeedDialog.showGetPluginDialog(this);
}
@@ -477,7 +473,7 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
final Button butIncSpeed = (Button) dialog.findViewById(R.id.butIncSpeed);
butIncSpeed.setOnClickListener(v -> {
if(controller != null && controller.canSetPlaybackSpeed()) {
- barPlaybackSpeed.setProgress(barPlaybackSpeed.getProgress() + 2);
+ barPlaybackSpeed.setProgress(barPlaybackSpeed.getProgress() + 1);
} else {
VariableSpeedDialog.showGetPluginDialog(this);
}
@@ -492,12 +488,20 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
UserPreferences.setPlaybackSpeed(String.valueOf(currentSpeed));
}
+ String[] availableSpeeds = UserPreferences.getPlaybackSpeedArray();
+ final float minPlaybackSpeed = availableSpeeds.length > 1 ?
+ Float.valueOf(availableSpeeds[0]) : DEFAULT_MIN_PLAYBACK_SPEED;
+ float maxPlaybackSpeed = availableSpeeds.length > 1 ?
+ Float.valueOf(availableSpeeds[availableSpeeds.length - 1]) : DEFAULT_MAX_PLAYBACK_SPEED;
+ int progressMax = (int) ((maxPlaybackSpeed - minPlaybackSpeed) / PLAYBACK_SPEED_STEP);
+ barPlaybackSpeed.setMax(progressMax);
+
txtvPlaybackSpeed.setText(String.format("%.2fx", currentSpeed));
barPlaybackSpeed.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
if(controller != null && controller.canSetPlaybackSpeed()) {
- float playbackSpeed = (progress + 10) / 20.0f;
+ float playbackSpeed = progress * PLAYBACK_SPEED_STEP + minPlaybackSpeed;
controller.setPlaybackSpeed(playbackSpeed);
String speedPref = String.format(Locale.US, "%.2f", playbackSpeed);
UserPreferences.setPlaybackSpeed(speedPref);
@@ -505,7 +509,8 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
txtvPlaybackSpeed.setText(speedStr);
} else if(fromUser) {
float speed = Float.valueOf(UserPreferences.getPlaybackSpeed());
- barPlaybackSpeed.post(() -> barPlaybackSpeed.setProgress((int) (20 * speed) - 10));
+ barPlaybackSpeed.post(() -> barPlaybackSpeed.setProgress(
+ (int) ((speed - minPlaybackSpeed) / PLAYBACK_SPEED_STEP)));
}
}
@@ -520,7 +525,7 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
public void onStopTrackingTouch(SeekBar seekBar) {
}
});
- barPlaybackSpeed.setProgress((int) (20 * currentSpeed) - 10);
+ barPlaybackSpeed.setProgress((int) ((currentSpeed - minPlaybackSpeed) / PLAYBACK_SPEED_STEP));
final SeekBar barLeftVolume = (SeekBar) dialog.findViewById(R.id.volume_left);
barLeftVolume.setProgress(UserPreferences.getLeftVolumePercentage());
@@ -534,6 +539,22 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
stereoToMono.setText(stereoToMono.getText() + " [" + sonicOnly + "]");
}
+ if (UserPreferences.useExoplayer()) {
+ barRightVolume.setEnabled(false);
+ }
+
+ final CheckBox skipSilence = (CheckBox) dialog.findViewById(R.id.skipSilence);
+ skipSilence.setChecked(UserPreferences.isSkipSilence());
+ if (!UserPreferences.useExoplayer()) {
+ skipSilence.setEnabled(false);
+ String exoplayerOnly = getString(R.string.exoplayer_only);
+ skipSilence.setText(skipSilence.getText() + " [" + exoplayerOnly + "]");
+ }
+ skipSilence.setOnCheckedChangeListener((buttonView, isChecked) -> {
+ UserPreferences.setSkipSilence(isChecked);
+ controller.setSkipSilence(isChecked);
+ });
+
barLeftVolume.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
@@ -577,11 +598,6 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
Uri uri = Uri.parse(getWebsiteLinkWithFallback(media));
startActivity(new Intent(Intent.ACTION_VIEW, uri));
break;
- case R.id.support_item:
- if (media instanceof FeedMedia) {
- DBTasks.flattrItemIfLoggedIn(this, ((FeedMedia) media).getItem());
- }
- break;
case R.id.share_link_item:
if (media instanceof FeedMedia) {
ShareUtils.shareFeedItemLink(this, ((FeedMedia) media).getItem());
@@ -633,18 +649,6 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
super.onResume();
Log.d(TAG, "onResume()");
StorageUtils.checkStorageAvailability(this);
- if (controller != null) {
- controller.init();
- }
- }
-
- public void onEventMainThread(ServiceEvent event) {
- Log.d(TAG, "onEvent(" + event + ")");
- if (event.action == ServiceEvent.Action.SERVICE_STARTED) {
- if (controller != null) {
- controller.init();
- }
- }
}
/**
@@ -661,8 +665,11 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
if (controller == null || txtvPosition == null || txtvLength == null) {
return;
}
- int currentPosition = controller.getPosition();
- int duration = controller.getDuration();
+
+ int currentPosition = TimeSpeedConverter.convert(controller.getPosition());
+ int duration = TimeSpeedConverter.convert(controller.getDuration());
+ int remainingTime = TimeSpeedConverter.convert(
+ controller.getDuration() - controller.getPosition());
Log.d(TAG, "currentPosition " + Converter.getDurationStringLong(currentPosition));
if (currentPosition == PlaybackService.INVALID_TIME ||
duration == PlaybackService.INVALID_TIME) {
@@ -671,7 +678,7 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
}
txtvPosition.setText(Converter.getDurationStringLong(currentPosition));
if (showTimeLeft) {
- txtvLength.setText("-" + Converter.getDurationStringLong(duration - currentPosition));
+ txtvLength.setText("-" + Converter.getDurationStringLong(remainingTime));
} else {
txtvLength.setText(Converter.getDurationStringLong(duration));
}
@@ -819,9 +826,13 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
String length;
if (showTimeLeft) {
- length = "-" + Converter.getDurationStringLong(media.getDuration() - media.getPosition());
+ int remainingTime = TimeSpeedConverter.convert(
+ media.getDuration() - media.getPosition());
+
+ length = "-" + Converter.getDurationStringLong(remainingTime);
} else {
- length = Converter.getDurationStringLong(media.getDuration());
+ int duration = TimeSpeedConverter.convert(media.getDuration());
+ length = Converter.getDurationStringLong(duration);
}
txtvLength.setText(length);
@@ -924,7 +935,8 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
prog = controller.onSeekBarProgressChanged(seekBar, progress, fromUser, txtvPosition);
if (showTimeLeft && prog != 0) {
int duration = controller.getDuration();
- String length = "-" + Converter.getDurationStringLong(duration - (int) (prog * duration));
+ int timeLeft = TimeSpeedConverter.convert(duration - (int) (prog * duration));
+ String length = "-" + Converter.getDurationStringLong(timeLeft);
txtvLength.setText(length);
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerInfoActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerInfoActivity.java
index a2389dabd..4fec1cfc5 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerInfoActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerInfoActivity.java
@@ -7,7 +7,6 @@ import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.os.Build;
import android.os.Bundle;
-import android.support.annotation.Nullable;
import android.support.design.widget.AppBarLayout;
import android.support.design.widget.Snackbar;
import android.support.v4.app.Fragment;
@@ -34,7 +33,6 @@ import com.viewpagerindicator.CirclePageIndicator;
import java.util.List;
import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.adapter.ChaptersListAdapter;
import de.danoeh.antennapod.adapter.NavListAdapter;
import de.danoeh.antennapod.core.asynctask.FeedRemover;
import de.danoeh.antennapod.core.dialog.ConfirmationDialog;
@@ -48,7 +46,6 @@ import de.danoeh.antennapod.core.service.playback.PlayerStatus;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.util.IntentUtils;
-import de.danoeh.antennapod.core.util.download.AutoUpdateManager;
import de.danoeh.antennapod.core.util.playback.Playable;
import de.danoeh.antennapod.core.util.playback.PlaybackController;
import de.danoeh.antennapod.dialog.RenameFeedDialog;
@@ -62,11 +59,13 @@ import de.danoeh.antennapod.fragment.PlaybackHistoryFragment;
import de.danoeh.antennapod.fragment.QueueFragment;
import de.danoeh.antennapod.fragment.SubscriptionFragment;
import de.danoeh.antennapod.menuhandler.NavDrawerActivity;
-import de.greenrobot.event.EventBus;
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;
/**
* Activity for playing files that do not require a video surface.
@@ -102,19 +101,12 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem
private ActionBarDrawerToggle drawerToggle;
private int mPosition = -1;
- private Playable media;
private ViewPager pager;
private MediaplayerInfoPagerAdapter pagerAdapter;
private Disposable disposable;
@Override
- protected void onPause() {
- super.onPause();
- EventBus.getDefault().unregister(this);
- }
-
- @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
supportPostponeEnterTransition();
@@ -124,13 +116,11 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem
protected void onStop() {
super.onStop();
Log.d(TAG, "onStop()");
- if(pagerAdapter != null) {
- pagerAdapter.setController(null);
- }
if (disposable != null) {
disposable.dispose();
}
EventDistributor.getInstance().unregister(contentUpdate);
+ EventBus.getDefault().unregister(this);
saveCurrentFragment();
}
@@ -180,15 +170,8 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem
}
@Override
- protected void onResume() {
- super.onResume();
- if(pagerAdapter != null && controller != null && controller.getMedia() != media) {
- media = controller.getMedia();
- pagerAdapter.onMediaChanged(media);
- pagerAdapter.setController(controller);
- }
- AutoUpdateManager.checkShouldRefreshFeeds(getApplicationContext());
-
+ protected void onStart() {
+ super.onStart();
EventDistributor.getInstance().register(contentUpdate);
EventBus.getDefault().register(this);
loadData();
@@ -278,8 +261,8 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem
butCastDisconnect = findViewById(R.id.butCastDisconnect);
pager = findViewById(R.id.pager);
- pagerAdapter = new MediaplayerInfoPagerAdapter(getSupportFragmentManager(), media);
- pagerAdapter.setController(controller);
+ pager.setOffscreenPageLimit(3);
+ pagerAdapter = new MediaplayerInfoPagerAdapter(getSupportFragmentManager());
pager.setAdapter(pagerAdapter);
CirclePageIndicator pageIndicator = findViewById(R.id.page_indicator);
pageIndicator.setViewPager(pager);
@@ -290,37 +273,6 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem
}
@Override
- protected void onPositionObserverUpdate() {
- super.onPositionObserverUpdate();
- notifyMediaPositionChanged();
- }
-
- @Override
- protected boolean loadMediaInfo() {
- if (!super.loadMediaInfo()) {
- return false;
- }
- if(controller != null && controller.getMedia() != media) {
- media = controller.getMedia();
- pagerAdapter.onMediaChanged(media);
- }
- return true;
- }
-
- private void notifyMediaPositionChanged() {
- if(pagerAdapter == null) {
- return;
- }
- ChaptersFragment chaptersFragment = pagerAdapter.getChaptersFragment();
- if(chaptersFragment != null) {
- ChaptersListAdapter adapter = (ChaptersListAdapter) chaptersFragment.getListAdapter();
- if (adapter != null) {
- adapter.notifyDataSetChanged();
- }
- }
- }
-
- @Override
protected void onReloadNotification(int notificationCode) {
if (notificationCode == PlaybackService.EXTRA_CODE_VIDEO) {
Log.d(TAG, "ReloadNotification received, switching to Videoplayer now");
@@ -387,8 +339,8 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem
}
Feed feed = navDrawerData.feeds.get(position - navAdapter.getSubscriptionOffset());
switch(item.getItemId()) {
- case R.id.mark_all_seen_item:
- DBWriter.markFeedSeen(feed.getId());
+ case R.id.remove_all_new_flags_item:
+ DBWriter.removeFeedNewFlag(feed.getId());
return true;
case R.id.mark_all_read_item:
DBWriter.markFeedRead(feed.getId());
@@ -483,6 +435,7 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem
}, error -> Log.e(TAG, Log.getStackTraceString(error)));
}
+ @Subscribe(threadMode = ThreadMode.MAIN)
public void onEventMainThread(MessageEvent event) {
Log.d(TAG, "onEvent(" + event + ")");
View parentLayout = findViewById(R.id.drawer_layout);
@@ -566,50 +519,11 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem
}
};
- public interface MediaplayerInfoContentFragment {
- void onMediaChanged(Playable media);
- }
-
private static class MediaplayerInfoPagerAdapter extends FragmentStatePagerAdapter {
-
private static final String TAG = "MPInfoPagerAdapter";
- private Playable media;
- private PlaybackController controller;
-
- public MediaplayerInfoPagerAdapter(FragmentManager fm, Playable media) {
+ public MediaplayerInfoPagerAdapter(FragmentManager fm) {
super(fm);
- this.media = media;
- }
-
- private CoverFragment coverFragment;
- private ItemDescriptionFragment itemDescriptionFragment;
- private ChaptersFragment chaptersFragment;
-
- public void onMediaChanged(Playable media) {
- Log.d(TAG, "media changing to " + ((media != null) ? media.getEpisodeTitle() : "null"));
- this.media = media;
- if(coverFragment != null) {
- coverFragment.onMediaChanged(media);
- }
- if(itemDescriptionFragment != null) {
- itemDescriptionFragment.onMediaChanged(media);
- }
- if(chaptersFragment != null) {
- chaptersFragment.onMediaChanged(media);
- }
- }
-
- public void setController(PlaybackController controller) {
- this.controller = controller;
- if(chaptersFragment != null) {
- chaptersFragment.setController(controller);
- }
- }
-
- @Nullable
- public ChaptersFragment getChaptersFragment() {
- return chaptersFragment;
}
@Override
@@ -617,21 +531,11 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem
Log.d(TAG, "getItem(" + position + ")");
switch (position) {
case POS_COVER:
- if(coverFragment == null) {
- coverFragment = CoverFragment.newInstance(media);
- }
- return coverFragment;
+ return new CoverFragment();
case POS_DESCR:
- if(itemDescriptionFragment == null) {
- itemDescriptionFragment = ItemDescriptionFragment.newInstance(media, true, true);
- }
- return itemDescriptionFragment;
+ return new ItemDescriptionFragment();
case POS_CHAPTERS:
- if(chaptersFragment == null) {
- chaptersFragment = ChaptersFragment.newInstance(media);
- chaptersFragment.setController(controller);
- }
- return chaptersFragment;
+ return new ChaptersFragment();
default:
return null;
}
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java
index 73da9a834..ea7687bc9 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java
@@ -4,7 +4,10 @@ import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
+import android.graphics.LightingColorFilter;
+import android.os.Build;
import android.os.Bundle;
+import android.support.annotation.NonNull;
import android.support.annotation.UiThread;
import android.support.v4.app.NavUtils;
import android.support.v7.app.ActionBar;
@@ -27,9 +30,13 @@ import android.widget.Spinner;
import android.widget.TextView;
import com.bumptech.glide.Glide;
-
import com.bumptech.glide.request.RequestOptions;
+
+import de.danoeh.antennapod.core.glide.FastBlurTransformation;
import org.apache.commons.lang3.StringUtils;
+import org.greenrobot.eventbus.EventBus;
+import org.greenrobot.eventbus.Subscribe;
+import org.greenrobot.eventbus.ThreadMode;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
@@ -57,15 +64,16 @@ import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DownloadRequestException;
import de.danoeh.antennapod.core.storage.DownloadRequester;
import de.danoeh.antennapod.core.syndication.handler.FeedHandler;
+import de.danoeh.antennapod.core.syndication.handler.FeedHandlerResult;
import de.danoeh.antennapod.core.syndication.handler.UnsupportedFeedtypeException;
import de.danoeh.antennapod.core.util.DownloadError;
import de.danoeh.antennapod.core.util.FileNameGenerator;
+import de.danoeh.antennapod.core.util.Optional;
import de.danoeh.antennapod.core.util.StorageUtils;
import de.danoeh.antennapod.core.util.URLChecker;
import de.danoeh.antennapod.core.util.syndication.FeedDiscoverer;
import de.danoeh.antennapod.core.util.syndication.HtmlToPlainText;
import de.danoeh.antennapod.dialog.AuthenticationDialog;
-import de.greenrobot.event.EventBus;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
@@ -120,6 +128,7 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
}
};
+ @Subscribe(threadMode = ThreadMode.MAIN)
public void onEventMainThread(DownloadEvent event) {
Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
setSubscribeButtonState(feed);
@@ -140,7 +149,7 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
StorageUtils.checkStorageAvailability(this);
- final String feedUrl;
+ String feedUrl = null;
if (getIntent().hasExtra(ARG_FEEDURL)) {
feedUrl = getIntent().getStringExtra(ARG_FEEDURL);
} else if (TextUtils.equals(getIntent().getAction(), Intent.ACTION_SEND)
@@ -150,16 +159,23 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
if (actionBar != null) {
actionBar.setTitle(R.string.add_feed_label);
}
- } else {
- throw new IllegalArgumentException("Activity must be started with feedurl argument!");
}
- Log.d(TAG, "Activity was started with url " + feedUrl);
- setLoadingLayout();
- if (savedInstanceState == null) {
- startFeedDownload(feedUrl, null, null);
+ if (feedUrl == null) {
+ Log.e(TAG, "feedUrl is null.");
+ new AlertDialog.Builder(OnlineFeedViewActivity.this).
+ setNeutralButton(android.R.string.ok,
+ (dialog, which) -> finish()).
+ setTitle(R.string.error_label).
+ setMessage(R.string.null_value_podcast_error).create().show();
} else {
- startFeedDownload(feedUrl, savedInstanceState.getString("username"), savedInstanceState.getString("password"));
+ Log.d(TAG, "Activity was started with url " + feedUrl);
+ setLoadingLayout();
+ if (savedInstanceState == null) {
+ startFeedDownload(feedUrl, null, null);
+ } else {
+ startFeedDownload(feedUrl, savedInstanceState.getString("username"), savedInstanceState.getString("password"));
+ }
}
}
@@ -183,24 +199,19 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
}
@Override
- protected void onResume() {
- super.onResume();
+ protected void onStart() {
+ super.onStart();
isPaused = false;
EventDistributor.getInstance().register(listener);
EventBus.getDefault().register(this);
}
@Override
- protected void onPause() {
- super.onPause();
+ protected void onStop() {
+ super.onStop();
isPaused = true;
EventDistributor.getInstance().unregister(listener);
EventBus.getDefault().unregister(this);
- }
-
- @Override
- protected void onStop() {
- super.onStop();
if (downloader != null && !downloader.isFinished()) {
downloader.cancel();
}
@@ -280,12 +291,7 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
error -> Log.e(TAG, Log.getStackTraceString(error)));
}
- private void checkDownloadResult(DownloadStatus status) {
- if (status == null) {
- Log.wtf(TAG, "DownloadStatus returned by Downloader was null");
- finish();
- return;
- }
+ private void checkDownloadResult(@NonNull DownloadStatus status) {
if (status.isCancelled()) {
return;
}
@@ -312,30 +318,12 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
}
Log.d(TAG, "Parsing feed");
- parser = Observable.fromCallable(() -> {
- FeedHandler handler = new FeedHandler();
- try {
- return handler.parseFeed(feed);
- } catch (UnsupportedFeedtypeException e) {
- Log.d(TAG, "Unsupported feed type detected");
- if ("html".equalsIgnoreCase(e.getRootElement())) {
- showFeedDiscoveryDialog(new File(feed.getFile_url()), feed.getDownload_url());
- return null;
- } else {
- throw e;
- }
- } catch (Exception e) {
- Log.e(TAG, Log.getStackTraceString(e));
- throw e;
- } finally {
- boolean rc = new File(feed.getFile_url()).delete();
- Log.d(TAG, "Deleted feed source file. Result: " + rc);
- }
- })
+ parser = Observable.fromCallable(this::doParseFeed)
.subscribeOn(Schedulers.computation())
.observeOn(AndroidSchedulers.mainThread())
- .subscribe(result -> {
- if(result != null) {
+ .subscribe(optionalResult -> {
+ if(optionalResult.isPresent()) {
+ FeedHandlerResult result = optionalResult.get();
beforeShowFeedInformation(result.feed);
showFeedInformation(result.feed, result.alternateFeedUrls);
}
@@ -347,6 +335,33 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
});
}
+ @NonNull
+ private Optional<FeedHandlerResult> doParseFeed() throws Exception {
+ FeedHandler handler = new FeedHandler();
+ try {
+ return Optional.of(handler.parseFeed(feed));
+ } catch (UnsupportedFeedtypeException e) {
+ Log.d(TAG, "Unsupported feed type detected");
+ if ("html".equalsIgnoreCase(e.getRootElement())) {
+ boolean dialogShown = showFeedDiscoveryDialog(new File(feed.getFile_url()), feed.getDownload_url());
+ if (dialogShown) {
+ return Optional.empty();
+ } else {
+ Log.d(TAG, "Supplied feed is an HTML web page that has no references to any feed");
+ throw e;
+ }
+ } else {
+ throw e;
+ }
+ } catch (Exception e) {
+ Log.e(TAG, Log.getStackTraceString(e));
+ throw e;
+ } finally {
+ boolean rc = new File(feed.getFile_url()).delete();
+ Log.d(TAG, "Deleted feed source file. Result: " + rc);
+ }
+ }
+
/**
* Called after the feed has been downloaded and parsed and before showFeedInformation is called.
* This method is executed on a background thread
@@ -381,6 +396,7 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
this.selectedDownloadUrl = feed.getDownload_url();
EventDistributor.getInstance().register(listener);
ListView listView = findViewById(R.id.listview);
+ listView.setSelector(android.R.color.transparent);
LayoutInflater inflater = LayoutInflater.from(this);
View header = inflater.inflate(R.layout.onlinefeedview_header, listView, false);
listView.addHeaderView(header);
@@ -388,6 +404,10 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
listView.setAdapter(new FeedItemlistDescriptionAdapter(this, 0, feed.getItems()));
ImageView cover = header.findViewById(R.id.imgvCover);
+ ImageView headerBackground = header.findViewById(R.id.imgvBackground);
+ header.findViewById(R.id.butShowInfo).setVisibility(View.INVISIBLE);
+ header.findViewById(R.id.butShowSettings).setVisibility(View.INVISIBLE);
+ headerBackground.setColorFilter(new LightingColorFilter(0xff828282, 0x000000));
TextView title = header.findViewById(R.id.txtvTitle);
TextView author = header.findViewById(R.id.txtvAuthor);
TextView description = header.findViewById(R.id.txtvDescription);
@@ -405,6 +425,15 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
.fitCenter()
.dontAnimate())
.into(cover);
+ Glide.with(this)
+ .load(feed.getImageUrl())
+ .apply(new RequestOptions()
+ .placeholder(R.color.image_readability_tint)
+ .error(R.color.image_readability_tint)
+ .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
+ .transform(new FastBlurTransformation())
+ .dontAnimate())
+ .into(headerBackground);
}
title.setText(feed.getTitle());
@@ -433,6 +462,17 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
}
});
+ final int MAX_LINES_COLLAPSED = 10;
+ description.setMaxLines(MAX_LINES_COLLAPSED);
+ description.setOnClickListener(v -> {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN
+ && description.getMaxLines() > MAX_LINES_COLLAPSED) {
+ description.setMaxLines(MAX_LINES_COLLAPSED);
+ } else {
+ description.setMaxLines(2000);
+ }
+ });
+
if (alternateFeedUrls.isEmpty()) {
spAlternateUrls.setVisibility(View.GONE);
} else {
@@ -530,21 +570,25 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
}
}
- private void showFeedDiscoveryDialog(File feedFile, String baseUrl) {
+ /**
+ *
+ * @return true if a FeedDiscoveryDialog is shown, false otherwise (e.g., due to no feed found).
+ */
+ private boolean showFeedDiscoveryDialog(File feedFile, String baseUrl) {
FeedDiscoverer fd = new FeedDiscoverer();
final Map<String, String> urlsMap;
try {
urlsMap = fd.findLinks(feedFile, baseUrl);
if (urlsMap == null || urlsMap.isEmpty()) {
- return;
+ return false;
}
} catch (IOException e) {
e.printStackTrace();
- return;
+ return false;
}
if (isPaused || isFinishing()) {
- return;
+ return false;
}
final List<String> titles = new ArrayList<>();
@@ -580,6 +624,7 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
}
dialog = ab.show();
});
+ return true;
}
private class FeedViewAuthenticationDialog extends AuthenticationDialog {
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportFromPathActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportFromPathActivity.java
index a63d3b735..158e3b7a4 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportFromPathActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportFromPathActivity.java
@@ -25,7 +25,6 @@ public class OpmlImportFromPathActivity extends OpmlImportBaseActivity {
private static final int CHOOSE_OPML_FILE = 1;
- private Intent intentPickAction;
private Intent intentGetContentAction;
@Override
@@ -36,50 +35,30 @@ public class OpmlImportFromPathActivity extends OpmlImportBaseActivity {
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
setContentView(R.layout.opml_import);
- final TextView txtvHeaderExplanation1 = findViewById(R.id.txtvHeadingExplanation1);
- final TextView txtvExplanation1 = findViewById(R.id.txtvExplanation1);
- final TextView txtvHeaderExplanation2 = findViewById(R.id.txtvHeadingExplanation2);
- final TextView txtvExplanation2 = findViewById(R.id.txtvExplanation2);
- final TextView txtvHeaderExplanation3 = findViewById(R.id.txtvHeadingExplanation3);
+ final TextView txtvHeaderExplanation = findViewById(R.id.txtvHeadingExplanation);
+ final TextView txtvExplanation = findViewById(R.id.txtvExplanation);
+ final TextView txtvHeaderExplanationOpenWith = findViewById(R.id.txtvHeadingExplanationOpenWith);
Button butChooseFilesystem = findViewById(R.id.butChooseFileFromFilesystem);
- butChooseFilesystem.setOnClickListener(v -> chooseFileFromFilesystem());
-
- Button butChooseExternal = findViewById(R.id.butChooseFileFromExternal);
- butChooseExternal.setOnClickListener(v -> chooseFileFromExternal());
+ butChooseFilesystem.setOnClickListener(v -> chooseFileFromExternal());
int nextOption = 1;
String optionLabel = getString(R.string.opml_import_option);
- intentPickAction = new Intent(Intent.ACTION_PICK);
-
- if(!IntentUtils.isCallable(getApplicationContext(), intentPickAction)) {
- intentPickAction.setData(null);
- if(!IntentUtils.isCallable(getApplicationContext(), intentPickAction)) {
- txtvHeaderExplanation1.setVisibility(View.GONE);
- txtvExplanation1.setVisibility(View.GONE);
- findViewById(R.id.divider1).setVisibility(View.GONE);
- butChooseFilesystem.setVisibility(View.GONE);
- }
- }
- if(txtvExplanation1.getVisibility() == View.VISIBLE) {
- txtvHeaderExplanation1.setText(String.format(optionLabel, nextOption));
- nextOption++;
- }
-
intentGetContentAction = new Intent(Intent.ACTION_GET_CONTENT);
intentGetContentAction.addCategory(Intent.CATEGORY_OPENABLE);
intentGetContentAction.setType("*/*");
- if(!IntentUtils.isCallable(getApplicationContext(), intentGetContentAction)) {
- txtvHeaderExplanation2.setVisibility(View.GONE);
- txtvExplanation2.setVisibility(View.GONE);
- findViewById(R.id.divider2).setVisibility(View.GONE);
- butChooseExternal.setVisibility(View.GONE);
- } else {
- txtvHeaderExplanation2.setText(String.format(optionLabel, nextOption));
+
+ if (IntentUtils.isCallable(getApplicationContext(), intentGetContentAction)) {
+ txtvHeaderExplanation.setText(String.format(optionLabel, nextOption));
nextOption++;
+ } else {
+ txtvHeaderExplanation.setVisibility(View.GONE);
+ txtvExplanation.setVisibility(View.GONE);
+ findViewById(R.id.divider).setVisibility(View.GONE);
+ butChooseFilesystem.setVisibility(View.GONE);
}
- txtvHeaderExplanation3.setText(String.format(optionLabel, nextOption));
+ txtvHeaderExplanationOpenWith.setText(String.format(optionLabel, nextOption));
}
@Override
@@ -106,18 +85,6 @@ public class OpmlImportFromPathActivity extends OpmlImportBaseActivity {
}
}
- /*
- * Creates an implicit intent to launch a file manager which lets
- * the user choose a specific OPML-file to import from.
- */
- private void chooseFileFromFilesystem() {
- try {
- startActivityForResult(intentPickAction, CHOOSE_OPML_FILE);
- } catch (ActivityNotFoundException e) {
- Log.e(TAG, "No activity found. Should never happen...");
- }
- }
-
private void chooseFileFromExternal() {
try {
startActivityForResult(intentGetContentAction, CHOOSE_OPML_FILE);
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/PreferenceActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/PreferenceActivity.java
index 452e91bd3..7e0ae173f 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/PreferenceActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/PreferenceActivity.java
@@ -1,12 +1,9 @@
package de.danoeh.antennapod.activity;
-import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
-import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceFragmentCompat;
-import android.support.v7.preference.PreferenceScreen;
import android.view.Menu;
import android.view.MenuItem;
import android.view.ViewGroup;
@@ -15,56 +12,24 @@ import android.widget.FrameLayout;
import com.bytehamster.lib.preferencesearch.SearchPreferenceResult;
import com.bytehamster.lib.preferencesearch.SearchPreferenceResultListener;
-import java.lang.ref.WeakReference;
-
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.preferences.UserPreferences;
-import de.danoeh.antennapod.preferences.PreferenceController;
+import de.danoeh.antennapod.fragment.preferences.AutoDownloadPreferencesFragment;
+import de.danoeh.antennapod.fragment.preferences.GpodderPreferencesFragment;
+import de.danoeh.antennapod.fragment.preferences.IntegrationsPreferencesFragment;
+import de.danoeh.antennapod.fragment.preferences.MainPreferencesFragment;
+import de.danoeh.antennapod.fragment.preferences.NetworkPreferencesFragment;
+import de.danoeh.antennapod.fragment.preferences.PlaybackPreferencesFragment;
+import de.danoeh.antennapod.fragment.preferences.StoragePreferencesFragment;
+import de.danoeh.antennapod.fragment.preferences.UserInterfacePreferencesFragment;
/**
* PreferenceActivity for API 11+. In order to change the behavior of the preference UI, see
* PreferenceController.
*/
public class PreferenceActivity extends AppCompatActivity implements SearchPreferenceResultListener {
-
- public static final String PARAM_RESOURCE = "resource";
- private static WeakReference<PreferenceActivity> instance;
- private PreferenceController preferenceController;
- private final PreferenceController.PreferenceUI preferenceUI = new PreferenceController.PreferenceUI() {
- private PreferenceFragmentCompat fragment;
-
- @Override
- public void setFragment(PreferenceFragmentCompat fragment) {
- this.fragment = fragment;
- }
-
- @Override
- public PreferenceFragmentCompat getFragment() {
- return fragment;
- }
-
- @Override
- public Preference findPreference(CharSequence key) {
- return fragment.findPreference(key);
- }
-
- @Override
- public PreferenceScreen getPreferenceScreen() {
- return fragment.getPreferenceScreen();
- }
-
- @Override
- public AppCompatActivity getActivity() {
- return PreferenceActivity.this;
- }
- };
-
@Override
protected void onCreate(Bundle savedInstanceState) {
- // This must be the FIRST thing we do, otherwise other code may not have the
- // reference it needs
- instance = new WeakReference<>(this);
-
setTheme(UserPreferences.getTheme());
super.onCreate(savedInstanceState);
@@ -73,38 +38,63 @@ public class PreferenceActivity extends AppCompatActivity implements SearchPrefe
ab.setDisplayHomeAsUpEnabled(true);
}
- // set up layout
FrameLayout root = new FrameLayout(this);
root.setId(R.id.content);
root.setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT));
setContentView(root);
- // we need to create the PreferenceController before the MainFragment
- // since the MainFragment depends on the preferenceController already being created
- preferenceController = new PreferenceController(preferenceUI);
+ getSupportFragmentManager().beginTransaction().replace(R.id.content, new MainPreferencesFragment()).commit();
- showPreferenceScreen(R.xml.preferences, false);
}
- private void showPreferenceScreen(int screen, boolean addHistory) {
- PreferenceFragmentCompat prefFragment = new MainFragment();
- preferenceUI.setFragment(prefFragment);
- Bundle args = new Bundle();
- args.putInt(PARAM_RESOURCE, screen);
- prefFragment.setArguments(args);
- if (addHistory) {
- getSupportFragmentManager().beginTransaction().replace(R.id.content, prefFragment)
- .addToBackStack(getString(PreferenceController.getTitleOfPage(screen))).commit();
- } else {
- getSupportFragmentManager().beginTransaction().replace(R.id.content, prefFragment).commit();
+ private PreferenceFragmentCompat getPreferenceScreen(int screen) {
+ PreferenceFragmentCompat prefFragment = null;
+
+ if (screen == R.xml.preferences_user_interface) {
+ prefFragment = new UserInterfacePreferencesFragment();
+ } else if (screen == R.xml.preferences_integrations) {
+ prefFragment = new IntegrationsPreferencesFragment();
+ } else if (screen == R.xml.preferences_network) {
+ prefFragment = new NetworkPreferencesFragment();
+ } else if (screen == R.xml.preferences_storage) {
+ prefFragment = new StoragePreferencesFragment();
+ } else if (screen == R.xml.preferences_autodownload) {
+ prefFragment = new AutoDownloadPreferencesFragment();
+ } else if (screen == R.xml.preferences_gpodder) {
+ prefFragment = new GpodderPreferencesFragment();
+ } else if (screen == R.xml.preferences_playback) {
+ prefFragment = new PlaybackPreferencesFragment();
}
+ return prefFragment;
}
- @Override
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- super.onActivityResult(requestCode, resultCode, data);
- preferenceController.onActivityResult(requestCode, resultCode, data);
+ public static int getTitleOfPage(int preferences) {
+ switch (preferences) {
+ case R.xml.preferences_network:
+ return R.string.network_pref;
+ case R.xml.preferences_autodownload:
+ return R.string.pref_automatic_download_title;
+ case R.xml.preferences_playback:
+ return R.string.playback_pref;
+ case R.xml.preferences_storage:
+ return R.string.storage_pref;
+ case R.xml.preferences_user_interface:
+ return R.string.user_interface_label;
+ case R.xml.preferences_integrations:
+ return R.string.integrations_label;
+ case R.xml.preferences_gpodder:
+ return R.string.gpodnet_main_label;
+ default:
+ return R.string.settings_label;
+ }
+ }
+
+ public PreferenceFragmentCompat openScreen(int screen) {
+ PreferenceFragmentCompat fragment = getPreferenceScreen(screen);
+ getSupportFragmentManager().beginTransaction().replace(R.id.content, fragment)
+ .addToBackStack(getString(getTitleOfPage(screen))).commit();
+ return fragment;
}
@Override
@@ -115,72 +105,20 @@ public class PreferenceActivity extends AppCompatActivity implements SearchPrefe
@Override
public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case android.R.id.home:
- if (getSupportFragmentManager().getBackStackEntryCount() == 0) {
- finish();
- } else {
- getSupportFragmentManager().popBackStack();
- }
- return true;
- default:
- return false;
+ if (item.getItemId() == android.R.id.home) {
+ if (getSupportFragmentManager().getBackStackEntryCount() == 0) {
+ finish();
+ } else {
+ getSupportFragmentManager().popBackStack();
+ }
+ return true;
}
+ return false;
}
@Override
public void onSearchResultClicked(SearchPreferenceResult result) {
- showPreferenceScreen(result.getResourceFile(), true);
- result.highlight(preferenceUI.getFragment());
- }
-
- public static class MainFragment extends PreferenceFragmentCompat {
- private int screen;
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setRetainInstance(true);
- }
-
- @Override
- public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
- screen = getArguments().getInt(PARAM_RESOURCE);
- addPreferencesFromResource(screen);
- PreferenceActivity activity = instance.get();
- if (activity != null && activity.preferenceController != null) {
- activity.preferenceUI.setFragment(this);
- activity.preferenceController.onCreate(screen);
- }
- }
-
- @Override
- public void onResume() {
- super.onResume();
- PreferenceActivity activity = instance.get();
- if(activity != null && activity.preferenceController != null) {
- activity.setTitle(PreferenceController.getTitleOfPage(screen));
- activity.preferenceUI.setFragment(this);
- activity.preferenceController.onResume(screen);
- }
- }
-
- @Override
- public void onPause() {
- PreferenceActivity activity = instance.get();
- if (screen == R.xml.preferences_gpodder) {
- activity.preferenceController.unregisterGpodnet();
- }
- super.onPause();
- }
-
- @Override
- public void onStop() {
- PreferenceActivity activity = instance.get();
- if (screen == R.xml.preferences_storage) {
- activity.preferenceController.unsubscribeExportSubscription();
- }
- super.onStop();
- }
+ PreferenceFragmentCompat fragment = openScreen(result.getResourceFile());
+ result.highlight(fragment);
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/gpoddernet/GpodnetAuthenticationActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/gpoddernet/GpodnetAuthenticationActivity.java
index 8fcdb4371..2d7898d5b 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/gpoddernet/GpodnetAuthenticationActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/gpoddernet/GpodnetAuthenticationActivity.java
@@ -25,6 +25,8 @@ import android.widget.ViewFlipper;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
import de.danoeh.antennapod.BuildConfig;
import de.danoeh.antennapod.R;
@@ -102,10 +104,6 @@ public class GpodnetAuthenticationActivity extends AppCompatActivity {
return super.onOptionsItemSelected(item);
}
- @Override
- public void onConfigurationChanged(Configuration newConfig) {
- }
-
private void setupLoginView(View view) {
final EditText username = view.findViewById(R.id.etxtUsername);
final EditText password = view.findViewById(R.id.etxtPassword);
@@ -123,6 +121,11 @@ public class GpodnetAuthenticationActivity extends AppCompatActivity {
final String usernameStr = username.getText().toString();
final String passwordStr = password.getText().toString();
+ if (usernameHasUnwantedChars(usernameStr)) {
+ txtvError.setText(R.string.gpodnetsync_username_characters_error);
+ txtvError.setVisibility(View.VISIBLE);
+ return;
+ }
if (BuildConfig.DEBUG) Log.d(TAG, "Checking login credentials");
AsyncTask<GpodnetService, Void, Void> authTask = new AsyncTask<GpodnetService, Void, Void>() {
@@ -395,4 +398,10 @@ public class GpodnetAuthenticationActivity extends AppCompatActivity {
finish();
}
}
+
+ private boolean usernameHasUnwantedChars(String username) {
+ Pattern special = Pattern.compile("[!@#$%&*()+=|<>?{}\\[\\]~]");
+ Matcher containsUnwantedChars = special.matcher(username);
+ return containsUnwantedChars.find();
+ }
}
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/ActionButtonCallback.java b/app/src/main/java/de/danoeh/antennapod/adapter/ActionButtonCallback.java
deleted file mode 100644
index e6b42efcb..000000000
--- a/app/src/main/java/de/danoeh/antennapod/adapter/ActionButtonCallback.java
+++ /dev/null
@@ -1,9 +0,0 @@
-package de.danoeh.antennapod.adapter;
-
-import de.danoeh.antennapod.core.feed.FeedItem;
-import de.danoeh.antennapod.core.util.LongList;
-
-interface ActionButtonCallback {
- /** Is called when the action button of a list item has been pressed. */
- void onActionButtonPressed(FeedItem item, LongList queueIds);
-}
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/ActionButtonUtils.java b/app/src/main/java/de/danoeh/antennapod/adapter/ActionButtonUtils.java
deleted file mode 100644
index a915692d1..000000000
--- a/app/src/main/java/de/danoeh/antennapod/adapter/ActionButtonUtils.java
+++ /dev/null
@@ -1,97 +0,0 @@
-package de.danoeh.antennapod.adapter;
-
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.view.View;
-import android.widget.ImageButton;
-
-import org.apache.commons.lang3.Validate;
-
-import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.core.feed.FeedItem;
-import de.danoeh.antennapod.core.feed.FeedMedia;
-import de.danoeh.antennapod.core.storage.DownloadRequester;
-
-/**
- * Utility methods for the action button that is displayed on the right hand side
- * of a listitem.
- */
-class ActionButtonUtils {
-
- private final int[] labels;
- private final TypedArray drawables;
- private final Context context;
-
- public ActionButtonUtils(Context context) {
- Validate.notNull(context);
-
- this.context = context.getApplicationContext();
- drawables = context.obtainStyledAttributes(new int[] {
- R.attr.av_play,
- R.attr.navigation_cancel,
- R.attr.av_download,
- R.attr.av_pause,
- R.attr.navigation_accept,
- R.attr.content_new
- });
- labels = new int[] {
- R.string.play_label,
- R.string.cancel_download_label,
- R.string.download_label,
- R.string.mark_read_label,
- R.string.add_to_queue_label
- };
- }
-
- /**
- * Sets the displayed bitmap and content description of the given
- * action button so that it matches the state of the FeedItem.
- */
- @SuppressWarnings("ResourceType")
- public void configureActionButton(ImageButton butSecondary, FeedItem item, boolean isInQueue) {
- Validate.isTrue(butSecondary != null && item != null, "butSecondary or item was null");
-
- final FeedMedia media = item.getMedia();
- if (media != null) {
- final boolean isDownloadingMedia = DownloadRequester.getInstance().isDownloadingFile(media);
- if (!media.isDownloaded()) {
- if (isDownloadingMedia) {
- // item is being downloaded
- butSecondary.setVisibility(View.VISIBLE);
- butSecondary.setImageDrawable(drawables.getDrawable(1));
- butSecondary.setContentDescription(context.getString(labels[1]));
- } else {
- // item is not downloaded and not being downloaded
- if(DefaultActionButtonCallback.userAllowedMobileDownloads() ||
- !DefaultActionButtonCallback.userChoseAddToQueue() || isInQueue) {
- butSecondary.setVisibility(View.VISIBLE);
- butSecondary.setImageDrawable(drawables.getDrawable(2));
- butSecondary.setContentDescription(context.getString(labels[2]));
- } else {
- // mobile download not allowed yet, item is not in queue and user chose add to queue
- butSecondary.setVisibility(View.VISIBLE);
- butSecondary.setImageDrawable(drawables.getDrawable(5));
- butSecondary.setContentDescription(context.getString(labels[4]));
- }
- }
- } else {
- // item is downloaded
- butSecondary.setVisibility(View.VISIBLE);
- if (media.isCurrentlyPlaying()) {
- butSecondary.setImageDrawable(drawables.getDrawable(3));
- } else {
- butSecondary.setImageDrawable(drawables.getDrawable(0));
- }
- butSecondary.setContentDescription(context.getString(labels[0]));
- }
- } else {
- if (item.isPlayed()) {
- butSecondary.setVisibility(View.INVISIBLE);
- } else {
- butSecondary.setVisibility(View.VISIBLE);
- butSecondary.setImageDrawable(drawables.getDrawable(4));
- butSecondary.setContentDescription(context.getString(labels[3]));
- }
- }
- }
-}
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/AdapterUtils.java b/app/src/main/java/de/danoeh/antennapod/adapter/AdapterUtils.java
index 5c58d00f2..315b3a173 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/AdapterUtils.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/AdapterUtils.java
@@ -49,7 +49,7 @@ class AdapterUtils {
} else if (!media.isDownloaded()) {
if (media.getSize() > 0) {
txtvPos.setText(Converter.byteToString(media.getSize()));
- } else if(NetworkUtils.isDownloadAllowed() && !media.checkedOnSizeButUnknown()) {
+ } else if(NetworkUtils.isEpisodeHeadDownloadAllowed() && !media.checkedOnSizeButUnknown()) {
txtvPos.setText("{fa-spinner}");
Iconify.addIcons(txtvPos);
NetworkUtils.getFeedMediaSizeObservable(media)
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/AllEpisodesRecycleAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/AllEpisodesRecycleAdapter.java
index 0b2b81edb..2eff33339 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/AllEpisodesRecycleAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/AllEpisodesRecycleAdapter.java
@@ -26,6 +26,7 @@ import java.lang.ref.WeakReference;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
+import de.danoeh.antennapod.adapter.actionbutton.ItemActionButton;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.storage.DownloadRequester;
@@ -46,8 +47,6 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR
private final WeakReference<MainActivity> mainActivityRef;
private final ItemAccess itemAccess;
- private final ActionButtonCallback actionButtonCallback;
- private final ActionButtonUtils actionButtonUtils;
private final boolean showOnlyNewEpisodes;
private FeedItem selectedItem;
@@ -57,13 +56,10 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR
public AllEpisodesRecycleAdapter(MainActivity mainActivity,
ItemAccess itemAccess,
- ActionButtonCallback actionButtonCallback,
boolean showOnlyNewEpisodes) {
super();
this.mainActivityRef = new WeakReference<>(mainActivity);
this.itemAccess = itemAccess;
- this.actionButtonUtils = new ActionButtonUtils(mainActivity);
- this.actionButtonCallback = actionButtonCallback;
this.showOnlyNewEpisodes = showOnlyNewEpisodes;
playingBackGroundColor = ThemeUtils.getColorFromAttr(mainActivity, R.attr.currently_playing_background);
@@ -134,7 +130,7 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR
holder.txtvDuration.setText(Converter.getDurationStringLong(media.getDuration()));
} else if (media.getSize() > 0) {
holder.txtvDuration.setText(Converter.byteToString(media.getSize()));
- } else if(NetworkUtils.isDownloadAllowed() && !media.checkedOnSizeButUnknown()) {
+ } else if(NetworkUtils.isEpisodeHeadDownloadAllowed() && !media.checkedOnSizeButUnknown()) {
holder.txtvDuration.setText("{fa-spinner}");
Iconify.addIcons(holder.txtvDuration);
NetworkUtils.getFeedMediaSizeObservable(media)
@@ -186,10 +182,11 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR
holder.queueStatus.setVisibility(View.INVISIBLE);
}
- actionButtonUtils.configureActionButton(holder.butSecondary, item, isInQueue);
+ ItemActionButton actionButton = ItemActionButton.forItem(item, isInQueue);
+ actionButton.configure(holder.butSecondary, mainActivityRef.get());
+
holder.butSecondary.setFocusable(false);
holder.butSecondary.setTag(item);
- holder.butSecondary.setOnClickListener(secondaryActionListener);
new CoverLoader(mainActivityRef.get())
.withUri(item.getImageLocation())
@@ -215,14 +212,6 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR
return itemAccess.getCount();
}
- private final View.OnClickListener secondaryActionListener = new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- FeedItem item = (FeedItem) v.getTag();
- actionButtonCallback.onActionButtonPressed(item, itemAccess.getQueueIds());
- }
- };
-
public class Holder extends RecyclerView.ViewHolder
implements View.OnClickListener,
View.OnCreateContextMenuListener,
@@ -290,7 +279,7 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR
};
FeedItemMenuHandler.onPrepareMenu(contextMenuInterface, item, true, null);
- contextMenuInterface.setItemVisibility(R.id.mark_as_seen_item, item.isNew());
+ contextMenuInterface.setItemVisibility(R.id.remove_new_flag_item, item.isNew());
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/CoverLoader.java b/app/src/main/java/de/danoeh/antennapod/adapter/CoverLoader.java
index 54ecdae77..79dc1f96e 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/CoverLoader.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/CoverLoader.java
@@ -66,7 +66,7 @@ public class CoverLoader {
options = options.error(errorResource);
}
- RequestBuilder builder = Glide.with(activity)
+ RequestBuilder<Drawable> builder = Glide.with(activity)
.load(uri)
.apply(options);
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/DataFolderAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/DataFolderAdapter.java
new file mode 100644
index 000000000..909fd6459
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/DataFolderAdapter.java
@@ -0,0 +1,148 @@
+package de.danoeh.antennapod.adapter;
+
+import android.app.Dialog;
+import android.content.Context;
+import android.support.annotation.NonNull;
+import android.support.v4.content.ContextCompat;
+import android.support.v7.widget.RecyclerView;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.RadioButton;
+import android.widget.TextView;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.core.preferences.UserPreferences;
+import de.danoeh.antennapod.core.util.Converter;
+import de.danoeh.antennapod.core.util.StorageUtils;
+import de.danoeh.antennapod.dialog.ChooseDataFolderDialog;
+import me.zhanghai.android.materialprogressbar.MaterialProgressBar;
+
+
+public class DataFolderAdapter extends RecyclerView.Adapter<DataFolderAdapter.ViewHolder> {
+ private final ChooseDataFolderDialog.RunnableWithString selectionHandler;
+ private final String currentPath;
+ private final List<StoragePath> entries;
+ private final String freeSpaceString;
+ private Dialog dialog;
+
+ public DataFolderAdapter(Context context, ChooseDataFolderDialog.RunnableWithString selectionHandler) {
+ this.entries = getStorageEntries(context);
+ this.currentPath = getCurrentPath();
+ this.selectionHandler = selectionHandler;
+ this.freeSpaceString = context.getString(R.string.choose_data_directory_available_space);
+ }
+
+ @NonNull
+ @Override
+ public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+ LayoutInflater inflater = LayoutInflater.from(parent.getContext());
+ View entryView = inflater.inflate(R.layout.choose_data_folder_dialog_entry, parent, false);
+ return new ViewHolder(entryView);
+ }
+
+ @Override
+ public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
+ StoragePath storagePath = entries.get(position);
+ String freeSpace = Converter.byteToString(storagePath.getAvailableSpace());
+ String totalSpace = Converter.byteToString(storagePath.getTotalSpace());
+
+ holder.path.setText(storagePath.getShortPath());
+ holder.size.setText(String.format(freeSpaceString, freeSpace, totalSpace));
+ holder.progressBar.setProgress(storagePath.getUsagePercentage());
+ holder.root.setOnClickListener((View v) -> selectAndDismiss(storagePath));
+ holder.radioButton.setOnClickListener((View v) -> selectAndDismiss(storagePath));
+ if (storagePath.getFullPath().equals(currentPath)) {
+ holder.radioButton.toggle();
+ }
+ }
+
+ @Override
+ public int getItemCount() {
+ if (currentPath == null) {
+ return 0;
+ } else {
+ return entries.size();
+ }
+ }
+
+ public void setDialog(Dialog dialog) {
+ this.dialog = dialog;
+ }
+
+ private String getCurrentPath() {
+ File dataFolder = UserPreferences.getDataFolder(null);
+ if (dataFolder != null) return dataFolder.getAbsolutePath();
+ return null;
+ }
+
+ private List<StoragePath> getStorageEntries(Context context) {
+ File[] mediaDirs = ContextCompat.getExternalFilesDirs(context, null);
+ final List<StoragePath> entries = new ArrayList<>(mediaDirs.length);
+ for (File dir : mediaDirs) {
+ if (isNotWritable(dir)) continue;
+
+ entries.add(new StoragePath(dir.getAbsolutePath()));
+ }
+ return entries;
+ }
+
+ private boolean isNotWritable(File dir) {
+ return dir == null || !dir.exists() || !dir.canRead() || !dir.canWrite();
+ }
+
+ private void selectAndDismiss(StoragePath storagePath) {
+ selectionHandler.run(storagePath.getFullPath());
+ dialog.dismiss();
+ }
+
+ class ViewHolder extends RecyclerView.ViewHolder {
+ private View root;
+ private TextView path;
+ private TextView size;
+ private RadioButton radioButton;
+ private MaterialProgressBar progressBar;
+
+ ViewHolder(View itemView) {
+ super(itemView);
+ root = itemView.findViewById(R.id.root);
+ path = itemView.findViewById(R.id.path);
+ size = itemView.findViewById(R.id.size);
+ radioButton = itemView.findViewById(R.id.radio_button);
+ progressBar = itemView.findViewById(R.id.used_space);
+ }
+ }
+
+ class StoragePath {
+ private final String path;
+
+ StoragePath(String path) {
+ this.path = path;
+ }
+
+ String getShortPath() {
+ int prefixIndex = path.indexOf("Android");
+ return (prefixIndex > 0) ? path.substring(0, prefixIndex) : path;
+ }
+
+ String getFullPath() {
+ return this.path;
+ }
+
+ long getAvailableSpace() {
+ return StorageUtils.getFreeSpaceAvailable(path);
+ }
+
+ long getTotalSpace() {
+ return StorageUtils.getTotalSpaceAvailable(path);
+ }
+
+ int getUsagePercentage() {
+ return 100 - (int) (100 * getAvailableSpace() / (float) getTotalSpace());
+ }
+ }
+} \ No newline at end of file
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/DefaultActionButtonCallback.java b/app/src/main/java/de/danoeh/antennapod/adapter/DefaultActionButtonCallback.java
deleted file mode 100644
index 1286d9dc7..000000000
--- a/app/src/main/java/de/danoeh/antennapod/adapter/DefaultActionButtonCallback.java
+++ /dev/null
@@ -1,139 +0,0 @@
-package de.danoeh.antennapod.adapter;
-
-import android.content.Context;
-import android.support.annotation.NonNull;
-import android.content.Intent;
-import android.widget.Toast;
-
-import com.afollestad.materialdialogs.MaterialDialog;
-
-import org.apache.commons.lang3.Validate;
-
-import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.core.dialog.DownloadRequestErrorDialogCreator;
-import de.danoeh.antennapod.core.feed.FeedItem;
-import de.danoeh.antennapod.core.feed.FeedMedia;
-import de.danoeh.antennapod.core.preferences.UserPreferences;
-import de.danoeh.antennapod.core.service.playback.PlaybackService;
-import de.danoeh.antennapod.core.storage.DBReader;
-import de.danoeh.antennapod.core.storage.DBTasks;
-import de.danoeh.antennapod.core.storage.DBWriter;
-import de.danoeh.antennapod.core.storage.DownloadRequestException;
-import de.danoeh.antennapod.core.storage.DownloadRequester;
-import de.danoeh.antennapod.core.util.IntentUtils;
-import de.danoeh.antennapod.core.util.LongList;
-import de.danoeh.antennapod.core.util.NetworkUtils;
-import de.danoeh.antennapod.core.util.playback.PlaybackServiceStarter;
-
-/**
- * Default implementation of an ActionButtonCallback
- */
-public class DefaultActionButtonCallback implements ActionButtonCallback {
-
- private static final String TAG = "DefaultActionButtonCallback";
-
- private final Context context;
-
- private static final int TEN_MINUTES_IN_MILLIS = 60 * 1000 * 10;
-
- // remember timestamp when user allowed downloading via mobile connection
- private static long allowMobileDownloadsTimestamp;
- private static long onlyAddToQueueTimeStamp;
-
- public DefaultActionButtonCallback(Context context) {
- Validate.notNull(context);
- this.context = context;
- }
-
- public static boolean userAllowedMobileDownloads() {
- return System.currentTimeMillis() - allowMobileDownloadsTimestamp < TEN_MINUTES_IN_MILLIS;
- }
-
- public static boolean userChoseAddToQueue() {
- return System.currentTimeMillis() - onlyAddToQueueTimeStamp < TEN_MINUTES_IN_MILLIS;
- }
-
- @Override
- public void onActionButtonPressed(final FeedItem item, final LongList queueIds) {
-
- if (item.hasMedia()) {
- final FeedMedia media = item.getMedia();
- boolean isDownloading = DownloadRequester.getInstance().isDownloadingFile(media);
- if (!isDownloading && !media.isDownloaded()) {
- if (NetworkUtils.isDownloadAllowed() || userAllowedMobileDownloads()) {
- try {
- DBTasks.downloadFeedItems(context, item);
- Toast.makeText(context, R.string.status_downloading_label, Toast.LENGTH_SHORT).show();
- } catch (DownloadRequestException e) {
- e.printStackTrace();
- DownloadRequestErrorDialogCreator.newRequestErrorDialog(context, e.getMessage());
- }
- } else if(userChoseAddToQueue() && !queueIds.contains(item.getId())) {
- DBWriter.addQueueItem(context, item);
- Toast.makeText(context, R.string.added_to_queue_label, Toast.LENGTH_SHORT).show();
- } else {
- confirmMobileDownload(context, item);
- }
- } else if (isDownloading) {
- DownloadRequester.getInstance().cancelDownload(context, media);
- if(UserPreferences.isEnableAutodownload()) {
- DBWriter.setFeedItemAutoDownload(media.getItem(), false);
- Toast.makeText(context, R.string.download_canceled_autodownload_enabled_msg, Toast.LENGTH_LONG).show();
- } else {
- Toast.makeText(context, R.string.download_canceled_msg, Toast.LENGTH_LONG).show();
- }
- } else { // media is downloaded
- if (media.isCurrentlyPlaying()) {
- new PlaybackServiceStarter(context, media)
- .startWhenPrepared(true)
- .shouldStream(false)
- .start();
- IntentUtils.sendLocalBroadcast(context, PlaybackService.ACTION_PAUSE_PLAY_CURRENT_EPISODE);
- } else if (media.isCurrentlyPaused()) {
- new PlaybackServiceStarter(context, media)
- .startWhenPrepared(true)
- .shouldStream(false)
- .start();
- IntentUtils.sendLocalBroadcast(context, PlaybackService.ACTION_RESUME_PLAY_CURRENT_EPISODE);
- } else {
- DBTasks.playMedia(context, media, false, true, false);
- }
- }
- } else {
- if (!item.isPlayed()) {
- DBWriter.markItemPlayed(item, FeedItem.PLAYED, true);
- }
- }
- }
-
- private void confirmMobileDownload(final Context context, final FeedItem item) {
- MaterialDialog.Builder builder = new MaterialDialog.Builder(context);
- builder
- .title(R.string.confirm_mobile_download_dialog_title)
- .content(R.string.confirm_mobile_download_dialog_message)
- .positiveText(context.getText(R.string.confirm_mobile_download_dialog_enable_temporarily))
- .onPositive((dialog, which) -> {
- allowMobileDownloadsTimestamp = System.currentTimeMillis();
- try {
- DBTasks.downloadFeedItems(context, item);
- Toast.makeText(context, R.string.status_downloading_label, Toast.LENGTH_SHORT).show();
- } catch (DownloadRequestException e) {
- e.printStackTrace();
- DownloadRequestErrorDialogCreator.newRequestErrorDialog(context, e.getMessage());
- }
- });
- LongList queueIds = DBReader.getQueueIDList();
- if(!queueIds.contains(item.getId())) {
- builder
- .content(R.string.confirm_mobile_download_dialog_message_not_in_queue)
- .neutralText(R.string.confirm_mobile_download_dialog_only_add_to_queue)
- .onNeutral((dialog, which) -> {
- onlyAddToQueueTimeStamp = System.currentTimeMillis();
- DBWriter.addQueueItem(context, item);
- Toast.makeText(context, R.string.added_to_queue_label, Toast.LENGTH_SHORT).show();
- });
- }
- builder.show();
- }
-
-}
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/FeedDiscoverAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/FeedDiscoverAdapter.java
new file mode 100644
index 000000000..df7ec46e0
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/FeedDiscoverAdapter.java
@@ -0,0 +1,76 @@
+package de.danoeh.antennapod.adapter;
+
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.ImageView;
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.request.RequestOptions;
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.activity.MainActivity;
+import de.danoeh.antennapod.discovery.PodcastSearchResult;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.List;
+
+public class FeedDiscoverAdapter extends BaseAdapter {
+
+ private final WeakReference<MainActivity> mainActivityRef;
+ private final List<PodcastSearchResult> data = new ArrayList<>();
+
+ public FeedDiscoverAdapter(MainActivity mainActivity) {
+ this.mainActivityRef = new WeakReference<>(mainActivity);
+ }
+
+ public void updateData(List<PodcastSearchResult> newData) {
+ data.clear();
+ data.addAll(newData);
+ notifyDataSetChanged();
+ }
+
+ @Override
+ public int getCount() {
+ return data.size();
+ }
+
+ @Override
+ public PodcastSearchResult getItem(int position) {
+ return data.get(position);
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return 0;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ Holder holder;
+
+ if (convertView == null) {
+ convertView = View.inflate(mainActivityRef.get(), R.layout.quick_feed_discovery_item, null);
+ holder = new Holder();
+ holder.imageView = convertView.findViewById(R.id.discovery_cover);
+ convertView.setTag(holder);
+ } else {
+ holder = (Holder) convertView.getTag();
+ }
+
+
+ final PodcastSearchResult podcast = getItem(position);
+ Glide.with(mainActivityRef.get())
+ .load(podcast.imageUrl)
+ .apply(new RequestOptions()
+ .placeholder(R.color.light_gray)
+ .fitCenter()
+ .dontAnimate())
+ .into(holder.imageView);
+
+ return convertView;
+ }
+
+ static class Holder {
+ ImageView imageView;
+ }
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistAdapter.java
index 738a0a636..a365b1b2e 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistAdapter.java
@@ -7,7 +7,6 @@ import android.support.v4.content.ContextCompat;
import android.text.Layout;
import android.view.LayoutInflater;
import android.view.View;
-import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.Adapter;
import android.widget.BaseAdapter;
@@ -18,6 +17,7 @@ import android.widget.ProgressBar;
import android.widget.TextView;
import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.adapter.actionbutton.ItemActionButton;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.feed.MediaType;
@@ -31,14 +31,12 @@ import de.danoeh.antennapod.core.util.ThemeUtils;
*/
public class FeedItemlistAdapter extends BaseAdapter {
- private final ActionButtonCallback callback;
private final ItemAccess itemAccess;
private final Context context;
private final boolean showFeedtitle;
private final int selectedItemIndex;
/** true if played items should be made partially transparent */
private final boolean makePlayedItemsTransparent;
- private final ActionButtonUtils actionButtonUtils;
private static final int SELECTION_NONE = -1;
@@ -47,16 +45,13 @@ public class FeedItemlistAdapter extends BaseAdapter {
public FeedItemlistAdapter(Context context,
ItemAccess itemAccess,
- ActionButtonCallback callback,
boolean showFeedtitle,
boolean makePlayedItemsTransparent) {
super();
- this.callback = callback;
this.context = context;
this.itemAccess = itemAccess;
this.showFeedtitle = showFeedtitle;
this.selectedItemIndex = SELECTION_NONE;
- this.actionButtonUtils = new ActionButtonUtils(context);
this.makePlayedItemsTransparent = makePlayedItemsTransparent;
playingBackGroundColor = ThemeUtils.getColorFromAttr(context, R.attr.currently_playing_background);
@@ -199,10 +194,11 @@ public class FeedItemlistAdapter extends BaseAdapter {
}
}
- actionButtonUtils.configureActionButton(holder.butAction, item, isInQueue);
+ ItemActionButton actionButton = ItemActionButton.forItem(item, isInQueue);
+ actionButton.configure(holder.butAction, context);
+
holder.butAction.setFocusable(false);
holder.butAction.setTag(item);
- holder.butAction.setOnClickListener(butActionListener);
} else {
convertView.setVisibility(View.GONE);
@@ -210,14 +206,6 @@ public class FeedItemlistAdapter extends BaseAdapter {
return convertView;
}
- private final OnClickListener butActionListener = new OnClickListener() {
- @Override
- public void onClick(View v) {
- FeedItem item = (FeedItem) v.getTag();
- callback.onActionButtonPressed(item, itemAccess.getQueueIds());
- }
- };
-
static class Holder {
LinearLayout container;
TextView title;
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/NavListAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/NavListAdapter.java
index fbf6b804a..be8e52cfc 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/NavListAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/NavListAdapter.java
@@ -8,6 +8,7 @@ import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import android.preference.PreferenceManager;
import android.support.v7.app.AlertDialog;
+import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -214,10 +215,17 @@ public class NavListAdapter extends BaseAdapter
}
if (v != null && viewType != VIEW_TYPE_SECTION_DIVIDER) {
TextView txtvTitle = v.findViewById(R.id.txtvTitle);
+ TypedValue typedValue = new TypedValue();
+
if (position == itemAccess.getSelectedItemIndex()) {
txtvTitle.setTypeface(null, Typeface.BOLD);
+ v.getContext().getTheme().resolveAttribute(de.danoeh.antennapod.core.R.attr.drawer_activated_color, typedValue, true);
+ v.setBackgroundResource(typedValue.resourceId);
+
} else {
txtvTitle.setTypeface(null, Typeface.NORMAL);
+ v.getContext().getTheme().resolveAttribute(de.danoeh.antennapod.core.R.attr.nav_drawer_background, typedValue, true);
+ v.setBackgroundResource(typedValue.resourceId);
}
}
return v;
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/QueueRecyclerAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/QueueRecyclerAdapter.java
index df8cafb9d..382abfb32 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/QueueRecyclerAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/QueueRecyclerAdapter.java
@@ -1,5 +1,6 @@
package de.danoeh.antennapod.adapter;
+import android.content.Context;
import android.os.Build;
import android.support.annotation.Nullable;
import android.support.v4.content.ContextCompat;
@@ -22,8 +23,6 @@ import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
-import com.bumptech.glide.Glide;
-import com.bumptech.glide.request.RequestOptions;
import com.joanzapata.iconify.Iconify;
import org.apache.commons.lang3.ArrayUtils;
@@ -32,9 +31,9 @@ import java.lang.ref.WeakReference;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
+import de.danoeh.antennapod.adapter.actionbutton.ItemActionButton;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
-import de.danoeh.antennapod.core.glide.ApGlideSettings;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.storage.DownloadRequester;
import de.danoeh.antennapod.core.util.Converter;
@@ -54,8 +53,6 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap
private final WeakReference<MainActivity> mainActivity;
private final ItemAccess itemAccess;
- private final ActionButtonCallback actionButtonCallback;
- private final ActionButtonUtils actionButtonUtils;
private final ItemTouchHelper itemTouchHelper;
private boolean locked;
@@ -67,13 +64,10 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap
public QueueRecyclerAdapter(MainActivity mainActivity,
ItemAccess itemAccess,
- ActionButtonCallback actionButtonCallback,
ItemTouchHelper itemTouchHelper) {
super();
this.mainActivity = new WeakReference<>(mainActivity);
this.itemAccess = itemAccess;
- this.actionButtonUtils = new ActionButtonUtils(mainActivity);
- this.actionButtonCallback = actionButtonCallback;
this.itemTouchHelper = itemTouchHelper;
locked = UserPreferences.isQueueLocked();
@@ -258,7 +252,7 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap
} else {
if(media.getSize() > 0) {
progressLeft.setText(Converter.byteToString(media.getSize()));
- } else if(NetworkUtils.isDownloadAllowed() && !media.checkedOnSizeButUnknown()) {
+ } else if(NetworkUtils.isEpisodeHeadDownloadAllowed() && !media.checkedOnSizeButUnknown()) {
progressLeft.setText("{fa-spinner}");
Iconify.addIcons(progressLeft);
NetworkUtils.getFeedMediaSizeObservable(media)
@@ -287,10 +281,11 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap
}
}
- actionButtonUtils.configureActionButton(butSecondary, item, true);
+ ItemActionButton actionButton = ItemActionButton.forItem(item, true);
+ actionButton.configure(butSecondary, mainActivity.get());
+
butSecondary.setFocusable(false);
butSecondary.setTag(item);
- butSecondary.setOnClickListener(secondaryActionListener);
new CoverLoader(mainActivity.get())
.withUri(item.getImageLocation())
@@ -302,15 +297,6 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap
}
- private final View.OnClickListener secondaryActionListener = new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- FeedItem item = (FeedItem) v.getTag();
- actionButtonCallback.onActionButtonPressed(item, itemAccess.getQueueIds());
- }
- };
-
-
public interface ItemAccess {
FeedItem getItem(int position);
int getCount();
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/AddToQueueActionButton.java b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/AddToQueueActionButton.java
new file mode 100644
index 000000000..3299db3ab
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/AddToQueueActionButton.java
@@ -0,0 +1,32 @@
+package de.danoeh.antennapod.adapter.actionbutton;
+
+import android.content.Context;
+import android.support.annotation.AttrRes;
+import android.support.annotation.StringRes;
+
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.core.feed.FeedItem;
+
+class AddToQueueActionButton extends ItemActionButton {
+
+ AddToQueueActionButton(FeedItem item) {
+ super(item);
+ }
+
+ @Override
+ @StringRes
+ public int getLabel() {
+ return R.string.add_to_queue_label;
+ }
+
+ @Override
+ @AttrRes
+ public int getDrawable() {
+ return R.attr.content_new;
+ }
+
+ @Override
+ public void onClick(Context context) {
+ MobileDownloadHelper.confirmMobileDownload(context, item);
+ }
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/CancelDownloadActionButton.java b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/CancelDownloadActionButton.java
new file mode 100644
index 000000000..1275a799b
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/CancelDownloadActionButton.java
@@ -0,0 +1,44 @@
+package de.danoeh.antennapod.adapter.actionbutton;
+
+import android.content.Context;
+import android.support.annotation.AttrRes;
+import android.support.annotation.StringRes;
+import android.widget.Toast;
+
+import de.danoeh.antennapod.R;
+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.storage.DBWriter;
+import de.danoeh.antennapod.core.storage.DownloadRequester;
+
+class CancelDownloadActionButton extends ItemActionButton {
+
+ CancelDownloadActionButton(FeedItem item) {
+ super(item);
+ }
+
+ @Override
+ @StringRes
+ public int getLabel() {
+ return R.string.cancel_download_label;
+ }
+
+ @Override
+ @AttrRes
+ public int getDrawable() {
+ return R.attr.navigation_cancel;
+ }
+
+ @Override
+ public void onClick(Context context) {
+ FeedMedia media = item.getMedia();
+ DownloadRequester.getInstance().cancelDownload(context, media);
+ if (UserPreferences.isEnableAutodownload()) {
+ DBWriter.setFeedItemAutoDownload(media.getItem(), false);
+ Toast.makeText(context, R.string.download_canceled_autodownload_enabled_msg, Toast.LENGTH_LONG).show();
+ } else {
+ Toast.makeText(context, R.string.download_canceled_msg, Toast.LENGTH_LONG).show();
+ }
+ }
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/DownloadActionButton.java b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/DownloadActionButton.java
new file mode 100644
index 000000000..c1559528e
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/DownloadActionButton.java
@@ -0,0 +1,74 @@
+package de.danoeh.antennapod.adapter.actionbutton;
+
+import android.content.Context;
+import android.support.annotation.AttrRes;
+import android.support.annotation.NonNull;
+import android.support.annotation.StringRes;
+import android.widget.Toast;
+
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.core.dialog.DownloadRequestErrorDialogCreator;
+import de.danoeh.antennapod.core.feed.FeedItem;
+import de.danoeh.antennapod.core.feed.FeedMedia;
+import de.danoeh.antennapod.core.storage.DBTasks;
+import de.danoeh.antennapod.core.storage.DBWriter;
+import de.danoeh.antennapod.core.storage.DownloadRequestException;
+import de.danoeh.antennapod.core.storage.DownloadRequester;
+import de.danoeh.antennapod.core.util.NetworkUtils;
+
+class DownloadActionButton extends ItemActionButton {
+ private boolean isInQueue;
+
+ DownloadActionButton(FeedItem item, boolean isInQueue) {
+ super(item);
+ this.isInQueue = isInQueue;
+ }
+
+ @Override
+ @StringRes
+ public int getLabel() {
+ return R.string.download_label;
+ }
+
+ @Override
+ @AttrRes
+ public int getDrawable() {
+ return R.attr.av_download;
+ }
+
+ @Override
+ public void onClick(Context context) {
+ final FeedMedia media = item.getMedia();
+ if (media == null || shouldNotDownload(media)) {
+ return;
+ }
+
+ if (NetworkUtils.isEpisodeDownloadAllowed() || MobileDownloadHelper.userAllowedMobileDownloads()) {
+ downloadEpisode(context);
+ } else if (MobileDownloadHelper.userChoseAddToQueue() && !isInQueue) {
+ addEpisodeToQueue(context);
+ } else {
+ MobileDownloadHelper.confirmMobileDownload(context, item);
+ }
+ }
+
+ private boolean shouldNotDownload(@NonNull FeedMedia media) {
+ boolean isDownloading = DownloadRequester.getInstance().isDownloadingFile(media);
+ return isDownloading || media.isDownloaded();
+ }
+
+ private void addEpisodeToQueue(Context context) {
+ DBWriter.addQueueItem(context, item);
+ Toast.makeText(context, R.string.added_to_queue_label, Toast.LENGTH_SHORT).show();
+ }
+
+ private void downloadEpisode(Context context) {
+ try {
+ DBTasks.downloadFeedItems(context, item);
+ Toast.makeText(context, R.string.status_downloading_label, Toast.LENGTH_SHORT).show();
+ } catch (DownloadRequestException e) {
+ e.printStackTrace();
+ DownloadRequestErrorDialogCreator.newRequestErrorDialog(context, e.getMessage());
+ }
+ }
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/ItemActionButton.java b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/ItemActionButton.java
new file mode 100644
index 000000000..da5ebf6e1
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/ItemActionButton.java
@@ -0,0 +1,63 @@
+package de.danoeh.antennapod.adapter.actionbutton;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.support.annotation.AttrRes;
+import android.support.annotation.NonNull;
+import android.support.annotation.StringRes;
+import android.view.View;
+import android.widget.ImageButton;
+
+import de.danoeh.antennapod.core.feed.FeedItem;
+import de.danoeh.antennapod.core.feed.FeedMedia;
+import de.danoeh.antennapod.core.storage.DownloadRequester;
+
+public abstract class ItemActionButton {
+ FeedItem item;
+
+ ItemActionButton(FeedItem item) {
+ this.item = item;
+ }
+
+ @StringRes
+ abstract public int getLabel();
+
+ @AttrRes
+ abstract public int getDrawable();
+
+ abstract public void onClick(Context context);
+
+ public int getVisibility() {
+ return View.VISIBLE;
+ }
+
+ @NonNull
+ public static ItemActionButton forItem(@NonNull FeedItem item, boolean isInQueue) {
+ final FeedMedia media = item.getMedia();
+ if (media == null) {
+ return new MarkAsPlayedActionButton(item);
+ }
+
+ final boolean isDownloadingMedia = DownloadRequester.getInstance().isDownloadingFile(media);
+ if (media.isDownloaded()) {
+ return new PlayActionButton(item);
+ } else if (isDownloadingMedia) {
+ return new CancelDownloadActionButton(item);
+ } else if (MobileDownloadHelper.userAllowedMobileDownloads() || !MobileDownloadHelper.userChoseAddToQueue() || isInQueue) {
+ return new DownloadActionButton(item, isInQueue);
+ } else {
+ return new AddToQueueActionButton(item);
+ }
+ }
+
+ public void configure(@NonNull ImageButton button, Context context) {
+ TypedArray drawables = context.obtainStyledAttributes(new int[]{getDrawable()});
+
+ button.setVisibility(getVisibility());
+ button.setContentDescription(context.getString(getLabel()));
+ button.setImageDrawable(drawables.getDrawable(0));
+ button.setOnClickListener((view) -> onClick(context));
+
+ drawables.recycle();
+ }
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/MarkAsPlayedActionButton.java b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/MarkAsPlayedActionButton.java
new file mode 100644
index 000000000..4d906cee5
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/MarkAsPlayedActionButton.java
@@ -0,0 +1,41 @@
+package de.danoeh.antennapod.adapter.actionbutton;
+
+import android.content.Context;
+import android.support.annotation.AttrRes;
+import android.support.annotation.StringRes;
+import android.view.View;
+
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.core.feed.FeedItem;
+import de.danoeh.antennapod.core.storage.DBWriter;
+
+class MarkAsPlayedActionButton extends ItemActionButton {
+
+ MarkAsPlayedActionButton(FeedItem item) {
+ super(item);
+ }
+
+ @Override
+ @StringRes
+ public int getLabel() {
+ return R.string.mark_read_label;
+ }
+
+ @Override
+ @AttrRes
+ public int getDrawable() {
+ return R.attr.navigation_accept;
+ }
+
+ @Override
+ public void onClick(Context context) {
+ if (!item.isPlayed()) {
+ DBWriter.markItemPlayed(item, FeedItem.PLAYED, true);
+ }
+ }
+
+ @Override
+ public int getVisibility() {
+ return (item.isPlayed()) ? View.INVISIBLE : View.VISIBLE;
+ }
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/MobileDownloadHelper.java b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/MobileDownloadHelper.java
new file mode 100644
index 000000000..f8d2a139e
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/MobileDownloadHelper.java
@@ -0,0 +1,60 @@
+package de.danoeh.antennapod.adapter.actionbutton;
+
+import android.content.Context;
+import android.widget.Toast;
+
+import com.afollestad.materialdialogs.MaterialDialog;
+
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.core.dialog.DownloadRequestErrorDialogCreator;
+import de.danoeh.antennapod.core.feed.FeedItem;
+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.DownloadRequestException;
+
+class MobileDownloadHelper {
+ private static long addToQueueTimestamp;
+ private static long allowMobileDownloadTimestamp;
+ private static final int TEN_MINUTES_IN_MILLIS = 10 * 60 * 1000;
+
+ static boolean userChoseAddToQueue() {
+ return System.currentTimeMillis() - addToQueueTimestamp < TEN_MINUTES_IN_MILLIS;
+ }
+
+ static boolean userAllowedMobileDownloads() {
+ return System.currentTimeMillis() - allowMobileDownloadTimestamp < TEN_MINUTES_IN_MILLIS;
+ }
+
+ static void confirmMobileDownload(final Context context, final FeedItem item) {
+ MaterialDialog.Builder builder = new MaterialDialog.Builder(context)
+ .title(R.string.confirm_mobile_download_dialog_title)
+ .content(R.string.confirm_mobile_download_dialog_message)
+ .positiveText(context.getText(R.string.confirm_mobile_download_dialog_enable_temporarily))
+ .onPositive((dialog, which) -> downloadFeedItems(context, item));
+ if (!DBReader.getQueueIDList().contains(item.getId())) {
+ builder
+ .content(R.string.confirm_mobile_download_dialog_message_not_in_queue)
+ .neutralText(R.string.confirm_mobile_download_dialog_only_add_to_queue)
+ .onNeutral((dialog, which) -> addToQueue(context, item));
+ }
+ builder.show();
+ }
+
+ private static void addToQueue(Context context, FeedItem item) {
+ addToQueueTimestamp = System.currentTimeMillis();
+ DBWriter.addQueueItem(context, item);
+ Toast.makeText(context, R.string.added_to_queue_label, Toast.LENGTH_SHORT).show();
+ }
+
+ private static void downloadFeedItems(Context context, FeedItem item) {
+ allowMobileDownloadTimestamp = System.currentTimeMillis();
+ try {
+ DBTasks.downloadFeedItems(context, item);
+ Toast.makeText(context, R.string.status_downloading_label, Toast.LENGTH_SHORT).show();
+ } catch (DownloadRequestException e) {
+ e.printStackTrace();
+ DownloadRequestErrorDialogCreator.newRequestErrorDialog(context, e.getMessage());
+ }
+ }
+} \ No newline at end of file
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/PlayActionButton.java b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/PlayActionButton.java
new file mode 100644
index 000000000..3992c7240
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/PlayActionButton.java
@@ -0,0 +1,63 @@
+package de.danoeh.antennapod.adapter.actionbutton;
+
+import android.content.Context;
+import android.support.annotation.AttrRes;
+import android.support.annotation.StringRes;
+
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.core.feed.FeedItem;
+import de.danoeh.antennapod.core.feed.FeedMedia;
+import de.danoeh.antennapod.core.storage.DBTasks;
+import de.danoeh.antennapod.core.util.IntentUtils;
+import de.danoeh.antennapod.core.util.playback.PlaybackServiceStarter;
+
+import static de.danoeh.antennapod.core.service.playback.PlaybackService.ACTION_PAUSE_PLAY_CURRENT_EPISODE;
+import static de.danoeh.antennapod.core.service.playback.PlaybackService.ACTION_RESUME_PLAY_CURRENT_EPISODE;
+
+class PlayActionButton extends ItemActionButton {
+
+ PlayActionButton(FeedItem item) {
+ super(item);
+ }
+
+ @Override
+ @StringRes
+ public int getLabel() {
+ return R.string.play_label;
+ }
+
+ @Override
+ @AttrRes
+ public int getDrawable() {
+ FeedMedia media = item.getMedia();
+ if (media != null && media.isCurrentlyPlaying()) {
+ return R.attr.av_pause;
+ } else {
+ return R.attr.av_play;
+ }
+ }
+
+ @Override
+ public void onClick(Context context) {
+ FeedMedia media = item.getMedia();
+ if (media == null) {
+ return;
+ }
+
+ if (media.isPlaying()) {
+ togglePlayPause(context, media);
+ } else {
+ DBTasks.playMedia(context, media, false, true, false);
+ }
+ }
+
+ private void togglePlayPause(Context context, FeedMedia media) {
+ new PlaybackServiceStarter(context, media)
+ .startWhenPrepared(true)
+ .shouldStream(false)
+ .start();
+
+ String pauseOrResume = media.isCurrentlyPlaying() ? ACTION_PAUSE_PLAY_CURRENT_EPISODE : ACTION_RESUME_PLAY_CURRENT_EPISODE;
+ IntentUtils.sendLocalBroadcast(context, pauseOrResume);
+ }
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/itunes/ItunesAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/itunes/ItunesAdapter.java
index 2cf17c85f..f5213e4ab 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/itunes/ItunesAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/itunes/ItunesAdapter.java
@@ -2,7 +2,6 @@ package de.danoeh.antennapod.adapter.itunes;
import android.content.Context;
import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
@@ -13,18 +12,14 @@ import com.bumptech.glide.Glide;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
import com.bumptech.glide.request.RequestOptions;
-import de.danoeh.antennapod.core.glide.ApGlideSettings;
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
+import de.danoeh.antennapod.discovery.PodcastSearchResult;
import java.util.List;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
-import de.mfietz.fyydlin.SearchHit;
-public class ItunesAdapter extends ArrayAdapter<ItunesAdapter.Podcast> {
+public class ItunesAdapter extends ArrayAdapter<PodcastSearchResult> {
/**
* Related Context
*/
@@ -33,7 +28,7 @@ public class ItunesAdapter extends ArrayAdapter<ItunesAdapter.Podcast> {
/**
* List holding the podcasts found in the search
*/
- private final List<Podcast> data;
+ private final List<PodcastSearchResult> data;
/**
* Constructor.
@@ -41,7 +36,7 @@ public class ItunesAdapter extends ArrayAdapter<ItunesAdapter.Podcast> {
* @param context Related context
* @param objects Search result
*/
- public ItunesAdapter(Context context, List<Podcast> objects) {
+ public ItunesAdapter(Context context, List<PodcastSearchResult> objects) {
super(context, 0, objects);
this.data = objects;
this.context = context;
@@ -51,7 +46,7 @@ public class ItunesAdapter extends ArrayAdapter<ItunesAdapter.Podcast> {
@Override
public View getView(int position, View convertView, @NonNull ViewGroup parent) {
//Current podcast
- Podcast podcast = data.get(position);
+ PodcastSearchResult podcast = data.get(position);
//ViewHolder
PodcastViewHolder viewHolder;
@@ -94,75 +89,6 @@ public class ItunesAdapter extends ArrayAdapter<ItunesAdapter.Podcast> {
}
/**
- * Represents an individual podcast on the iTunes Store.
- */
- public static class Podcast { //TODO: Move this out eventually. Possibly to core.itunes.model
-
- /**
- * The name of the podcast
- */
- public final String title;
-
- /**
- * URL of the podcast image
- */
- @Nullable
- public final String imageUrl;
- /**
- * URL of the podcast feed
- */
- @Nullable
- public final String feedUrl;
-
-
- private Podcast(String title, @Nullable String imageUrl, @Nullable String feedUrl) {
- this.title = title;
- this.imageUrl = imageUrl;
- this.feedUrl = feedUrl;
- }
-
- /**
- * Constructs a Podcast instance from a iTunes search result
- *
- * @param json object holding the podcast information
- * @throws JSONException
- */
- public static Podcast fromSearch(JSONObject json) {
- String title = json.optString("collectionName", "");
- String imageUrl = json.optString("artworkUrl100", null);
- String feedUrl = json.optString("feedUrl", null);
- return new Podcast(title, imageUrl, feedUrl);
- }
-
- public static Podcast fromSearch(SearchHit searchHit) {
- return new Podcast(searchHit.getTitle(), searchHit.getThumbImageURL(), searchHit.getXmlUrl());
- }
-
- /**
- * Constructs a Podcast instance from iTunes toplist entry
- *
- * @param json object holding the podcast information
- * @throws JSONException
- */
- public static Podcast fromToplist(JSONObject json) throws JSONException {
- String title = json.getJSONObject("title").getString("label");
- String imageUrl = null;
- JSONArray images = json.getJSONArray("im:image");
- for(int i=0; imageUrl == null && i < images.length(); i++) {
- JSONObject image = images.getJSONObject(i);
- String height = image.getJSONObject("attributes").getString("height");
- if(Integer.parseInt(height) >= 100) {
- imageUrl = image.getString("label");
- }
- }
- String feedUrl = "https://itunes.apple.com/lookup?id=" +
- json.getJSONObject("id").getJSONObject("attributes").getString("im:id");
- return new Podcast(title, imageUrl, feedUrl);
- }
-
- }
-
- /**
* View holder object for the GridView
*/
static class PodcastViewHolder {
diff --git a/app/src/main/java/de/danoeh/antennapod/asynctask/ExportWorker.java b/app/src/main/java/de/danoeh/antennapod/asynctask/ExportWorker.java
index 7c3c570a0..219725b01 100644
--- a/app/src/main/java/de/danoeh/antennapod/asynctask/ExportWorker.java
+++ b/app/src/main/java/de/danoeh/antennapod/asynctask/ExportWorker.java
@@ -23,15 +23,15 @@ public class ExportWorker {
private static final String TAG = "ExportWorker";
private static final String DEFAULT_OUTPUT_NAME = "antennapod-feeds";
- private final ExportWriter exportWriter;
- private final File output;
+ private final @NonNull ExportWriter exportWriter;
+ private final @NonNull File output;
- public ExportWorker(ExportWriter exportWriter) {
+ public ExportWorker(@NonNull ExportWriter exportWriter) {
this(exportWriter, new File(UserPreferences.getDataFolder(EXPORT_DIR),
DEFAULT_OUTPUT_NAME + "." + exportWriter.fileExtension()));
}
- private ExportWorker(ExportWriter exportWriter, @NonNull File output) {
+ private ExportWorker(@NonNull ExportWriter exportWriter, @NonNull File output) {
this.exportWriter = exportWriter;
this.output = output;
}
diff --git a/app/src/main/java/de/danoeh/antennapod/config/ClientConfigurator.java b/app/src/main/java/de/danoeh/antennapod/config/ClientConfigurator.java
index 4138738f6..3dd7c350d 100644
--- a/app/src/main/java/de/danoeh/antennapod/config/ClientConfigurator.java
+++ b/app/src/main/java/de/danoeh/antennapod/config/ClientConfigurator.java
@@ -16,7 +16,6 @@ class ClientConfigurator {
ClientConfig.downloadServiceCallbacks = new DownloadServiceCallbacksImpl();
ClientConfig.gpodnetCallbacks = new GpodnetCallbacksImpl();
ClientConfig.playbackServiceCallbacks = new PlaybackServiceCallbacksImpl();
- ClientConfig.flattrCallbacks = new FlattrCallbacksImpl();
ClientConfig.dbTasksCallbacks = new DBTasksCallbacksImpl();
ClientConfig.castCallbacks = new CastCallbackImpl();
}
diff --git a/app/src/main/java/de/danoeh/antennapod/config/FlattrCallbacksImpl.java b/app/src/main/java/de/danoeh/antennapod/config/FlattrCallbacksImpl.java
deleted file mode 100644
index 3817db6de..000000000
--- a/app/src/main/java/de/danoeh/antennapod/config/FlattrCallbacksImpl.java
+++ /dev/null
@@ -1,53 +0,0 @@
-package de.danoeh.antennapod.config;
-
-
-import android.app.PendingIntent;
-import android.content.Context;
-import android.content.Intent;
-import android.util.Log;
-
-import org.shredzone.flattr4j.oauth.AccessToken;
-
-import de.danoeh.antennapod.BuildConfig;
-import de.danoeh.antennapod.activity.FlattrAuthActivity;
-import de.danoeh.antennapod.activity.MainActivity;
-import de.danoeh.antennapod.core.FlattrCallbacks;
-
-public class FlattrCallbacksImpl implements FlattrCallbacks {
- private static final String TAG = "FlattrCallbacksImpl";
-
- @Override
- public boolean flattrEnabled() {
- return true;
- }
-
- @Override
- public Intent getFlattrAuthenticationActivityIntent(Context context) {
- return new Intent(context, FlattrAuthActivity.class);
- }
-
- @Override
- public PendingIntent getFlattrFailedNotificationContentIntent(Context context) {
- return PendingIntent.getActivity(context, 0, new Intent(context, MainActivity.class), 0);
- }
-
- @Override
- public String getFlattrAppKey() {
- return BuildConfig.FLATTR_APP_KEY;
- }
-
- @Override
- public String getFlattrAppSecret() {
- return BuildConfig.FLATTR_APP_SECRET;
- }
-
- @Override
- public void handleFlattrAuthenticationSuccess(AccessToken token) {
- FlattrAuthActivity instance = FlattrAuthActivity.getInstance();
- if (instance != null) {
- instance.handleAuthenticationSuccess();
- } else {
- Log.e(TAG, "FlattrAuthActivity instance was null");
- }
- }
-}
diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/AutoFlattrPreferenceDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/AutoFlattrPreferenceDialog.java
deleted file mode 100644
index c28342374..000000000
--- a/app/src/main/java/de/danoeh/antennapod/dialog/AutoFlattrPreferenceDialog.java
+++ /dev/null
@@ -1,97 +0,0 @@
-package de.danoeh.antennapod.dialog;
-
-import android.annotation.SuppressLint;
-import android.app.Activity;
-import android.content.Context;
-import android.support.v7.app.AlertDialog;
-import android.view.View;
-import android.widget.CheckBox;
-import android.widget.SeekBar;
-import android.widget.TextView;
-
-import org.apache.commons.lang3.Validate;
-
-import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.core.preferences.UserPreferences;
-
-/**
- * Creates a new AlertDialog that displays preferences for auto-flattring to the user.
- */
-public class AutoFlattrPreferenceDialog {
-
- private AutoFlattrPreferenceDialog() {
- }
-
- public static void newAutoFlattrPreferenceDialog(final Activity activity, final AutoFlattrPreferenceDialogInterface callback) {
- Validate.notNull(activity);
- Validate.notNull(callback);
-
- AlertDialog.Builder builder = new AlertDialog.Builder(activity);
-
- @SuppressLint("InflateParams") View view = activity.getLayoutInflater().inflate(R.layout.autoflattr_preference_dialog, null);
- final CheckBox chkAutoFlattr = view.findViewById(R.id.chkAutoFlattr);
- final SeekBar skbPercent = view.findViewById(R.id.skbPercent);
- final TextView txtvStatus = view.findViewById(R.id.txtvStatus);
-
- chkAutoFlattr.setChecked(UserPreferences.isAutoFlattr());
- skbPercent.setEnabled(chkAutoFlattr.isChecked());
- txtvStatus.setEnabled(chkAutoFlattr.isChecked());
-
- final int initialValue = (int) (UserPreferences.getAutoFlattrPlayedDurationThreshold() * 100.0f);
- setStatusMsgText(activity, txtvStatus, initialValue);
- skbPercent.setProgress(initialValue);
-
- chkAutoFlattr.setOnClickListener(v -> {
- skbPercent.setEnabled(chkAutoFlattr.isChecked());
- txtvStatus.setEnabled(chkAutoFlattr.isChecked());
- });
-
- skbPercent.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
- @Override
- public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
- setStatusMsgText(activity, txtvStatus, progress);
- }
-
- @Override
- public void onStartTrackingTouch(SeekBar seekBar) {
-
- }
-
- @Override
- public void onStopTrackingTouch(SeekBar seekBar) {
-
- }
- });
-
- builder.setTitle(R.string.pref_auto_flattr_title)
- .setView(view)
- .setPositiveButton(R.string.confirm_label, (dialog, which) -> {
- float progDouble = ((float) skbPercent.getProgress()) / 100.0f;
- callback.onConfirmed(chkAutoFlattr.isChecked(), progDouble);
- dialog.dismiss();
- })
- .setNegativeButton(R.string.cancel_label, (dialog, which) -> {
- callback.onCancelled();
- dialog.dismiss();
- })
- .setCancelable(false).show();
- }
-
- private static void setStatusMsgText(Context context, TextView txtvStatus, int progress) {
- if (progress == 0) {
- txtvStatus.setText(R.string.auto_flattr_ater_beginning);
- } else if (progress == 100) {
- txtvStatus.setText(R.string.auto_flattr_ater_end);
- } else {
- txtvStatus.setText(context.getString(R.string.auto_flattr_after_percent, progress));
- }
- }
-
- public interface AutoFlattrPreferenceDialogInterface {
- void onCancelled();
-
- void onConfirmed(boolean autoFlattrEnabled, float autoFlattrValue);
- }
-
-
-}
diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/ChooseDataFolderDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/ChooseDataFolderDialog.java
index e8faa7c29..c185a5557 100644
--- a/app/src/main/java/de/danoeh/antennapod/dialog/ChooseDataFolderDialog.java
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/ChooseDataFolderDialog.java
@@ -1,20 +1,11 @@
package de.danoeh.antennapod.dialog;
import android.content.Context;
-import android.os.Build;
-import android.support.v4.content.ContextCompat;
-import android.text.Html;
import com.afollestad.materialdialogs.MaterialDialog;
-import java.io.File;
-import java.util.ArrayList;
-import java.util.List;
-
import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.core.preferences.UserPreferences;
-import de.danoeh.antennapod.core.util.Converter;
-import de.danoeh.antennapod.core.util.StorageUtils;
+import de.danoeh.antennapod.adapter.DataFolderAdapter;
public class ChooseDataFolderDialog {
@@ -31,39 +22,9 @@ public class ChooseDataFolderDialog {
private ChooseDataFolderDialog() {}
public static void showDialog(final Context context, RunnableWithString handlerFunc) {
- File dataFolder = UserPreferences.getDataFolder(null);
- if (dataFolder == null) {
- new MaterialDialog.Builder(context)
- .title(R.string.error_label)
- .content(R.string.external_storage_error_msg)
- .neutralText(android.R.string.ok)
- .show();
- return;
- }
- String dataFolderPath = dataFolder.getAbsolutePath();
- int selectedIndex = -1;
- int index = 0;
- File[] mediaDirs = ContextCompat.getExternalFilesDirs(context, null);
- final List<String> folders = new ArrayList<>(mediaDirs.length);
- final List<CharSequence> choices = new ArrayList<>(mediaDirs.length);
- for (File dir : mediaDirs) {
- if(dir == null || !dir.exists() || !dir.canRead() || !dir.canWrite()) {
- continue;
- }
- String path = dir.getAbsolutePath();
- folders.add(path);
- if(dataFolderPath.equals(path)) {
- selectedIndex = index;
- }
- int prefixIndex = path.indexOf("Android");
- String choice = (prefixIndex > 0) ? path.substring(0, prefixIndex) : path;
- long bytes = StorageUtils.getFreeSpaceAvailable(path);
- String item = String.format(
- "<small>%1$s [%2$s]</small>", choice, Converter.byteToString(bytes));
- choices.add(fromHtmlVersioned(item));
- index++;
- }
- if (choices.isEmpty()) {
+ DataFolderAdapter adapter = new DataFolderAdapter(context, handlerFunc);
+
+ if (adapter.getItemCount() == 0) {
new MaterialDialog.Builder(context)
.title(R.string.error_label)
.content(R.string.external_storage_error_msg)
@@ -71,27 +32,16 @@ public class ChooseDataFolderDialog {
.show();
return;
}
+
MaterialDialog dialog = new MaterialDialog.Builder(context)
.title(R.string.choose_data_directory)
.content(R.string.choose_data_directory_message)
- .items(choices)
- .itemsCallbackSingleChoice(selectedIndex, (dialog1, itemView, which, text) -> {
- String folder = folders.get(which);
- handlerFunc.run(folder);
- return true;
- })
+ .adapter(adapter, null)
.negativeText(R.string.cancel_label)
.cancelable(true)
.build();
+ adapter.setDialog(dialog);
dialog.show();
}
- @SuppressWarnings("deprecation")
- private static CharSequence fromHtmlVersioned(final String html) {
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
- return Html.fromHtml(html);
- }
- return Html.fromHtml(html, Html.FROM_HTML_MODE_LEGACY);
- }
-
} \ No newline at end of file
diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/EpisodeFilterDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/EpisodeFilterDialog.java
new file mode 100644
index 000000000..ba778be56
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/EpisodeFilterDialog.java
@@ -0,0 +1,71 @@
+package de.danoeh.antennapod.dialog;
+
+import android.app.Dialog;
+import android.content.Context;
+import android.os.Bundle;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.RadioButton;
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.core.feed.FeedFilter;
+
+/**
+ * Displays a dialog with a text box for filtering episodes and two radio buttons for exclusion/inclusion
+ */
+public abstract class EpisodeFilterDialog extends Dialog {
+
+ private final FeedFilter initialFilter;
+
+ public EpisodeFilterDialog(Context context, FeedFilter filter) {
+ super(context);
+ this.initialFilter = filter;
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.episode_filter_dialog);
+ final EditText etxtEpisodeFilterText = findViewById(R.id.etxtEpisodeFilterText);
+ final RadioButton radioInclude = findViewById(R.id.radio_filter_include);
+ final RadioButton radioExclude = findViewById(R.id.radio_filter_exclude);
+ final Button butConfirm = findViewById(R.id.butConfirm);
+ final Button butCancel = findViewById(R.id.butCancel);
+
+ setTitle(R.string.episode_filters_label);
+ setOnCancelListener(dialog -> onCancelled());
+
+ if (initialFilter.includeOnly()) {
+ radioInclude.setChecked(true);
+ etxtEpisodeFilterText.setText(initialFilter.getIncludeFilter());
+ } else if(initialFilter.excludeOnly()) {
+ radioExclude.setChecked(true);
+ etxtEpisodeFilterText.setText(initialFilter.getExcludeFilter());
+ } else {
+ radioExclude.setChecked(false);
+ radioInclude.setChecked(false);
+ etxtEpisodeFilterText.setText("");
+ }
+
+
+ butCancel.setOnClickListener(v -> cancel());
+ butConfirm.setOnClickListener(v -> {
+
+ String includeString = "";
+ String excludeString = "";
+ if (radioInclude.isChecked()) {
+ includeString = etxtEpisodeFilterText.getText().toString();
+ } else {
+ excludeString = etxtEpisodeFilterText.getText().toString();
+ }
+
+ onConfirmed(new FeedFilter(includeString, excludeString));
+ dismiss();
+ });
+ }
+
+ protected void onCancelled() {
+
+ }
+
+ protected abstract void onConfirmed(FeedFilter filter);
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/EpisodesApplyActionFragment.java b/app/src/main/java/de/danoeh/antennapod/dialog/EpisodesApplyActionFragment.java
index 07a64cde8..7697aaa69 100644
--- a/app/src/main/java/de/danoeh/antennapod/dialog/EpisodesApplyActionFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/EpisodesApplyActionFragment.java
@@ -3,10 +3,20 @@ package de.danoeh.antennapod.dialog;
import android.app.AlertDialog;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
+import android.os.Build;
import android.os.Bundle;
+import android.support.annotation.IdRes;
+import android.support.annotation.NonNull;
+import android.support.annotation.PluralsRes;
+import android.support.annotation.StringRes;
+import android.support.design.widget.Snackbar;
import android.support.v4.app.ActivityCompat;
import android.support.v4.app.Fragment;
import android.support.v4.util.ArrayMap;
+import android.support.v4.view.ViewCompat;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.AppCompatActivity;
+import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
@@ -14,11 +24,12 @@ import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
-import android.widget.Button;
import android.widget.ListView;
-import android.widget.Toast;
+
+import com.leinardi.android.speeddial.SpeedDialView;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
@@ -35,22 +46,42 @@ public class EpisodesApplyActionFragment extends Fragment {
public static final String TAG = "EpisodeActionFragment";
- public static final int ACTION_QUEUE = 1;
- private static final int ACTION_MARK_PLAYED = 2;
- private static final int ACTION_MARK_UNPLAYED = 4;
- private static final int ACTION_DOWNLOAD = 8;
- public static final int ACTION_REMOVE = 16;
- private static final int ACTION_ALL = ACTION_QUEUE | ACTION_MARK_PLAYED | ACTION_MARK_UNPLAYED
- | ACTION_DOWNLOAD | ACTION_REMOVE;
+ public static final int ACTION_ADD_TO_QUEUE = 1;
+ public static final int ACTION_REMOVE_FROM_QUEUE = 2;
+ private static final int ACTION_MARK_PLAYED = 4;
+ private static final int ACTION_MARK_UNPLAYED = 8;
+ private static final int ACTION_DOWNLOAD = 16;
+ public static final int ACTION_DELETE = 32;
+ private static final int ACTION_ALL = ACTION_ADD_TO_QUEUE | ACTION_REMOVE_FROM_QUEUE
+ | ACTION_MARK_PLAYED | ACTION_MARK_UNPLAYED | ACTION_DOWNLOAD | ACTION_DELETE;
+
+ /**
+ * Specify an action (defined by #flag) 's UI bindings.
+ *
+ * Includes: the menu / action item and the actual logic
+ */
+ private class ActionBinding {
+ int flag;
+ @IdRes
+ final int actionItemId;
+ @NonNull
+ final Runnable action;
+
+ ActionBinding(int flag, @IdRes int actionItemId, @NonNull Runnable action) {
+ this.flag = flag;
+ this.actionItemId = actionItemId;
+ this.action = action;
+ }
+ }
+
+ private final List<? extends ActionBinding> actionBindings;
private ListView mListView;
private ArrayAdapter<String> mAdapter;
- private Button btnAddToQueue;
- private Button btnMarkAsPlayed;
- private Button btnMarkAsUnplayed;
- private Button btnDownload;
- private Button btnDelete;
+ private SpeedDialView mSpeedDialView;
+ @NonNull
+ private CharSequence actionBarTitleOriginal = "";
private final Map<Long,FeedItem> idMap = new ArrayMap<>();
private final List<FeedItem> episodes = new ArrayList<>();
@@ -60,6 +91,23 @@ public class EpisodesApplyActionFragment extends Fragment {
private MenuItem mSelectToggle;
+ public EpisodesApplyActionFragment() {
+ actionBindings = Arrays.asList(
+ new ActionBinding(ACTION_ADD_TO_QUEUE,
+ R.id.add_to_queue_batch, this::queueChecked),
+ new ActionBinding(ACTION_REMOVE_FROM_QUEUE,
+ R.id.remove_from_queue_batch, this::removeFromQueueChecked),
+ new ActionBinding(ACTION_MARK_PLAYED,
+ R.id.mark_read_batch, this::markedCheckedPlayed),
+ new ActionBinding(ACTION_MARK_UNPLAYED,
+ R.id.mark_unread_batch, this::markedCheckedUnplayed),
+ new ActionBinding(ACTION_DOWNLOAD,
+ R.id.download_batch, this::downloadChecked),
+ new ActionBinding(ACTION_DELETE,
+ R.id.delete_batch, this::deleteChecked)
+ );
+ }
+
public static EpisodesApplyActionFragment newInstance(List<FeedItem> items) {
return newInstance(items, ACTION_ALL);
}
@@ -77,6 +125,7 @@ public class EpisodesApplyActionFragment extends Fragment {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ setRetainInstance(true);
setHasOptionsMenu(true);
}
@@ -124,57 +173,58 @@ public class EpisodesApplyActionFragment extends Fragment {
}
mAdapter = new ArrayAdapter<>(getActivity(),
- android.R.layout.simple_list_item_multiple_choice, titles);
+ R.layout.simple_list_item_multiple_choice_on_start, titles);
mListView.setAdapter(mAdapter);
- checkAll();
- int lastVisibleDiv = 0;
- btnAddToQueue = view.findViewById(R.id.btnAddToQueue);
- if((actions & ACTION_QUEUE) != 0) {
- btnAddToQueue.setOnClickListener(v -> queueChecked());
- lastVisibleDiv = R.id.divider1;
- } else {
- btnAddToQueue.setVisibility(View.GONE);
- view.findViewById(R.id.divider1).setVisibility(View.GONE);
- }
- btnMarkAsPlayed = view.findViewById(R.id.btnMarkAsPlayed);
- if((actions & ACTION_MARK_PLAYED) != 0) {
- btnMarkAsPlayed.setOnClickListener(v -> markedCheckedPlayed());
- lastVisibleDiv = R.id.divider2;
- } else {
- btnMarkAsPlayed.setVisibility(View.GONE);
- view.findViewById(R.id.divider2).setVisibility(View.GONE);
- }
- btnMarkAsUnplayed = view.findViewById(R.id.btnMarkAsUnplayed);
- if((actions & ACTION_MARK_UNPLAYED) != 0) {
- btnMarkAsUnplayed.setOnClickListener(v -> markedCheckedUnplayed());
- lastVisibleDiv = R.id.divider3;
- } else {
- btnMarkAsUnplayed.setVisibility(View.GONE);
- view.findViewById(R.id.divider3).setVisibility(View.GONE);
- }
- btnDownload = view.findViewById(R.id.btnDownload);
- if((actions & ACTION_DOWNLOAD) != 0) {
- btnDownload.setOnClickListener(v -> downloadChecked());
- lastVisibleDiv = R.id.divider4;
- } else {
- btnDownload.setVisibility(View.GONE);
- view.findViewById(R.id.divider4).setVisibility(View.GONE);
+ saveActionBarTitle(); // needed when we dynamically change the title based on selection
+
+ // Init action UI (via a FAB Speed Dial)
+ mSpeedDialView = view.findViewById(R.id.fabSD);
+ mSpeedDialView.inflate(R.menu.episodes_apply_action_speeddial);
+
+ // show only specified actions, and bind speed dial UIs to the actual logic
+ for (ActionBinding binding : actionBindings) {
+ if ((actions & binding.flag) == 0) {
+ mSpeedDialView.removeActionItemById(binding.actionItemId);
+ }
}
- btnDelete = view.findViewById(R.id.btnDelete);
- if((actions & ACTION_REMOVE) != 0) {
- btnDelete.setOnClickListener(v -> deleteChecked());
- } else {
- btnDelete.setVisibility(View.GONE);
- if(lastVisibleDiv > 0) {
- view.findViewById(lastVisibleDiv).setVisibility(View.GONE);
+
+ mSpeedDialView.setOnActionSelectedListener(actionItem -> {
+ ActionBinding selectedBinding = null;
+ for (ActionBinding binding : actionBindings) {
+ if (actionItem.getId() == binding.actionItemId) {
+ selectedBinding = binding;
+ break;
+ }
}
+ if (selectedBinding != null) {
+ selectedBinding.action.run();
+ } else {
+ Log.e(TAG, "Unrecognized speed dial action item. Do nothing. id=" + actionItem.getId());
+ }
+ return true;
+ });
+
+ if (Build.VERSION.SDK_INT == 23 || Build.VERSION.SDK_INT == 24) {
+ ViewCompat.setElevation(view.findViewById(R.id.fabSDScrollCtr), 8);
}
+ showSpeedDialIfAnyChecked();
+
return view;
}
@Override
+ public void onStop() {
+ restoreActionBarTitle(); // it might have been changed to "N selected". Restore original.
+ super.onStop();
+ }
+
+ private void showSpeedDialIfAnyChecked() {
+ mSpeedDialView.setVisibility(checkedIds.size() > 0 ? View.VISIBLE : View.GONE);
+ }
+
+ @Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
inflater.inflate(R.menu.episodes_apply_action_options, menu);
@@ -196,11 +246,9 @@ public class EpisodesApplyActionFragment extends Fragment {
int[] icon = new int[1];
if (checkedIds.size() == episodes.size()) {
- icon[0] = R.attr.ic_check_box;
- } else if (checkedIds.size() == 0) {
- icon[0] = R.attr.ic_check_box_outline;
+ icon[0] = R.attr.ic_select_none;
} else {
- icon[0] = R.attr.ic_indeterminate_check_box;
+ icon[0] = R.attr.ic_select_all;
}
TypedArray a = getActivity().obtainStyledAttributes(icon);
@@ -212,7 +260,7 @@ public class EpisodesApplyActionFragment extends Fragment {
@Override
public boolean onOptionsItemSelected(MenuItem item) {
- int resId = 0;
+ @StringRes int resId = 0;
switch(item.getItemId()) {
case R.id.select_options:
return true;
@@ -272,7 +320,8 @@ public class EpisodesApplyActionFragment extends Fragment {
return true;
}
if(resId != 0) {
- Toast.makeText(getActivity(), resId, Toast.LENGTH_SHORT).show();
+ Snackbar.make(getActivity().findViewById(R.id.content), resId, Snackbar.LENGTH_SHORT)
+ .show();
return true;
} else {
return false;
@@ -410,21 +459,56 @@ public class EpisodesApplyActionFragment extends Fragment {
mListView.setItemChecked(i, checked);
}
ActivityCompat.invalidateOptionsMenu(EpisodesApplyActionFragment.this.getActivity());
+ showSpeedDialIfAnyChecked();
+ updateActionBarTitle();
+ }
+
+ private void saveActionBarTitle() {
+ ActionBar actionBar = ((AppCompatActivity) getActivity()).getSupportActionBar();
+ if (actionBar != null) {
+ CharSequence title = actionBar.getTitle();
+ if (title == null) {
+ title = "";
+ }
+ actionBarTitleOriginal = title;
+ }
+ }
+
+ private void restoreActionBarTitle() {
+ ActionBar actionBar = ((AppCompatActivity) getActivity()).getSupportActionBar();
+ if (actionBar != null) {
+ actionBar.setTitle(actionBarTitleOriginal);
+ }
+ }
+
+ private void updateActionBarTitle() {
+ ActionBar actionBar = ((AppCompatActivity) getActivity()).getSupportActionBar();
+ if (actionBar != null) {
+ CharSequence title = checkedIds.size() > 0 ?
+ getString(R.string.num_selected_label, checkedIds.size()) :
+ actionBarTitleOriginal;
+ actionBar.setTitle(title);
+ }
}
private void queueChecked() {
DBWriter.addQueueItem(getActivity(), true, checkedIds.toArray());
- close();
+ close(R.plurals.added_to_queue_batch_label, checkedIds.size());
+ }
+
+ private void removeFromQueueChecked() {
+ DBWriter.removeQueueItem(getActivity(), true, checkedIds.toArray());
+ close(R.plurals.removed_from_queue_batch_label, checkedIds.size());
}
private void markedCheckedPlayed() {
DBWriter.markItemPlayed(FeedItem.PLAYED, checkedIds.toArray());
- close();
+ close(R.plurals.marked_read_batch_label, checkedIds.size());
}
private void markedCheckedUnplayed() {
DBWriter.markItemPlayed(FeedItem.UNPLAYED, checkedIds.toArray());
- close();
+ close(R.plurals.marked_unread_batch_label, checkedIds.size());
}
private void downloadChecked() {
@@ -441,7 +525,7 @@ public class EpisodesApplyActionFragment extends Fragment {
e.printStackTrace();
DownloadRequestErrorDialogCreator.newRequestErrorDialog(getActivity(), e.getMessage());
}
- close();
+ close(R.plurals.downloading_batch_label, checkedIds.size());
}
private void deleteChecked() {
@@ -451,10 +535,18 @@ public class EpisodesApplyActionFragment extends Fragment {
DBWriter.deleteFeedMediaOfItem(getActivity(), episode.getMedia().getId());
}
}
- close();
+ close(R.plurals.deleted_episode_batch_label, checkedIds.size());
}
- private void close() {
+ private void close(@PluralsRes int msgId, int numItems) {
+ if (numItems > 0) {
+ Snackbar.make(getActivity().findViewById(R.id.content),
+ getResources().getQuantityString(msgId, numItems, numItems),
+ Snackbar.LENGTH_LONG
+ )
+ .setAction(android.R.string.ok, v -> {})
+ .show();
+ }
getActivity().getSupportFragmentManager().popBackStack();
}
diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/ProxyDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/ProxyDialog.java
index 8f2629b43..c1008a380 100644
--- a/app/src/main/java/de/danoeh/antennapod/dialog/ProxyDialog.java
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/ProxyDialog.java
@@ -3,6 +3,7 @@ package de.danoeh.antennapod.dialog;
import android.app.Dialog;
import android.content.Context;
import android.content.res.TypedArray;
+import android.os.Build;
import android.support.v4.content.ContextCompat;
import android.text.Editable;
import android.text.TextUtils;
@@ -23,6 +24,8 @@ import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.SocketAddress;
+import java.util.ArrayList;
+import java.util.List;
import java.util.concurrent.TimeUnit;
import de.danoeh.antennapod.R;
@@ -92,7 +95,11 @@ public class ProxyDialog {
if(!TextUtils.isEmpty(port)) {
portValue = Integer.valueOf(port);
}
- proxy = ProxyConfig.http(host, portValue, username, password);
+ if (Proxy.Type.valueOf(type) == Proxy.Type.SOCKS) {
+ proxy = ProxyConfig.socks(host, portValue, username, password);
+ } else {
+ proxy = ProxyConfig.http(host, portValue, username, password);
+ }
}
UserPreferences.setProxyConfig(proxy);
AntennapodHttpClient.reinit();
@@ -103,7 +110,13 @@ public class ProxyDialog {
.build();
View view = dialog.getCustomView();
spType = view.findViewById(R.id.spType);
- String[] types = { Proxy.Type.DIRECT.name(), Proxy.Type.HTTP.name() };
+
+ List<String> types= new ArrayList<>();
+ types.add(Proxy.Type.DIRECT.name());
+ types.add(Proxy.Type.HTTP.name());
+ if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+ types.add(Proxy.Type.SOCKS.name());
+ }
ArrayAdapter<String> adapter = new ArrayAdapter<>(context,
android.R.layout.simple_spinner_item, types);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
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 ece184035..24656ed29 100644
--- a/app/src/main/java/de/danoeh/antennapod/dialog/RatingDialog.java
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/RatingDialog.java
@@ -6,6 +6,7 @@ import android.content.Intent;
import android.content.SharedPreferences;
import android.net.Uri;
import android.support.annotation.Nullable;
+import android.support.annotation.VisibleForTesting;
import android.util.Log;
import com.afollestad.materialdialogs.MaterialDialog;
@@ -73,7 +74,8 @@ public class RatingDialog {
return mPreferences.getBoolean(KEY_RATED, false);
}
- private static void saveRated() {
+ @VisibleForTesting
+ public static void saveRated() {
mPreferences
.edit()
.putBoolean(KEY_RATED, true)
diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/SleepTimerDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/SleepTimerDialog.java
index 4b8601ec6..dc056a3f9 100644
--- a/app/src/main/java/de/danoeh/antennapod/dialog/SleepTimerDialog.java
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/SleepTimerDialog.java
@@ -18,7 +18,7 @@ import com.afollestad.materialdialogs.MaterialDialog;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.event.MessageEvent;
import de.danoeh.antennapod.core.preferences.SleepTimerPreferences;
-import de.greenrobot.event.EventBus;
+import org.greenrobot.eventbus.EventBus;
public abstract class SleepTimerDialog {
diff --git a/app/src/main/java/de/danoeh/antennapod/discovery/CombinedSearcher.java b/app/src/main/java/de/danoeh/antennapod/discovery/CombinedSearcher.java
new file mode 100644
index 000000000..18d65a03c
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/discovery/CombinedSearcher.java
@@ -0,0 +1,96 @@
+package de.danoeh.antennapod.discovery;
+
+import android.content.Context;
+import android.util.Log;
+import android.util.Pair;
+import io.reactivex.Single;
+import io.reactivex.SingleOnSubscribe;
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.disposables.Disposable;
+import io.reactivex.schedulers.Schedulers;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.CountDownLatch;
+
+public class CombinedSearcher implements PodcastSearcher {
+ private static final String TAG = "CombinedSearcher";
+
+ private final List<Pair<PodcastSearcher, Float>> searchProviders = new ArrayList<>();
+
+ public CombinedSearcher(Context context) {
+ addProvider(new FyydPodcastSearcher(), 1.f);
+ addProvider(new ItunesPodcastSearcher(context), 1.f);
+ addProvider(new GpodnetPodcastSearcher(), 0.6f);
+ }
+
+ private void addProvider(PodcastSearcher provider, float priority) {
+ searchProviders.add(new Pair<>(provider, priority));
+ }
+
+ public Single<List<PodcastSearchResult>> search(String query) {
+ ArrayList<Disposable> disposables = new ArrayList<>();
+ List<List<PodcastSearchResult>> singleResults = new ArrayList<>(Collections.nCopies(searchProviders.size(), null));
+ CountDownLatch latch = new CountDownLatch(searchProviders.size());
+ for (int i = 0; i < searchProviders.size(); i++) {
+ Pair<PodcastSearcher, Float> searchProviderInfo = searchProviders.get(i);
+ PodcastSearcher searcher = searchProviderInfo.first;
+ final int index = i;
+ disposables.add(searcher.search(query).subscribe(e -> {
+ singleResults.set(index, e);
+ latch.countDown();
+ }, throwable -> {
+ Log.d(TAG, Log.getStackTraceString(throwable));
+ latch.countDown();
+ }
+ ));
+ }
+
+ return Single.create((SingleOnSubscribe<List<PodcastSearchResult>>) subscriber -> {
+ latch.await();
+ List<PodcastSearchResult> results = weightSearchResults(singleResults);
+ subscriber.onSuccess(results);
+ })
+ .doOnDispose(() -> {
+ for (Disposable disposable : disposables) {
+ disposable.dispose();
+ }
+ })
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread());
+ }
+
+ private List<PodcastSearchResult> weightSearchResults(List<List<PodcastSearchResult>> singleResults) {
+ HashMap<String, Float> resultRanking = new HashMap<>();
+ HashMap<String, PodcastSearchResult> urlToResult = new HashMap<>();
+ for (int i = 0; i < singleResults.size(); i++) {
+ float providerPriority = searchProviders.get(i).second;
+ List<PodcastSearchResult> providerResults = singleResults.get(i);
+ if (providerResults == null) {
+ continue;
+ }
+ for (int position = 0; position < providerResults.size(); position++) {
+ PodcastSearchResult result = providerResults.get(position);
+ urlToResult.put(result.feedUrl, result);
+
+ float ranking = 0;
+ if (resultRanking.containsKey(result.feedUrl)) {
+ ranking = resultRanking.get(result.feedUrl);
+ }
+ ranking += 1.f / (position + 1.f);
+ resultRanking.put(result.feedUrl, ranking * providerPriority);
+ }
+ }
+ List<Map.Entry<String, Float>> sortedResults = new ArrayList<>(resultRanking.entrySet());
+ Collections.sort(sortedResults, (o1, o2) -> Double.compare(o2.getValue(), o1.getValue()));
+
+ List<PodcastSearchResult> results = new ArrayList<>();
+ for (Map.Entry<String, Float> res : sortedResults) {
+ results.add(urlToResult.get(res.getKey()));
+ }
+ return results;
+ }
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/discovery/FyydPodcastSearcher.java b/app/src/main/java/de/danoeh/antennapod/discovery/FyydPodcastSearcher.java
new file mode 100644
index 000000000..529a9e3d5
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/discovery/FyydPodcastSearcher.java
@@ -0,0 +1,38 @@
+package de.danoeh.antennapod.discovery;
+
+import de.danoeh.antennapod.core.service.download.AntennapodHttpClient;
+import de.mfietz.fyydlin.FyydClient;
+import de.mfietz.fyydlin.FyydResponse;
+import de.mfietz.fyydlin.SearchHit;
+import io.reactivex.Single;
+import io.reactivex.SingleOnSubscribe;
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.schedulers.Schedulers;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class FyydPodcastSearcher implements PodcastSearcher {
+ private final FyydClient client = new FyydClient(AntennapodHttpClient.getHttpClient());
+
+ public Single<List<PodcastSearchResult>> search(String query) {
+ return Single.create((SingleOnSubscribe<List<PodcastSearchResult>>) subscriber -> {
+ FyydResponse response = client.searchPodcasts(query, 10)
+ .subscribeOn(Schedulers.io())
+ .blockingGet();
+
+ ArrayList<PodcastSearchResult> searchResults = new ArrayList<>();
+
+ if (!response.getData().isEmpty()) {
+ for (SearchHit searchHit : response.getData()) {
+ PodcastSearchResult podcast = PodcastSearchResult.fromFyyd(searchHit);
+ searchResults.add(podcast);
+ }
+ }
+
+ subscriber.onSuccess(searchResults);
+ })
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread());
+ }
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/discovery/GpodnetPodcastSearcher.java b/app/src/main/java/de/danoeh/antennapod/discovery/GpodnetPodcastSearcher.java
new file mode 100644
index 000000000..6e5debb38
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/discovery/GpodnetPodcastSearcher.java
@@ -0,0 +1,38 @@
+package de.danoeh.antennapod.discovery;
+
+import de.danoeh.antennapod.core.gpoddernet.GpodnetService;
+import de.danoeh.antennapod.core.gpoddernet.GpodnetServiceException;
+import de.danoeh.antennapod.core.gpoddernet.model.GpodnetPodcast;
+import io.reactivex.Single;
+import io.reactivex.SingleOnSubscribe;
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.schedulers.Schedulers;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class GpodnetPodcastSearcher implements PodcastSearcher {
+ public Single<List<PodcastSearchResult>> search(String query) {
+ return Single.create((SingleOnSubscribe<List<PodcastSearchResult>>) subscriber -> {
+ GpodnetService service = null;
+ try {
+ service = new GpodnetService();
+ List<GpodnetPodcast> gpodnetPodcasts = service.searchPodcasts(query, 0);
+ List<PodcastSearchResult> results = new ArrayList<>();
+ for (GpodnetPodcast podcast : gpodnetPodcasts) {
+ results.add(PodcastSearchResult.fromGpodder(podcast));
+ }
+ subscriber.onSuccess(results);
+ } catch (GpodnetServiceException e) {
+ e.printStackTrace();
+ subscriber.onError(e);
+ } finally {
+ if (service != null) {
+ service.shutdown();
+ }
+ }
+ })
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread());
+ }
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/discovery/ItunesPodcastSearcher.java b/app/src/main/java/de/danoeh/antennapod/discovery/ItunesPodcastSearcher.java
new file mode 100644
index 000000000..a91aae1a8
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/discovery/ItunesPodcastSearcher.java
@@ -0,0 +1,74 @@
+package de.danoeh.antennapod.discovery;
+
+import android.content.Context;
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.core.ClientConfig;
+import de.danoeh.antennapod.core.service.download.AntennapodHttpClient;
+import io.reactivex.Single;
+import io.reactivex.SingleOnSubscribe;
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.schedulers.Schedulers;
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.Response;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import java.util.ArrayList;
+import java.util.List;
+
+public class ItunesPodcastSearcher implements PodcastSearcher {
+ private static final String ITUNES_API_URL = "https://itunes.apple.com/search?media=podcast&term=%s";
+ private final Context context;
+
+ public ItunesPodcastSearcher(Context context) {
+ this.context = context;
+ }
+
+ public Single<List<PodcastSearchResult>> search(String query) {
+ return Single.create((SingleOnSubscribe<List<PodcastSearchResult>>) subscriber -> {
+ String encodedQuery;
+ try {
+ encodedQuery = URLEncoder.encode(query, "UTF-8");
+ } catch (UnsupportedEncodingException e) {
+ // this won't ever be thrown
+ encodedQuery = query;
+ }
+
+ String formattedUrl = String.format(ITUNES_API_URL, encodedQuery);
+
+ OkHttpClient client = AntennapodHttpClient.getHttpClient();
+ Request.Builder httpReq = new Request.Builder()
+ .url(formattedUrl)
+ .header("User-Agent", ClientConfig.USER_AGENT);
+ List<PodcastSearchResult> podcasts = new ArrayList<>();
+ try {
+ Response response = client.newCall(httpReq.build()).execute();
+
+ if (response.isSuccessful()) {
+ String resultString = response.body().string();
+ JSONObject result = new JSONObject(resultString);
+ JSONArray j = result.getJSONArray("results");
+
+ for (int i = 0; i < j.length(); i++) {
+ JSONObject podcastJson = j.getJSONObject(i);
+ PodcastSearchResult podcast = PodcastSearchResult.fromItunes(podcastJson);
+ podcasts.add(podcast);
+ }
+ } else {
+ String prefix = context.getString(R.string.error_msg_prefix);
+ subscriber.onError(new IOException(prefix + response));
+ }
+ } catch (IOException | JSONException e) {
+ subscriber.onError(e);
+ }
+ subscriber.onSuccess(podcasts);
+ })
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread());
+ }
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/discovery/ItunesTopListLoader.java b/app/src/main/java/de/danoeh/antennapod/discovery/ItunesTopListLoader.java
new file mode 100644
index 000000000..bc9133258
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/discovery/ItunesTopListLoader.java
@@ -0,0 +1,110 @@
+package de.danoeh.antennapod.discovery;
+
+import android.content.Context;
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.core.ClientConfig;
+import de.danoeh.antennapod.core.service.download.AntennapodHttpClient;
+import io.reactivex.Single;
+import io.reactivex.SingleOnSubscribe;
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.schedulers.Schedulers;
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.Response;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+
+public class ItunesTopListLoader {
+ private final Context context;
+
+ public ItunesTopListLoader(Context context) {
+ this.context = context;
+ }
+
+ public Single<List<PodcastSearchResult>> loadToplist(int limit) {
+ return Single.create((SingleOnSubscribe<List<PodcastSearchResult>>) emitter -> {
+ String lang = Locale.getDefault().getLanguage();
+ OkHttpClient client = AntennapodHttpClient.getHttpClient();
+ String feedString;
+ try {
+ try {
+ feedString = getTopListFeed(client, lang, limit);
+ } catch (IOException e) {
+ feedString = getTopListFeed(client, "us", limit);
+ }
+ List<PodcastSearchResult> podcasts = parseFeed(feedString);
+ emitter.onSuccess(podcasts);
+ } catch (IOException | JSONException e) {
+ emitter.onError(e);
+ }
+ })
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread());
+ }
+
+ public Single<String> getFeedUrl(PodcastSearchResult podcast) {
+ if (!podcast.feedUrl.contains("itunes.apple.com")) {
+ return Single.just(podcast.feedUrl)
+ .observeOn(AndroidSchedulers.mainThread());
+ }
+ return Single.create((SingleOnSubscribe<String>) emitter -> {
+ OkHttpClient client = AntennapodHttpClient.getHttpClient();
+ Request.Builder httpReq = new Request.Builder()
+ .url(podcast.feedUrl)
+ .header("User-Agent", ClientConfig.USER_AGENT);
+ try {
+ Response response = client.newCall(httpReq.build()).execute();
+ if (response.isSuccessful()) {
+ String resultString = response.body().string();
+ JSONObject result = new JSONObject(resultString);
+ JSONObject results = result.getJSONArray("results").getJSONObject(0);
+ String feedUrl = results.getString("feedUrl");
+ emitter.onSuccess(feedUrl);
+ } else {
+ String prefix = context.getString(R.string.error_msg_prefix);
+ emitter.onError(new IOException(prefix + response));
+ }
+ } catch (IOException | JSONException e) {
+ emitter.onError(e);
+ }
+ })
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread());
+ }
+
+ private String getTopListFeed(OkHttpClient client, String language, int limit) throws IOException {
+ String url = "https://itunes.apple.com/%s/rss/toppodcasts/limit="+limit+"/explicit=true/json";
+ Request.Builder httpReq = new Request.Builder()
+ .header("User-Agent", ClientConfig.USER_AGENT)
+ .url(String.format(url, language));
+
+ try (Response response = client.newCall(httpReq.build()).execute()) {
+ if (response.isSuccessful()) {
+ return response.body().string();
+ }
+ String prefix = context.getString(R.string.error_msg_prefix);
+ throw new IOException(prefix + response);
+ }
+ }
+
+ private List<PodcastSearchResult> parseFeed(String jsonString) throws JSONException {
+ JSONObject result = new JSONObject(jsonString);
+ JSONObject feed = result.getJSONObject("feed");
+ JSONArray entries = feed.getJSONArray("entry");
+
+ List<PodcastSearchResult> results = new ArrayList<>();
+ for (int i=0; i < entries.length(); i++) {
+ JSONObject json = entries.getJSONObject(i);
+ results.add(PodcastSearchResult.fromItunesToplist(json));
+ }
+
+ return results;
+ }
+
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/discovery/PodcastSearchResult.java b/app/src/main/java/de/danoeh/antennapod/discovery/PodcastSearchResult.java
new file mode 100644
index 000000000..ca9ed83d7
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/discovery/PodcastSearchResult.java
@@ -0,0 +1,81 @@
+package de.danoeh.antennapod.discovery;
+
+import android.support.annotation.Nullable;
+import de.danoeh.antennapod.core.gpoddernet.model.GpodnetPodcast;
+import de.mfietz.fyydlin.SearchHit;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+public class PodcastSearchResult {
+
+ /**
+ * The name of the podcast
+ */
+ public final String title;
+
+ /**
+ * URL of the podcast image
+ */
+ @Nullable
+ public final String imageUrl;
+ /**
+ * URL of the podcast feed
+ */
+ @Nullable
+ public final String feedUrl;
+
+
+ private PodcastSearchResult(String title, @Nullable String imageUrl, @Nullable String feedUrl) {
+ this.title = title;
+ this.imageUrl = imageUrl;
+ this.feedUrl = feedUrl;
+ }
+
+ public static PodcastSearchResult dummy() {
+ return new PodcastSearchResult("", "", "");
+ }
+
+ /**
+ * Constructs a Podcast instance from a iTunes search result
+ *
+ * @param json object holding the podcast information
+ * @throws JSONException
+ */
+ public static PodcastSearchResult fromItunes(JSONObject json) {
+ String title = json.optString("collectionName", "");
+ String imageUrl = json.optString("artworkUrl100", null);
+ String feedUrl = json.optString("feedUrl", null);
+ return new PodcastSearchResult(title, imageUrl, feedUrl);
+ }
+
+ /**
+ * Constructs a Podcast instance from iTunes toplist entry
+ *
+ * @param json object holding the podcast information
+ * @throws JSONException
+ */
+ public static PodcastSearchResult fromItunesToplist(JSONObject json) throws JSONException {
+ String title = json.getJSONObject("title").getString("label");
+ String imageUrl = null;
+ JSONArray images = json.getJSONArray("im:image");
+ for(int i=0; imageUrl == null && i < images.length(); i++) {
+ JSONObject image = images.getJSONObject(i);
+ String height = image.getJSONObject("attributes").getString("height");
+ if(Integer.parseInt(height) >= 100) {
+ imageUrl = image.getString("label");
+ }
+ }
+ String feedUrl = "https://itunes.apple.com/lookup?id=" +
+ json.getJSONObject("id").getJSONObject("attributes").getString("im:id");
+ return new PodcastSearchResult(title, imageUrl, feedUrl);
+ }
+
+ public static PodcastSearchResult fromFyyd(SearchHit searchHit) {
+ return new PodcastSearchResult(searchHit.getTitle(), searchHit.getThumbImageURL(), searchHit.getXmlUrl());
+ }
+
+ public static PodcastSearchResult fromGpodder(GpodnetPodcast searchHit) {
+ return new PodcastSearchResult(searchHit.getTitle(), searchHit.getLogoUrl(), searchHit.getUrl());
+ }
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/discovery/PodcastSearcher.java b/app/src/main/java/de/danoeh/antennapod/discovery/PodcastSearcher.java
new file mode 100644
index 000000000..b19d7d695
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/discovery/PodcastSearcher.java
@@ -0,0 +1,10 @@
+package de.danoeh.antennapod.discovery;
+
+import io.reactivex.Single;
+import io.reactivex.disposables.Disposable;
+import io.reactivex.functions.Consumer;
+import java.util.List;
+
+public interface PodcastSearcher {
+ Single<List<PodcastSearchResult>> search(String query);
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/AddFeedFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/AddFeedFragment.java
index ee2373da8..35bcaa76e 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/AddFeedFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/AddFeedFragment.java
@@ -2,13 +2,20 @@ package de.danoeh.antennapod.fragment;
import android.content.Intent;
import android.os.Bundle;
+import android.provider.MediaStore;
import android.support.v4.app.Fragment;
+import android.view.ContextMenu;
+import android.view.KeyEvent;
import android.view.LayoutInflater;
+import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
+import android.view.inputmethod.EditorInfo;
import android.widget.Button;
import android.widget.EditText;
+import android.widget.ImageView;
+import android.widget.TextView;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.activity.OnlineFeedViewActivity;
@@ -27,11 +34,28 @@ public class AddFeedFragment extends Fragment {
*/
private static final String ARG_FEED_URL = "feedurl";
+ private EditText combinedFeedSearchBox;
+ private MainActivity activity;
+
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
View root = inflater.inflate(R.layout.addfeed, container, false);
+ activity = (MainActivity) getActivity();
+ activity.getSupportActionBar().setTitle(R.string.add_feed_label);
+
+ setupAdvancedSearchButtons(root);
+ setupSeachBox(root);
+
+ View butOpmlImport = root.findViewById(R.id.btn_opml_import);
+ butOpmlImport.setOnClickListener(v -> startActivity(new Intent(getActivity(),
+ OpmlImportFromPathActivity.class)));
+
+ return root;
+ }
+
+ private void setupSeachBox(View root) {
final EditText etxtFeedurl = root.findViewById(R.id.etxtFeedurl);
Bundle args = getArguments();
@@ -39,32 +63,69 @@ public class AddFeedFragment extends Fragment {
etxtFeedurl.setText(args.getString(ARG_FEED_URL));
}
- Button butSearchITunes = root.findViewById(R.id.butSearchItunes);
- Button butBrowserGpoddernet = root.findViewById(R.id.butBrowseGpoddernet);
- Button butSearchFyyd = root.findViewById(R.id.butSearchFyyd);
- Button butOpmlImport = root.findViewById(R.id.butOpmlImport);
- Button butConfirm = root.findViewById(R.id.butConfirm);
+ Button butConfirmAddUrl = root.findViewById(R.id.butConfirm);
+ butConfirmAddUrl.setOnClickListener(v -> {
+ addUrl(etxtFeedurl.getText().toString());
+ });
- final MainActivity activity = (MainActivity) getActivity();
- activity.getSupportActionBar().setTitle(R.string.add_feed_label);
+ combinedFeedSearchBox = root.findViewById(R.id.combinedFeedSearchBox);
+ combinedFeedSearchBox.setOnEditorActionListener((v, actionId, event) -> {
+ if (actionId == EditorInfo.IME_ACTION_SEARCH) {
+ performSearch();
+ return true;
+ }
+ return false;
+ });
+ }
- butSearchITunes.setOnClickListener(v -> activity.loadChildFragment(new ItunesSearchFragment()));
+ private void setupAdvancedSearchButtons(View root) {
+ View butAdvancedSearch = root.findViewById(R.id.advanced_search);
+ registerForContextMenu(butAdvancedSearch);
+ butAdvancedSearch.setOnClickListener(v -> butAdvancedSearch.showContextMenu());
+ }
- butBrowserGpoddernet.setOnClickListener(v -> activity.loadChildFragment(new GpodnetMainFragment()));
+ @Override
+ public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
+ getActivity().getMenuInflater().inflate(R.menu.advanced_search, menu);
+ }
- butSearchFyyd.setOnClickListener(v -> activity.loadChildFragment(new FyydSearchFragment()));
+ @Override
+ public boolean onContextItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.search_fyyd:
+ activity.loadChildFragment(new FyydSearchFragment());
+ return true;
+ case R.id.search_gpodder:
+ activity.loadChildFragment(new GpodnetMainFragment());
+ return true;
+ case R.id.search_itunes:
+ activity.loadChildFragment(new ItunesSearchFragment());
+ return true;
+ }
+ return false;
+ }
- butOpmlImport.setOnClickListener(v -> startActivity(new Intent(getActivity(),
- OpmlImportFromPathActivity.class)));
- butConfirm.setOnClickListener(v -> {
- Intent intent = new Intent(getActivity(), OnlineFeedViewActivity.class);
- intent.putExtra(OnlineFeedViewActivity.ARG_FEEDURL, etxtFeedurl.getText().toString());
- intent.putExtra(OnlineFeedViewActivity.ARG_TITLE, getString(R.string.add_feed_label));
- startActivity(intent);
- });
+ private void addUrl(String url) {
+ Intent intent = new Intent(getActivity(), OnlineFeedViewActivity.class);
+ intent.putExtra(OnlineFeedViewActivity.ARG_FEEDURL, url);
+ intent.putExtra(OnlineFeedViewActivity.ARG_TITLE, getString(R.string.add_feed_label));
+ startActivity(intent);
+ }
- return root;
+ private void performSearch() {
+ String query = combinedFeedSearchBox.getText().toString();
+
+ if (query.startsWith("http")) {
+ addUrl(query);
+ return;
+ }
+
+ Bundle bundle = new Bundle();
+ bundle.putString(CombinedSearchFragment.ARGUMENT_QUERY, query);
+ CombinedSearchFragment fragment = new CombinedSearchFragment();
+ fragment.setArguments(bundle);
+ activity.loadChildFragment(fragment);
}
@Override
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java
index ef522d3b3..62d798cf6 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java
@@ -5,6 +5,7 @@ import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.Handler;
+import android.support.annotation.NonNull;
import android.support.design.widget.Snackbar;
import android.support.v4.app.Fragment;
import android.support.v4.view.MenuItemCompat;
@@ -24,12 +25,16 @@ import android.widget.Toast;
import com.yqritc.recyclerviewflexibledivider.HorizontalDividerItemDecoration;
+import org.greenrobot.eventbus.EventBus;
+import org.greenrobot.eventbus.Subscribe;
+import org.greenrobot.eventbus.ThreadMode;
+
+import java.util.ArrayList;
import java.util.List;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.adapter.AllEpisodesRecycleAdapter;
-import de.danoeh.antennapod.adapter.DefaultActionButtonCallback;
import de.danoeh.antennapod.core.dialog.ConfirmationDialog;
import de.danoeh.antennapod.core.event.DownloadEvent;
import de.danoeh.antennapod.core.event.DownloaderUpdate;
@@ -39,6 +44,7 @@ import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.preferences.UserPreferences;
+import de.danoeh.antennapod.core.service.download.DownloadRequest;
import de.danoeh.antennapod.core.service.download.DownloadService;
import de.danoeh.antennapod.core.service.download.Downloader;
import de.danoeh.antennapod.core.storage.DBReader;
@@ -49,7 +55,7 @@ import de.danoeh.antennapod.core.util.FeedItemUtil;
import de.danoeh.antennapod.core.util.LongList;
import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler;
import de.danoeh.antennapod.menuhandler.MenuItemUtils;
-import de.greenrobot.event.EventBus;
+import de.danoeh.antennapod.view.EmptyViewHandler;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
@@ -74,12 +80,12 @@ public class AllEpisodesFragment extends Fragment {
RecyclerView recyclerView;
AllEpisodesRecycleAdapter listAdapter;
private ProgressBar progLoading;
+ EmptyViewHandler emptyView;
- List<FeedItem> episodes;
- private List<Downloader> downloaderList;
-
- private boolean itemsLoaded = false;
- private boolean viewsCreated = false;
+ @NonNull
+ List<FeedItem> episodes = new ArrayList<>();
+ @NonNull
+ private List<Downloader> downloaderList = new ArrayList<>();
private boolean isUpdatingFeeds;
boolean isMenuInvalidationAllowed = false;
@@ -87,36 +93,32 @@ public class AllEpisodesFragment extends Fragment {
Disposable disposable;
private LinearLayoutManager layoutManager;
- boolean showOnlyNewEpisodes() { return false; }
- String getPrefName() { return DEFAULT_PREF_NAME; }
+ boolean showOnlyNewEpisodes() {
+ return false;
+ }
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setHasOptionsMenu(true);
+ String getPrefName() {
+ return DEFAULT_PREF_NAME;
}
@Override
public void onStart() {
super.onStart();
+ setHasOptionsMenu(true);
EventDistributor.getInstance().register(contentUpdate);
- if (viewsCreated && itemsLoaded) {
- onFragmentLoaded();
- }
+ EventBus.getDefault().register(this);
+ loadItems();
}
@Override
public void onResume() {
super.onResume();
- EventBus.getDefault().registerSticky(this);
- loadItems();
registerForContextMenu(recyclerView);
}
@Override
public void onPause() {
super.onPause();
- EventBus.getDefault().unregister(this);
saveScrollPosition();
unregisterForContextMenu(recyclerView);
}
@@ -124,23 +126,18 @@ public class AllEpisodesFragment extends Fragment {
@Override
public void onStop() {
super.onStop();
+ EventBus.getDefault().unregister(this);
EventDistributor.getInstance().unregister(contentUpdate);
if (disposable != null) {
disposable.dispose();
}
}
- @Override
- public void onDestroyView() {
- super.onDestroyView();
- resetViewState();
- }
-
private void saveScrollPosition() {
int firstItem = layoutManager.findFirstVisibleItemPosition();
View firstItemView = layoutManager.findViewByPosition(firstItem);
float topOffset;
- if(firstItemView == null) {
+ if (firstItemView == null) {
topOffset = 0;
} else {
topOffset = firstItemView.getTop();
@@ -167,43 +164,35 @@ public class AllEpisodesFragment extends Fragment {
}
}
- void resetViewState() {
- viewsCreated = false;
- listAdapter = null;
- }
-
-
private final MenuItemUtils.UpdateRefreshMenuItemChecker updateRefreshMenuItemChecker =
() -> DownloadService.isRunning && DownloadRequester.getInstance().isDownloadingFeeds();
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
- if(!isAdded()) {
+ if (!isAdded()) {
return;
}
super.onCreateOptionsMenu(menu, inflater);
- if (itemsLoaded) {
- inflater.inflate(R.menu.episodes, menu);
-
- MenuItem searchItem = menu.findItem(R.id.action_search);
- final SearchView sv = (SearchView) MenuItemCompat.getActionView(searchItem);
- MenuItemUtils.adjustTextColor(getActivity(), sv);
- sv.setQueryHint(getString(R.string.search_hint));
- sv.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
- @Override
- public boolean onQueryTextSubmit(String s) {
- sv.clearFocus();
- ((MainActivity) getActivity()).loadChildFragment(SearchFragment.newInstance(s));
- return true;
- }
+ inflater.inflate(R.menu.episodes, menu);
+
+ MenuItem searchItem = menu.findItem(R.id.action_search);
+ final SearchView sv = (SearchView) MenuItemCompat.getActionView(searchItem);
+ MenuItemUtils.adjustTextColor(getActivity(), sv);
+ sv.setQueryHint(getString(R.string.search_hint));
+ sv.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
+ @Override
+ public boolean onQueryTextSubmit(String s) {
+ sv.clearFocus();
+ ((MainActivity) requireActivity()).loadChildFragment(SearchFragment.newInstance(s));
+ return true;
+ }
- @Override
- public boolean onQueryTextChange(String s) {
- return false;
- }
- });
- isUpdatingFeeds = MenuItemUtils.updateRefreshMenuItem(menu, R.id.refresh_item, updateRefreshMenuItemChecker);
- }
+ @Override
+ public boolean onQueryTextChange(String s) {
+ return false;
+ }
+ });
+ isUpdatingFeeds = MenuItemUtils.updateRefreshMenuItem(menu, R.id.refresh_item, updateRefreshMenuItemChecker);
}
@Override
@@ -211,11 +200,11 @@ public class AllEpisodesFragment extends Fragment {
super.onPrepareOptionsMenu(menu);
MenuItem markAllRead = menu.findItem(R.id.mark_all_read_item);
if (markAllRead != null) {
- markAllRead.setVisible(!showOnlyNewEpisodes() && episodes != null && !episodes.isEmpty());
+ markAllRead.setVisible(!showOnlyNewEpisodes() && !episodes.isEmpty());
}
- MenuItem markAllSeen = menu.findItem(R.id.mark_all_seen_item);
- if(markAllSeen != null) {
- markAllSeen.setVisible(showOnlyNewEpisodes() && episodes != null && !episodes.isEmpty());
+ MenuItem removeAllNewFlags = menu.findItem(R.id.remove_all_new_flags_item);
+ if (removeAllNewFlags != null) {
+ removeAllNewFlags.setVisible(showOnlyNewEpisodes() && !episodes.isEmpty());
}
}
@@ -243,19 +232,19 @@ public class AllEpisodesFragment extends Fragment {
};
markAllReadConfirmationDialog.createNewDialog().show();
return true;
- case R.id.mark_all_seen_item:
- ConfirmationDialog markAllSeenConfirmationDialog = new ConfirmationDialog(getActivity(),
- R.string.mark_all_seen_label,
- R.string.mark_all_seen_confirmation_msg) {
+ case R.id.remove_all_new_flags_item:
+ ConfirmationDialog removeAllNewFlagsConfirmationDialog = new ConfirmationDialog(getActivity(),
+ R.string.remove_all_new_flags_label,
+ R.string.remove_all_new_flags_confirmation_msg) {
@Override
public void onConfirmButtonPressed(DialogInterface dialog) {
dialog.dismiss();
- DBWriter.markNewItemsSeen();
- Toast.makeText(getActivity(), R.string.mark_all_seen_msg, Toast.LENGTH_SHORT).show();
+ DBWriter.removeAllNewFlags();
+ Toast.makeText(getActivity(), R.string.removed_all_new_flags_msg, Toast.LENGTH_SHORT).show();
}
};
- markAllSeenConfirmationDialog.createNewDialog().show();
+ removeAllNewFlagsConfirmationDialog.createNewDialog().show();
return true;
default:
return false;
@@ -269,98 +258,102 @@ public class AllEpisodesFragment extends Fragment {
@Override
public boolean onContextItemSelected(MenuItem item) {
Log.d(TAG, "onContextItemSelected() called with: " + "item = [" + item + "]");
- if(!isVisible()) {
+ if (!getUserVisibleHint()) {
return false;
}
- if(item.getItemId() == R.id.share_item) {
+ if (!isVisible()) {
+ return false;
+ }
+ if (item.getItemId() == R.id.share_item) {
return true; // avoids that the position is reset when we need it in the submenu
}
- FeedItem selectedItem = listAdapter.getSelectedItem();
- if (selectedItem == null) {
- Log.i(TAG, "Selected item was null, ignoring selection");
+ if (listAdapter.getSelectedItem() == null) {
+ Log.i(TAG, "Selected item or listAdapter was null, ignoring selection");
return super.onContextItemSelected(item);
}
+ FeedItem selectedItem = listAdapter.getSelectedItem();
- // Mark as seen contains UI logic specific to All/New/FavoriteSegments,
+ // Remove new flag contains UI logic specific to All/New/FavoriteSegments,
// e.g., Undo with Snackbar,
// and is handled by this class rather than the generic FeedItemMenuHandler
- // Undo is useful for Mark as seen, given there is no UI to undo it otherwise,
+ // Undo is useful for Remove new flag, given there is no UI to undo it otherwise,
// i.e., there is context menu item for Mark as new
- if (R.id.mark_as_seen_item == item.getItemId()) {
- markItemAsSeenWithUndo(selectedItem);
+ if (R.id.remove_new_flag_item == item.getItemId()) {
+ removeNewFlagWithUndo(selectedItem);
return true;
}
return FeedItemMenuHandler.onMenuItemClicked(getActivity(), item.getItemId(), selectedItem);
}
+ @NonNull
@Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
- return onCreateViewHelper(inflater, container, savedInstanceState,
- R.layout.all_episodes_fragment);
- }
-
- View onCreateViewHelper(LayoutInflater inflater,
- ViewGroup container,
- Bundle savedInstanceState,
- int fragmentResource) {
+ public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
+ View root = inflater.inflate(R.layout.all_episodes_fragment, container, false);
- View root = inflater.inflate(fragmentResource, container, false);
-
+ layoutManager = new LinearLayoutManager(getActivity());
recyclerView = root.findViewById(android.R.id.list);
+ recyclerView.setLayoutManager(layoutManager);
+ recyclerView.setHasFixedSize(true);
+ recyclerView.addItemDecoration(new HorizontalDividerItemDecoration.Builder(getActivity()).build());
+ recyclerView.setVisibility(View.GONE);
+
RecyclerView.ItemAnimator animator = recyclerView.getItemAnimator();
if (animator instanceof SimpleItemAnimator) {
((SimpleItemAnimator) animator).setSupportsChangeAnimations(false);
}
- layoutManager = new LinearLayoutManager(getActivity());
- recyclerView.setLayoutManager(layoutManager);
- recyclerView.setHasFixedSize(true);
- recyclerView.addItemDecoration(new HorizontalDividerItemDecoration.Builder(getActivity()).build());
progLoading = root.findViewById(R.id.progLoading);
+ progLoading.setVisibility(View.VISIBLE);
- if (!itemsLoaded) {
- progLoading.setVisibility(View.VISIBLE);
- }
+ emptyView = new EmptyViewHandler(getContext());
+ emptyView.attachToRecyclerView(recyclerView);
+ emptyView.setIcon(R.attr.feed);
+ emptyView.setTitle(R.string.no_all_episodes_head_label);
+ emptyView.setMessage(R.string.no_all_episodes_label);
- viewsCreated = true;
-
- if (itemsLoaded) {
- onFragmentLoaded();
- }
+ createRecycleAdapter(recyclerView, emptyView);
+ emptyView.hide();
return root;
}
- private void onFragmentLoaded() {
- if (listAdapter == null) {
- MainActivity mainActivity = (MainActivity) getActivity();
- listAdapter = new AllEpisodesRecycleAdapter(mainActivity, itemAccess,
- new DefaultActionButtonCallback(mainActivity), showOnlyNewEpisodes());
- listAdapter.setHasStableIds(true);
- recyclerView.setAdapter(listAdapter);
- }
+ private void onFragmentLoaded(List<FeedItem> episodes) {
+ this.episodes = episodes;
listAdapter.notifyDataSetChanged();
+
+ if (episodes.size() == 0) {
+ createRecycleAdapter(recyclerView, emptyView);
+ }
+
restoreScrollPosition();
- getActivity().supportInvalidateOptionsMenu();
- updateShowOnlyEpisodesListViewState();
+ requireActivity().invalidateOptionsMenu();
+ }
+
+ /**
+ * Currently, we need to recreate the list adapter in order to be able to undo last item via the
+ * snackbar. See #3084 for details.
+ */
+ private void createRecycleAdapter(RecyclerView recyclerView, EmptyViewHandler emptyViewHandler) {
+ MainActivity mainActivity = (MainActivity) getActivity();
+ listAdapter = new AllEpisodesRecycleAdapter(mainActivity, itemAccess, showOnlyNewEpisodes());
+ listAdapter.setHasStableIds(true);
+ recyclerView.setAdapter(listAdapter);
+ emptyViewHandler.updateAdapter(listAdapter);
}
private final AllEpisodesRecycleAdapter.ItemAccess itemAccess = new AllEpisodesRecycleAdapter.ItemAccess() {
@Override
public int getCount() {
- if (episodes != null) {
- return episodes.size();
- }
- return 0;
+ return episodes.size();
}
@Override
public FeedItem getItem(int position) {
- if (episodes != null && 0 <= position && position < episodes.size()) {
+ if (0 <= position && position < episodes.size()) {
return episodes.get(position);
}
return null;
@@ -368,11 +361,8 @@ public class AllEpisodesFragment extends Fragment {
@Override
public LongList getItemsIds() {
- if(episodes == null) {
- return new LongList(0);
- }
LongList ids = new LongList(episodes.size());
- for(FeedItem episode : episodes) {
+ for (FeedItem episode : episodes) {
ids.add(episode.getId());
}
return ids;
@@ -380,12 +370,11 @@ public class AllEpisodesFragment extends Fragment {
@Override
public int getItemDownloadProgressPercent(FeedItem item) {
- if (downloaderList != null) {
- for (Downloader downloader : downloaderList) {
- if (downloader.getDownloadRequest().getFeedfileType() == FeedMedia.FEEDFILETYPE_FEEDMEDIA
- && downloader.getDownloadRequest().getFeedfileId() == item.getMedia().getId()) {
- return downloader.getDownloadRequest().getProgressPercent();
- }
+ for (Downloader downloader : downloaderList) {
+ DownloadRequest downloadRequest = downloader.getDownloadRequest();
+ if (downloadRequest.getFeedfileType() == FeedMedia.FEEDFILETYPE_FEEDMEDIA
+ && downloadRequest.getFeedfileId() == item.getMedia().getId()) {
+ return downloadRequest.getProgressPercent();
}
}
return 0;
@@ -399,11 +388,8 @@ public class AllEpisodesFragment extends Fragment {
@Override
public LongList getQueueIds() {
LongList queueIds = new LongList();
- if(episodes == null) {
- return queueIds;
- }
- for(FeedItem item : episodes) {
- if(item.isTagged(FeedItem.TAG_QUEUE)) {
+ for (FeedItem item : episodes) {
+ if (item.isTagged(FeedItem.TAG_QUEUE)) {
queueIds.add(item.getId());
}
}
@@ -412,34 +398,39 @@ public class AllEpisodesFragment extends Fragment {
};
+ @Subscribe(threadMode = ThreadMode.MAIN)
public void onEventMainThread(FeedItemEvent event) {
Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
- if(episodes == null || listAdapter == null) {
- return;
- }
- for(int i=0, size = event.items.size(); i < size; i++) {
- FeedItem item = event.items.get(i);
+ for (FeedItem item : event.items) {
int pos = FeedItemUtil.indexOfItemWithId(episodes, item.getId());
- if(pos >= 0) {
+ if (pos >= 0) {
episodes.remove(pos);
- episodes.add(pos, item);
- listAdapter.notifyItemChanged(pos);
+ if (shouldUpdatedItemRemainInList(item)) {
+ episodes.add(pos, item);
+ listAdapter.notifyItemChanged(pos);
+ } else {
+ listAdapter.notifyItemRemoved(pos);
+ }
}
}
}
+ protected boolean shouldUpdatedItemRemainInList(FeedItem item) {
+ return true;
+ }
+ @Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
public void onEventMainThread(DownloadEvent event) {
Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
DownloaderUpdate update = event.update;
downloaderList = update.downloaders;
if (isMenuInvalidationAllowed && isUpdatingFeeds != update.feedIds.length > 0) {
- getActivity().supportInvalidateOptionsMenu();
+ requireActivity().invalidateOptionsMenu();
}
- if(listAdapter != null && update.mediaIds.length > 0) {
- for(long mediaId : update.mediaIds) {
+ if (update.mediaIds.length > 0) {
+ for (long mediaId : update.mediaIds) {
int pos = FeedItemUtil.indexOfItemWithMediaId(episodes, mediaId);
- if(pos >= 0) {
+ if (pos >= 0) {
listAdapter.notifyItemChanged(pos);
}
}
@@ -452,49 +443,36 @@ public class AllEpisodesFragment extends Fragment {
if ((arg & EVENTS) != 0) {
loadItems();
if (isUpdatingFeeds != updateRefreshMenuItemChecker.isRefreshing()) {
- getActivity().supportInvalidateOptionsMenu();
+ requireActivity().invalidateOptionsMenu();
}
}
}
};
- private void updateShowOnlyEpisodesListViewState() {
- }
-
void loadItems() {
if (disposable != null) {
disposable.dispose();
}
- if (viewsCreated && !itemsLoaded) {
- recyclerView.setVisibility(View.GONE);
- progLoading.setVisibility(View.VISIBLE);
- }
disposable = Observable.fromCallable(this::loadData)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(data -> {
- recyclerView.setVisibility(View.VISIBLE);
progLoading.setVisibility(View.GONE);
- if (data != null) {
- episodes = data;
- itemsLoaded = true;
- if (viewsCreated) {
- onFragmentLoaded();
- }
- }
+ onFragmentLoaded(data);
}, error -> Log.e(TAG, Log.getStackTraceString(error)));
}
+ @NonNull
List<FeedItem> loadData() {
return DBReader.getRecentlyPublishedEpisodes(RECENT_EPISODES_LIMIT);
}
- void markItemAsSeenWithUndo(FeedItem item) {
+ void removeNewFlagWithUndo(FeedItem item) {
if (item == null) {
return;
}
- Log.d(TAG, "markItemAsSeenWithUndo(" + item.getId() + ")");
+ Log.d(TAG, "removeNewFlagWithUndo(" + item.getId() + ")");
if (disposable != null) {
disposable.dispose();
}
@@ -503,14 +481,14 @@ public class AllEpisodesFragment extends Fragment {
DBWriter.markItemPlayed(FeedItem.UNPLAYED, item.getId());
final Handler h = new Handler(getActivity().getMainLooper());
- final Runnable r = () -> {
+ final Runnable r = () -> {
FeedMedia media = item.getMedia();
if (media != null && media.hasAlmostEnded() && UserPreferences.isAutoDelete()) {
DBWriter.deleteFeedMediaOfItem(getActivity(), media.getId());
}
};
- Snackbar snackbar = Snackbar.make(getView(), getString(R.string.marked_as_seen_label),
+ Snackbar snackbar = Snackbar.make(getView(), getString(R.string.removed_new_flag_label),
Snackbar.LENGTH_LONG);
snackbar.setAction(getString(R.string.undo), v -> {
DBWriter.markItemPlayed(FeedItem.NEW, item.getId());
@@ -518,7 +496,6 @@ public class AllEpisodesFragment extends Fragment {
h.removeCallbacks(r);
});
snackbar.show();
- h.postDelayed(r, (int)Math.ceil(snackbar.getDuration() * 1.05f));
+ h.postDelayed(r, (int) Math.ceil(snackbar.getDuration() * 1.05f));
}
-
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ChaptersFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ChaptersFragment.java
index 4d34d076d..4bebfe4c9 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/ChaptersFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/ChaptersFragment.java
@@ -6,28 +6,28 @@ import android.util.Log;
import android.view.View;
import android.widget.ListView;
+import java.util.List;
+import java.util.ListIterator;
+
import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.activity.MediaplayerInfoActivity.MediaplayerInfoContentFragment;
import de.danoeh.antennapod.adapter.ChaptersListAdapter;
import de.danoeh.antennapod.core.feed.Chapter;
import de.danoeh.antennapod.core.util.playback.Playable;
import de.danoeh.antennapod.core.util.playback.PlaybackController;
+import de.danoeh.antennapod.view.EmptyViewHandler;
+import io.reactivex.Maybe;
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.disposables.Disposable;
+import io.reactivex.schedulers.Schedulers;
-public class ChaptersFragment extends ListFragment implements MediaplayerInfoContentFragment {
-
+public class ChaptersFragment extends ListFragment {
private static final String TAG = "ChaptersFragment";
-
- private Playable media;
- private PlaybackController controller;
-
private ChaptersListAdapter adapter;
+ private PlaybackController controller;
+ private Disposable disposable;
+ private EmptyViewHandler emptyView;
- public static ChaptersFragment newInstance(Playable media) {
- ChaptersFragment f = new ChaptersFragment();
- f.media = media;
- return f;
- }
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
@@ -38,11 +38,13 @@ public class ChaptersFragment extends ListFragment implements MediaplayerInfoCon
final int vertPadding = getResources().getDimensionPixelSize(R.dimen.list_vertical_padding);
lv.setPadding(0, vertPadding, 0, vertPadding);
+ emptyView = new EmptyViewHandler(getContext());
+ emptyView.attachToListView(lv);
+ emptyView.setIcon(R.attr.ic_bookmark);
+ emptyView.setTitle(R.string.no_chapters_head_label);
+ emptyView.setMessage(R.string.no_chapters_label);
+
adapter = new ChaptersListAdapter(getActivity(), 0, pos -> {
- if(controller == null) {
- Log.d(TAG, "controller is null");
- return;
- }
Chapter chapter = (Chapter) getListAdapter().getItem(pos);
controller.seekToChapter(chapter);
});
@@ -50,42 +52,83 @@ public class ChaptersFragment extends ListFragment implements MediaplayerInfoCon
}
@Override
- public void onResume() {
- super.onResume();
- adapter.setMedia(media);
- adapter.notifyDataSetChanged();
- if(media == null || media.getChapters() == null) {
- setEmptyText(getString(R.string.no_chapters_label));
- } else {
- setEmptyText(null);
+ public void onStart() {
+ super.onStart();
+ controller = new PlaybackController(getActivity(), false) {
+ @Override
+ public boolean loadMediaInfo() {
+ ChaptersFragment.this.loadMediaInfo();
+ return true;
+ }
+
+ @Override
+ public void onPositionObserverUpdate() {
+ adapter.notifyDataSetChanged();
+ }
+ };
+ controller.init();
+
+ loadMediaInfo();
+ }
+
+ @Override
+ public void onDestroyView() {
+ super.onDestroyView();
+
+ if (disposable != null) {
+ disposable.dispose();
}
}
- public void onDestroy() {
- super.onDestroy();
- adapter = null;
+ @Override
+ public void onStop() {
+ super.onStop();
+ controller.release();
controller = null;
}
- @Override
- public void onMediaChanged(Playable media) {
- if(this.media == media) {
- return;
+ private void scrollTo(int position) {
+ getListView().setSelection(position);
+ }
+
+ private int getCurrentChapter(Playable media) {
+ int currentPosition = controller.getPosition();
+
+ List<Chapter> chapters = media.getChapters();
+ for (final ListIterator<Chapter> it = chapters.listIterator(); it.hasNext(); ) {
+ Chapter chapter = it.next();
+ if (chapter.getStart() > currentPosition) {
+ return it.previousIndex() - 1;
+ }
}
- this.media = media;
+ return chapters.size() - 1;
+ }
+
+ private void loadMediaInfo() {
+ if (disposable != null) {
+ disposable.dispose();
+ }
+ disposable = Maybe.create(emitter -> {
+ Playable media = controller.getMedia();
+ if (media != null) {
+ emitter.onSuccess(media);
+ } else {
+ emitter.onComplete();
+ }
+ })
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(media -> onMediaChanged((Playable) media),
+ error -> Log.e(TAG, Log.getStackTraceString(error)));
+ }
+
+ private void onMediaChanged(Playable media) {
if (adapter != null) {
adapter.setMedia(media);
adapter.notifyDataSetChanged();
- if(media == null || media.getChapters() == null || media.getChapters().size() == 0) {
- setEmptyText(getString(R.string.no_items_label));
- } else {
- setEmptyText(null);
+ if (media != null && media.getChapters() != null && media.getChapters().size() != 0) {
+ scrollTo(getCurrentChapter(media));
}
}
}
-
- public void setController(PlaybackController controller) {
- this.controller = controller;
- }
-
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/CombinedSearchFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/CombinedSearchFragment.java
new file mode 100644
index 000000000..1d9020f0d
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/CombinedSearchFragment.java
@@ -0,0 +1,173 @@
+package de.danoeh.antennapod.fragment;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v4.view.MenuItemCompat;
+import android.support.v7.widget.SearchView;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.GridView;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.activity.OnlineFeedViewActivity;
+import de.danoeh.antennapod.adapter.itunes.ItunesAdapter;
+import de.danoeh.antennapod.discovery.CombinedSearcher;
+import de.danoeh.antennapod.discovery.PodcastSearchResult;
+import de.danoeh.antennapod.menuhandler.MenuItemUtils;
+import io.reactivex.disposables.Disposable;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class CombinedSearchFragment extends Fragment {
+
+ private static final String TAG = "CombinedSearchFragment";
+ public static final String ARGUMENT_QUERY = "query";
+
+ /**
+ * Adapter responsible with the search results
+ */
+ private ItunesAdapter adapter;
+ private GridView gridView;
+ private ProgressBar progressBar;
+ private TextView txtvError;
+ private Button butRetry;
+ private TextView txtvEmpty;
+
+ /**
+ * List of podcasts retreived from the search
+ */
+ private List<PodcastSearchResult> searchResults = new ArrayList<>();
+ private Disposable disposable;
+
+ /**
+ * Constructor
+ */
+ public CombinedSearchFragment() {
+ // Required empty public constructor
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setHasOptionsMenu(true);
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ // Inflate the layout for this fragment
+ View root = inflater.inflate(R.layout.fragment_itunes_search, container, false);
+ gridView = root.findViewById(R.id.gridView);
+ adapter = new ItunesAdapter(getActivity(), new ArrayList<>());
+ gridView.setAdapter(adapter);
+
+ //Show information about the podcast when the list item is clicked
+ gridView.setOnItemClickListener((parent, view1, position, id) -> {
+ PodcastSearchResult podcast = searchResults.get(position);
+ Intent intent = new Intent(getActivity(), OnlineFeedViewActivity.class);
+ intent.putExtra(OnlineFeedViewActivity.ARG_FEEDURL, podcast.feedUrl);
+ intent.putExtra(OnlineFeedViewActivity.ARG_TITLE, podcast.title);
+ startActivity(intent);
+ });
+ progressBar = root.findViewById(R.id.progressBar);
+ txtvError = root.findViewById(R.id.txtvError);
+ butRetry = root.findViewById(R.id.butRetry);
+ txtvEmpty = root.findViewById(android.R.id.empty);
+
+ return root;
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ if (disposable != null) {
+ disposable.dispose();
+ }
+ adapter = null;
+ }
+
+ @Override
+ public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+ super.onCreateOptionsMenu(menu, inflater);
+ inflater.inflate(R.menu.itunes_search, menu);
+ MenuItem searchItem = menu.findItem(R.id.action_search);
+ final SearchView sv = (SearchView) MenuItemCompat.getActionView(searchItem);
+ MenuItemUtils.adjustTextColor(getActivity(), sv);
+ sv.setQueryHint(getString(R.string.search_label));
+ sv.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
+ @Override
+ public boolean onQueryTextSubmit(String s) {
+ sv.clearFocus();
+ search(s);
+ return true;
+ }
+
+ @Override
+ public boolean onQueryTextChange(String s) {
+ return false;
+ }
+ });
+ MenuItemCompat.setOnActionExpandListener(searchItem, new MenuItemCompat.OnActionExpandListener() {
+ @Override
+ public boolean onMenuItemActionExpand(MenuItem item) {
+ return true;
+ }
+
+ @Override
+ public boolean onMenuItemActionCollapse(MenuItem item) {
+ getActivity().getSupportFragmentManager().popBackStack();
+ return true;
+ }
+ });
+ MenuItemCompat.expandActionView(searchItem);
+
+ if (getArguments() != null && getArguments().getString(ARGUMENT_QUERY, null) != null) {
+ sv.setQuery(getArguments().getString(ARGUMENT_QUERY, null), true);
+ }
+ }
+
+ private void search(String query) {
+ if (disposable != null) {
+ disposable.dispose();
+ }
+
+ showOnlyProgressBar();
+
+ CombinedSearcher searcher = new CombinedSearcher(getContext());
+ disposable = searcher.search(query).subscribe(result -> {
+ searchResults = result;
+ progressBar.setVisibility(View.GONE);
+
+ adapter.clear();
+ adapter.addAll(searchResults);
+ adapter.notifyDataSetInvalidated();
+ gridView.setVisibility(!searchResults.isEmpty() ? View.VISIBLE : View.GONE);
+ txtvEmpty.setVisibility(searchResults.isEmpty() ? View.VISIBLE : View.GONE);
+
+ }, error -> {
+ Log.e(TAG, Log.getStackTraceString(error));
+ progressBar.setVisibility(View.GONE);
+ txtvError.setText(error.toString());
+ txtvError.setVisibility(View.VISIBLE);
+ butRetry.setOnClickListener(v -> search(query));
+ butRetry.setVisibility(View.VISIBLE);
+ });
+ }
+
+ private void showOnlyProgressBar() {
+ gridView.setVisibility(View.GONE);
+ txtvError.setVisibility(View.GONE);
+ butRetry.setVisibility(View.GONE);
+ txtvEmpty.setVisibility(View.GONE);
+ progressBar.setVisibility(View.VISIBLE);
+ }
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java
index 4bba9b255..705151062 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java
@@ -1,7 +1,7 @@
package de.danoeh.antennapod.fragment;
-import android.content.Context;
import android.os.Bundle;
+import android.support.annotation.NonNull;
import android.support.v4.app.ListFragment;
import android.util.Log;
import android.view.Menu;
@@ -10,6 +10,7 @@ import android.view.MenuItem;
import android.view.View;
import android.widget.ListView;
+import java.util.ArrayList;
import java.util.List;
import de.danoeh.antennapod.R;
@@ -21,11 +22,15 @@ import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.util.FeedItemUtil;
import de.danoeh.antennapod.dialog.EpisodesApplyActionFragment;
+import de.danoeh.antennapod.view.EmptyViewHandler;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
+import static de.danoeh.antennapod.dialog.EpisodesApplyActionFragment.ACTION_ADD_TO_QUEUE;
+import static de.danoeh.antennapod.dialog.EpisodesApplyActionFragment.ACTION_DELETE;
+
/**
* Displays all running downloads and provides a button to delete them
*/
@@ -37,24 +42,27 @@ public class CompletedDownloadsFragment extends ListFragment {
EventDistributor.DOWNLOADLOG_UPDATE |
EventDistributor.UNREAD_ITEMS_UPDATE;
- private List<FeedItem> items;
+ private List<FeedItem> items = new ArrayList<>();
private DownloadedEpisodesListAdapter listAdapter;
-
- private boolean viewCreated = false;
-
private Disposable disposable;
@Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
+ public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
setHasOptionsMenu(true);
- loadItems();
+ addVerticalPadding();
+ addEmptyView();
+
+ listAdapter = new DownloadedEpisodesListAdapter(getActivity(), itemAccess);
+ setListAdapter(listAdapter);
+ setListShown(false);
}
@Override
public void onStart() {
super.onStart();
EventDistributor.getInstance().register(contentUpdate);
+ loadItems();
}
@Override
@@ -67,99 +75,54 @@ public class CompletedDownloadsFragment extends ListFragment {
}
@Override
- public void onDetach() {
- super.onDetach();
- if (disposable != null) {
- disposable.dispose();
- }
- }
-
- @Override
- public void onDestroyView() {
- super.onDestroyView();
- listAdapter = null;
- viewCreated = false;
- if (disposable != null) {
- disposable.dispose();
- }
- }
-
- @Override
- public void onAttach(Context context) {
- super.onAttach(context);
- if (viewCreated && items != null) {
- onFragmentLoaded();
- }
- }
-
- @Override
- public void onViewCreated(View view, Bundle savedInstanceState) {
- super.onViewCreated(view, savedInstanceState);
-
- // add padding
- final ListView lv = getListView();
- lv.setClipToPadding(false);
- final int vertPadding = getResources().getDimensionPixelSize(R.dimen.list_vertical_padding);
- lv.setPadding(0, vertPadding, 0, vertPadding);
-
- viewCreated = true;
- if (items != null && getActivity() != null) {
- onFragmentLoaded();
- }
- }
-
- @Override
public void onListItemClick(ListView l, View v, int position, long id) {
super.onListItemClick(l, v, position, id);
position -= l.getHeaderViewsCount();
long[] ids = FeedItemUtil.getIds(items);
- ((MainActivity) getActivity()).loadChildFragment(ItemFragment.newInstance(ids, position));
- }
-
- private void onFragmentLoaded() {
- if (listAdapter == null) {
- listAdapter = new DownloadedEpisodesListAdapter(getActivity(), itemAccess);
- setListAdapter(listAdapter);
- }
- setListShown(true);
- listAdapter.notifyDataSetChanged();
- getActivity().supportInvalidateOptionsMenu();
+ ((MainActivity) requireActivity()).loadChildFragment(ItemFragment.newInstance(ids, position));
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
- if(!isAdded()) {
- return;
- }
super.onCreateOptionsMenu(menu, inflater);
- if(items != null) {
- inflater.inflate(R.menu.downloads_completed, menu);
- menu.findItem(R.id.episode_actions).setVisible(items.size() > 0);
- }
+ inflater.inflate(R.menu.downloads_completed, menu);
+ menu.findItem(R.id.episode_actions).setVisible(items.size() > 0);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case R.id.episode_actions:
- EpisodesApplyActionFragment fragment = EpisodesApplyActionFragment
- .newInstance(items, EpisodesApplyActionFragment.ACTION_REMOVE | EpisodesApplyActionFragment.ACTION_QUEUE);
- ((MainActivity) getActivity()).loadChildFragment(fragment);
- return true;
- default:
- return false;
+ if (item.getItemId() == R.id.episode_actions) {
+ ((MainActivity) requireActivity())
+ .loadChildFragment(EpisodesApplyActionFragment.newInstance(items, ACTION_DELETE | ACTION_ADD_TO_QUEUE));
+ return true;
}
+ return false;
+ }
+
+ private void addEmptyView() {
+ EmptyViewHandler emptyView = new EmptyViewHandler(getActivity());
+ emptyView.setIcon(R.attr.av_download);
+ emptyView.setTitle(R.string.no_comp_downloads_head_label);
+ emptyView.setMessage(R.string.no_comp_downloads_label);
+ emptyView.attachToListView(getListView());
+ }
+
+ private void addVerticalPadding() {
+ final ListView lv = getListView();
+ lv.setClipToPadding(false);
+ final int vertPadding = getResources().getDimensionPixelSize(R.dimen.list_vertical_padding);
+ lv.setPadding(0, vertPadding, 0, vertPadding);
}
private final DownloadedEpisodesListAdapter.ItemAccess itemAccess = new DownloadedEpisodesListAdapter.ItemAccess() {
@Override
public int getCount() {
- return (items != null) ? items.size() : 0;
+ return items.size();
}
@Override
public FeedItem getItem(int position) {
- if (items != null && 0 <= position && position < items.size()) {
+ if (0 <= position && position < items.size()) {
return items.get(position);
} else {
return null;
@@ -168,7 +131,7 @@ public class CompletedDownloadsFragment extends ListFragment {
@Override
public void onFeedItemSecondaryAction(FeedItem item) {
- DBWriter.deleteFeedMediaOfItem(getActivity(), item.getMedia().getId());
+ DBWriter.deleteFeedMediaOfItem(requireActivity(), item.getMedia().getId());
}
};
@@ -185,18 +148,18 @@ public class CompletedDownloadsFragment extends ListFragment {
if (disposable != null) {
disposable.dispose();
}
- if (items == null && viewCreated) {
- setListShown(false);
- }
disposable = Observable.fromCallable(DBReader::getDownloadedItems)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(result -> {
items = result;
- if (viewCreated && getActivity() != null) {
- onFragmentLoaded();
- }
- }, error -> Log.e(TAG, Log.getStackTraceString(error)));
+ onItemsLoaded();
+ }, error -> Log.e(TAG, Log.getStackTraceString(error)));
}
+ private void onItemsLoaded() {
+ setListShown(true);
+ listAdapter.notifyDataSetChanged();
+ requireActivity().invalidateOptionsMenu();
+ }
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/CoverFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/CoverFragment.java
index 5a061c7e6..db9dd9530 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/CoverFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/CoverFragment.java
@@ -1,6 +1,7 @@
package de.danoeh.antennapod.fragment;
import android.os.Bundle;
+import android.support.annotation.NonNull;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
@@ -13,74 +14,68 @@ import com.bumptech.glide.Glide;
import com.bumptech.glide.request.RequestOptions;
import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.activity.MediaplayerInfoActivity.MediaplayerInfoContentFragment;
import de.danoeh.antennapod.core.glide.ApGlideSettings;
import de.danoeh.antennapod.core.util.playback.Playable;
+import de.danoeh.antennapod.core.util.playback.PlaybackController;
+import io.reactivex.Maybe;
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.disposables.Disposable;
+import io.reactivex.schedulers.Schedulers;
/**
* Displays the cover and the title of a FeedItem.
*/
-public class CoverFragment extends Fragment implements MediaplayerInfoContentFragment {
+public class CoverFragment extends Fragment {
private static final String TAG = "CoverFragment";
- private Playable media;
-
private View root;
private TextView txtvPodcastTitle;
private TextView txtvEpisodeTitle;
private ImageView imgvCover;
-
- public static CoverFragment newInstance(Playable item) {
- CoverFragment f = new CoverFragment();
- f.media = item;
- return f;
- }
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- if (media == null) {
- Log.e(TAG, TAG + " was called without media");
- }
- }
+ private PlaybackController controller;
+ private Disposable disposable;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
+ setRetainInstance(true);
root = inflater.inflate(R.layout.cover_fragment, container, false);
txtvPodcastTitle = root.findViewById(R.id.txtvPodcastTitle);
txtvEpisodeTitle = root.findViewById(R.id.txtvEpisodeTitle);
imgvCover = root.findViewById(R.id.imgvCover);
+ imgvCover.setOnClickListener(v -> onPlayPause());
return root;
}
private void loadMediaInfo() {
- if (media != null) {
- txtvPodcastTitle.setText(media.getFeedTitle());
- txtvEpisodeTitle.setText(media.getEpisodeTitle());
- Glide.with(this)
- .load(media.getImageLocation())
- .apply(new RequestOptions()
- .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
- .dontAnimate()
- .fitCenter())
- .into(imgvCover);
- } else {
- Log.w(TAG, "loadMediaInfo was called while media was null");
+ if (disposable != null) {
+ disposable.dispose();
}
+ disposable = Maybe.create(emitter -> {
+ Playable media = controller.getMedia();
+ if (media != null) {
+ emitter.onSuccess(media);
+ } else {
+ emitter.onComplete();
+ }
+ })
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(media -> displayMediaInfo((Playable) media),
+ error -> Log.e(TAG, Log.getStackTraceString(error)));
}
- @Override
- public void onStart() {
- Log.d(TAG, "On Start");
- super.onStart();
- if (media != null) {
- Log.d(TAG, "Loading media info");
- loadMediaInfo();
- } else {
- Log.w(TAG, "Unable to load media info: media was null");
- }
+ private void displayMediaInfo(@NonNull Playable media) {
+ txtvPodcastTitle.setText(media.getFeedTitle());
+ txtvEpisodeTitle.setText(media.getEpisodeTitle());
+ Glide.with(this)
+ .load(media.getImageLocation())
+ .apply(new RequestOptions()
+ .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
+ .dontAnimate()
+ .fitCenter())
+ .into(imgvCover);
}
@Override
@@ -91,14 +86,40 @@ public class CoverFragment extends Fragment implements MediaplayerInfoContentFra
}
@Override
- public void onMediaChanged(Playable media) {
- if(this.media == media) {
- return;
- }
- this.media = media;
- if (isAdded()) {
- loadMediaInfo();
+ public void onStart() {
+ super.onStart();
+ controller = new PlaybackController(getActivity(), false) {
+ @Override
+ public boolean loadMediaInfo() {
+ CoverFragment.this.loadMediaInfo();
+ return true;
+ }
+
+ };
+ controller.init();
+ loadMediaInfo();
+ }
+
+ @Override
+ public void onStop() {
+ super.onStop();
+ controller.release();
+ controller = null;
+ }
+
+ @Override
+ public void onDestroyView() {
+ super.onDestroyView();
+
+ if (disposable != null) {
+ disposable.dispose();
}
}
+ void onPlayPause() {
+ if (controller == null) {
+ return;
+ }
+ controller.playPause();
+ }
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/DownloadLogFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/DownloadLogFragment.java
index 5ab6bac63..26b115b4b 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/DownloadLogFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/DownloadLogFragment.java
@@ -14,15 +14,18 @@ import android.view.View;
import android.widget.ListView;
import android.widget.TextView;
+import java.util.ArrayList;
import java.util.List;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.adapter.DownloadLogAdapter;
import de.danoeh.antennapod.core.feed.EventDistributor;
+import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.service.download.DownloadStatus;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBWriter;
+import de.danoeh.antennapod.view.EmptyViewHandler;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
@@ -35,18 +38,13 @@ public class DownloadLogFragment extends ListFragment {
private static final String TAG = "DownloadLogFragment";
- private List<DownloadStatus> downloadLog;
+ private List<DownloadStatus> downloadLog = new ArrayList<>();
private DownloadLogAdapter adapter;
-
- private boolean viewsCreated = false;
- private boolean itemsLoaded = false;
-
private Disposable disposable;
@Override
public void onStart() {
super.onStart();
- setHasOptionsMenu(true);
EventDistributor.getInstance().register(contentUpdate);
loadItems();
}
@@ -55,7 +53,7 @@ public class DownloadLogFragment extends ListFragment {
public void onStop() {
super.onStop();
EventDistributor.getInstance().unregister(contentUpdate);
- if(disposable != null) {
+ if (disposable != null) {
disposable.dispose();
}
}
@@ -63,6 +61,7 @@ public class DownloadLogFragment extends ListFragment {
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
+ setHasOptionsMenu(true);
// add padding
final ListView lv = getListView();
@@ -70,17 +69,17 @@ public class DownloadLogFragment extends ListFragment {
final int vertPadding = getResources().getDimensionPixelSize(R.dimen.list_vertical_padding);
lv.setPadding(0, vertPadding, 0, vertPadding);
- viewsCreated = true;
- if (itemsLoaded) {
- onFragmentLoaded();
- }
+ EmptyViewHandler emptyView = new EmptyViewHandler(getActivity());
+ emptyView.setIcon(R.attr.av_download);
+ emptyView.setTitle(R.string.no_log_downloads_head_label);
+ emptyView.setMessage(R.string.no_log_downloads_label);
+ emptyView.attachToListView(getListView());
+
+ adapter = new DownloadLogAdapter(getActivity(), itemAccess);
+ setListAdapter(adapter);
}
private void onFragmentLoaded() {
- if (adapter == null) {
- adapter = new DownloadLogAdapter(getActivity(), itemAccess);
- setListAdapter(adapter);
- }
setListShown(true);
adapter.notifyDataSetChanged();
getActivity().supportInvalidateOptionsMenu();
@@ -93,10 +92,18 @@ public class DownloadLogFragment extends ListFragment {
DownloadStatus status = adapter.getItem(position);
String url = "unknown";
String message = getString(R.string.download_successful);
- FeedMedia media = DBReader.getFeedMedia(status.getFeedfileId());
- if (media != null) {
- url = media.getDownload_url();
+ if (status.getFeedfileType() == FeedMedia.FEEDFILETYPE_FEEDMEDIA) {
+ FeedMedia media = DBReader.getFeedMedia(status.getFeedfileId());
+ if (media != null) {
+ url = media.getDownload_url();
+ }
+ } else if (status.getFeedfileType() == Feed.FEEDFILETYPE_FEED) {
+ Feed feed = DBReader.getFeed(status.getFeedfileId());
+ if (feed != null) {
+ url = feed.getDownload_url();
+ }
}
+
if (!status.isSuccessful()) {
message = status.getReasonDetailed();
}
@@ -113,12 +120,12 @@ public class DownloadLogFragment extends ListFragment {
@Override
public int getCount() {
- return (downloadLog != null) ? downloadLog.size() : 0;
+ return downloadLog.size();
}
@Override
public DownloadStatus getItem(int position) {
- if (downloadLog != null && 0 <= position && position < downloadLog.size()) {
+ if (0 <= position && position < downloadLog.size()) {
return downloadLog.get(position);
} else {
return null;
@@ -138,27 +145,23 @@ public class DownloadLogFragment extends ListFragment {
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
- if(!isAdded()) {
+ if (!isAdded()) {
return;
}
super.onCreateOptionsMenu(menu, inflater);
- if (itemsLoaded) {
- MenuItem clearHistory = menu.add(Menu.NONE, R.id.clear_history_item, Menu.CATEGORY_CONTAINER, R.string.clear_history_label);
- MenuItemCompat.setShowAsAction(clearHistory, MenuItemCompat.SHOW_AS_ACTION_IF_ROOM);
- TypedArray drawables = getActivity().obtainStyledAttributes(new int[]{R.attr.content_discard});
- clearHistory.setIcon(drawables.getDrawable(0));
- drawables.recycle();
- }
+ MenuItem clearHistory = menu.add(Menu.NONE, R.id.clear_history_item, Menu.CATEGORY_CONTAINER, R.string.clear_history_label);
+ MenuItemCompat.setShowAsAction(clearHistory, MenuItemCompat.SHOW_AS_ACTION_IF_ROOM);
+ TypedArray drawables = getActivity().obtainStyledAttributes(new int[]{R.attr.content_discard});
+ clearHistory.setIcon(drawables.getDrawable(0));
+ drawables.recycle();
}
@Override
public void onPrepareOptionsMenu(Menu menu) {
super.onPrepareOptionsMenu(menu);
- if (itemsLoaded) {
- MenuItem menuItem = menu.findItem(R.id.clear_history_item);
- if(menuItem != null) {
- menuItem.setVisible(downloadLog != null && !downloadLog.isEmpty());
- }
+ MenuItem menuItem = menu.findItem(R.id.clear_history_item);
+ if (menuItem != null) {
+ menuItem.setVisible(!downloadLog.isEmpty());
}
}
@@ -178,7 +181,7 @@ public class DownloadLogFragment extends ListFragment {
}
private void loadItems() {
- if(disposable != null) {
+ if (disposable != null) {
disposable.dispose();
}
disposable = Observable.fromCallable(DBReader::getDownloadLog)
@@ -187,12 +190,8 @@ public class DownloadLogFragment extends ListFragment {
.subscribe(result -> {
if (result != null) {
downloadLog = result;
- itemsLoaded = true;
- if (viewsCreated) {
- onFragmentLoaded();
- }
+ onFragmentLoaded();
}
}, error -> Log.e(TAG, Log.getStackTraceString(error)));
}
-
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java
index de2f04590..348c73b92 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java
@@ -18,6 +18,7 @@ import com.bumptech.glide.Glide;
import com.bumptech.glide.request.RequestOptions;
import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.core.event.ServiceEvent;
import de.danoeh.antennapod.core.feed.MediaType;
import de.danoeh.antennapod.core.glide.ApGlideSettings;
import de.danoeh.antennapod.core.service.playback.PlaybackService;
@@ -90,10 +91,6 @@ public class ExternalPlayerFragment extends Fragment {
loadMediaInfo();
}
- public void connectToPlaybackService() {
- controller.init();
- }
-
private PlaybackController setupPlaybackController() {
return new PlaybackController(getActivity(), true) {
@@ -109,12 +106,12 @@ public class ExternalPlayerFragment extends Fragment {
@Override
public boolean loadMediaInfo() {
- ExternalPlayerFragment fragment = ExternalPlayerFragment.this;
- if (fragment != null) {
- return fragment.loadMediaInfo();
- } else {
- return false;
- }
+ return ExternalPlayerFragment.this.loadMediaInfo();
+ }
+
+ @Override
+ public void setupGUI() {
+ ExternalPlayerFragment.this.loadMediaInfo();
}
@Override
@@ -133,17 +130,29 @@ public class ExternalPlayerFragment extends Fragment {
public void onResume() {
super.onResume();
onPositionObserverUpdate();
+ }
+ @Override
+ public void onStart() {
+ super.onStart();
+ controller = setupPlaybackController();
controller.init();
+ loadMediaInfo();
}
@Override
- public void onDestroy() {
- super.onDestroy();
- Log.d(TAG, "Fragment is about to be destroyed");
+ public void onStop() {
+ super.onStop();
if (controller != null) {
controller.release();
+ controller = null;
}
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ Log.d(TAG, "Fragment is about to be destroyed");
if (disposable != null) {
disposable.dispose();
}
@@ -196,7 +205,8 @@ public class ExternalPlayerFragment extends Fragment {
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(media -> updateUi((Playable) media),
- error -> Log.e(TAG, Log.getStackTraceString(error)));
+ error -> Log.e(TAG, Log.getStackTraceString(error)),
+ () -> fragmentLayout.setVisibility(View.GONE));
return true;
}
@@ -217,7 +227,7 @@ public class ExternalPlayerFragment extends Fragment {
.into(imgvCover);
fragmentLayout.setVisibility(View.VISIBLE);
- if (controller.isPlayingVideoLocally()) {
+ if (controller != null && controller.isPlayingVideoLocally()) {
butPlay.setVisibility(View.GONE);
} else {
butPlay.setVisibility(View.VISIBLE);
@@ -232,7 +242,9 @@ public class ExternalPlayerFragment extends Fragment {
}
private void onPositionObserverUpdate() {
- if (controller.getPosition() == PlaybackService.INVALID_TIME
+ if (controller == null) {
+ return;
+ } else if (controller.getPosition() == PlaybackService.INVALID_TIME
|| controller.getDuration() == PlaybackService.INVALID_TIME) {
return;
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/FavoriteEpisodesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/FavoriteEpisodesFragment.java
index 70f82c2ec..bb029b731 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/FavoriteEpisodesFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/FavoriteEpisodesFragment.java
@@ -1,6 +1,7 @@
package de.danoeh.antennapod.fragment;
import android.os.Bundle;
+import android.support.annotation.NonNull;
import android.support.design.widget.Snackbar;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.helper.ItemTouchHelper;
@@ -9,6 +10,8 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import org.greenrobot.eventbus.Subscribe;
+
import java.util.List;
import de.danoeh.antennapod.R;
@@ -18,38 +21,38 @@ import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBWriter;
-
/**
* Like 'EpisodesFragment' except that it only shows favorite episodes and
* supports swiping to remove from favorites.
*/
-
public class FavoriteEpisodesFragment extends AllEpisodesFragment {
private static final String TAG = "FavoriteEpisodesFrag";
-
private static final String PREF_NAME = "PrefFavoriteEpisodesFragment";
@Override
- protected boolean showOnlyNewEpisodes() { return true; }
+ protected boolean showOnlyNewEpisodes() {
+ return true;
+ }
@Override
- protected String getPrefName() { return PREF_NAME; }
+ protected String getPrefName() {
+ return PREF_NAME;
+ }
+ @Subscribe
public void onEvent(FavoritesEvent event) {
- Log.d(TAG, "onEvent() called with: " + "event = [" + event + "]");
+ Log.d(TAG, String.format("onEvent() called with: event = [%s]", event));
loadItems();
}
+ @NonNull
@Override
- protected void resetViewState() {
- super.resetViewState();
- }
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
- View root = super.onCreateViewHelper(inflater, container, savedInstanceState,
- R.layout.all_episodes_fragment);
+ public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ View root = super.onCreateView(inflater, container, savedInstanceState);
+ emptyView.setIcon(R.attr.ic_unfav);
+ emptyView.setTitle(R.string.no_fav_episodes_head_label);
+ emptyView.setMessage(R.string.no_fav_episodes_label);
ItemTouchHelper.SimpleCallback simpleItemTouchCallback = new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) {
@Override
@@ -59,8 +62,8 @@ public class FavoriteEpisodesFragment extends AllEpisodesFragment {
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int swipeDir) {
- AllEpisodesRecycleAdapter.Holder holder = (AllEpisodesRecycleAdapter.Holder)viewHolder;
- Log.d(TAG, "remove(" + holder.getItemId() + ")");
+ AllEpisodesRecycleAdapter.Holder holder = (AllEpisodesRecycleAdapter.Holder) viewHolder;
+ Log.d(TAG, String.format("remove(%s)", holder.getItemId()));
if (disposable != null) {
disposable.dispose();
@@ -69,8 +72,7 @@ public class FavoriteEpisodesFragment extends AllEpisodesFragment {
if (item != null) {
DBWriter.removeFavoriteItem(item);
- Snackbar snackbar = Snackbar.make(root, getString(R.string.removed_item),
- Snackbar.LENGTH_LONG);
+ Snackbar snackbar = Snackbar.make(root, getString(R.string.removed_item), Snackbar.LENGTH_LONG);
snackbar.setAction(getString(R.string.undo), v -> DBWriter.addFavoriteItem(item));
snackbar.show();
}
@@ -82,6 +84,7 @@ public class FavoriteEpisodesFragment extends AllEpisodesFragment {
return root;
}
+ @NonNull
@Override
protected List<FeedItem> loadData() {
return DBReader.getFavoriteItemsList();
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/FeedSettingsFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/FeedSettingsFragment.java
new file mode 100644
index 000000000..4fb3d90f5
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/FeedSettingsFragment.java
@@ -0,0 +1,177 @@
+package de.danoeh.antennapod.fragment;
+
+import android.arch.lifecycle.ViewModelProviders;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.support.v14.preference.SwitchPreference;
+import android.support.v7.preference.ListPreference;
+import android.support.v7.preference.PreferenceFragmentCompat;
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.core.dialog.ConfirmationDialog;
+import de.danoeh.antennapod.core.feed.Feed;
+import de.danoeh.antennapod.core.feed.FeedFilter;
+import de.danoeh.antennapod.core.feed.FeedPreferences;
+import de.danoeh.antennapod.core.preferences.UserPreferences;
+import de.danoeh.antennapod.core.storage.DBWriter;
+import de.danoeh.antennapod.dialog.AuthenticationDialog;
+import de.danoeh.antennapod.dialog.EpisodeFilterDialog;
+import de.danoeh.antennapod.viewmodel.FeedSettingsViewModel;
+
+import static de.danoeh.antennapod.activity.FeedSettingsActivity.EXTRA_FEED_ID;
+
+public class FeedSettingsFragment extends PreferenceFragmentCompat {
+ private static final CharSequence PREF_EPISODE_FILTER = "episodeFilter";
+ private Feed feed;
+ private FeedPreferences feedPreferences;
+
+ @Override
+ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
+ addPreferencesFromResource(R.xml.feed_settings);
+
+ long feedId = getArguments().getLong(EXTRA_FEED_ID);
+ ViewModelProviders.of(getActivity()).get(FeedSettingsViewModel.class).getFeed(feedId)
+ .subscribe(result -> {
+ feed = result;
+ feedPreferences = feed.getPreferences();
+
+ setupAutoDownloadPreference();
+ setupKeepUpdatedPreference();
+ setupAutoDeletePreference();
+ setupAuthentificationPreference();
+ setupEpisodeFilterPreference();
+
+ updateAutoDeleteSummary();
+ updateAutoDownloadEnabled();
+ }).dispose();
+ }
+
+ private void setupEpisodeFilterPreference() {
+ findPreference(PREF_EPISODE_FILTER).setOnPreferenceClickListener(preference -> {
+ new EpisodeFilterDialog(getContext(), feedPreferences.getFilter()) {
+ @Override
+ protected void onConfirmed(FeedFilter filter) {
+ feedPreferences.setFilter(filter);
+ feed.savePreferences();
+ }
+ }.show();
+ return false;
+ });
+ }
+
+ private void setupAuthentificationPreference() {
+ findPreference("authentication").setOnPreferenceClickListener(preference -> {
+ new AuthenticationDialog(getContext(),
+ R.string.authentication_label, true, false,
+ feedPreferences.getUsername(), feedPreferences.getPassword()) {
+ @Override
+ protected void onConfirmed(String username, String password, boolean saveUsernamePassword) {
+ feedPreferences.setUsername(username);
+ feedPreferences.setPassword(password);
+ feed.savePreferences();
+ }
+ }.show();
+ return false;
+ });
+ }
+
+ private void setupAutoDeletePreference() {
+ ListPreference autoDeletePreference = (ListPreference) findPreference("autoDelete");
+ autoDeletePreference.setOnPreferenceChangeListener((preference, newValue) -> {
+ switch ((String) newValue) {
+ case "global":
+ feedPreferences.setAutoDeleteAction(FeedPreferences.AutoDeleteAction.GLOBAL);
+ break;
+ case "always":
+ feedPreferences.setAutoDeleteAction(FeedPreferences.AutoDeleteAction.YES);
+ break;
+ case "never":
+ feedPreferences.setAutoDeleteAction(FeedPreferences.AutoDeleteAction.NO);
+ break;
+ }
+ feed.savePreferences();
+ updateAutoDeleteSummary();
+ return false;
+ });
+ }
+
+ private void updateAutoDeleteSummary() {
+ ListPreference autoDeletePreference = (ListPreference) findPreference("autoDelete");
+
+ switch (feedPreferences.getAutoDeleteAction()) {
+ case GLOBAL:
+ autoDeletePreference.setSummary(R.string.feed_auto_download_global);
+ autoDeletePreference.setValue("global");
+ break;
+ case YES:
+ autoDeletePreference.setSummary(R.string.feed_auto_download_always);
+ autoDeletePreference.setValue("always");
+ break;
+ case NO:
+ autoDeletePreference.setSummary(R.string.feed_auto_download_never);
+ autoDeletePreference.setValue("never");
+ break;
+ }
+ }
+
+ private void setupKeepUpdatedPreference() {
+ SwitchPreference pref = (SwitchPreference) findPreference("keepUpdated");
+
+ pref.setChecked(feedPreferences.getKeepUpdated());
+ pref.setOnPreferenceChangeListener((preference, newValue) -> {
+ boolean checked = newValue == Boolean.TRUE;
+ feedPreferences.setKeepUpdated(checked);
+ feed.savePreferences();
+ pref.setChecked(checked);
+ return false;
+ });
+ }
+
+ private void setupAutoDownloadPreference() {
+ SwitchPreference pref = (SwitchPreference) findPreference("autoDownload");
+
+ pref.setEnabled(UserPreferences.isEnableAutodownload());
+ if (UserPreferences.isEnableAutodownload()) {
+ pref.setChecked(feedPreferences.getAutoDownload());
+ } else {
+ pref.setChecked(false);
+ pref.setSummary(R.string.auto_download_disabled_globally);
+ }
+
+ pref.setOnPreferenceChangeListener((preference, newValue) -> {
+ boolean checked = newValue == Boolean.TRUE;
+
+ feedPreferences.setAutoDownload(checked);
+ feed.savePreferences();
+ updateAutoDownloadEnabled();
+ ApplyToEpisodesDialog dialog = new ApplyToEpisodesDialog(getActivity(), checked);
+ dialog.createNewDialog().show();
+ pref.setChecked(checked);
+ return false;
+ });
+ }
+
+ private void updateAutoDownloadEnabled() {
+ if (feed != null && feed.getPreferences() != null) {
+ boolean enabled = feed.getPreferences().getAutoDownload() && UserPreferences.isEnableAutodownload();
+ findPreference(PREF_EPISODE_FILTER).setEnabled(enabled);
+ }
+ }
+
+ private class ApplyToEpisodesDialog extends ConfirmationDialog {
+ private final boolean autoDownload;
+
+ ApplyToEpisodesDialog(Context context, boolean autoDownload) {
+ super(context, R.string.auto_download_apply_to_items_title,
+ R.string.auto_download_apply_to_items_message);
+ this.autoDownload = autoDownload;
+ setPositiveText(R.string.yes);
+ setNegativeText(R.string.no);
+ }
+
+ @Override
+ public void onConfirmButtonPressed(DialogInterface dialog) {
+ DBWriter.setFeedsItemsAutoDownload(feed, autoDownload);
+ }
+ }
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/FyydSearchFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/FyydSearchFragment.java
index dadc596e2..9c16cfe56 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/FyydSearchFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/FyydSearchFragment.java
@@ -16,24 +16,16 @@ import android.widget.Button;
import android.widget.GridView;
import android.widget.ProgressBar;
import android.widget.TextView;
-
-import java.util.ArrayList;
-import java.util.List;
-
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.OnlineFeedViewActivity;
import de.danoeh.antennapod.adapter.itunes.ItunesAdapter;
-import de.danoeh.antennapod.core.service.download.AntennapodHttpClient;
+import de.danoeh.antennapod.discovery.FyydPodcastSearcher;
+import de.danoeh.antennapod.discovery.PodcastSearchResult;
import de.danoeh.antennapod.menuhandler.MenuItemUtils;
-import de.mfietz.fyydlin.FyydClient;
-import de.mfietz.fyydlin.FyydResponse;
-import de.mfietz.fyydlin.SearchHit;
-import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
-import io.reactivex.schedulers.Schedulers;
-import static de.danoeh.antennapod.adapter.itunes.ItunesAdapter.Podcast;
-import static java.util.Collections.emptyList;
+import java.util.ArrayList;
+import java.util.List;
public class FyydSearchFragment extends Fragment {
@@ -49,12 +41,10 @@ public class FyydSearchFragment extends Fragment {
private Button butRetry;
private TextView txtvEmpty;
- private final FyydClient client = new FyydClient(AntennapodHttpClient.getHttpClient());
-
/**
* List of podcasts retreived from the search
*/
- private List<Podcast> searchResults;
+ private List<PodcastSearchResult> searchResults;
private Disposable disposable;
/**
@@ -81,7 +71,7 @@ public class FyydSearchFragment extends Fragment {
//Show information about the podcast when the list item is clicked
gridView.setOnItemClickListener((parent, view1, position, id) -> {
- Podcast podcast = searchResults.get(position);
+ PodcastSearchResult podcast = searchResults.get(position);
Intent intent = new Intent(getActivity(), OnlineFeedViewActivity.class);
intent.putExtra(OnlineFeedViewActivity.ARG_FEEDURL, podcast.feedUrl);
intent.putExtra(OnlineFeedViewActivity.ARG_TITLE, podcast.title);
@@ -145,20 +135,26 @@ public class FyydSearchFragment extends Fragment {
disposable.dispose();
}
showOnlyProgressBar();
- disposable = client.searchPodcasts(query, 10)
- .subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe(result -> {
- progressBar.setVisibility(View.GONE);
- processSearchResult(result);
- }, error -> {
- Log.e(TAG, Log.getStackTraceString(error));
- progressBar.setVisibility(View.GONE);
- txtvError.setText(error.toString());
- txtvError.setVisibility(View.VISIBLE);
- butRetry.setOnClickListener(v -> search(query));
- butRetry.setVisibility(View.VISIBLE);
- });
+
+ FyydPodcastSearcher searcher = new FyydPodcastSearcher();
+ disposable = searcher.search(query).subscribe(result -> {
+ searchResults = result;
+ progressBar.setVisibility(View.GONE);
+
+ adapter.clear();
+ adapter.addAll(searchResults);
+ adapter.notifyDataSetInvalidated();
+ gridView.setVisibility(!searchResults.isEmpty() ? View.VISIBLE : View.GONE);
+ txtvEmpty.setVisibility(searchResults.isEmpty() ? View.VISIBLE : View.GONE);
+
+ }, error -> {
+ Log.e(TAG, Log.getStackTraceString(error));
+ progressBar.setVisibility(View.GONE);
+ txtvError.setText(error.toString());
+ txtvError.setVisibility(View.VISIBLE);
+ butRetry.setOnClickListener(v -> search(query));
+ butRetry.setVisibility(View.VISIBLE);
+ });
}
private void showOnlyProgressBar() {
@@ -168,25 +164,4 @@ public class FyydSearchFragment extends Fragment {
txtvEmpty.setVisibility(View.GONE);
progressBar.setVisibility(View.VISIBLE);
}
-
- private void processSearchResult(FyydResponse response) {
- adapter.clear();
- if (!response.getData().isEmpty()) {
- adapter.clear();
- searchResults = new ArrayList<>();
- for (SearchHit searchHit : response.getData()) {
- Podcast podcast = Podcast.fromSearch(searchHit);
- searchResults.add(podcast);
- }
- } else {
- searchResults = emptyList();
- }
- for(Podcast podcast : searchResults) {
- adapter.add(podcast);
- }
- adapter.notifyDataSetInvalidated();
- gridView.setVisibility(!searchResults.isEmpty() ? View.VISIBLE : View.GONE);
- txtvEmpty.setVisibility(searchResults.isEmpty() ? View.VISIBLE : View.GONE);
- }
-
}
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 4ee7a06ad..5cf2c5eeb 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java
@@ -10,7 +10,9 @@ import android.content.SharedPreferences;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.net.Uri;
+import android.os.Build;
import android.os.Bundle;
+import android.support.annotation.NonNull;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.ContextMenu;
@@ -27,16 +29,11 @@ import android.widget.Toast;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MediaplayerInfoActivity;
-import de.danoeh.antennapod.activity.MediaplayerInfoActivity.MediaplayerInfoContentFragment;
-import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.preferences.UserPreferences;
-import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.util.Converter;
import de.danoeh.antennapod.core.util.IntentUtils;
import de.danoeh.antennapod.core.util.NetworkUtils;
import de.danoeh.antennapod.core.util.ShareUtils;
-import de.danoeh.antennapod.core.util.ShownotesProvider;
-import de.danoeh.antennapod.core.util.playback.Playable;
import de.danoeh.antennapod.core.util.playback.PlaybackController;
import de.danoeh.antennapod.core.util.playback.Timeline;
import io.reactivex.Observable;
@@ -47,7 +44,7 @@ import io.reactivex.schedulers.Schedulers;
/**
* Displays the description of a Playable object in a Webview.
*/
-public class ItemDescriptionFragment extends Fragment implements MediaplayerInfoContentFragment {
+public class ItemDescriptionFragment extends Fragment {
private static final String TAG = "ItemDescriptionFragment";
@@ -55,58 +52,16 @@ public class ItemDescriptionFragment extends Fragment implements MediaplayerInfo
private static final String PREF_SCROLL_Y = "prefScrollY";
private static final String PREF_PLAYABLE_ID = "prefPlayableId";
- private static final String ARG_PLAYABLE = "arg.playable";
- private static final String ARG_FEEDITEM_ID = "arg.feeditem";
-
- private static final String ARG_SAVE_STATE = "arg.saveState";
- private static final String ARG_HIGHLIGHT_TIMECODES = "arg.highlightTimecodes";
-
private WebView webvDescription;
-
- private ShownotesProvider shownotesProvider;
- private Playable media;
-
private Disposable webViewLoader;
+ private PlaybackController controller;
/**
* URL that was selected via long-press.
*/
private String selectedURL;
- /**
- * True if Fragment should save its state (e.g. scrolling position) in a
- * shared preference.
- */
- private boolean saveState;
-
- /**
- * True if Fragment should highlight timecodes (e.g. time codes in the HH:MM:SS format).
- */
- private boolean highlightTimecodes;
-
- public static ItemDescriptionFragment newInstance(Playable media,
- boolean saveState,
- boolean highlightTimecodes) {
- ItemDescriptionFragment f = new ItemDescriptionFragment();
- Bundle args = new Bundle();
- args.putParcelable(ARG_PLAYABLE, media);
- args.putBoolean(ARG_SAVE_STATE, saveState);
- args.putBoolean(ARG_HIGHLIGHT_TIMECODES, highlightTimecodes);
- f.setArguments(args);
- return f;
- }
-
- public static ItemDescriptionFragment newInstance(FeedItem item, boolean saveState, boolean highlightTimecodes) {
- ItemDescriptionFragment f = new ItemDescriptionFragment();
- Bundle args = new Bundle();
- args.putLong(ARG_FEEDITEM_ID, item.getId());
- args.putBoolean(ARG_SAVE_STATE, saveState);
- args.putBoolean(ARG_HIGHLIGHT_TIMECODES, highlightTimecodes);
- f.setArguments(args);
- return f;
- }
- @SuppressLint("NewApi")
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
@@ -126,6 +81,10 @@ public class ItemDescriptionFragment extends Fragment implements MediaplayerInfo
webvDescription.getSettings().setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
// Use cached resources, even if they have expired
}
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ webvDescription.getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
+ }
+
webvDescription.getSettings().setUseWideViewPort(false);
webvDescription.getSettings().setLayoutAlgorithm(WebSettings.LayoutAlgorithm.SINGLE_COLUMN);
webvDescription.getSettings().setLoadWithOverviewMode(true);
@@ -175,39 +134,6 @@ public class ItemDescriptionFragment extends Fragment implements MediaplayerInfo
}
}
- @SuppressLint("NewApi")
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- Log.d(TAG, "Creating fragment");
- Bundle args = getArguments();
- saveState = args.getBoolean(ARG_SAVE_STATE, false);
- highlightTimecodes = args.getBoolean(ARG_HIGHLIGHT_TIMECODES, false);
- }
-
- @Override
- public void onViewCreated(View view, Bundle savedInstanceState) {
- super.onViewCreated(view, savedInstanceState);
- Bundle args = getArguments();
- if (args.containsKey(ARG_PLAYABLE)) {
- if (media == null) {
- media = args.getParcelable(ARG_PLAYABLE);
- shownotesProvider = media;
- }
- load();
- } else if (args.containsKey(ARG_FEEDITEM_ID)) {
- long id = getArguments().getLong(ARG_FEEDITEM_ID);
- Observable.defer(() -> Observable.just(DBReader.getFeedItem(id)))
- .subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe(feedItem -> {
- shownotesProvider = feedItem;
- load();
- }, error -> Log.e(TAG, Log.getStackTraceString(error)));
- }
- }
-
-
private final View.OnLongClickListener webViewLongClickListener = new View.OnLongClickListener() {
@Override
@@ -300,22 +226,20 @@ public class ItemDescriptionFragment extends Fragment implements MediaplayerInfo
if(webViewLoader != null) {
webViewLoader.dispose();
}
- if(shownotesProvider == null) {
- return;
- }
webViewLoader = Observable.fromCallable(this::loadData)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(data -> {
- webvDescription.loadDataWithBaseURL(null, data, "text/html",
+ webvDescription.loadDataWithBaseURL("https://127.0.0.1", data, "text/html",
"utf-8", "about:blank");
Log.d(TAG, "Webview loaded");
}, error -> Log.e(TAG, Log.getStackTraceString(error)));
}
+ @NonNull
private String loadData() {
- Timeline timeline = new Timeline(getActivity(), shownotesProvider);
- return timeline.processShownotes(highlightTimecodes);
+ Timeline timeline = new Timeline(getActivity(), controller.getMedia());
+ return timeline.processShownotes(true);
}
@Override
@@ -325,42 +249,38 @@ public class ItemDescriptionFragment extends Fragment implements MediaplayerInfo
}
private void savePreference() {
- if (saveState) {
- Log.d(TAG, "Saving preferences");
- SharedPreferences prefs = getActivity().getSharedPreferences(PREF,
- Activity.MODE_PRIVATE);
- SharedPreferences.Editor editor = prefs.edit();
- if (media != null && webvDescription != null) {
- Log.d(TAG, "Saving scroll position: " + webvDescription.getScrollY());
- editor.putInt(PREF_SCROLL_Y, webvDescription.getScrollY());
- editor.putString(PREF_PLAYABLE_ID, media.getIdentifier()
- .toString());
- } else {
- Log.d(TAG, "savePreferences was called while media or webview was null");
- editor.putInt(PREF_SCROLL_Y, -1);
- editor.putString(PREF_PLAYABLE_ID, "");
- }
- editor.commit();
+ Log.d(TAG, "Saving preferences");
+ SharedPreferences prefs = getActivity().getSharedPreferences(PREF,
+ Activity.MODE_PRIVATE);
+ SharedPreferences.Editor editor = prefs.edit();
+ if (controller != null && controller.getMedia() != null && webvDescription != null) {
+ Log.d(TAG, "Saving scroll position: " + webvDescription.getScrollY());
+ editor.putInt(PREF_SCROLL_Y, webvDescription.getScrollY());
+ editor.putString(PREF_PLAYABLE_ID, controller.getMedia().getIdentifier()
+ .toString());
+ } else {
+ Log.d(TAG, "savePreferences was called while media or webview was null");
+ editor.putInt(PREF_SCROLL_Y, -1);
+ editor.putString(PREF_PLAYABLE_ID, "");
}
+ editor.commit();
}
private boolean restoreFromPreference() {
- if (saveState) {
- Log.d(TAG, "Restoring from preferences");
- Activity activity = getActivity();
- if (activity != null) {
- SharedPreferences prefs = activity.getSharedPreferences(
- PREF, Activity.MODE_PRIVATE);
- String id = prefs.getString(PREF_PLAYABLE_ID, "");
- int scrollY = prefs.getInt(PREF_SCROLL_Y, -1);
- if (scrollY != -1 && media != null
- && id.equals(media.getIdentifier().toString())
- && webvDescription != null) {
- Log.d(TAG, "Restored scroll Position: " + scrollY);
- webvDescription.scrollTo(webvDescription.getScrollX(),
- scrollY);
- return true;
- }
+ Log.d(TAG, "Restoring from preferences");
+ Activity activity = getActivity();
+ if (activity != null) {
+ SharedPreferences prefs = activity.getSharedPreferences(
+ PREF, Activity.MODE_PRIVATE);
+ String id = prefs.getString(PREF_PLAYABLE_ID, "");
+ int scrollY = prefs.getInt(PREF_SCROLL_Y, -1);
+ if (controller != null && scrollY != -1 && controller.getMedia() != null
+ && id.equals(controller.getMedia().getIdentifier().toString())
+ && webvDescription != null) {
+ Log.d(TAG, "Restored scroll Position: " + scrollY);
+ webvDescription.scrollTo(webvDescription.getScrollX(),
+ scrollY);
+ return true;
}
}
return false;
@@ -377,15 +297,27 @@ public class ItemDescriptionFragment extends Fragment implements MediaplayerInfo
}
@Override
- public void onMediaChanged(Playable media) {
- if(this.media == media) {
- return;
- }
- this.media = media;
- this.shownotesProvider = media;
- if (webvDescription != null) {
- load();
- }
+ public void onStart() {
+ super.onStart();
+ controller = new PlaybackController(getActivity(), false) {
+ @Override
+ public boolean loadMediaInfo() {
+ if (getMedia() == null) {
+ return false;
+ }
+ load();
+ return true;
+ }
+
+ };
+ controller.init();
+ load();
}
+ @Override
+ public void onStop() {
+ super.onStop();
+ controller.release();
+ controller = null;
+ }
}
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 bcca281d4..432ada44e 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java
@@ -36,13 +36,16 @@ import com.joanzapata.iconify.Iconify;
import com.joanzapata.iconify.widget.IconButton;
import org.apache.commons.lang3.ArrayUtils;
+import org.greenrobot.eventbus.EventBus;
+import org.greenrobot.eventbus.Subscribe;
+import org.greenrobot.eventbus.ThreadMode;
import java.util.List;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.CastEnabledActivity;
import de.danoeh.antennapod.activity.MainActivity;
-import de.danoeh.antennapod.adapter.DefaultActionButtonCallback;
+import de.danoeh.antennapod.adapter.actionbutton.ItemActionButton;
import de.danoeh.antennapod.core.event.DownloadEvent;
import de.danoeh.antennapod.core.event.DownloaderUpdate;
import de.danoeh.antennapod.core.event.FeedItemEvent;
@@ -61,14 +64,12 @@ import de.danoeh.antennapod.core.util.Converter;
import de.danoeh.antennapod.core.util.DateUtils;
import de.danoeh.antennapod.core.util.Flavors;
import de.danoeh.antennapod.core.util.IntentUtils;
-import de.danoeh.antennapod.core.util.LongList;
import de.danoeh.antennapod.core.util.NetworkUtils;
import de.danoeh.antennapod.core.util.ShareUtils;
import de.danoeh.antennapod.core.util.playback.Timeline;
import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler;
import de.danoeh.antennapod.view.OnSwipeGesture;
import de.danoeh.antennapod.view.SwipeGestureDetector;
-import de.greenrobot.event.EventBus;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
@@ -196,6 +197,9 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
webvDescription.getSettings().setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
// Use cached resources, even if they have expired
}
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ webvDescription.getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
+ }
webvDescription.getSettings().setUseWideViewPort(false);
webvDescription.getSettings().setLayoutAlgorithm(
WebSettings.LayoutAlgorithm.NARROW_COLUMNS);
@@ -227,9 +231,9 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
if (item == null) {
return;
}
- DefaultActionButtonCallback actionButtonCallback = new DefaultActionButtonCallback(getActivity());
- actionButtonCallback.onActionButtonPressed(item, item.isTagged(FeedItem.TAG_QUEUE) ?
- LongList.of(item.getId()) : new LongList(0));
+ ItemActionButton actionButton = ItemActionButton.forItem(item, item.isTagged(FeedItem.TAG_QUEUE));
+ actionButton.onClick(getActivity());
+
FeedMedia media = item.getMedia();
if (media != null && media.isDownloaded()) {
// playback was started, dialog should close itself
@@ -262,14 +266,19 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ EventDistributor.getInstance().register(contentUpdate);
+ EventBus.getDefault().register(this);
load();
}
@Override
public void onResume() {
super.onResume();
- EventDistributor.getInstance().register(contentUpdate);
- EventBus.getDefault().registerSticky(this);
if(itemsLoaded) {
progbarLoading.setVisibility(View.GONE);
updateAppearance();
@@ -277,8 +286,8 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
}
@Override
- public void onPause() {
- super.onPause();
+ public void onStop() {
+ super.onStop();
EventDistributor.getInstance().unregister(contentUpdate);
EventBus.getDefault().unregister(this);
}
@@ -297,19 +306,20 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
@Override
public boolean onSwipeLeftToRight() {
- Log.d(TAG, "onSwipeLeftToRight()");
- feedItemPos = feedItemPos - 1;
- if(feedItemPos < 0) {
- feedItemPos = feedItems.length - 1;
- }
- load();
- return true;
+ return swipeFeedItem(-1);
}
@Override
public boolean onSwipeRightToLeft() {
- Log.d(TAG, "onSwipeRightToLeft()");
- feedItemPos = (feedItemPos + 1) % feedItems.length;
+ return swipeFeedItem(+1);
+ }
+
+ private boolean swipeFeedItem(int position) {
+ Log.d(TAG, String.format("onSwipe() shift: %s", position));
+ feedItemPos = (feedItemPos + position) % feedItems.length;
+ if (feedItemPos < 0) {
+ feedItemPos = feedItems.length - 1;
+ }
load();
return true;
}
@@ -358,7 +368,7 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
private void onFragmentLoaded() {
if (webviewData != null) {
- webvDescription.loadDataWithBaseURL(null, webviewData, "text/html", "utf-8", "about:blank");
+ webvDescription.loadDataWithBaseURL("https://127.0.0.1", webviewData, "text/html", "utf-8", "about:blank");
}
updateAppearance();
}
@@ -537,6 +547,7 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
((MainActivity)getActivity()).loadChildFragment(fragment);
}
+ @Subscribe(threadMode = ThreadMode.MAIN)
public void onEventMainThread(FeedItemEvent event) {
Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
for(FeedItem item : event.items) {
@@ -547,6 +558,7 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
}
}
+ @Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
public void onEventMainThread(DownloadEvent event) {
Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
DownloaderUpdate update = event.update;
@@ -588,10 +600,12 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
}, error -> Log.e(TAG, Log.getStackTraceString(error)));
}
+ @Nullable
private FeedItem loadInBackground() {
FeedItem feedItem = DBReader.getFeedItem(feedItems[feedItemPos]);
- if (feedItem != null) {
- Timeline t = new Timeline(getActivity(), feedItem);
+ Context context = getContext();
+ if (feedItem != null && context != null) {
+ Timeline t = new Timeline(context, feedItem);
webviewData = t.processShownotes(false);
}
return feedItem;
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ItemlistFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ItemlistFragment.java
index d9e318069..0c75af986 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/ItemlistFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItemlistFragment.java
@@ -6,6 +6,7 @@ import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.LightingColorFilter;
import android.os.Bundle;
+import android.support.annotation.NonNull;
import android.support.v4.app.ListFragment;
import android.support.v4.view.MenuItemCompat;
import android.support.v7.widget.SearchView;
@@ -25,11 +26,13 @@ import android.widget.TextView;
import com.bumptech.glide.Glide;
import com.bumptech.glide.request.RequestOptions;
-import com.joanzapata.iconify.IconDrawable;
import com.joanzapata.iconify.Iconify;
import com.joanzapata.iconify.widget.IconTextView;
import org.apache.commons.lang3.Validate;
+import org.greenrobot.eventbus.EventBus;
+import org.greenrobot.eventbus.Subscribe;
+import org.greenrobot.eventbus.ThreadMode;
import java.util.List;
@@ -37,7 +40,6 @@ import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.FeedInfoActivity;
import de.danoeh.antennapod.activity.FeedSettingsActivity;
import de.danoeh.antennapod.activity.MainActivity;
-import de.danoeh.antennapod.adapter.DefaultActionButtonCallback;
import de.danoeh.antennapod.adapter.FeedItemlistAdapter;
import de.danoeh.antennapod.core.asynctask.FeedRemover;
import de.danoeh.antennapod.core.dialog.ConfirmationDialog;
@@ -61,13 +63,13 @@ import de.danoeh.antennapod.core.storage.DownloadRequestException;
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.Optional;
import de.danoeh.antennapod.core.util.gui.MoreContentListFooterUtil;
import de.danoeh.antennapod.dialog.EpisodesApplyActionFragment;
import de.danoeh.antennapod.dialog.RenameFeedDialog;
import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler;
import de.danoeh.antennapod.menuhandler.FeedMenuHandler;
import de.danoeh.antennapod.menuhandler.MenuItemUtils;
-import de.greenrobot.event.EventBus;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
@@ -94,8 +96,6 @@ public class ItemlistFragment extends ListFragment {
private long feedID;
private Feed feed;
- private boolean itemsLoaded = false;
- private boolean viewsCreated = false;
private boolean headerCreated = false;
private List<Downloader> downloaderList;
@@ -103,7 +103,7 @@ public class ItemlistFragment extends ListFragment {
private MoreContentListFooterUtil listFooter;
private boolean isUpdatingFeed;
-
+
private TextView txtvTitle;
private IconTextView txtvFailure;
private ImageView imgvBackground;
@@ -142,24 +142,21 @@ public class ItemlistFragment extends ListFragment {
@Override
public void onStart() {
super.onStart();
- if (viewsCreated && itemsLoaded) {
- onFragmentLoaded();
- }
+ EventDistributor.getInstance().register(contentUpdate);
+ EventBus.getDefault().register(this);
+ loadItems();
}
@Override
public void onResume() {
super.onResume();
- EventDistributor.getInstance().register(contentUpdate);
- EventBus.getDefault().registerSticky(this);
((MainActivity)getActivity()).getSupportActionBar().setTitle("");
updateProgressBarVisibility();
- loadItems();
}
@Override
- public void onPause() {
- super.onPause();
+ public void onStop() {
+ super.onStop();
EventDistributor.getInstance().unregister(contentUpdate);
EventBus.getDefault().unregister(this);
if(disposable != null) {
@@ -175,7 +172,6 @@ public class ItemlistFragment extends ListFragment {
private void resetViewState() {
adapter = null;
- viewsCreated = false;
listFooter = null;
}
@@ -188,45 +184,43 @@ public class ItemlistFragment extends ListFragment {
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
- if(!isAdded()) {
+ if (!isAdded()) {
return;
}
super.onCreateOptionsMenu(menu, inflater);
- if (itemsLoaded) {
- FeedMenuHandler.onCreateOptionsMenu(inflater, menu);
-
- MenuItem searchItem = menu.findItem(R.id.action_search);
- final SearchView sv = (SearchView) MenuItemCompat.getActionView(searchItem);
- MenuItemUtils.adjustTextColor(getActivity(), sv);
- sv.setQueryHint(getString(R.string.search_hint));
- sv.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
- @Override
- public boolean onQueryTextSubmit(String s) {
- sv.clearFocus();
- if (itemsLoaded) {
- ((MainActivity) getActivity()).loadChildFragment(SearchFragment.newInstance(s, feed.getId()));
- }
- return true;
- }
+ FeedMenuHandler.onCreateOptionsMenu(inflater, menu);
- @Override
- public boolean onQueryTextChange(String s) {
- return false;
+ MenuItem searchItem = menu.findItem(R.id.action_search);
+ final SearchView sv = (SearchView) MenuItemCompat.getActionView(searchItem);
+ MenuItemUtils.adjustTextColor(getActivity(), sv);
+ sv.setQueryHint(getString(R.string.search_hint));
+ sv.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
+ @Override
+ public boolean onQueryTextSubmit(String s) {
+ sv.clearFocus();
+ if (feed != null) {
+ ((MainActivity) getActivity()).loadChildFragment(SearchFragment.newInstance(s, feed.getId()));
}
- });
- if(feed == null || feed.getLink() == null) {
- menu.findItem(R.id.share_link_item).setVisible(false);
- menu.findItem(R.id.visit_website_item).setVisible(false);
+ return true;
}
- isUpdatingFeed = MenuItemUtils.updateRefreshMenuItem(menu, R.id.refresh_item, updateRefreshMenuItemChecker);
+ @Override
+ public boolean onQueryTextChange(String s) {
+ return false;
+ }
+ });
+ if (feed == null || feed.getLink() == null) {
+ menu.findItem(R.id.share_link_item).setVisible(false);
+ menu.findItem(R.id.visit_website_item).setVisible(false);
}
+
+ isUpdatingFeed = MenuItemUtils.updateRefreshMenuItem(menu, R.id.refresh_item, updateRefreshMenuItemChecker);
}
@Override
public void onPrepareOptionsMenu(Menu menu) {
- if (itemsLoaded) {
+ if (feed != null) {
FeedMenuHandler.onPrepareOptionsMenu(menu, feed);
}
}
@@ -339,11 +333,6 @@ public class ItemlistFragment extends ListFragment {
super.onViewCreated(view, savedInstanceState);
registerForContextMenu(getListView());
-
- viewsCreated = true;
- if (itemsLoaded) {
- onFragmentLoaded();
- }
}
@Override
@@ -358,6 +347,7 @@ public class ItemlistFragment extends ListFragment {
activity.getSupportActionBar().setTitle(feed.getTitle());
}
+ @Subscribe
public void onEvent(FeedEvent event) {
Log.d(TAG, "onEvent() called with: " + "event = [" + event + "]");
if(event.feedId == feedID) {
@@ -365,6 +355,7 @@ public class ItemlistFragment extends ListFragment {
}
}
+ @Subscribe(threadMode = ThreadMode.MAIN)
public void onEventMainThread(FeedItemEvent event) {
Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
if(feed == null || feed.getItems() == null || adapter == null) {
@@ -379,6 +370,7 @@ public class ItemlistFragment extends ListFragment {
}
}
+ @Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
public void onEventMainThread(DownloadEvent event) {
Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
DownloaderUpdate update = event.update;
@@ -422,7 +414,7 @@ public class ItemlistFragment extends ListFragment {
setListAdapter(null);
setupHeaderView();
setupFooterView();
- adapter = new FeedItemlistAdapter(getActivity(), itemAccess, new DefaultActionButtonCallback(getActivity()), false, true);
+ adapter = new FeedItemlistAdapter(getActivity(), itemAccess, false, true);
setListAdapter(adapter);
}
refreshHeaderView();
@@ -498,7 +490,7 @@ public class ItemlistFragment extends ListFragment {
butShowInfo.setOnClickListener(v -> showFeedInfo());
imgvCover.setOnClickListener(v -> showFeedInfo());
butShowSettings.setOnClickListener(v -> {
- if (viewsCreated && itemsLoaded) {
+ if (feed != null) {
Intent startIntent = new Intent(getActivity(), FeedSettingsActivity.class);
startIntent.putExtra(FeedSettingsActivity.EXTRA_FEED_ID,
feed.getId());
@@ -509,7 +501,7 @@ public class ItemlistFragment extends ListFragment {
}
private void showFeedInfo() {
- if (viewsCreated && itemsLoaded) {
+ if (feed != null) {
Intent startIntent = new Intent(getActivity(), FeedInfoActivity.class);
startIntent.putExtra(FeedInfoActivity.EXTRA_FEED_ID,
feed.getId());
@@ -618,24 +610,20 @@ public class ItemlistFragment extends ListFragment {
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(result -> {
- if (result != null) {
- feed = result;
- itemsLoaded = true;
- if (viewsCreated) {
- onFragmentLoaded();
- }
- }
+ feed = result.orElse(null);
+ onFragmentLoaded();
}, error -> Log.e(TAG, Log.getStackTraceString(error)));
}
- private Feed loadData() {
+ @NonNull
+ private Optional<Feed> loadData() {
Feed feed = DBReader.getFeed(feedID);
- DBReader.loadAdditionalFeedItemListData(feed.getItems());
- if(feed != null && feed.getItemFilter() != null) {
+ if (feed != null && feed.getItemFilter() != null) {
+ DBReader.loadAdditionalFeedItemListData(feed.getItems());
FeedItemFilter filter = feed.getItemFilter();
feed.setItems(filter.filter(feed.getItems()));
}
- return feed;
+ return Optional.ofNullable(feed);
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ItunesSearchFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ItunesSearchFragment.java
index a0e2ca22a..80767bef2 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/ItunesSearchFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItunesSearchFragment.java
@@ -5,6 +5,7 @@ import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.view.MenuItemCompat;
import android.support.v7.widget.SearchView;
+import android.support.annotation.NonNull;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
@@ -19,13 +20,14 @@ import android.widget.TextView;
import com.afollestad.materialdialogs.MaterialDialog;
+import de.danoeh.antennapod.discovery.ItunesPodcastSearcher;
+import de.danoeh.antennapod.discovery.ItunesTopListLoader;
+import de.danoeh.antennapod.discovery.PodcastSearchResult;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
-import java.io.UnsupportedEncodingException;
-import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
@@ -45,15 +47,11 @@ import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
-import static de.danoeh.antennapod.adapter.itunes.ItunesAdapter.Podcast;
-
//Searches iTunes store for given string and displays results in a list
public class ItunesSearchFragment extends Fragment {
private static final String TAG = "ItunesSearchFragment";
- private static final String API_URL = "https://itunes.apple.com/search?media=podcast&term=%s";
-
/**
* Adapter responsible with the search results
@@ -68,21 +66,21 @@ public class ItunesSearchFragment extends Fragment {
/**
* List of podcasts retreived from the search
*/
- private List<Podcast> searchResults;
- private List<Podcast> topList;
+ private List<PodcastSearchResult> searchResults;
+ private List<PodcastSearchResult> topList;
private Disposable disposable;
/**
* Replace adapter data with provided search results from SearchTask.
* @param result List of Podcast objects containing search results
*/
- private void updateData(List<Podcast> result) {
+ private void updateData(List<PodcastSearchResult> result) {
this.searchResults = result;
adapter.clear();
if (result != null && result.size() > 0) {
gridView.setVisibility(View.VISIBLE);
txtvEmpty.setVisibility(View.GONE);
- for (Podcast p : result) {
+ for (PodcastSearchResult p : result) {
adapter.add(p);
}
adapter.notifyDataSetInvalidated();
@@ -106,7 +104,7 @@ public class ItunesSearchFragment extends Fragment {
}
@Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View root = inflater.inflate(R.layout.fragment_itunes_search, container, false);
@@ -116,59 +114,31 @@ public class ItunesSearchFragment extends Fragment {
//Show information about the podcast when the list item is clicked
gridView.setOnItemClickListener((parent, view1, position, id) -> {
- Podcast podcast = searchResults.get(position);
- if(podcast.feedUrl == null) {
+ PodcastSearchResult podcast = searchResults.get(position);
+ if (podcast.feedUrl == null) {
return;
}
- if (!podcast.feedUrl.contains("itunes.apple.com")) {
- Intent intent = new Intent(getActivity(), OnlineFeedViewActivity.class);
- intent.putExtra(OnlineFeedViewActivity.ARG_FEEDURL, podcast.feedUrl);
- intent.putExtra(OnlineFeedViewActivity.ARG_TITLE, "iTunes");
- startActivity(intent);
- } else {
- gridView.setVisibility(View.GONE);
- progressBar.setVisibility(View.VISIBLE);
- disposable = Single.create((SingleOnSubscribe<String>) emitter -> {
- OkHttpClient client = AntennapodHttpClient.getHttpClient();
- Request.Builder httpReq = new Request.Builder()
- .url(podcast.feedUrl)
- .header("User-Agent", ClientConfig.USER_AGENT);
- try {
- Response response = client.newCall(httpReq.build()).execute();
- if (response.isSuccessful()) {
- String resultString = response.body().string();
- JSONObject result = new JSONObject(resultString);
- JSONObject results = result.getJSONArray("results").getJSONObject(0);
- String feedUrl = results.getString("feedUrl");
- emitter.onSuccess(feedUrl);
- } else {
- String prefix = getString(R.string.error_msg_prefix);
- emitter.onError(new IOException(prefix + response));
- }
- } catch (IOException | JSONException e) {
- emitter.onError(e);
- }
- })
- .subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe(feedUrl -> {
- progressBar.setVisibility(View.GONE);
- gridView.setVisibility(View.VISIBLE);
- Intent intent = new Intent(getActivity(), OnlineFeedViewActivity.class);
- intent.putExtra(OnlineFeedViewActivity.ARG_FEEDURL, feedUrl);
- intent.putExtra(OnlineFeedViewActivity.ARG_TITLE, "iTunes");
- startActivity(intent);
- }, error -> {
- Log.e(TAG, Log.getStackTraceString(error));
- progressBar.setVisibility(View.GONE);
- gridView.setVisibility(View.VISIBLE);
- String prefix = getString(R.string.error_msg_prefix);
- new MaterialDialog.Builder(getActivity())
- .content(prefix + " " + error.getMessage())
- .neutralText(android.R.string.ok)
- .show();
- });
- }
+ gridView.setVisibility(View.GONE);
+ progressBar.setVisibility(View.VISIBLE);
+ ItunesTopListLoader loader = new ItunesTopListLoader(getContext());
+ disposable = loader.getFeedUrl(podcast)
+ .subscribe(feedUrl -> {
+ progressBar.setVisibility(View.GONE);
+ gridView.setVisibility(View.VISIBLE);
+ Intent intent = new Intent(getActivity(), OnlineFeedViewActivity.class);
+ intent.putExtra(OnlineFeedViewActivity.ARG_FEEDURL, feedUrl);
+ intent.putExtra(OnlineFeedViewActivity.ARG_TITLE, "iTunes");
+ startActivity(intent);
+ }, error -> {
+ Log.e(TAG, Log.getStackTraceString(error));
+ progressBar.setVisibility(View.GONE);
+ gridView.setVisibility(View.VISIBLE);
+ String prefix = getString(R.string.error_msg_prefix);
+ new MaterialDialog.Builder(getActivity())
+ .content(prefix + " " + error.getMessage())
+ .neutralText(android.R.string.ok)
+ .show();
+ });
});
progressBar = root.findViewById(R.id.progressBar);
txtvError = root.findViewById(R.id.txtvError);
@@ -236,47 +206,9 @@ public class ItunesSearchFragment extends Fragment {
butRetry.setVisibility(View.GONE);
txtvEmpty.setVisibility(View.GONE);
progressBar.setVisibility(View.VISIBLE);
- disposable = Single.create((SingleOnSubscribe<List<Podcast>>) emitter -> {
- String lang = Locale.getDefault().getLanguage();
- String url = "https://itunes.apple.com/" + lang + "/rss/toppodcasts/limit=25/explicit=true/json";
- OkHttpClient client = AntennapodHttpClient.getHttpClient();
- Request.Builder httpReq = new Request.Builder()
- .url(url)
- .header("User-Agent", ClientConfig.USER_AGENT);
- List<Podcast> results = new ArrayList<>();
- try {
- Response response = client.newCall(httpReq.build()).execute();
- if(!response.isSuccessful()) {
- // toplist for language does not exist, fall back to united states
- url = "https://itunes.apple.com/us/rss/toppodcasts/limit=25/explicit=true/json";
- httpReq = new Request.Builder()
- .url(url)
- .header("User-Agent", ClientConfig.USER_AGENT);
- response = client.newCall(httpReq.build()).execute();
- }
- if(response.isSuccessful()) {
- String resultString = response.body().string();
- JSONObject result = new JSONObject(resultString);
- JSONObject feed = result.getJSONObject("feed");
- JSONArray entries = feed.getJSONArray("entry");
- for(int i=0; i < entries.length(); i++) {
- JSONObject json = entries.getJSONObject(i);
- Podcast podcast = Podcast.fromToplist(json);
- results.add(podcast);
- }
- }
- else {
- String prefix = getString(R.string.error_msg_prefix);
- emitter.onError(new IOException(prefix + response));
- }
- } catch (IOException | JSONException e) {
- emitter.onError(e);
- }
- emitter.onSuccess(results);
- })
- .subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
+ ItunesTopListLoader loader = new ItunesTopListLoader(getContext());
+ disposable = loader.loadToplist(25)
.subscribe(podcasts -> {
progressBar.setVisibility(View.GONE);
topList = podcasts;
@@ -300,61 +232,19 @@ public class ItunesSearchFragment extends Fragment {
butRetry.setVisibility(View.GONE);
txtvEmpty.setVisibility(View.GONE);
progressBar.setVisibility(View.VISIBLE);
- disposable = Single.create((SingleOnSubscribe<List<Podcast>>) subscriber -> {
- String encodedQuery = null;
- try {
- encodedQuery = URLEncoder.encode(query, "UTF-8");
- } catch (UnsupportedEncodingException e) {
- // this won't ever be thrown
- }
- if (encodedQuery == null) {
- encodedQuery = query; // failsafe
- }
-
- //Spaces in the query need to be replaced with '+' character.
- String formattedUrl = String.format(API_URL, query).replace(' ', '+');
- OkHttpClient client = AntennapodHttpClient.getHttpClient();
- Request.Builder httpReq = new Request.Builder()
- .url(formattedUrl)
- .header("User-Agent", ClientConfig.USER_AGENT);
- List<Podcast> podcasts = new ArrayList<>();
- try {
- Response response = client.newCall(httpReq.build()).execute();
-
- if(response.isSuccessful()) {
- String resultString = response.body().string();
- JSONObject result = new JSONObject(resultString);
- JSONArray j = result.getJSONArray("results");
-
- for (int i = 0; i < j.length(); i++) {
- JSONObject podcastJson = j.getJSONObject(i);
- Podcast podcast = Podcast.fromSearch(podcastJson);
- podcasts.add(podcast);
- }
- }
- else {
- String prefix = getString(R.string.error_msg_prefix);
- subscriber.onError(new IOException(prefix + response));
- }
- } catch (IOException | JSONException e) {
- subscriber.onError(e);
- }
- subscriber.onSuccess(podcasts);
- })
- .subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe(podcasts -> {
- progressBar.setVisibility(View.GONE);
- updateData(podcasts);
- }, error -> {
- Log.e(TAG, Log.getStackTraceString(error));
- progressBar.setVisibility(View.GONE);
- txtvError.setText(error.toString());
- txtvError.setVisibility(View.VISIBLE);
- butRetry.setOnClickListener(v -> search(query));
- butRetry.setVisibility(View.VISIBLE);
- });
+ ItunesPodcastSearcher searcher = new ItunesPodcastSearcher(getContext());
+ disposable = searcher.search(query).subscribe(podcasts -> {
+ progressBar.setVisibility(View.GONE);
+ updateData(podcasts);
+ }, error -> {
+ Log.e(TAG, Log.getStackTraceString(error));
+ progressBar.setVisibility(View.GONE);
+ txtvError.setText(error.toString());
+ txtvError.setVisibility(View.VISIBLE);
+ butRetry.setOnClickListener(v -> search(query));
+ butRetry.setVisibility(View.VISIBLE);
+ });
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/NewEpisodesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/NewEpisodesFragment.java
index 6695ba427..1bf907aee 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/NewEpisodesFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/NewEpisodesFragment.java
@@ -1,9 +1,9 @@
package de.danoeh.antennapod.fragment;
import android.os.Bundle;
+import android.support.annotation.NonNull;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.helper.ItemTouchHelper;
-import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -12,53 +12,39 @@ import java.util.List;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.adapter.AllEpisodesRecycleAdapter;
-import de.danoeh.antennapod.core.event.FeedItemEvent;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.storage.DBReader;
-import de.danoeh.antennapod.core.util.FeedItemUtil;
-
/**
* Like 'EpisodesFragment' except that it only shows new episodes and
* supports swiping to mark as read.
*/
-
public class NewEpisodesFragment extends AllEpisodesFragment {
public static final String TAG = "NewEpisodesFragment";
-
private static final String PREF_NAME = "PrefNewEpisodesFragment";
@Override
- protected boolean showOnlyNewEpisodes() { return true; }
-
- @Override
- protected String getPrefName() { return PREF_NAME; }
+ protected boolean showOnlyNewEpisodes() {
+ return true;
+ }
@Override
- protected void resetViewState() {
- super.resetViewState();
+ protected String getPrefName() {
+ return PREF_NAME;
}
@Override
- public void onEventMainThread(FeedItemEvent event) {
- Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
- if(episodes == null) {
- return;
- }
- for(FeedItem item : event.items) {
- int pos = FeedItemUtil.indexOfItemWithId(episodes, item.getId());
- if(pos >= 0 && item.isTagged(FeedItem.TAG_QUEUE)) {
- episodes.remove(pos);
- listAdapter.notifyItemRemoved(pos);
- }
- }
+ protected boolean shouldUpdatedItemRemainInList(FeedItem item) {
+ return item.isNew();
}
+ @NonNull
@Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
- View root = super.onCreateViewHelper(inflater, container, savedInstanceState,
- R.layout.all_episodes_fragment);
+ public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ View root = super.onCreateView(inflater, container, savedInstanceState);
+ emptyView.setTitle(R.string.no_new_episodes_head_label);
+ emptyView.setMessage(R.string.no_new_episodes_label);
ItemTouchHelper.SimpleCallback simpleItemTouchCallback = new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) {
@Override
@@ -68,8 +54,8 @@ public class NewEpisodesFragment extends AllEpisodesFragment {
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int swipeDir) {
- AllEpisodesRecycleAdapter.Holder holder = (AllEpisodesRecycleAdapter.Holder)viewHolder;
- markItemAsSeenWithUndo(holder.getFeedItem());
+ AllEpisodesRecycleAdapter.Holder holder = (AllEpisodesRecycleAdapter.Holder) viewHolder;
+ removeNewFlagWithUndo(holder.getFeedItem());
}
@Override
@@ -86,6 +72,7 @@ public class NewEpisodesFragment extends AllEpisodesFragment {
super.onSelectedChanged(viewHolder, actionState);
}
+
@Override
public void clearView(RecyclerView recyclerView,
RecyclerView.ViewHolder viewHolder) {
@@ -105,9 +92,9 @@ public class NewEpisodesFragment extends AllEpisodesFragment {
return root;
}
+ @NonNull
@Override
protected List<FeedItem> loadData() {
return DBReader.getNewItemsList();
}
-
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java
index c2a9200c8..e2060481f 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java
@@ -1,8 +1,8 @@
package de.danoeh.antennapod.fragment;
-import android.content.Context;
import android.content.res.TypedArray;
import android.os.Bundle;
+import android.support.annotation.NonNull;
import android.support.v4.app.ListFragment;
import android.support.v4.view.MenuItemCompat;
import android.util.Log;
@@ -12,11 +12,14 @@ import android.view.MenuItem;
import android.view.View;
import android.widget.ListView;
+import org.greenrobot.eventbus.EventBus;
+import org.greenrobot.eventbus.Subscribe;
+import org.greenrobot.eventbus.ThreadMode;
+
import java.util.List;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
-import de.danoeh.antennapod.adapter.DefaultActionButtonCallback;
import de.danoeh.antennapod.adapter.FeedItemlistAdapter;
import de.danoeh.antennapod.core.event.DownloadEvent;
import de.danoeh.antennapod.core.event.DownloaderUpdate;
@@ -29,7 +32,7 @@ import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.util.FeedItemUtil;
import de.danoeh.antennapod.core.util.LongList;
-import de.greenrobot.event.EventBus;
+import de.danoeh.antennapod.view.EmptyViewHandler;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
@@ -44,23 +47,10 @@ public class PlaybackHistoryFragment extends ListFragment {
private List<FeedItem> playbackHistory;
private FeedItemlistAdapter adapter;
-
- private boolean itemsLoaded = false;
- private boolean viewsCreated = false;
-
private List<Downloader> downloaderList;
-
private Disposable disposable;
@Override
- public void onAttach(Context context) {
- super.onAttach(context);
- if (viewsCreated && itemsLoaded) {
- onFragmentLoaded();
- }
- }
-
- @Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
@@ -77,63 +67,43 @@ public class PlaybackHistoryFragment extends ListFragment {
final int vertPadding = getResources().getDimensionPixelSize(R.dimen.list_vertical_padding);
lv.setPadding(0, vertPadding, 0, vertPadding);
- viewsCreated = true;
- if (itemsLoaded) {
- onFragmentLoaded();
- }
- }
-
+ EmptyViewHandler emptyView = new EmptyViewHandler(getActivity());
+ emptyView.setIcon(R.attr.ic_history);
+ emptyView.setTitle(R.string.no_history_head_label);
+ emptyView.setMessage(R.string.no_history_label);
+ emptyView.attachToListView(getListView());
- @Override
- public void onResume() {
- super.onResume();
- EventBus.getDefault().registerSticky(this);
- loadItems();
+ // played items shoudln't be transparent for this fragment since, *all* items
+ // in this fragment will, by definition, be played. So it serves no purpose and can make
+ // it harder to read.
+ adapter = new FeedItemlistAdapter(getActivity(), itemAccess, true, false);
+ setListAdapter(adapter);
}
@Override
public void onStart() {
super.onStart();
EventDistributor.getInstance().register(contentUpdate);
- }
-
- @Override
- public void onPause() {
- super.onPause();
- EventBus.getDefault().unregister(this);
+ EventBus.getDefault().register(this);
+ loadItems();
}
@Override
public void onStop() {
super.onStop();
+ EventBus.getDefault().unregister(this);
EventDistributor.getInstance().unregister(contentUpdate);
- if(disposable != null) {
+ if (disposable != null) {
disposable.dispose();
}
}
- @Override
- public void onDetach() {
- super.onDetach();
- if(disposable != null) {
- disposable.dispose();
- }
- }
-
- @Override
- public void onDestroyView() {
- super.onDestroyView();
- adapter = null;
- viewsCreated = false;
- }
-
+ @Subscribe(sticky = true)
public void onEvent(DownloadEvent event) {
Log.d(TAG, "onEvent() called with: " + "event = [" + event + "]");
DownloaderUpdate update = event.update;
downloaderList = update.downloaders;
- if (adapter != null) {
- adapter.notifyDataSetChanged();
- }
+ adapter.notifyDataSetChanged();
}
@Override
@@ -146,27 +116,23 @@ public class PlaybackHistoryFragment extends ListFragment {
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
- if(!isAdded()) {
+ if (!isAdded()) {
return;
}
super.onCreateOptionsMenu(menu, inflater);
- if (itemsLoaded) {
- MenuItem clearHistory = menu.add(Menu.NONE, R.id.clear_history_item, Menu.CATEGORY_CONTAINER, R.string.clear_history_label);
- MenuItemCompat.setShowAsAction(clearHistory, MenuItemCompat.SHOW_AS_ACTION_IF_ROOM);
- TypedArray drawables = getActivity().obtainStyledAttributes(new int[]{R.attr.content_discard});
- clearHistory.setIcon(drawables.getDrawable(0));
- drawables.recycle();
- }
+ MenuItem clearHistory = menu.add(Menu.NONE, R.id.clear_history_item, Menu.CATEGORY_CONTAINER, R.string.clear_history_label);
+ MenuItemCompat.setShowAsAction(clearHistory, MenuItemCompat.SHOW_AS_ACTION_IF_ROOM);
+ TypedArray drawables = getActivity().obtainStyledAttributes(new int[]{R.attr.content_discard});
+ clearHistory.setIcon(drawables.getDrawable(0));
+ drawables.recycle();
}
@Override
public void onPrepareOptionsMenu(Menu menu) {
super.onPrepareOptionsMenu(menu);
- if (itemsLoaded) {
- MenuItem menuItem = menu.findItem(R.id.clear_history_item);
- if (menuItem != null) {
- menuItem.setVisible(playbackHistory != null && !playbackHistory.isEmpty());
- }
+ MenuItem menuItem = menu.findItem(R.id.clear_history_item);
+ if (menuItem != null) {
+ menuItem.setVisible(playbackHistory != null && !playbackHistory.isEmpty());
}
}
@@ -185,6 +151,7 @@ public class PlaybackHistoryFragment extends ListFragment {
}
}
+ @Subscribe(threadMode = ThreadMode.MAIN)
public void onEventMainThread(FeedItemEvent event) {
Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
if(playbackHistory == null) {
@@ -211,15 +178,6 @@ public class PlaybackHistoryFragment extends ListFragment {
};
private void onFragmentLoaded() {
- if (adapter == null) {
- // played items shoudln't be transparent for this fragment since, *all* items
- // in this fragment will, by definition, be played. So it serves no purpose and can make
- // it harder to read.
- adapter = new FeedItemlistAdapter(getActivity(), itemAccess,
- new DefaultActionButtonCallback(getActivity()), true, false);
- setListAdapter(adapter);
- }
- setListShown(true);
adapter.notifyDataSetChanged();
getActivity().supportInvalidateOptionsMenu();
}
@@ -278,18 +236,15 @@ public class PlaybackHistoryFragment extends ListFragment {
.subscribe(result -> {
if (result != null) {
playbackHistory = result;
- itemsLoaded = true;
- if (viewsCreated) {
- onFragmentLoaded();
- }
+ onFragmentLoaded();
}
}, error -> Log.e(TAG, Log.getStackTraceString(error)));
}
+ @NonNull
private List<FeedItem> loadData() {
List<FeedItem> history = DBReader.getPlaybackHistory();
DBReader.loadAdditionalFeedItemListData(history);
return history;
}
-
}
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 faeabf75c..0fe413954 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java
@@ -28,7 +28,6 @@ import java.util.List;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
-import de.danoeh.antennapod.adapter.DefaultActionButtonCallback;
import de.danoeh.antennapod.adapter.QueueRecyclerAdapter;
import de.danoeh.antennapod.core.dialog.ConfirmationDialog;
import de.danoeh.antennapod.core.event.DownloadEvent;
@@ -50,13 +49,22 @@ import de.danoeh.antennapod.core.util.Converter;
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.dialog.EpisodesApplyActionFragment;
import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler;
import de.danoeh.antennapod.menuhandler.MenuItemUtils;
-import de.greenrobot.event.EventBus;
+
+import de.danoeh.antennapod.view.EmptyViewHandler;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
+import org.greenrobot.eventbus.EventBus;
+import org.greenrobot.eventbus.Subscribe;
+import org.greenrobot.eventbus.ThreadMode;
+
+import static de.danoeh.antennapod.dialog.EpisodesApplyActionFragment.ACTION_DELETE;
+import static de.danoeh.antennapod.dialog.EpisodesApplyActionFragment.ACTION_REMOVE_FROM_QUEUE;
/**
* Shows all items in the queue
@@ -72,7 +80,7 @@ public class QueueFragment extends Fragment {
private TextView infoBar;
private RecyclerView recyclerView;
private QueueRecyclerAdapter recyclerAdapter;
- private TextView txtvEmpty;
+ private EmptyViewHandler emptyView;
private ProgressBar progLoading;
private List<FeedItem> queue;
@@ -102,20 +110,20 @@ public class QueueFragment extends Fragment {
if (queue != null) {
onFragmentLoaded(true);
}
- }
-
- @Override
- public void onResume() {
- super.onResume();
loadItems(true);
EventDistributor.getInstance().register(contentUpdate);
- EventBus.getDefault().registerSticky(this);
+ EventBus.getDefault().register(this);
}
@Override
public void onPause() {
super.onPause();
saveScrollPosition();
+ }
+
+ @Override
+ public void onStop() {
+ super.onStop();
EventDistributor.getInstance().unregister(contentUpdate);
EventBus.getDefault().unregister(this);
if(disposable != null) {
@@ -123,9 +131,13 @@ public class QueueFragment extends Fragment {
}
}
+ @Subscribe(threadMode = ThreadMode.MAIN)
public void onEventMainThread(QueueEvent event) {
Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
- if(queue == null || recyclerAdapter == null) {
+ if (queue == null) {
+ return;
+ } else if (recyclerAdapter == null) {
+ loadItems(true);
return;
}
switch(event.action) {
@@ -158,9 +170,13 @@ public class QueueFragment extends Fragment {
onFragmentLoaded(false);
}
+ @Subscribe(threadMode = ThreadMode.MAIN)
public void onEventMainThread(FeedItemEvent event) {
Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
- if(queue == null || recyclerAdapter == null) {
+ if (queue == null) {
+ return;
+ } else if (recyclerAdapter == null) {
+ loadItems(true);
return;
}
for(int i=0, size = event.items.size(); i < size; i++) {
@@ -170,10 +186,12 @@ public class QueueFragment extends Fragment {
queue.remove(pos);
queue.add(pos, item);
recyclerAdapter.notifyItemChanged(pos);
+ refreshInfoBar();
}
}
}
+ @Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
public void onEventMainThread(DownloadEvent event) {
Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
DownloaderUpdate update = event.update;
@@ -259,6 +277,19 @@ public class QueueFragment extends Fragment {
MenuItemUtils.refreshLockItem(getActivity(), menu);
+ // Show Lock Item only if queue is sorted manually
+ boolean keepSorted = UserPreferences.isQueueKeepSorted();
+ MenuItem lockItem = menu.findItem(R.id.queue_lock);
+ lockItem.setVisible(!keepSorted);
+
+ // Random sort is not supported in keep sorted mode
+ MenuItem sortRandomItem = menu.findItem(R.id.queue_sort_random);
+ sortRandomItem.setVisible(!keepSorted);
+
+ // Set keep sorted checkbox
+ MenuItem keepSortedItem = menu.findItem(R.id.queue_keep_sorted);
+ keepSortedItem.setChecked(keepSorted);
+
isUpdatingFeeds = MenuItemUtils.updateRefreshMenuItem(menu, R.id.refresh_item, updateRefreshMenuItemChecker);
}
}
@@ -271,7 +302,9 @@ public class QueueFragment extends Fragment {
boolean newLockState = !UserPreferences.isQueueLocked();
UserPreferences.setQueueLocked(newLockState);
getActivity().supportInvalidateOptionsMenu();
- recyclerAdapter.setLocked(newLockState);
+ if (recyclerAdapter != null) {
+ recyclerAdapter.setLocked(newLockState);
+ }
if (newLockState) {
Snackbar.make(getActivity().findViewById(R.id.content), R.string
.queue_locked, Snackbar.LENGTH_SHORT).show();
@@ -301,38 +334,55 @@ public class QueueFragment extends Fragment {
};
conDialog.createNewDialog().show();
return true;
+ case R.id.episode_actions:
+ ((MainActivity) requireActivity()) .loadChildFragment(
+ EpisodesApplyActionFragment.newInstance(queue, ACTION_DELETE | ACTION_REMOVE_FROM_QUEUE));
+ return true;
case R.id.queue_sort_episode_title_asc:
- QueueSorter.sort(getActivity(), QueueSorter.Rule.EPISODE_TITLE_ASC, true);
+ setSortOrder(SortOrder.EPISODE_TITLE_A_Z);
return true;
case R.id.queue_sort_episode_title_desc:
- QueueSorter.sort(getActivity(), QueueSorter.Rule.EPISODE_TITLE_DESC, true);
+ setSortOrder(SortOrder.EPISODE_TITLE_Z_A);
return true;
case R.id.queue_sort_date_asc:
- QueueSorter.sort(getActivity(), QueueSorter.Rule.DATE_ASC, true);
+ setSortOrder(SortOrder.DATE_OLD_NEW);
return true;
case R.id.queue_sort_date_desc:
- QueueSorter.sort(getActivity(), QueueSorter.Rule.DATE_DESC, true);
+ setSortOrder(SortOrder.DATE_NEW_OLD);
return true;
case R.id.queue_sort_duration_asc:
- QueueSorter.sort(getActivity(), QueueSorter.Rule.DURATION_ASC, true);
+ setSortOrder(SortOrder.DURATION_SHORT_LONG);
return true;
case R.id.queue_sort_duration_desc:
- QueueSorter.sort(getActivity(), QueueSorter.Rule.DURATION_DESC, true);
+ setSortOrder(SortOrder.DURATION_LONG_SHORT);
return true;
case R.id.queue_sort_feed_title_asc:
- QueueSorter.sort(getActivity(), QueueSorter.Rule.FEED_TITLE_ASC, true);
+ setSortOrder(SortOrder.FEED_TITLE_A_Z);
return true;
case R.id.queue_sort_feed_title_desc:
- QueueSorter.sort(getActivity(), QueueSorter.Rule.FEED_TITLE_DESC, true);
+ setSortOrder(SortOrder.FEED_TITLE_Z_A);
return true;
case R.id.queue_sort_random:
- QueueSorter.sort(getActivity(), QueueSorter.Rule.RANDOM, true);
+ setSortOrder(SortOrder.RANDOM);
return true;
case R.id.queue_sort_smart_shuffle_asc:
- QueueSorter.sort(getActivity(), QueueSorter.Rule.SMART_SHUFFLE_ASC, true);
+ setSortOrder(SortOrder.SMART_SHUFFLE_OLD_NEW);
return true;
case R.id.queue_sort_smart_shuffle_desc:
- QueueSorter.sort(getActivity(), QueueSorter.Rule.SMART_SHUFFLE_DESC, true);
+ setSortOrder(SortOrder.SMART_SHUFFLE_NEW_OLD);
+ return true;
+ case R.id.queue_keep_sorted:
+ boolean keepSortedOld = UserPreferences.isQueueKeepSorted();
+ boolean keepSortedNew = !keepSortedOld;
+ UserPreferences.setQueueKeepSorted(keepSortedNew);
+ if (keepSortedNew) {
+ SortOrder sortOrder = UserPreferences.getQueueKeepSortedOrder();
+ QueueSorter.sort(sortOrder, true);
+ recyclerAdapter.setLocked(true);
+ } else {
+ recyclerAdapter.setLocked(UserPreferences.isQueueLocked());
+ }
+ getActivity().invalidateOptionsMenu();
return true;
default:
return false;
@@ -342,6 +392,15 @@ public class QueueFragment extends Fragment {
}
}
+ /**
+ * This method is called if the user clicks on a sort order menu item.
+ *
+ * @param sortOrder New sort order.
+ */
+ private void setSortOrder(SortOrder sortOrder) {
+ UserPreferences.setQueueKeepSortedOrder(sortOrder);
+ QueueSorter.sort(sortOrder, true);
+ }
@Override
public boolean onContextItemSelected(MenuItem item) {
@@ -431,7 +490,7 @@ public class QueueFragment extends Fragment {
final FeedItem item = queue.get(position);
final boolean isRead = item.isPlayed();
DBWriter.markItemPlayed(FeedItem.PLAYED, false, item.getId());
- DBWriter.removeQueueItem(getActivity(), item, true);
+ DBWriter.removeQueueItem(getActivity(), true, item);
Snackbar snackbar = Snackbar.make(root, getString(R.string.marked_as_read_label), Snackbar.LENGTH_LONG);
snackbar.setAction(getString(R.string.undo), v -> {
DBWriter.addQueueItemAt(getActivity(), item.getId(), position, false);
@@ -494,8 +553,12 @@ public class QueueFragment extends Fragment {
);
itemTouchHelper.attachToRecyclerView(recyclerView);
- txtvEmpty = root.findViewById(android.R.id.empty);
- txtvEmpty.setVisibility(View.GONE);
+ emptyView = new EmptyViewHandler(getContext());
+ emptyView.attachToRecyclerView(recyclerView);
+ emptyView.setIcon(R.attr.stat_playlist);
+ emptyView.setTitle(R.string.no_items_header_label);
+ emptyView.setMessage(R.string.no_items_label);
+
progLoading = root.findViewById(R.id.progLoading);
progLoading.setVisibility(View.VISIBLE);
@@ -503,19 +566,19 @@ public class QueueFragment extends Fragment {
}
private void onFragmentLoaded(final boolean restoreScrollPosition) {
- if (recyclerAdapter == null) {
- MainActivity activity = (MainActivity) getActivity();
- recyclerAdapter = new QueueRecyclerAdapter(activity, itemAccess,
- new DefaultActionButtonCallback(activity), itemTouchHelper);
- recyclerAdapter.setHasStableIds(true);
- recyclerView.setAdapter(recyclerAdapter);
- }
- if(queue == null || queue.size() == 0) {
- recyclerView.setVisibility(View.GONE);
- txtvEmpty.setVisibility(View.VISIBLE);
- } else {
- txtvEmpty.setVisibility(View.GONE);
+ if (queue != null && queue.size() > 0) {
+ if (recyclerAdapter == null) {
+ MainActivity activity = (MainActivity) getActivity();
+ recyclerAdapter = new QueueRecyclerAdapter(activity, itemAccess, itemTouchHelper);
+ recyclerAdapter.setHasStableIds(true);
+ recyclerView.setAdapter(recyclerAdapter);
+ emptyView.updateAdapter(recyclerAdapter);
+ }
recyclerView.setVisibility(View.VISIBLE);
+ } else {
+ recyclerAdapter = null;
+ recyclerView.setVisibility(View.GONE);
+ emptyView.updateAdapter(recyclerAdapter);
}
if (restoreScrollPosition) {
@@ -628,22 +691,19 @@ public class QueueFragment extends Fragment {
}
if (queue == null) {
recyclerView.setVisibility(View.GONE);
- txtvEmpty.setVisibility(View.GONE);
+ emptyView.hide();
progLoading.setVisibility(View.VISIBLE);
}
disposable = Observable.fromCallable(DBReader::getQueue)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(items -> {
- if(items != null) {
- progLoading.setVisibility(View.GONE);
- queue = items;
- onFragmentLoaded(restoreScrollPosition);
- if(recyclerAdapter != null) {
- recyclerAdapter.notifyDataSetChanged();
- }
+ progLoading.setVisibility(View.GONE);
+ queue = items;
+ onFragmentLoaded(restoreScrollPosition);
+ if(recyclerAdapter != null) {
+ recyclerAdapter.notifyDataSetChanged();
}
}, error -> Log.e(TAG, Log.getStackTraceString(error)));
}
-
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/QuickFeedDiscoveryFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/QuickFeedDiscoveryFragment.java
new file mode 100644
index 000000000..e4213cc6b
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/QuickFeedDiscoveryFragment.java
@@ -0,0 +1,119 @@
+package de.danoeh.antennapod.fragment;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.GridView;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+import com.afollestad.materialdialogs.MaterialDialog;
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.activity.MainActivity;
+import de.danoeh.antennapod.activity.OnlineFeedViewActivity;
+import de.danoeh.antennapod.adapter.FeedDiscoverAdapter;
+import de.danoeh.antennapod.discovery.ItunesTopListLoader;
+import de.danoeh.antennapod.discovery.PodcastSearchResult;
+import io.reactivex.disposables.Disposable;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+public class QuickFeedDiscoveryFragment extends Fragment implements AdapterView.OnItemClickListener {
+ private static final String TAG = "FeedDiscoveryFragment";
+
+ private ProgressBar progressBar;
+ private Disposable disposable;
+ private FeedDiscoverAdapter adapter;
+ private GridView subscriptionGridLayout;
+ private TextView errorTextView;
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ super.onCreateView(inflater, container, savedInstanceState);
+ View root = inflater.inflate(R.layout.quick_feed_discovery, container, false);
+ View discoverMore = root.findViewById(R.id.discover_more);
+ discoverMore.setOnClickListener(v ->
+ ((MainActivity) getActivity()).loadChildFragment(new ItunesSearchFragment()));
+
+ subscriptionGridLayout = root.findViewById(R.id.discover_grid);
+ progressBar = root.findViewById(R.id.discover_progress_bar);
+ errorTextView = root.findViewById(R.id.discover_error);
+
+ adapter = new FeedDiscoverAdapter((MainActivity) getActivity());
+ subscriptionGridLayout.setAdapter(adapter);
+ subscriptionGridLayout.setOnItemClickListener(this);
+
+ // Fill with dummy elements to have a fixed height and
+ // prevent the UI elements below from jumping on slow connections
+ List<PodcastSearchResult> dummies = new ArrayList<>();
+ for (int i = 0; i < 8; i++) {
+ dummies.add(PodcastSearchResult.dummy());
+ }
+ adapter.updateData(dummies);
+
+ loadToplist();
+
+ return root;
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ if (disposable != null) {
+ disposable.dispose();
+ }
+ }
+
+ private void loadToplist() {
+ progressBar.setVisibility(View.VISIBLE);
+ subscriptionGridLayout.setVisibility(View.INVISIBLE);
+ errorTextView.setVisibility(View.GONE);
+
+ ItunesTopListLoader loader = new ItunesTopListLoader(getContext());
+ disposable = loader.loadToplist(8)
+ .subscribe(podcasts -> {
+ errorTextView.setVisibility(View.GONE);
+ progressBar.setVisibility(View.GONE);
+ subscriptionGridLayout.setVisibility(View.VISIBLE);
+ adapter.updateData(podcasts);
+ }, error -> {
+ Log.e(TAG, Log.getStackTraceString(error));
+ errorTextView.setText(error.getLocalizedMessage());
+ errorTextView.setVisibility(View.VISIBLE);
+ progressBar.setVisibility(View.GONE);
+ subscriptionGridLayout.setVisibility(View.INVISIBLE);
+ });
+ }
+
+ @Override
+ public void onItemClick(AdapterView<?> parent, final View view, int position, long id) {
+ PodcastSearchResult podcast = adapter.getItem(position);
+ if (podcast.feedUrl == null) {
+ return;
+ }
+ view.setAlpha(0.5f);
+ ItunesTopListLoader loader = new ItunesTopListLoader(getContext());
+ disposable = loader.getFeedUrl(podcast)
+ .subscribe(feedUrl -> {
+ view.setAlpha(1f);
+ Intent intent = new Intent(getActivity(), OnlineFeedViewActivity.class);
+ intent.putExtra(OnlineFeedViewActivity.ARG_FEEDURL, feedUrl);
+ intent.putExtra(OnlineFeedViewActivity.ARG_TITLE, getString(R.string.add_feed_label));
+ startActivity(intent);
+ }, error -> {
+ Log.e(TAG, Log.getStackTraceString(error));
+ view.setAlpha(1f);
+ String prefix = getString(R.string.error_msg_prefix);
+ new MaterialDialog.Builder(getActivity())
+ .content(prefix + " " + error.getMessage())
+ .neutralText(android.R.string.ok)
+ .show();
+ });
+ }
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/RunningDownloadsFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/RunningDownloadsFragment.java
index 66c59b7f7..2a7f7d12b 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/RunningDownloadsFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/RunningDownloadsFragment.java
@@ -7,6 +7,10 @@ import android.view.View;
import android.widget.ListView;
import android.widget.Toast;
+import org.greenrobot.eventbus.EventBus;
+import org.greenrobot.eventbus.Subscribe;
+
+import java.util.ArrayList;
import java.util.List;
import de.danoeh.antennapod.R;
@@ -20,7 +24,7 @@ import de.danoeh.antennapod.core.service.download.Downloader;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.storage.DownloadRequester;
-import de.greenrobot.event.EventBus;
+import de.danoeh.antennapod.view.EmptyViewHandler;
/**
* Displays all running downloads and provides actions to cancel them
@@ -30,7 +34,7 @@ public class RunningDownloadsFragment extends ListFragment {
private static final String TAG = "RunningDownloadsFrag";
private DownloadlistAdapter adapter;
- private List<Downloader> downloaderList;
+ private List<Downloader> downloaderList = new ArrayList<>();
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
@@ -44,17 +48,24 @@ public class RunningDownloadsFragment extends ListFragment {
adapter = new DownloadlistAdapter(getActivity(), itemAccess);
setListAdapter(adapter);
+
+ EmptyViewHandler emptyView = new EmptyViewHandler(getActivity());
+ emptyView.setIcon(R.attr.av_download);
+ emptyView.setTitle(R.string.no_run_downloads_head_label);
+ emptyView.setMessage(R.string.no_run_downloads_label);
+ emptyView.attachToListView(getListView());
+
}
@Override
- public void onResume() {
- super.onResume();
- EventBus.getDefault().registerSticky(this);
+ public void onStart() {
+ super.onStart();
+ EventBus.getDefault().register(this);
}
@Override
- public void onPause() {
- super.onPause();
+ public void onStop() {
+ super.onStop();
EventBus.getDefault().unregister(this);
}
@@ -62,28 +73,25 @@ public class RunningDownloadsFragment extends ListFragment {
public void onDestroy() {
super.onDestroy();
setListAdapter(null);
- adapter = null;
}
+ @Subscribe(sticky = true)
public void onEvent(DownloadEvent event) {
Log.d(TAG, "onEvent() called with: " + "event = [" + event + "]");
DownloaderUpdate update = event.update;
downloaderList = update.downloaders;
- if (adapter != null) {
- adapter.notifyDataSetChanged();
- }
+ adapter.notifyDataSetChanged();
}
-
private final DownloadlistAdapter.ItemAccess itemAccess = new DownloadlistAdapter.ItemAccess() {
@Override
public int getCount() {
- return (downloaderList != null) ? downloaderList.size() : 0;
+ return downloaderList.size();
}
@Override
public Downloader getItem(int position) {
- if (downloaderList != null && 0 <= position && position < downloaderList.size()) {
+ if (0 <= position && position < downloaderList.size()) {
return downloaderList.get(position);
} else {
return null;
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java
index 8322a5573..0892bce0a 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java
@@ -2,6 +2,7 @@ package de.danoeh.antennapod.fragment;
import android.content.Context;
import android.os.Bundle;
+import android.support.annotation.NonNull;
import android.support.v4.app.ListFragment;
import android.support.v4.view.MenuItemCompat;
import android.support.v7.app.AppCompatActivity;
@@ -13,6 +14,7 @@ import android.view.MenuItem;
import android.view.View;
import android.widget.ListView;
+import java.util.ArrayList;
import java.util.List;
import de.danoeh.antennapod.R;
@@ -39,11 +41,7 @@ public class SearchFragment extends ListFragment {
private static final String ARG_FEED = "feed";
private SearchlistAdapter searchAdapter;
- private List<SearchResult> searchResults;
-
- private boolean viewCreated = false;
- private boolean itemsLoaded = false;
-
+ private List<SearchResult> searchResults = new ArrayList<>();
private Disposable disposable;
/**
@@ -73,13 +71,13 @@ public class SearchFragment extends ListFragment {
super.onCreate(savedInstanceState);
setRetainInstance(true);
setHasOptionsMenu(true);
- search();
}
@Override
public void onStart() {
super.onStart();
EventDistributor.getInstance().register(contentUpdate);
+ search();
}
@Override
@@ -92,21 +90,6 @@ public class SearchFragment extends ListFragment {
}
@Override
- public void onDetach() {
- super.onDetach();
- if(disposable != null) {
- disposable.dispose();
- }
- }
-
- @Override
- public void onDestroyView() {
- super.onDestroyView();
- searchAdapter = null;
- viewCreated = false;
- }
-
- @Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
@@ -117,10 +100,9 @@ public class SearchFragment extends ListFragment {
lv.setPadding(0, vertPadding, 0, vertPadding);
((AppCompatActivity) getActivity()).getSupportActionBar().setTitle(R.string.search_label);
- viewCreated = true;
- if (itemsLoaded) {
- onFragmentLoaded();
- }
+
+ searchAdapter = new SearchlistAdapter(getActivity(), itemAccess);
+ setListAdapter(searchAdapter);
}
@Override
@@ -141,28 +123,26 @@ public class SearchFragment extends ListFragment {
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
- if (itemsLoaded) {
- MenuItem item = menu.add(Menu.NONE, R.id.search_item, Menu.NONE, R.string.search_label);
- MenuItemCompat.setShowAsAction(item, MenuItemCompat.SHOW_AS_ACTION_IF_ROOM);
- final SearchView sv = new SearchView(getActivity());
- sv.setQueryHint(getString(R.string.search_hint));
- sv.setQuery(getArguments().getString(ARG_QUERY), false);
- sv.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
- @Override
- public boolean onQueryTextSubmit(String s) {
- getArguments().putString(ARG_QUERY, s);
- itemsLoaded = false;
- search();
- return true;
- }
-
- @Override
- public boolean onQueryTextChange(String s) {
- return false;
- }
- });
- MenuItemCompat.setActionView(item, sv);
- }
+ MenuItem item = menu.add(Menu.NONE, R.id.search_item, Menu.NONE, R.string.search_label);
+ MenuItemCompat.setShowAsAction(item, MenuItemCompat.SHOW_AS_ACTION_IF_ROOM);
+ final SearchView sv = new SearchView(getActivity());
+ sv.setQueryHint(getString(R.string.search_hint));
+ sv.setQuery(getArguments().getString(ARG_QUERY), false);
+ sv.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
+ @Override
+ public boolean onQueryTextSubmit(String s) {
+ sv.clearFocus();
+ getArguments().putString(ARG_QUERY, s);
+ search();
+ return true;
+ }
+
+ @Override
+ public boolean onQueryTextChange(String s) {
+ return false;
+ }
+ });
+ MenuItemCompat.setActionView(item, sv);
}
private final EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
@@ -175,14 +155,9 @@ public class SearchFragment extends ListFragment {
}
};
- private void onFragmentLoaded() {
- if (searchAdapter == null) {
- searchAdapter = new SearchlistAdapter(getActivity(), itemAccess);
- setListAdapter(searchAdapter);
- }
+ private void onSearchResults(List<SearchResult> results) {
+ searchResults = results;
searchAdapter.notifyDataSetChanged();
- setListShown(true);
-
String query = getArguments().getString(ARG_QUERY);
setEmptyText(getString(R.string.no_results_for_query, query));
}
@@ -190,12 +165,12 @@ public class SearchFragment extends ListFragment {
private final SearchlistAdapter.ItemAccess itemAccess = new SearchlistAdapter.ItemAccess() {
@Override
public int getCount() {
- return (searchResults != null) ? searchResults.size() : 0;
+ return searchResults.size();
}
@Override
public SearchResult getItem(int position) {
- if (searchResults != null && 0 <= position && position < searchResults.size()) {
+ if (0 <= position && position < searchResults.size()) {
return searchResults.get(position);
} else {
return null;
@@ -203,28 +178,17 @@ public class SearchFragment extends ListFragment {
}
};
-
private void search() {
if(disposable != null) {
disposable.dispose();
}
- if (viewCreated && !itemsLoaded) {
- setListShown(false);
- }
disposable = Observable.fromCallable(this::performSearch)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
- .subscribe(result -> {
- if (result != null) {
- itemsLoaded = true;
- searchResults = result;
- if (viewCreated) {
- onFragmentLoaded();
- }
- }
- }, error -> Log.e(TAG, Log.getStackTraceString(error)));
+ .subscribe(this::onSearchResults, error -> Log.e(TAG, Log.getStackTraceString(error)));
}
+ @NonNull
private List<SearchResult> performSearch() {
Bundle args = getArguments();
String query = args.getString(ARG_QUERY);
@@ -232,5 +196,4 @@ public class SearchFragment extends ListFragment {
Context context = getActivity();
return FeedSearcher.performSearch(context, query, feed);
}
-
}
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 5f09be8ce..15c6052a9 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/SubscriptionFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/SubscriptionFragment.java
@@ -1,11 +1,16 @@
package de.danoeh.antennapod.fragment;
+import android.annotation.SuppressLint;
+import android.content.Context;
import android.content.DialogInterface;
+import android.content.SharedPreferences;
import android.os.Bundle;
+import android.support.annotation.StringRes;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.ContextMenu;
import android.view.LayoutInflater;
+import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
@@ -13,6 +18,8 @@ import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.GridView;
+import java.util.concurrent.Callable;
+
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.adapter.SubscriptionsAdapter;
@@ -41,6 +48,8 @@ public class SubscriptionFragment extends Fragment {
private static final int EVENTS = EventDistributor.FEED_LIST_UPDATE
| EventDistributor.UNREAD_ITEMS_UPDATE;
+ private static final String PREFS = "SubscriptionFragment";
+ private static final String PREF_NUM_COLUMNS = "columns";
private GridView subscriptionGridLayout;
private DBReader.NavDrawerData navDrawerData;
@@ -49,19 +58,15 @@ public class SubscriptionFragment extends Fragment {
private int mPosition = -1;
private Disposable disposable;
-
- public SubscriptionFragment() {
- }
+ private SharedPreferences prefs;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
-
- // So, we certainly *don't* have an options menu,
- // but unless we say we do, old options menus sometimes
- // persist. mfietz thinks this causes the ActionBar to be invalidated
setHasOptionsMenu(true);
+
+ prefs = requireActivity().getSharedPreferences(PREFS, Context.MODE_PRIVATE);
}
@Override
@@ -69,31 +74,75 @@ public class SubscriptionFragment extends Fragment {
Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.fragment_subscriptions, container, false);
subscriptionGridLayout = root.findViewById(R.id.subscriptions_grid);
+ subscriptionGridLayout.setNumColumns(prefs.getInt(PREF_NUM_COLUMNS, 3));
registerForContextMenu(subscriptionGridLayout);
return root;
}
@Override
+ public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+ super.onCreateOptionsMenu(menu, inflater);
+ inflater.inflate(R.menu.subscriptions, menu);
+
+ int columns = prefs.getInt(PREF_NUM_COLUMNS, 3);
+ menu.findItem(R.id.subscription_num_columns_2).setChecked(columns == 2);
+ 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);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if (super.onOptionsItemSelected(item)) {
+ return true;
+ }
+ switch (item.getItemId()) {
+ case R.id.subscription_num_columns_2:
+ setColumnNumber(2);
+ return true;
+ case R.id.subscription_num_columns_3:
+ setColumnNumber(3);
+ return true;
+ case R.id.subscription_num_columns_4:
+ setColumnNumber(4);
+ return true;
+ case R.id.subscription_num_columns_5:
+ setColumnNumber(5);
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ private void setColumnNumber(int columns) {
+ subscriptionGridLayout.setNumColumns(columns);
+ prefs.edit().putInt(PREF_NUM_COLUMNS, columns).apply();
+ getActivity().invalidateOptionsMenu();
+ }
+
+ @Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
subscriptionAdapter = new SubscriptionsAdapter((MainActivity)getActivity(), itemAccess);
-
subscriptionGridLayout.setAdapter(subscriptionAdapter);
-
- loadSubscriptions();
-
subscriptionGridLayout.setOnItemClickListener(subscriptionAdapter);
if (getActivity() instanceof MainActivity) {
((MainActivity) getActivity()).getSupportActionBar().setTitle(R.string.subscriptions_label);
}
+ }
+ @Override
+ public void onStart() {
+ super.onStart();
EventDistributor.getInstance().register(contentUpdate);
+ loadSubscriptions();
}
@Override
- public void onDestroy() {
- super.onDestroy();
+ public void onStop() {
+ super.onStop();
+ EventDistributor.getInstance().unregister(contentUpdate);
if(disposable != null) {
disposable.dispose();
}
@@ -126,7 +175,7 @@ public class SubscriptionFragment extends Fragment {
Feed feed = (Feed)selectedObject;
- MenuInflater inflater = getActivity().getMenuInflater();
+ MenuInflater inflater = requireActivity().getMenuInflater();
inflater.inflate(R.menu.nav_feed_context, menu);
menu.setHeaderTitle(feed.getTitle());
@@ -136,7 +185,6 @@ public class SubscriptionFragment extends Fragment {
@Override
public boolean onContextItemSelected(MenuItem item) {
-
final int position = mPosition;
mPosition = -1; // reset
if(position < 0) {
@@ -151,84 +199,73 @@ public class SubscriptionFragment extends Fragment {
Feed feed = (Feed)selectedObject;
switch(item.getItemId()) {
- case R.id.mark_all_seen_item:
- ConfirmationDialog markAllSeenConfirmationDialog = new ConfirmationDialog(getActivity(),
- R.string.mark_all_seen_label,
- R.string.mark_all_seen_confirmation_msg) {
-
- @Override
- public void onConfirmButtonPressed(DialogInterface dialog) {
- dialog.dismiss();
-
- Observable.fromCallable(() -> DBWriter.markFeedSeen(feed.getId()))
- .subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe(result -> loadSubscriptions(),
- error -> Log.e(TAG, Log.getStackTraceString(error)));
- }
- };
- markAllSeenConfirmationDialog.createNewDialog().show();
+ case R.id.remove_all_new_flags_item:
+ displayConfirmationDialog(
+ R.string.remove_all_new_flags_label,
+ R.string.remove_all_new_flags_confirmation_msg,
+ () -> DBWriter.removeFeedNewFlag(feed.getId()));
return true;
case R.id.mark_all_read_item:
- ConfirmationDialog markAllReadConfirmationDialog = new ConfirmationDialog(getActivity(),
+ displayConfirmationDialog(
R.string.mark_all_read_label,
- R.string.mark_all_read_confirmation_msg) {
-
- @Override
- public void onConfirmButtonPressed(DialogInterface dialog) {
- dialog.dismiss();
- Observable.fromCallable(() -> DBWriter.markFeedRead(feed.getId()))
- .subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe(result -> loadSubscriptions(),
- error -> Log.e(TAG, Log.getStackTraceString(error)));
- }
- };
- markAllReadConfirmationDialog.createNewDialog().show();
+ R.string.mark_all_read_confirmation_msg,
+ () -> DBWriter.markFeedRead(feed.getId()));
return true;
case R.id.rename_item:
new RenameFeedDialog(getActivity(), feed).show();
return true;
case R.id.remove_item:
- final FeedRemover remover = new FeedRemover(getContext(), feed) {
- @Override
- protected void onPostExecute(Void result) {
- super.onPostExecute(result);
- loadSubscriptions();
- }
- };
- ConfirmationDialog conDialog = new ConfirmationDialog(getContext(),
- R.string.remove_feed_label,
- getString(R.string.feed_delete_confirmation_msg, feed.getTitle())) {
- @Override
- public void onConfirmButtonPressed(
- DialogInterface dialog) {
- dialog.dismiss();
- long mediaId = PlaybackPreferences.getCurrentlyPlayingFeedMediaId();
- if (mediaId > 0 &&
- FeedItemUtil.indexOfItemWithMediaId(feed.getItems(), mediaId) >= 0) {
- Log.d(TAG, "Currently playing episode is about to be deleted, skipping");
- remover.skipOnCompletion = true;
- int playerStatus = PlaybackPreferences.getCurrentPlayerStatus();
- if(playerStatus == PlaybackPreferences.PLAYER_STATUS_PLAYING) {
- IntentUtils.sendLocalBroadcast(getContext(), PlaybackService.ACTION_PAUSE_PLAY_CURRENT_EPISODE);
-
- }
- }
- remover.executeAsync();
- }
- };
- conDialog.createNewDialog().show();
+ displayRemoveFeedDialog(feed);
return true;
default:
return super.onContextItemSelected(item);
}
}
- @Override
- public void onResume() {
- super.onResume();
- loadSubscriptions();
+ private void displayRemoveFeedDialog(Feed feed) {
+ final FeedRemover remover = new FeedRemover(getContext(), feed) {
+ @Override
+ protected void onPostExecute(Void result) {
+ super.onPostExecute(result);
+ loadSubscriptions();
+ }
+ };
+
+ String message = getString(R.string.feed_delete_confirmation_msg, feed.getTitle());
+ ConfirmationDialog dialog = new ConfirmationDialog(getContext(), R.string.remove_feed_label, message) {
+ @Override
+ public void onConfirmButtonPressed(DialogInterface clickedDialog) {
+ clickedDialog.dismiss();
+ long mediaId = PlaybackPreferences.getCurrentlyPlayingFeedMediaId();
+ if (mediaId > 0 && FeedItemUtil.indexOfItemWithMediaId(feed.getItems(), mediaId) >= 0) {
+ Log.d(TAG, "Currently playing episode is about to be deleted, skipping");
+ remover.skipOnCompletion = true;
+ int playerStatus = PlaybackPreferences.getCurrentPlayerStatus();
+ if(playerStatus == PlaybackPreferences.PLAYER_STATUS_PLAYING) {
+ IntentUtils.sendLocalBroadcast(getContext(), PlaybackService.ACTION_PAUSE_PLAY_CURRENT_EPISODE);
+
+ }
+ }
+ remover.executeAsync();
+ }
+ };
+ dialog.createNewDialog().show();
+ }
+
+ private <T> void displayConfirmationDialog(@StringRes int title, @StringRes int message, Callable<? extends T> task) {
+ ConfirmationDialog dialog = new ConfirmationDialog(getActivity(), title, message) {
+ @Override
+ @SuppressLint("CheckResult")
+ public void onConfirmButtonPressed(DialogInterface clickedDialog) {
+ clickedDialog.dismiss();
+ Observable.fromCallable(task)
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(result -> loadSubscriptions(),
+ error -> Log.e(TAG, Log.getStackTraceString(error)));
+ }
+ };
+ dialog.createNewDialog().show();
}
private final EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AutoDownloadPreferencesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AutoDownloadPreferencesFragment.java
new file mode 100644
index 000000000..6cba798ba
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AutoDownloadPreferencesFragment.java
@@ -0,0 +1,184 @@
+package de.danoeh.antennapod.fragment.preferences;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.res.Resources;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiManager;
+import android.os.Bundle;
+import android.support.v7.preference.CheckBoxPreference;
+import android.support.v7.preference.ListPreference;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceFragmentCompat;
+import android.support.v7.preference.PreferenceScreen;
+import android.util.Log;
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.core.preferences.UserPreferences;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+public class AutoDownloadPreferencesFragment extends PreferenceFragmentCompat {
+ private static final String TAG = "AutoDnldPrefFragment";
+ private CheckBoxPreference[] selectedNetworks;
+
+ @Override
+ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
+ addPreferencesFromResource(R.xml.preferences_autodownload);
+
+ setupAutoDownloadScreen();
+ buildAutodownloadSelectedNetworksPreference();
+ setSelectedNetworksEnabled(UserPreferences.isEnableAutodownloadWifiFilter());
+ buildEpisodeCleanupPreference();
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ checkAutodownloadItemVisibility(UserPreferences.isEnableAutodownload());
+ }
+
+ private void setupAutoDownloadScreen() {
+ findPreference(UserPreferences.PREF_ENABLE_AUTODL).setOnPreferenceChangeListener(
+ (preference, newValue) -> {
+ if (newValue instanceof Boolean) {
+ checkAutodownloadItemVisibility((Boolean) newValue);
+ }
+ return true;
+ });
+ findPreference(UserPreferences.PREF_ENABLE_AUTODL_WIFI_FILTER)
+ .setOnPreferenceChangeListener(
+ (preference, newValue) -> {
+ if (newValue instanceof Boolean) {
+ setSelectedNetworksEnabled((Boolean) newValue);
+ return true;
+ } else {
+ return false;
+ }
+ }
+ );
+ }
+
+ private void checkAutodownloadItemVisibility(boolean autoDownload) {
+ findPreference(UserPreferences.PREF_EPISODE_CACHE_SIZE).setEnabled(autoDownload);
+ findPreference(UserPreferences.PREF_ENABLE_AUTODL_ON_BATTERY).setEnabled(autoDownload);
+ findPreference(UserPreferences.PREF_ENABLE_AUTODL_WIFI_FILTER).setEnabled(autoDownload);
+ findPreference(UserPreferences.PREF_EPISODE_CLEANUP).setEnabled(autoDownload);
+ setSelectedNetworksEnabled(autoDownload && UserPreferences.isEnableAutodownloadWifiFilter());
+ }
+
+ private static String blankIfNull(String val) {
+ return val == null ? "" : val;
+ }
+
+ private void buildAutodownloadSelectedNetworksPreference() {
+ final Activity activity = getActivity();
+
+ if (selectedNetworks != null) {
+ clearAutodownloadSelectedNetworsPreference();
+ }
+ // get configured networks
+ WifiManager wifiservice = (WifiManager) activity.getApplicationContext().getSystemService(Context.WIFI_SERVICE);
+ List<WifiConfiguration> networks = wifiservice.getConfiguredNetworks();
+
+ if (networks == null) {
+ Log.e(TAG, "Couldn't get list of configure Wi-Fi networks");
+ return;
+ }
+ Collections.sort(networks, (x, y) ->
+ blankIfNull(x.SSID).compareTo(blankIfNull(y.SSID)));
+ selectedNetworks = new CheckBoxPreference[networks.size()];
+ List<String> prefValues = Arrays.asList(UserPreferences
+ .getAutodownloadSelectedNetworks());
+ PreferenceScreen prefScreen = getPreferenceScreen();
+ Preference.OnPreferenceClickListener clickListener = preference -> {
+ if (preference instanceof CheckBoxPreference) {
+ String key = preference.getKey();
+ List<String> prefValuesList = new ArrayList<>(
+ Arrays.asList(UserPreferences
+ .getAutodownloadSelectedNetworks())
+ );
+ boolean newValue = ((CheckBoxPreference) preference)
+ .isChecked();
+ Log.d(TAG, "Selected network " + key + ". New state: " + newValue);
+
+ int index = prefValuesList.indexOf(key);
+ if (index >= 0 && !newValue) {
+ // remove network
+ prefValuesList.remove(index);
+ } else if (index < 0 && newValue) {
+ prefValuesList.add(key);
+ }
+
+ UserPreferences.setAutodownloadSelectedNetworks(
+ prefValuesList.toArray(new String[prefValuesList.size()])
+ );
+ return true;
+ } else {
+ return false;
+ }
+ };
+ // create preference for each known network. attach listener and set
+ // value
+ for (int i = 0; i < networks.size(); i++) {
+ WifiConfiguration config = networks.get(i);
+
+ CheckBoxPreference pref = new CheckBoxPreference(activity);
+ String key = Integer.toString(config.networkId);
+ pref.setTitle(config.SSID);
+ pref.setKey(key);
+ pref.setOnPreferenceClickListener(clickListener);
+ pref.setPersistent(false);
+ pref.setChecked(prefValues.contains(key));
+ selectedNetworks[i] = pref;
+ prefScreen.addPreference(pref);
+ }
+ }
+
+ private void clearAutodownloadSelectedNetworsPreference() {
+ if (selectedNetworks != null) {
+ PreferenceScreen prefScreen = getPreferenceScreen();
+
+ for (CheckBoxPreference network : selectedNetworks) {
+ if (network != null) {
+ prefScreen.removePreference(network);
+ }
+ }
+ }
+ }
+
+ private void buildEpisodeCleanupPreference() {
+ final Resources res = getActivity().getResources();
+
+ ListPreference pref = (ListPreference) findPreference(UserPreferences.PREF_EPISODE_CLEANUP);
+ String[] values = res.getStringArray(
+ R.array.episode_cleanup_values);
+ String[] entries = new String[values.length];
+ for (int x = 0; x < values.length; x++) {
+ int v = Integer.parseInt(values[x]);
+ if (v == UserPreferences.EPISODE_CLEANUP_QUEUE) {
+ entries[x] = res.getString(R.string.episode_cleanup_queue_removal);
+ } else if (v == UserPreferences.EPISODE_CLEANUP_NULL){
+ entries[x] = res.getString(R.string.episode_cleanup_never);
+ } else if (v == 0) {
+ entries[x] = res.getString(R.string.episode_cleanup_after_listening);
+ } else if (v > 0 && v < 24) {
+ entries[x] = res.getQuantityString(R.plurals.episode_cleanup_hours_after_listening, v, v);
+ } else {
+ int numDays = v / 24; // assume underlying value will be NOT fraction of days, e.g., 36 (hours)
+ entries[x] = res.getQuantityString(R.plurals.episode_cleanup_days_after_listening, numDays, numDays);
+ }
+ }
+ pref.setEntries(entries);
+ }
+
+ private void setSelectedNetworksEnabled(boolean b) {
+ if (selectedNetworks != null) {
+ for (Preference p : selectedNetworks) {
+ p.setEnabled(b);
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/GpodderPreferencesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/GpodderPreferencesFragment.java
new file mode 100644
index 000000000..491922056
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/GpodderPreferencesFragment.java
@@ -0,0 +1,144 @@
+package de.danoeh.antennapod.fragment.preferences;
+
+import android.app.Activity;
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceFragmentCompat;
+import android.text.Html;
+import android.text.format.DateUtils;
+import android.widget.Toast;
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.core.preferences.GpodnetPreferences;
+import de.danoeh.antennapod.core.service.GpodnetSyncService;
+import de.danoeh.antennapod.dialog.AuthenticationDialog;
+import de.danoeh.antennapod.dialog.GpodnetSetHostnameDialog;
+
+public class GpodderPreferencesFragment extends PreferenceFragmentCompat {
+ private static final String PREF_GPODNET_LOGIN = "pref_gpodnet_authenticate";
+ private static final String PREF_GPODNET_SETLOGIN_INFORMATION = "pref_gpodnet_setlogin_information";
+ private static final String PREF_GPODNET_SYNC = "pref_gpodnet_sync";
+ private static final String PREF_GPODNET_FORCE_FULL_SYNC = "pref_gpodnet_force_full_sync";
+ private static final String PREF_GPODNET_LOGOUT = "pref_gpodnet_logout";
+ private static final String PREF_GPODNET_HOSTNAME = "pref_gpodnet_hostname";
+ private static final String PREF_GPODNET_NOTIFICATIONS = "pref_gpodnet_notifications";
+
+ @Override
+ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
+ addPreferencesFromResource(R.xml.preferences_gpodder);
+ setupGpodderScreen();
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ GpodnetPreferences.registerOnSharedPreferenceChangeListener(gpoddernetListener);
+ updateGpodnetPreferenceScreen();
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ GpodnetPreferences.unregisterOnSharedPreferenceChangeListener(gpoddernetListener);
+ }
+
+ private final SharedPreferences.OnSharedPreferenceChangeListener gpoddernetListener =
+ (sharedPreferences, key) -> {
+ if (GpodnetPreferences.PREF_LAST_SYNC_ATTEMPT_TIMESTAMP.equals(key)) {
+ updateLastGpodnetSyncReport(GpodnetPreferences.getLastSyncAttemptResult(),
+ GpodnetPreferences.getLastSyncAttemptTimestamp());
+ }
+ };
+
+ private void setupGpodderScreen() {
+ final Activity activity = getActivity();
+
+ findPreference(PREF_GPODNET_SETLOGIN_INFORMATION)
+ .setOnPreferenceClickListener(preference -> {
+ AuthenticationDialog dialog = new AuthenticationDialog(activity,
+ R.string.pref_gpodnet_setlogin_information_title, false, false, GpodnetPreferences.getUsername(),
+ null) {
+
+ @Override
+ protected void onConfirmed(String username, String password, boolean saveUsernamePassword) {
+ GpodnetPreferences.setPassword(password);
+ }
+ };
+ dialog.show();
+ return true;
+ });
+ findPreference(PREF_GPODNET_SYNC).
+ setOnPreferenceClickListener(preference -> {
+ GpodnetSyncService.sendSyncIntent(getActivity().getApplicationContext());
+ Toast toast = Toast.makeText(getActivity(), R.string.pref_gpodnet_sync_started,
+ Toast.LENGTH_SHORT);
+ toast.show();
+ return true;
+ });
+ findPreference(PREF_GPODNET_FORCE_FULL_SYNC).
+ setOnPreferenceClickListener(preference -> {
+ GpodnetPreferences.setLastSubscriptionSyncTimestamp(0L);
+ GpodnetPreferences.setLastEpisodeActionsSyncTimestamp(0L);
+ GpodnetPreferences.setLastSyncAttempt(false, 0);
+ updateLastGpodnetSyncReport(false, 0);
+ GpodnetSyncService.sendSyncIntent(getActivity().getApplicationContext());
+ Toast toast = Toast.makeText(getActivity(), R.string.pref_gpodnet_sync_started,
+ Toast.LENGTH_SHORT);
+ toast.show();
+ return true;
+ });
+ findPreference(PREF_GPODNET_LOGOUT).setOnPreferenceClickListener(
+ preference -> {
+ GpodnetPreferences.logout();
+ Toast toast = Toast.makeText(activity, R.string.pref_gpodnet_logout_toast, Toast.LENGTH_SHORT);
+ toast.show();
+ updateGpodnetPreferenceScreen();
+ return true;
+ });
+ findPreference(PREF_GPODNET_HOSTNAME).setOnPreferenceClickListener(
+ preference -> {
+ GpodnetSetHostnameDialog.createDialog(activity).setOnDismissListener(dialog -> updateGpodnetPreferenceScreen());
+ return true;
+ });
+ }
+
+ private void updateGpodnetPreferenceScreen() {
+ final boolean loggedIn = GpodnetPreferences.loggedIn();
+ findPreference(PREF_GPODNET_LOGIN).setEnabled(!loggedIn);
+ findPreference(PREF_GPODNET_SETLOGIN_INFORMATION).setEnabled(loggedIn);
+ findPreference(PREF_GPODNET_SYNC).setEnabled(loggedIn);
+ findPreference(PREF_GPODNET_FORCE_FULL_SYNC).setEnabled(loggedIn);
+ findPreference(PREF_GPODNET_LOGOUT).setEnabled(loggedIn);
+ findPreference(PREF_GPODNET_NOTIFICATIONS).setEnabled(loggedIn);
+ if(loggedIn) {
+ String format = getActivity().getString(R.string.pref_gpodnet_login_status);
+ String summary = String.format(format, GpodnetPreferences.getUsername(),
+ GpodnetPreferences.getDeviceID());
+ findPreference(PREF_GPODNET_LOGOUT).setSummary(Html.fromHtml(summary));
+ updateLastGpodnetSyncReport(GpodnetPreferences.getLastSyncAttemptResult(),
+ GpodnetPreferences.getLastSyncAttemptTimestamp());
+ } else {
+ findPreference(PREF_GPODNET_LOGOUT).setSummary(null);
+ updateLastGpodnetSyncReport(false, 0);
+ }
+ findPreference(PREF_GPODNET_HOSTNAME).setSummary(GpodnetPreferences.getHostname());
+ }
+
+ private void updateLastGpodnetSyncReport(boolean successful, long lastTime) {
+ Preference sync = findPreference(PREF_GPODNET_SYNC);
+ if (lastTime != 0) {
+ sync.setSummary(getActivity().getString(R.string.pref_gpodnet_sync_changes_sum) + "\n" +
+ getActivity().getString(R.string.pref_gpodnet_sync_sum_last_sync_line,
+ getActivity().getString(successful ?
+ R.string.gpodnetsync_pref_report_successful :
+ R.string.gpodnetsync_pref_report_failed),
+ DateUtils.getRelativeDateTimeString(getActivity(),
+ lastTime,
+ DateUtils.MINUTE_IN_MILLIS,
+ DateUtils.WEEK_IN_MILLIS,
+ DateUtils.FORMAT_SHOW_TIME)));
+ } else {
+ sync.setSummary(getActivity().getString(R.string.pref_gpodnet_sync_changes_sum));
+ }
+ }
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/IntegrationsPreferencesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/IntegrationsPreferencesFragment.java
new file mode 100644
index 000000000..229274b76
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/IntegrationsPreferencesFragment.java
@@ -0,0 +1,23 @@
+package de.danoeh.antennapod.fragment.preferences;
+
+import android.os.Bundle;
+import android.support.v7.preference.PreferenceFragmentCompat;
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.activity.PreferenceActivity;
+
+public class IntegrationsPreferencesFragment extends PreferenceFragmentCompat {
+ private static final String PREF_SCREEN_GPODDER = "prefGpodderSettings";
+
+ @Override
+ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
+ addPreferencesFromResource(R.xml.preferences_integrations);
+ setupIntegrationsScreen();
+ }
+
+ private void setupIntegrationsScreen() {
+ findPreference(PREF_SCREEN_GPODDER).setOnPreferenceClickListener(preference -> {
+ ((PreferenceActivity) getActivity()).openScreen(R.xml.preferences_gpodder);
+ return true;
+ });
+ }
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/MainPreferencesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/MainPreferencesFragment.java
new file mode 100644
index 000000000..701d21ce0
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/MainPreferencesFragment.java
@@ -0,0 +1,147 @@
+package de.danoeh.antennapod.fragment.preferences;
+
+import android.content.ActivityNotFoundException;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.support.v4.content.FileProvider;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.preference.PreferenceFragmentCompat;
+import android.util.Log;
+import android.widget.Toast;
+import com.bytehamster.lib.preferencesearch.SearchConfiguration;
+import com.bytehamster.lib.preferencesearch.SearchPreference;
+import de.danoeh.antennapod.CrashReportWriter;
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.activity.AboutActivity;
+import de.danoeh.antennapod.activity.PreferenceActivity;
+import de.danoeh.antennapod.activity.StatisticsActivity;
+
+import java.util.List;
+
+public class MainPreferencesFragment extends PreferenceFragmentCompat {
+ private static final String TAG = "MainPreferencesFragment";
+
+ private static final String PREF_SCREEN_USER_INTERFACE = "prefScreenInterface";
+ private static final String PREF_SCREEN_PLAYBACK = "prefScreenPlayback";
+ private static final String PREF_SCREEN_NETWORK = "prefScreenNetwork";
+ private static final String PREF_SCREEN_INTEGRATIONS = "prefScreenIntegrations";
+ private static final String PREF_SCREEN_STORAGE = "prefScreenStorage";
+ private static final String PREF_KNOWN_ISSUES = "prefKnownIssues";
+ private static final String PREF_FAQ = "prefFaq";
+ private static final String PREF_SEND_CRASH_REPORT = "prefSendCrashReport";
+ private static final String STATISTICS = "statistics";
+ private static final String PREF_ABOUT = "prefAbout";
+
+ @Override
+ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
+ addPreferencesFromResource(R.xml.preferences);
+ setupMainScreen();
+ setupSearch();
+ }
+
+ private void setupMainScreen() {
+ findPreference(PREF_SCREEN_USER_INTERFACE).setOnPreferenceClickListener(preference -> {
+ ((PreferenceActivity) getActivity()).openScreen(R.xml.preferences_user_interface);
+ return true;
+ });
+ findPreference(PREF_SCREEN_PLAYBACK).setOnPreferenceClickListener(preference -> {
+ ((PreferenceActivity) getActivity()).openScreen(R.xml.preferences_playback);
+ return true;
+ });
+ findPreference(PREF_SCREEN_NETWORK).setOnPreferenceClickListener(preference -> {
+ ((PreferenceActivity) getActivity()).openScreen(R.xml.preferences_network);
+ return true;
+ });
+ findPreference(PREF_SCREEN_INTEGRATIONS).setOnPreferenceClickListener(preference -> {
+ ((PreferenceActivity) getActivity()).openScreen(R.xml.preferences_integrations);
+ return true;
+ });
+ findPreference(PREF_SCREEN_STORAGE).setOnPreferenceClickListener(preference -> {
+ ((PreferenceActivity) getActivity()).openScreen(R.xml.preferences_storage);
+ return true;
+ });
+
+ findPreference(PREF_ABOUT).setOnPreferenceClickListener(
+ preference -> {
+ startActivity(new Intent(getActivity(), AboutActivity.class));
+ return true;
+ }
+ );
+ findPreference(STATISTICS).setOnPreferenceClickListener(
+ preference -> {
+ startActivity(new Intent(getActivity(), StatisticsActivity.class));
+ return true;
+ }
+ );
+ findPreference(PREF_KNOWN_ISSUES).setOnPreferenceClickListener(preference -> {
+ openInBrowser("https://github.com/AntennaPod/AntennaPod/labels/bug");
+ return true;
+ });
+ findPreference(PREF_FAQ).setOnPreferenceClickListener(preference -> {
+ openInBrowser("http://antennapod.org/faq.html");
+ return true;
+ });
+ findPreference(PREF_SEND_CRASH_REPORT).setOnPreferenceClickListener(preference -> {
+ Context context = getActivity().getApplicationContext();
+ Intent emailIntent = new Intent(Intent.ACTION_SEND);
+ emailIntent.setType("text/plain");
+ emailIntent.putExtra(Intent.EXTRA_EMAIL, new String[]{"Martin.Fietz@gmail.com"});
+ emailIntent.putExtra(Intent.EXTRA_SUBJECT, "AntennaPod Crash Report");
+ emailIntent.putExtra(Intent.EXTRA_TEXT, "Please describe what you were doing when the app crashed");
+ // the attachment
+ Uri fileUri = FileProvider.getUriForFile(context, context.getString(R.string.provider_authority),
+ CrashReportWriter.getFile());
+ emailIntent.putExtra(Intent.EXTRA_STREAM, fileUri);
+ emailIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ String intentTitle = getActivity().getString(R.string.send_email);
+ if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) {
+ List<ResolveInfo> resInfoList = context.getPackageManager().queryIntentActivities(emailIntent, PackageManager.MATCH_DEFAULT_ONLY);
+ for (ResolveInfo resolveInfo : resInfoList) {
+ String packageName = resolveInfo.activityInfo.packageName;
+ context.grantUriPermission(packageName, fileUri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ }
+ }
+ getActivity().startActivity(Intent.createChooser(emailIntent, intentTitle));
+ return true;
+ });
+ }
+
+ private void openInBrowser(String url) {
+ try {
+ Intent myIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
+ startActivity(myIntent);
+ } catch (ActivityNotFoundException e) {
+ Toast.makeText(getActivity(), R.string.pref_no_browser_found, Toast.LENGTH_LONG).show();
+ Log.e(TAG, Log.getStackTraceString(e));
+ }
+ }
+
+ private void setupSearch() {
+ SearchPreference searchPreference = (SearchPreference) findPreference("searchPreference");
+ SearchConfiguration config = searchPreference.getSearchConfiguration();
+ config.setActivity((AppCompatActivity) getActivity());
+ config.setFragmentContainerViewId(R.id.content);
+ config.setBreadcrumbsEnabled(true);
+
+ config.index(R.xml.preferences_user_interface)
+ .addBreadcrumb(PreferenceActivity.getTitleOfPage(R.xml.preferences_user_interface));
+ config.index(R.xml.preferences_playback)
+ .addBreadcrumb(PreferenceActivity.getTitleOfPage(R.xml.preferences_playback));
+ config.index(R.xml.preferences_network)
+ .addBreadcrumb(PreferenceActivity.getTitleOfPage(R.xml.preferences_network));
+ config.index(R.xml.preferences_storage)
+ .addBreadcrumb(PreferenceActivity.getTitleOfPage(R.xml.preferences_storage));
+ config.index(R.xml.preferences_autodownload)
+ .addBreadcrumb(PreferenceActivity.getTitleOfPage(R.xml.preferences_network))
+ .addBreadcrumb(R.string.automation)
+ .addBreadcrumb(PreferenceActivity.getTitleOfPage(R.xml.preferences_autodownload));
+ config.index(R.xml.preferences_gpodder)
+ .addBreadcrumb(PreferenceActivity.getTitleOfPage(R.xml.preferences_integrations))
+ .addBreadcrumb(PreferenceActivity.getTitleOfPage(R.xml.preferences_gpodder));
+ }
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/NetworkPreferencesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/NetworkPreferencesFragment.java
new file mode 100644
index 000000000..ac2436e25
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/NetworkPreferencesFragment.java
@@ -0,0 +1,175 @@
+package de.danoeh.antennapod.fragment.preferences;
+
+import android.app.TimePickerDialog;
+import android.content.Context;
+import android.content.res.Resources;
+import android.os.Bundle;
+import android.support.v7.app.AlertDialog;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceFragmentCompat;
+import android.text.format.DateFormat;
+import com.afollestad.materialdialogs.MaterialDialog;
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.activity.PreferenceActivity;
+import de.danoeh.antennapod.core.preferences.UserPreferences;
+import de.danoeh.antennapod.dialog.ProxyDialog;
+import org.apache.commons.lang3.ArrayUtils;
+
+import java.util.Calendar;
+import java.util.GregorianCalendar;
+import java.util.concurrent.TimeUnit;
+
+public class NetworkPreferencesFragment extends PreferenceFragmentCompat {
+ private static final String PREF_SCREEN_AUTODL = "prefAutoDownloadSettings";
+ private static final String PREF_PROXY = "prefProxy";
+
+ @Override
+ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
+ addPreferencesFromResource(R.xml.preferences_network);
+ setupNetworkScreen();
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ setUpdateIntervalText();
+ setParallelDownloadsText(UserPreferences.getParallelDownloads());
+ }
+
+ private void setupNetworkScreen() {
+ findPreference(PREF_SCREEN_AUTODL).setOnPreferenceClickListener(preference -> {
+ ((PreferenceActivity) getActivity()).openScreen(R.xml.preferences_autodownload);
+ return true;
+ });
+ findPreference(UserPreferences.PREF_UPDATE_INTERVAL)
+ .setOnPreferenceClickListener(preference -> {
+ showUpdateIntervalTimePreferencesDialog();
+ return true;
+ });
+ findPreference(UserPreferences.PREF_PARALLEL_DOWNLOADS)
+ .setOnPreferenceChangeListener(
+ (preference, o) -> {
+ if (o instanceof Integer) {
+ setParallelDownloadsText((Integer) o);
+ }
+ return true;
+ }
+ );
+ // validate and set correct value: number of downloads between 1 and 50 (inclusive)
+ findPreference(PREF_PROXY).setOnPreferenceClickListener(preference -> {
+ ProxyDialog dialog = new ProxyDialog(getActivity());
+ dialog.createDialog().show();
+ return true;
+ });
+ }
+
+ private void setUpdateIntervalText() {
+ Context context = getActivity().getApplicationContext();
+ String val;
+ long interval = UserPreferences.getUpdateInterval();
+ if(interval > 0) {
+ int hours = (int) TimeUnit.MILLISECONDS.toHours(interval);
+ String hoursStr = context.getResources().getQuantityString(R.plurals.time_hours_quantified, hours, hours);
+ val = String.format(context.getString(R.string.pref_autoUpdateIntervallOrTime_every), hoursStr);
+ } else {
+ int[] timeOfDay = UserPreferences.getUpdateTimeOfDay();
+ if(timeOfDay.length == 2) {
+ Calendar cal = new GregorianCalendar();
+ cal.set(Calendar.HOUR_OF_DAY, timeOfDay[0]);
+ cal.set(Calendar.MINUTE, timeOfDay[1]);
+ String timeOfDayStr = DateFormat.getTimeFormat(context).format(cal.getTime());
+ val = String.format(context.getString(R.string.pref_autoUpdateIntervallOrTime_at),
+ timeOfDayStr);
+ } else {
+ val = context.getString(R.string.pref_smart_mark_as_played_disabled); // TODO: Is this a bug? Otherwise document why is this related to smart mark???
+ }
+ }
+ String summary = context.getString(R.string.pref_autoUpdateIntervallOrTime_sum) + "\n"
+ + String.format(context.getString(R.string.pref_current_value), val);
+ findPreference(UserPreferences.PREF_UPDATE_INTERVAL).setSummary(summary);
+ }
+
+ private void setParallelDownloadsText(int downloads) {
+ final Resources res = getActivity().getResources();
+
+ String s = Integer.toString(downloads)
+ + res.getString(R.string.parallel_downloads_suffix);
+ findPreference(UserPreferences.PREF_PARALLEL_DOWNLOADS).setSummary(s);
+ }
+
+ private void showUpdateIntervalTimePreferencesDialog() {
+ final Context context = getActivity();
+
+ MaterialDialog.Builder builder = new MaterialDialog.Builder(context);
+ builder.title(R.string.pref_autoUpdateIntervallOrTime_title);
+ builder.content(R.string.pref_autoUpdateIntervallOrTime_message);
+ builder.positiveText(R.string.pref_autoUpdateIntervallOrTime_Interval);
+ builder.negativeText(R.string.pref_autoUpdateIntervallOrTime_TimeOfDay);
+ builder.neutralText(R.string.pref_autoUpdateIntervallOrTime_Disable);
+ builder.onPositive((dialog, which) -> {
+ AlertDialog.Builder builder1 = new AlertDialog.Builder(context);
+ builder1.setTitle(context.getString(R.string.pref_autoUpdateIntervallOrTime_Interval));
+ final String[] values = context.getResources().getStringArray(R.array.update_intervall_values);
+ final String[] entries = getUpdateIntervalEntries(values);
+ long currInterval = UserPreferences.getUpdateInterval();
+ int checkedItem = -1;
+ if(currInterval > 0) {
+ String currIntervalStr = String.valueOf(TimeUnit.MILLISECONDS.toHours(currInterval));
+ checkedItem = ArrayUtils.indexOf(values, currIntervalStr);
+ }
+ builder1.setSingleChoiceItems(entries, checkedItem, (dialog1, which1) -> {
+ int hours = Integer.parseInt(values[which1]);
+ UserPreferences.setUpdateInterval(hours);
+ dialog1.dismiss();
+ setUpdateIntervalText();
+ });
+ builder1.setNegativeButton(context.getString(R.string.cancel_label), null);
+ builder1.show();
+ });
+ builder.onNegative((dialog, which) -> {
+ int hourOfDay = 7, minute = 0;
+ int[] updateTime = UserPreferences.getUpdateTimeOfDay();
+ if (updateTime.length == 2) {
+ hourOfDay = updateTime[0];
+ minute = updateTime[1];
+ }
+ TimePickerDialog timePickerDialog = new TimePickerDialog(context,
+ (view, selectedHourOfDay, selectedMinute) -> {
+ if (view.getTag() == null) { // onTimeSet() may get called twice!
+ view.setTag("TAGGED");
+ UserPreferences.setUpdateTimeOfDay(selectedHourOfDay, selectedMinute);
+ setUpdateIntervalText();
+ }
+ }, hourOfDay, minute, DateFormat.is24HourFormat(context));
+ timePickerDialog.setTitle(context.getString(R.string.pref_autoUpdateIntervallOrTime_TimeOfDay));
+ timePickerDialog.show();
+ });
+ builder.onNeutral((dialog, which) -> {
+ UserPreferences.disableAutoUpdate();
+ setUpdateIntervalText();
+ });
+ builder.show();
+ }
+
+ private String[] getUpdateIntervalEntries(final String[] values) {
+ final Resources res = getActivity().getResources();
+ String[] entries = new String[values.length];
+ for (int x = 0; x < values.length; x++) {
+ Integer v = Integer.parseInt(values[x]);
+ switch (v) {
+ case 0:
+ entries[x] = res.getString(R.string.pref_update_interval_hours_manual);
+ break;
+ case 1:
+ entries[x] = v + " " + res.getString(R.string.pref_update_interval_hours_singular);
+ break;
+ default:
+ entries[x] = v + " " + res.getString(R.string.pref_update_interval_hours_plural);
+ break;
+
+ }
+ }
+ return entries;
+ }
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/PlaybackPreferencesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/PlaybackPreferencesFragment.java
new file mode 100644
index 000000000..e1714d4bd
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/PlaybackPreferencesFragment.java
@@ -0,0 +1,92 @@
+package de.danoeh.antennapod.fragment.preferences;
+
+import android.app.Activity;
+import android.content.res.Resources;
+import android.os.Build;
+import android.os.Bundle;
+import android.support.v7.preference.ListPreference;
+import android.support.v7.preference.PreferenceFragmentCompat;
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.activity.MediaplayerActivity;
+import de.danoeh.antennapod.core.preferences.UserPreferences;
+import de.danoeh.antennapod.core.util.gui.PictureInPictureUtil;
+import de.danoeh.antennapod.dialog.VariableSpeedDialog;
+import de.danoeh.antennapod.preferences.PreferenceControllerFlavorHelper;
+
+public class PlaybackPreferencesFragment extends PreferenceFragmentCompat {
+ private static final String PREF_PLAYBACK_SPEED_LAUNCHER = "prefPlaybackSpeedLauncher";
+ private static final String PREF_PLAYBACK_REWIND_DELTA_LAUNCHER = "prefPlaybackRewindDeltaLauncher";
+ private static final String PREF_PLAYBACK_FAST_FORWARD_DELTA_LAUNCHER = "prefPlaybackFastForwardDeltaLauncher";
+
+ @Override
+ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
+ addPreferencesFromResource(R.xml.preferences_playback);
+
+ setupPlaybackScreen();
+ PreferenceControllerFlavorHelper.setupFlavoredUI(this);
+ buildSmartMarkAsPlayedPreference();
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ checkSonicItemVisibility();
+ }
+
+ private void setupPlaybackScreen() {
+ final Activity activity = getActivity();
+
+ findPreference(PREF_PLAYBACK_SPEED_LAUNCHER)
+ .setOnPreferenceClickListener(preference -> {
+ VariableSpeedDialog.showDialog(activity);
+ return true;
+ });
+ findPreference(PREF_PLAYBACK_REWIND_DELTA_LAUNCHER)
+ .setOnPreferenceClickListener(preference -> {
+ MediaplayerActivity.showSkipPreference(activity, MediaplayerActivity.SkipDirection.SKIP_REWIND);
+ return true;
+ });
+ findPreference(PREF_PLAYBACK_FAST_FORWARD_DELTA_LAUNCHER)
+ .setOnPreferenceClickListener(preference -> {
+ MediaplayerActivity.showSkipPreference(activity, MediaplayerActivity.SkipDirection.SKIP_FORWARD);
+ return true;
+ });
+ if (!PictureInPictureUtil.supportsPictureInPicture(activity)) {
+ ListPreference behaviour = (ListPreference) findPreference(UserPreferences.PREF_VIDEO_BEHAVIOR);
+ behaviour.setEntries(R.array.video_background_behavior_options_without_pip);
+ behaviour.setEntryValues(R.array.video_background_behavior_values_without_pip);
+ }
+ }
+
+ private void buildSmartMarkAsPlayedPreference() {
+ final Resources res = getActivity().getResources();
+
+ ListPreference pref = (ListPreference) findPreference(UserPreferences.PREF_SMART_MARK_AS_PLAYED_SECS);
+ String[] values = res.getStringArray(R.array.smart_mark_as_played_values);
+ String[] entries = new String[values.length];
+ for (int x = 0; x < values.length; x++) {
+ if(x == 0) {
+ entries[x] = res.getString(R.string.pref_smart_mark_as_played_disabled);
+ } else {
+ Integer v = Integer.parseInt(values[x]);
+ if(v < 60) {
+ entries[x] = res.getQuantityString(R.plurals.time_seconds_quantified, v, v);
+ } else {
+ v /= 60;
+ entries[x] = res.getQuantityString(R.plurals.time_minutes_quantified, v, v);
+ }
+ }
+ }
+ pref.setEntries(entries);
+ }
+
+
+
+ private void checkSonicItemVisibility() {
+ if (Build.VERSION.SDK_INT < 16) {
+ ListPreference p = (ListPreference) findPreference(UserPreferences.PREF_MEDIA_PLAYER);
+ p.setEntries(R.array.media_player_options_no_sonic);
+ p.setEntryValues(R.array.media_player_values_no_sonic);
+ }
+ }
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/StoragePreferencesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/StoragePreferencesFragment.java
new file mode 100644
index 000000000..b4226b546
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/StoragePreferencesFragment.java
@@ -0,0 +1,244 @@
+package de.danoeh.antennapod.fragment.preferences;
+
+import android.Manifest;
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.app.ProgressDialog;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.support.v4.app.ActivityCompat;
+import android.support.v4.content.FileProvider;
+import android.support.v7.app.AlertDialog;
+import android.support.v7.preference.PreferenceFragmentCompat;
+import android.util.Log;
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.activity.DirectoryChooserActivity;
+import de.danoeh.antennapod.activity.ImportExportActivity;
+import de.danoeh.antennapod.activity.OpmlImportFromPathActivity;
+import de.danoeh.antennapod.asynctask.ExportWorker;
+import de.danoeh.antennapod.core.export.ExportWriter;
+import de.danoeh.antennapod.core.export.html.HtmlWriter;
+import de.danoeh.antennapod.core.export.opml.OpmlWriter;
+import de.danoeh.antennapod.core.preferences.UserPreferences;
+import de.danoeh.antennapod.dialog.ChooseDataFolderDialog;
+import io.reactivex.Observable;
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.disposables.Disposable;
+import io.reactivex.schedulers.Schedulers;
+
+import java.io.File;
+import java.util.List;
+
+public class StoragePreferencesFragment extends PreferenceFragmentCompat {
+ private static final String TAG = "StoragePrefFragment";
+ private static final String PREF_OPML_EXPORT = "prefOpmlExport";
+ private static final String PREF_OPML_IMPORT = "prefOpmlImport";
+ private static final String PREF_HTML_EXPORT = "prefHtmlExport";
+ private static final String IMPORT_EXPORT = "importExport";
+ private static final String PREF_CHOOSE_DATA_DIR = "prefChooseDataDir";
+ private static final String[] EXTERNAL_STORAGE_PERMISSIONS = {
+ Manifest.permission.READ_EXTERNAL_STORAGE,
+ Manifest.permission.WRITE_EXTERNAL_STORAGE };
+ private static final int PERMISSION_REQUEST_EXTERNAL_STORAGE = 41;
+ private Disposable disposable;
+
+ @Override
+ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
+ addPreferencesFromResource(R.xml.preferences_storage);
+ setupStorageScreen();
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ setDataFolderText();
+ }
+
+ private void setupStorageScreen() {
+ final Activity activity = getActivity();
+
+ findPreference(IMPORT_EXPORT).setOnPreferenceClickListener(
+ preference -> {
+ activity.startActivity(new Intent(activity, ImportExportActivity.class));
+ return true;
+ }
+ );
+ findPreference(PREF_OPML_EXPORT).setOnPreferenceClickListener(
+ preference -> export(new OpmlWriter()));
+ findPreference(PREF_HTML_EXPORT).setOnPreferenceClickListener(
+ preference -> export(new HtmlWriter()));
+ findPreference(PREF_OPML_IMPORT).setOnPreferenceClickListener(
+ preference -> {
+ activity.startActivity(new Intent(activity, OpmlImportFromPathActivity.class));
+ return true;
+ });
+ findPreference(PREF_CHOOSE_DATA_DIR).setOnPreferenceClickListener(
+ preference -> {
+ if (Build.VERSION_CODES.KITKAT <= Build.VERSION.SDK_INT &&
+ Build.VERSION.SDK_INT <= Build.VERSION_CODES.LOLLIPOP_MR1) {
+ showChooseDataFolderDialog();
+ } else {
+ int readPermission = ActivityCompat.checkSelfPermission(
+ activity, Manifest.permission.READ_EXTERNAL_STORAGE);
+ int writePermission = ActivityCompat.checkSelfPermission(
+ activity, Manifest.permission.WRITE_EXTERNAL_STORAGE);
+ if (readPermission == PackageManager.PERMISSION_GRANTED &&
+ writePermission == PackageManager.PERMISSION_GRANTED) {
+ openDirectoryChooser();
+ } else {
+ requestPermission();
+ }
+ }
+ return true;
+ }
+ );
+ findPreference(PREF_CHOOSE_DATA_DIR)
+ .setOnPreferenceClickListener(
+ preference -> {
+ if (Build.VERSION.SDK_INT >= 19) {
+ showChooseDataFolderDialog();
+ } else {
+ Intent intent = new Intent(activity, DirectoryChooserActivity.class);
+ activity.startActivityForResult(intent,
+ DirectoryChooserActivity.RESULT_CODE_DIR_SELECTED);
+ }
+ return true;
+ }
+ );
+ findPreference(UserPreferences.PREF_IMAGE_CACHE_SIZE).setOnPreferenceChangeListener(
+ (preference, o) -> {
+ if (o instanceof String) {
+ int newValue = Integer.parseInt((String) o) * 1024 * 1024;
+ if (newValue != UserPreferences.getImageCacheSize()) {
+ AlertDialog.Builder dialog = new AlertDialog.Builder(getActivity());
+ dialog.setTitle(android.R.string.dialog_alert_title);
+ dialog.setMessage(R.string.pref_restart_required);
+ dialog.setPositiveButton(android.R.string.ok, null);
+ dialog.show();
+ }
+ return true;
+ }
+ return false;
+ }
+ );
+ }
+
+ private boolean export(ExportWriter exportWriter) {
+ Context context = getActivity();
+ final ProgressDialog progressDialog = new ProgressDialog(context);
+ progressDialog.setMessage(context.getString(R.string.exporting_label));
+ progressDialog.setIndeterminate(true);
+ progressDialog.show();
+ final AlertDialog.Builder alert = new AlertDialog.Builder(context)
+ .setNeutralButton(android.R.string.ok, (dialog, which) -> dialog.dismiss());
+ Observable<File> observable = new ExportWorker(exportWriter).exportObservable();
+ disposable = observable.subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(output -> {
+ alert.setTitle(R.string.export_success_title);
+ String message = context.getString(R.string.export_success_sum, output.toString());
+ alert.setMessage(message);
+ alert.setPositiveButton(R.string.send_label, (dialog, which) -> {
+ Uri fileUri = FileProvider.getUriForFile(context.getApplicationContext(),
+ context.getString(R.string.provider_authority), output);
+ Intent sendIntent = new Intent(Intent.ACTION_SEND);
+ sendIntent.putExtra(Intent.EXTRA_SUBJECT,
+ context.getResources().getText(R.string.opml_export_label));
+ sendIntent.putExtra(Intent.EXTRA_STREAM, fileUri);
+ sendIntent.setType("text/plain");
+ sendIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) {
+ List<ResolveInfo> resInfoList = context.getPackageManager().queryIntentActivities(sendIntent, PackageManager.MATCH_DEFAULT_ONLY);
+ for (ResolveInfo resolveInfo : resInfoList) {
+ String packageName = resolveInfo.activityInfo.packageName;
+ context.grantUriPermission(packageName, fileUri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ }
+ }
+ context.startActivity(Intent.createChooser(sendIntent,
+ context.getResources().getText(R.string.send_label)));
+ });
+ alert.create().show();
+ }, error -> {
+ alert.setTitle(R.string.export_error_label);
+ alert.setMessage(error.getMessage());
+ alert.show();
+ }, progressDialog::dismiss);
+ return true;
+ }
+
+ public void unsubscribeExportSubscription() {
+ if (disposable != null) {
+ disposable.dispose();
+ }
+ }
+
+ @SuppressLint("NewApi")
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (resultCode == Activity.RESULT_OK &&
+ requestCode == DirectoryChooserActivity.RESULT_CODE_DIR_SELECTED) {
+ String dir = data.getStringExtra(DirectoryChooserActivity.RESULT_SELECTED_DIR);
+
+ File path;
+ if(dir != null) {
+ path = new File(dir);
+ } else {
+ path = getActivity().getExternalFilesDir(null);
+ }
+ String message = null;
+ final Context context= getActivity().getApplicationContext();
+ if(!path.exists()) {
+ message = String.format(context.getString(R.string.folder_does_not_exist_error), dir);
+ } else if(!path.canRead()) {
+ message = String.format(context.getString(R.string.folder_not_readable_error), dir);
+ } else if(!path.canWrite()) {
+ message = String.format(context.getString(R.string.folder_not_writable_error), dir);
+ }
+
+ if(message == null) {
+ Log.d(TAG, "Setting data folder: " + dir);
+ UserPreferences.setDataFolder(dir);
+ setDataFolderText();
+ } else {
+ AlertDialog.Builder ab = new AlertDialog.Builder(getActivity());
+ ab.setMessage(message);
+ ab.setPositiveButton(android.R.string.ok, null);
+ ab.show();
+ }
+ }
+ }
+
+ private void setDataFolderText() {
+ File f = UserPreferences.getDataFolder(null);
+ if (f != null) {
+ findPreference(PREF_CHOOSE_DATA_DIR)
+ .setSummary(f.getAbsolutePath());
+ }
+ }
+
+ private void requestPermission() {
+ ActivityCompat.requestPermissions(getActivity(), EXTERNAL_STORAGE_PERMISSIONS,
+ PERMISSION_REQUEST_EXTERNAL_STORAGE);
+ }
+
+ private void openDirectoryChooser() {
+ Activity activity = getActivity();
+ Intent intent = new Intent(activity, DirectoryChooserActivity.class);
+ activity.startActivityForResult(intent, DirectoryChooserActivity.RESULT_CODE_DIR_SELECTED);
+ }
+
+ private void showChooseDataFolderDialog() {
+ ChooseDataFolderDialog.showDialog(
+ getActivity(), new ChooseDataFolderDialog.RunnableWithString() {
+ @Override
+ public void run(final String folder) {
+ UserPreferences.setDataFolder(folder);
+ setDataFolderText();
+ }
+ });
+ }
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/UserInterfacePreferencesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/UserInterfacePreferencesFragment.java
new file mode 100644
index 000000000..e1d44f7d3
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/UserInterfacePreferencesFragment.java
@@ -0,0 +1,165 @@
+package de.danoeh.antennapod.fragment.preferences;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.Build;
+import android.os.Bundle;
+import android.support.design.widget.Snackbar;
+import android.support.v7.app.AlertDialog;
+import android.support.v7.preference.PreferenceFragmentCompat;
+import android.widget.ListView;
+import android.widget.Toast;
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.activity.MainActivity;
+import de.danoeh.antennapod.core.preferences.UserPreferences;
+import org.apache.commons.lang3.ArrayUtils;
+
+import java.util.List;
+
+public class UserInterfacePreferencesFragment extends PreferenceFragmentCompat {
+ private static final String PREF_EXPANDED_NOTIFICATION = "prefExpandNotify";
+
+ @Override
+ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
+ addPreferencesFromResource(R.xml.preferences_user_interface);
+ setupInterfaceScreen();
+ }
+
+ private void setupInterfaceScreen() {
+
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
+ // disable expanded notification option on unsupported android versions
+ findPreference(PREF_EXPANDED_NOTIFICATION).setEnabled(false);
+ findPreference(PREF_EXPANDED_NOTIFICATION).setOnPreferenceClickListener(
+ preference -> {
+ Toast toast = Toast.makeText(getActivity(),
+ R.string.pref_expand_notify_unsupport_toast, Toast.LENGTH_SHORT);
+ toast.show();
+ return true;
+ }
+ );
+ }
+ findPreference(UserPreferences.PREF_THEME)
+ .setOnPreferenceChangeListener(
+ (preference, newValue) -> {
+ Intent i = new Intent(getActivity(), MainActivity.class);
+ i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK
+ | Intent.FLAG_ACTIVITY_NEW_TASK);
+ getActivity().finish();
+ startActivity(i);
+ return true;
+ }
+ );
+ findPreference(UserPreferences.PREF_HIDDEN_DRAWER_ITEMS)
+ .setOnPreferenceClickListener(preference -> {
+ showDrawerPreferencesDialog();
+ return true;
+ });
+
+ findPreference(UserPreferences.PREF_COMPACT_NOTIFICATION_BUTTONS)
+ .setOnPreferenceClickListener(preference -> {
+ showNotificationButtonsDialog();
+ return true;
+ });
+
+ findPreference(UserPreferences.PREF_BACK_BUTTON_BEHAVIOR)
+ .setOnPreferenceChangeListener((preference, newValue) -> {
+ if (newValue.equals("page")) {
+ final Context context = getActivity();
+ final String[] navTitles = context.getResources().getStringArray(R.array.back_button_go_to_pages);
+ final String[] navTags = context.getResources().getStringArray(R.array.back_button_go_to_pages_tags);
+ final String choice[] = { UserPreferences.getBackButtonGoToPage() };
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ builder.setTitle(R.string.back_button_go_to_page_title);
+ builder.setSingleChoiceItems(navTitles, ArrayUtils.indexOf(navTags, UserPreferences.getBackButtonGoToPage()), (dialogInterface, i) -> {
+ if (i >= 0) {
+ choice[0] = navTags[i];
+ }
+ });
+ builder.setPositiveButton(R.string.confirm_label, (dialogInterface, i) -> UserPreferences.setBackButtonGoToPage(choice[0]));
+ builder.setNegativeButton(R.string.cancel_label, null);
+ builder.create().show();
+ return true;
+ } else {
+ return true;
+ }
+ });
+
+ if (Build.VERSION.SDK_INT >= 26) {
+ findPreference(UserPreferences.PREF_EXPANDED_NOTIFICATION).setVisible(false);
+ }
+ }
+
+ private void showDrawerPreferencesDialog() {
+ final Context context = getActivity();
+ final List<String> hiddenDrawerItems = UserPreferences.getHiddenDrawerItems();
+ final String[] navTitles = context.getResources().getStringArray(R.array.nav_drawer_titles);
+ final String[] NAV_DRAWER_TAGS = MainActivity.NAV_DRAWER_TAGS;
+ boolean[] checked = new boolean[MainActivity.NAV_DRAWER_TAGS.length];
+ for(int i=0; i < NAV_DRAWER_TAGS.length; i++) {
+ String tag = NAV_DRAWER_TAGS[i];
+ if(!hiddenDrawerItems.contains(tag)) {
+ checked[i] = true;
+ }
+ }
+ AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ builder.setTitle(R.string.drawer_preferences);
+ builder.setMultiChoiceItems(navTitles, checked, (dialog, which, isChecked) -> {
+ if (isChecked) {
+ hiddenDrawerItems.remove(NAV_DRAWER_TAGS[which]);
+ } else {
+ hiddenDrawerItems.add(NAV_DRAWER_TAGS[which]);
+ }
+ });
+ builder.setPositiveButton(R.string.confirm_label, (dialog, which) ->
+ UserPreferences.setHiddenDrawerItems(hiddenDrawerItems));
+ builder.setNegativeButton(R.string.cancel_label, null);
+ builder.create().show();
+ }
+
+ private void showNotificationButtonsDialog() {
+ final Context context = getActivity();
+ final List<Integer> preferredButtons = UserPreferences.getCompactNotificationButtons();
+ final String[] allButtonNames = context.getResources().getStringArray(
+ R.array.compact_notification_buttons_options);
+ boolean[] checked = new boolean[allButtonNames.length]; // booleans default to false in java
+
+ for(int i=0; i < checked.length; i++) {
+ if(preferredButtons.contains(i)) {
+ checked[i] = true;
+ }
+ }
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ builder.setTitle(String.format(context.getResources().getString(
+ R.string.pref_compact_notification_buttons_dialog_title), 2));
+ builder.setMultiChoiceItems(allButtonNames, checked, (dialog, which, isChecked) -> {
+ checked[which] = isChecked;
+
+ if (isChecked) {
+ if (preferredButtons.size() < 2) {
+ preferredButtons.add(which);
+ } else {
+ // Only allow a maximum of two selections. This is because the notification
+ // on the lock screen can only display 3 buttons, and the play/pause button
+ // is always included.
+ checked[which] = false;
+ ListView selectionView = ((AlertDialog) dialog).getListView();
+ selectionView.setItemChecked(which, false);
+ Snackbar.make(
+ selectionView,
+ String.format(context.getResources().getString(
+ R.string.pref_compact_notification_buttons_dialog_error), 2),
+ Snackbar.LENGTH_SHORT).show();
+ }
+ } else {
+ preferredButtons.remove((Integer) which);
+ }
+ });
+ builder.setPositiveButton(R.string.confirm_label, (dialog, which) ->
+ UserPreferences.setCompactNotificationButtons(preferredButtons));
+ builder.setNegativeButton(R.string.cancel_label, null);
+ builder.create().show();
+ }
+}
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 ffdfa9516..156657a00 100644
--- a/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedItemMenuHandler.java
+++ b/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedItemMenuHandler.java
@@ -15,7 +15,6 @@ import de.danoeh.antennapod.core.gpoddernet.model.GpodnetEpisodeAction.Action;
import de.danoeh.antennapod.core.preferences.GpodnetPreferences;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.playback.PlaybackService;
-import de.danoeh.antennapod.core.storage.DBTasks;
import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.util.FeedItemUtil;
import de.danoeh.antennapod.core.util.IntentUtils;
@@ -68,16 +67,17 @@ public class FeedItemMenuHandler {
}
boolean hasMedia = selectedItem.getMedia() != null;
boolean isPlaying = hasMedia && selectedItem.getState() == FeedItem.State.PLAYING;
+ boolean keepSorted = UserPreferences.isQueueKeepSorted();
if (!isPlaying) {
mi.setItemVisibility(R.id.skip_episode_item, false);
}
boolean isInQueue = selectedItem.isTagged(FeedItem.TAG_QUEUE);
- if(queueAccess == null || queueAccess.size() == 0 || queueAccess.get(0) == selectedItem.getId()) {
+ if (queueAccess == null || queueAccess.size() == 0 || queueAccess.get(0) == selectedItem.getId() || keepSorted) {
mi.setItemVisibility(R.id.move_to_top_item, false);
}
- if(queueAccess == null || queueAccess.size() == 0 || queueAccess.get(queueAccess.size()-1) == selectedItem.getId()) {
+ if (queueAccess == null || queueAccess.size() == 0 || queueAccess.get(queueAccess.size()-1) == selectedItem.getId() || keepSorted) {
mi.setItemVisibility(R.id.move_to_bottom_item, false);
}
if (!isInQueue) {
@@ -101,7 +101,8 @@ public class FeedItemMenuHandler {
mi.setItemVisibility(R.id.share_download_url_with_position_item, false);
}
- mi.setItemVisibility(R.id.share_file, hasMedia && selectedItem.getMedia().fileExists());
+ boolean fileDownloaded = hasMedia && selectedItem.getMedia().fileExists();
+ mi.setItemVisibility(R.id.share_file, fileDownloaded);
if (selectedItem.isPlayed()) {
mi.setItemVisibility(R.id.mark_read_item, false);
@@ -122,14 +123,12 @@ public class FeedItemMenuHandler {
mi.setItemVisibility(R.id.deactivate_auto_download, false);
}
- if (selectedItem.getPaymentLink() == null || !selectedItem.getFlattrStatus().flattrable()) {
- mi.setItemVisibility(R.id.support_item, false);
- }
-
boolean isFavorite = selectedItem.isTagged(FeedItem.TAG_FAVORITE);
mi.setItemVisibility(R.id.add_to_favorites_item, !isFavorite);
mi.setItemVisibility(R.id.remove_from_favorites_item, isFavorite);
+ mi.setItemVisibility(R.id.remove_item, fileDownloaded);
+
return true;
}
@@ -196,7 +195,7 @@ public class FeedItemMenuHandler {
DBWriter.addQueueItem(context, selectedItem);
break;
case R.id.remove_from_queue_item:
- DBWriter.removeQueueItem(context, selectedItem, true);
+ DBWriter.removeQueueItem(context, true, selectedItem);
break;
case R.id.add_to_favorites_item:
DBWriter.addFavoriteItem(selectedItem);
@@ -226,9 +225,6 @@ public class FeedItemMenuHandler {
Toast.LENGTH_SHORT).show();
}
break;
- case R.id.support_item:
- DBTasks.flattrItemIfLoggedIn(context, selectedItem);
- break;
case R.id.share_link_item:
ShareUtils.shareFeedItemLink(context, selectedItem);
break;
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 bd4fe9bcf..0928cfd62 100644
--- a/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedMenuHandler.java
+++ b/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedMenuHandler.java
@@ -46,11 +46,6 @@ public class FeedMenuHandler {
}
Log.d(TAG, "Preparing options menu");
- if (selectedFeed.getPaymentLink() != null && selectedFeed.getFlattrStatus().flattrable()) {
- menu.findItem(R.id.support_item).setVisible(true);
- } else {
- menu.findItem(R.id.support_item).setVisible(false);
- }
menu.findItem(R.id.refresh_complete_item).setVisible(selectedFeed.isPaged());
@@ -98,9 +93,6 @@ public class FeedMenuHandler {
Toast.LENGTH_SHORT).show();
}
break;
- case R.id.support_item:
- DBTasks.flattrFeedIfLoggedIn(context, selectedFeed);
- break;
case R.id.share_link_item:
ShareUtils.shareFeedlink(context, selectedFeed);
break;
diff --git a/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceController.java b/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceController.java
deleted file mode 100644
index 31b2cbcb2..000000000
--- a/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceController.java
+++ /dev/null
@@ -1,1218 +0,0 @@
-package de.danoeh.antennapod.preferences;
-
-import android.Manifest;
-import android.annotation.SuppressLint;
-import android.app.Activity;
-import android.app.ProgressDialog;
-import android.app.TimePickerDialog;
-import android.content.ActivityNotFoundException;
-import android.content.Context;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.content.res.Resources;
-import android.net.Uri;
-import android.net.wifi.WifiConfiguration;
-import android.net.wifi.WifiManager;
-import android.os.Build;
-import android.os.Bundle;
-import android.support.design.widget.Snackbar;
-import android.support.v4.app.ActivityCompat;
-import android.support.v4.content.FileProvider;
-import android.support.v7.app.AlertDialog;
-import android.support.v7.app.AppCompatActivity;
-import android.support.v7.preference.CheckBoxPreference;
-import android.support.v7.preference.ListPreference;
-import android.support.v7.preference.Preference;
-import android.support.v7.preference.PreferenceFragmentCompat;
-import android.support.v7.preference.PreferenceManager;
-import android.support.v7.preference.PreferenceScreen;
-import android.text.Html;
-import android.text.format.DateFormat;
-import android.text.format.DateUtils;
-import android.util.Log;
-import android.widget.ListView;
-import android.widget.Toast;
-
-import com.afollestad.materialdialogs.MaterialDialog;
-import com.bytehamster.lib.preferencesearch.SearchConfiguration;
-import com.bytehamster.lib.preferencesearch.SearchPreference;
-
-import org.apache.commons.lang3.ArrayUtils;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Calendar;
-import java.util.Collections;
-import java.util.GregorianCalendar;
-import java.util.List;
-import java.util.concurrent.TimeUnit;
-
-import de.danoeh.antennapod.CrashReportWriter;
-import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.activity.AboutActivity;
-import de.danoeh.antennapod.activity.DirectoryChooserActivity;
-import de.danoeh.antennapod.activity.ImportExportActivity;
-import de.danoeh.antennapod.activity.MainActivity;
-import de.danoeh.antennapod.activity.MediaplayerActivity;
-import de.danoeh.antennapod.activity.OpmlImportFromPathActivity;
-import de.danoeh.antennapod.activity.PreferenceActivity;
-import de.danoeh.antennapod.activity.StatisticsActivity;
-import de.danoeh.antennapod.asynctask.ExportWorker;
-import de.danoeh.antennapod.core.export.ExportWriter;
-import de.danoeh.antennapod.core.export.html.HtmlWriter;
-import de.danoeh.antennapod.core.export.opml.OpmlWriter;
-import de.danoeh.antennapod.core.preferences.GpodnetPreferences;
-import de.danoeh.antennapod.core.preferences.UserPreferences;
-import de.danoeh.antennapod.core.service.GpodnetSyncService;
-import de.danoeh.antennapod.core.util.flattr.FlattrUtils;
-import de.danoeh.antennapod.core.util.gui.PictureInPictureUtil;
-import de.danoeh.antennapod.dialog.AuthenticationDialog;
-import de.danoeh.antennapod.dialog.AutoFlattrPreferenceDialog;
-import de.danoeh.antennapod.dialog.ChooseDataFolderDialog;
-import de.danoeh.antennapod.dialog.GpodnetSetHostnameDialog;
-import de.danoeh.antennapod.dialog.ProxyDialog;
-import de.danoeh.antennapod.dialog.VariableSpeedDialog;
-import io.reactivex.Observable;
-import io.reactivex.android.schedulers.AndroidSchedulers;
-import io.reactivex.disposables.Disposable;
-import io.reactivex.schedulers.Schedulers;
-
-import static de.danoeh.antennapod.activity.PreferenceActivity.PARAM_RESOURCE;
-
-/**
- * Sets up a preference UI that lets the user change user preferences.
- */
-
-public class PreferenceController implements SharedPreferences.OnSharedPreferenceChangeListener {
-
- private static final String TAG = "PreferenceController";
-
- private static final String PREF_SCREEN_USER_INTERFACE = "prefScreenInterface";
- private static final String PREF_SCREEN_PLAYBACK = "prefScreenPlayback";
- private static final String PREF_SCREEN_NETWORK = "prefScreenNetwork";
- private static final String PREF_SCREEN_INTEGRATIONS = "prefScreenIntegrations";
- private static final String PREF_SCREEN_STORAGE = "prefScreenStorage";
- private static final String PREF_SCREEN_AUTODL = "prefAutoDownloadSettings";
- private static final String PREF_SCREEN_FLATTR = "prefFlattrSettings";
- private static final String PREF_SCREEN_GPODDER = "prefGpodderSettings";
-
- private static final String PREF_FLATTR_AUTH = "pref_flattr_authenticate";
- private static final String PREF_FLATTR_REVOKE = "prefRevokeAccess";
- private static final String PREF_AUTO_FLATTR_PREFS = "prefAutoFlattrPrefs";
- private static final String PREF_OPML_EXPORT = "prefOpmlExport";
- private static final String PREF_OPML_IMPORT = "prefOpmlImport";
- private static final String PREF_HTML_EXPORT = "prefHtmlExport";
- private static final String STATISTICS = "statistics";
- private static final String IMPORT_EXPORT = "importExport";
- private static final String PREF_ABOUT = "prefAbout";
- private static final String PREF_CHOOSE_DATA_DIR = "prefChooseDataDir";
- private static final String PREF_PLAYBACK_SPEED_LAUNCHER = "prefPlaybackSpeedLauncher";
- private static final String PREF_PLAYBACK_REWIND_DELTA_LAUNCHER = "prefPlaybackRewindDeltaLauncher";
- private static final String PREF_PLAYBACK_FAST_FORWARD_DELTA_LAUNCHER = "prefPlaybackFastForwardDeltaLauncher";
- private static final String PREF_GPODNET_LOGIN = "pref_gpodnet_authenticate";
- private static final String PREF_GPODNET_SETLOGIN_INFORMATION = "pref_gpodnet_setlogin_information";
- private static final String PREF_GPODNET_SYNC = "pref_gpodnet_sync";
- private static final String PREF_GPODNET_FORCE_FULL_SYNC = "pref_gpodnet_force_full_sync";
- private static final String PREF_GPODNET_LOGOUT = "pref_gpodnet_logout";
- private static final String PREF_GPODNET_HOSTNAME = "pref_gpodnet_hostname";
- private static final String PREF_GPODNET_NOTIFICATIONS = "pref_gpodnet_notifications";
- private static final String PREF_EXPANDED_NOTIFICATION = "prefExpandNotify";
- private static final String PREF_PROXY = "prefProxy";
- private static final String PREF_KNOWN_ISSUES = "prefKnownIssues";
- private static final String PREF_FAQ = "prefFaq";
- private static final String PREF_SEND_CRASH_REPORT = "prefSendCrashReport";
- private static final String[] EXTERNAL_STORAGE_PERMISSIONS = {
- Manifest.permission.READ_EXTERNAL_STORAGE,
- Manifest.permission.WRITE_EXTERNAL_STORAGE };
- private static final int PERMISSION_REQUEST_EXTERNAL_STORAGE = 41;
- private final PreferenceUI ui;
- private final SharedPreferences.OnSharedPreferenceChangeListener gpoddernetListener =
- (sharedPreferences, key) -> {
- if (GpodnetPreferences.PREF_LAST_SYNC_ATTEMPT_TIMESTAMP.equals(key)) {
- updateLastGpodnetSyncReport(GpodnetPreferences.getLastSyncAttemptResult(),
- GpodnetPreferences.getLastSyncAttemptTimestamp());
- }
- };
- private CheckBoxPreference[] selectedNetworks;
- private Disposable disposable;
-
- public PreferenceController(PreferenceUI ui) {
- this.ui = ui;
- PreferenceManager.getDefaultSharedPreferences(ui.getActivity().getApplicationContext())
- .registerOnSharedPreferenceChangeListener(this);
- }
-
- @Override
- public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
-
- }
-
-
-
- public void onCreate(int screen) {
- switch (screen) {
- case R.xml.preferences:
- setupMainScreen();
- break;
- case R.xml.preferences_network:
- setupNetworkScreen();
- break;
- case R.xml.preferences_autodownload:
- setupAutoDownloadScreen();
- buildAutodownloadSelectedNetworksPreference();
- setSelectedNetworksEnabled(UserPreferences.isEnableAutodownloadWifiFilter());
- buildEpisodeCleanupPreference();
- break;
- case R.xml.preferences_playback:
- setupPlaybackScreen();
- PreferenceControllerFlavorHelper.setupFlavoredUI(ui);
- buildSmartMarkAsPlayedPreference();
- break;
- case R.xml.preferences_integrations:
- setupIntegrationsScreen();
- break;
- case R.xml.preferences_flattr:
- setupFlattrScreen();
- break;
- case R.xml.preferences_gpodder:
- setupGpodderScreen();
- break;
- case R.xml.preferences_storage:
- setupStorageScreen();
- break;
- case R.xml.preferences_user_interface:
- setupInterfaceScreen();
- break;
- }
- }
-
- private void setupInterfaceScreen() {
- final Activity activity = ui.getActivity();
-
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
- // disable expanded notification option on unsupported android versions
- ui.findPreference(PreferenceController.PREF_EXPANDED_NOTIFICATION).setEnabled(false);
- ui.findPreference(PreferenceController.PREF_EXPANDED_NOTIFICATION).setOnPreferenceClickListener(
- preference -> {
- Toast toast = Toast.makeText(activity,
- R.string.pref_expand_notify_unsupport_toast, Toast.LENGTH_SHORT);
- toast.show();
- return true;
- }
- );
- }
- ui.findPreference(UserPreferences.PREF_THEME)
- .setOnPreferenceChangeListener(
- (preference, newValue) -> {
- Intent i = new Intent(activity, MainActivity.class);
- i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK
- | Intent.FLAG_ACTIVITY_NEW_TASK);
- activity.finish();
- activity.startActivity(i);
- return true;
- }
- );
- ui.findPreference(UserPreferences.PREF_HIDDEN_DRAWER_ITEMS)
- .setOnPreferenceClickListener(preference -> {
- showDrawerPreferencesDialog();
- return true;
- });
-
- ui.findPreference(UserPreferences.PREF_COMPACT_NOTIFICATION_BUTTONS)
- .setOnPreferenceClickListener(preference -> {
- showNotificationButtonsDialog();
- return true;
- });
-
- ui.findPreference(UserPreferences.PREF_BACK_BUTTON_BEHAVIOR)
- .setOnPreferenceChangeListener((preference, newValue) -> {
- if (newValue.equals("page")) {
- final Context context = ui.getActivity();
- final String[] navTitles = context.getResources().getStringArray(R.array.back_button_go_to_pages);
- final String[] navTags = context.getResources().getStringArray(R.array.back_button_go_to_pages_tags);
- final String choice[] = { UserPreferences.getBackButtonGoToPage() };
-
- AlertDialog.Builder builder = new AlertDialog.Builder(context);
- builder.setTitle(R.string.back_button_go_to_page_title);
- builder.setSingleChoiceItems(navTitles, ArrayUtils.indexOf(navTags, UserPreferences.getBackButtonGoToPage()), (dialogInterface, i) -> {
- if (i >= 0) {
- choice[0] = navTags[i];
- }
- });
- builder.setPositiveButton(R.string.confirm_label, (dialogInterface, i) -> UserPreferences.setBackButtonGoToPage(choice[0]));
- builder.setNegativeButton(R.string.cancel_label, null);
- builder.create().show();
- return true;
- } else {
- return true;
- }
- });
-
- if (Build.VERSION.SDK_INT >= 26) {
- ui.findPreference(UserPreferences.PREF_EXPANDED_NOTIFICATION).setVisible(false);
- }
- }
-
- private void setupStorageScreen() {
- final Activity activity = ui.getActivity();
-
- ui.findPreference(PreferenceController.IMPORT_EXPORT).setOnPreferenceClickListener(
- preference -> {
- activity.startActivity(new Intent(activity, ImportExportActivity.class));
- return true;
- }
- );
- ui.findPreference(PreferenceController.PREF_OPML_EXPORT).setOnPreferenceClickListener(
- preference -> export(new OpmlWriter()));
- ui.findPreference(PreferenceController.PREF_HTML_EXPORT).setOnPreferenceClickListener(
- preference -> export(new HtmlWriter()));
- ui.findPreference(PreferenceController.PREF_OPML_IMPORT).setOnPreferenceClickListener(
- preference -> {
- activity.startActivity(new Intent(activity, OpmlImportFromPathActivity.class));
- return true;
- });
- ui.findPreference(PreferenceController.PREF_CHOOSE_DATA_DIR).setOnPreferenceClickListener(
- preference -> {
- if (Build.VERSION_CODES.KITKAT <= Build.VERSION.SDK_INT &&
- Build.VERSION.SDK_INT <= Build.VERSION_CODES.LOLLIPOP_MR1) {
- showChooseDataFolderDialog();
- } else {
- int readPermission = ActivityCompat.checkSelfPermission(
- activity, Manifest.permission.READ_EXTERNAL_STORAGE);
- int writePermission = ActivityCompat.checkSelfPermission(
- activity, Manifest.permission.WRITE_EXTERNAL_STORAGE);
- if (readPermission == PackageManager.PERMISSION_GRANTED &&
- writePermission == PackageManager.PERMISSION_GRANTED) {
- openDirectoryChooser();
- } else {
- requestPermission();
- }
- }
- return true;
- }
- );
- ui.findPreference(PreferenceController.PREF_CHOOSE_DATA_DIR)
- .setOnPreferenceClickListener(
- preference -> {
- if (Build.VERSION.SDK_INT >= 19) {
- showChooseDataFolderDialog();
- } else {
- Intent intent = new Intent(activity, DirectoryChooserActivity.class);
- activity.startActivityForResult(intent,
- DirectoryChooserActivity.RESULT_CODE_DIR_SELECTED);
- }
- return true;
- }
- );
- ui.findPreference(UserPreferences.PREF_IMAGE_CACHE_SIZE).setOnPreferenceChangeListener(
- (preference, o) -> {
- if (o instanceof String) {
- int newValue = Integer.parseInt((String) o) * 1024 * 1024;
- if (newValue != UserPreferences.getImageCacheSize()) {
- AlertDialog.Builder dialog = new AlertDialog.Builder(ui.getActivity());
- dialog.setTitle(android.R.string.dialog_alert_title);
- dialog.setMessage(R.string.pref_restart_required);
- dialog.setPositiveButton(android.R.string.ok, null);
- dialog.show();
- }
- return true;
- }
- return false;
- }
- );
- }
-
- private void setupIntegrationsScreen() {
- final AppCompatActivity activity = ui.getActivity();
-
- ui.findPreference(PREF_SCREEN_FLATTR).setOnPreferenceClickListener(preference -> {
- openScreen(R.xml.preferences_flattr, activity);
- return true;
- });
- ui.findPreference(PREF_SCREEN_GPODDER).setOnPreferenceClickListener(preference -> {
- openScreen(R.xml.preferences_gpodder, activity);
- return true;
- });
- }
-
- private void setupFlattrScreen() {
- final AppCompatActivity activity = ui.getActivity();
-
- ui.findPreference(PreferenceController.PREF_FLATTR_REVOKE).setOnPreferenceClickListener(
- preference -> {
- FlattrUtils.revokeAccessToken(activity);
- checkFlattrItemVisibility();
- return true;
- }
- );
-
- ui.findPreference(PreferenceController.PREF_AUTO_FLATTR_PREFS)
- .setOnPreferenceClickListener(preference -> {
- AutoFlattrPreferenceDialog.newAutoFlattrPreferenceDialog(activity,
- new AutoFlattrPreferenceDialog.AutoFlattrPreferenceDialogInterface() {
- @Override
- public void onCancelled() {
-
- }
-
- @Override
- public void onConfirmed(boolean autoFlattrEnabled, float autoFlattrValue) {
- UserPreferences.setAutoFlattrSettings(autoFlattrEnabled, autoFlattrValue);
- checkFlattrItemVisibility();
- }
- });
- return true;
- });
- }
-
- private void setupGpodderScreen() {
- final AppCompatActivity activity = ui.getActivity();
-
- ui.findPreference(PreferenceController.PREF_GPODNET_SETLOGIN_INFORMATION)
- .setOnPreferenceClickListener(preference -> {
- AuthenticationDialog dialog = new AuthenticationDialog(activity,
- R.string.pref_gpodnet_setlogin_information_title, false, false, GpodnetPreferences.getUsername(),
- null) {
-
- @Override
- protected void onConfirmed(String username, String password, boolean saveUsernamePassword) {
- GpodnetPreferences.setPassword(password);
- }
- };
- dialog.show();
- return true;
- });
- ui.findPreference(PreferenceController.PREF_GPODNET_SYNC).
- setOnPreferenceClickListener(preference -> {
- GpodnetSyncService.sendSyncIntent(ui.getActivity().getApplicationContext());
- Toast toast = Toast.makeText(ui.getActivity(), R.string.pref_gpodnet_sync_started,
- Toast.LENGTH_SHORT);
- toast.show();
- return true;
- });
- ui.findPreference(PreferenceController.PREF_GPODNET_FORCE_FULL_SYNC).
- setOnPreferenceClickListener(preference -> {
- GpodnetPreferences.setLastSubscriptionSyncTimestamp(0L);
- GpodnetPreferences.setLastEpisodeActionsSyncTimestamp(0L);
- GpodnetPreferences.setLastSyncAttempt(false, 0);
- updateLastGpodnetSyncReport(false, 0);
- GpodnetSyncService.sendSyncIntent(ui.getActivity().getApplicationContext());
- Toast toast = Toast.makeText(ui.getActivity(), R.string.pref_gpodnet_sync_started,
- Toast.LENGTH_SHORT);
- toast.show();
- return true;
- });
- ui.findPreference(PreferenceController.PREF_GPODNET_LOGOUT).setOnPreferenceClickListener(
- preference -> {
- GpodnetPreferences.logout();
- Toast toast = Toast.makeText(activity, R.string.pref_gpodnet_logout_toast, Toast.LENGTH_SHORT);
- toast.show();
- updateGpodnetPreferenceScreen();
- return true;
- });
- ui.findPreference(PreferenceController.PREF_GPODNET_HOSTNAME).setOnPreferenceClickListener(
- preference -> {
- GpodnetSetHostnameDialog.createDialog(activity).setOnDismissListener(dialog -> updateGpodnetPreferenceScreen());
- return true;
- });
- }
-
- private void setupPlaybackScreen() {
- final Activity activity = ui.getActivity();
-
- ui.findPreference(PreferenceController.PREF_PLAYBACK_SPEED_LAUNCHER)
- .setOnPreferenceClickListener(preference -> {
- VariableSpeedDialog.showDialog(activity);
- return true;
- });
- ui.findPreference(PreferenceController.PREF_PLAYBACK_REWIND_DELTA_LAUNCHER)
- .setOnPreferenceClickListener(preference -> {
- MediaplayerActivity.showSkipPreference(activity, MediaplayerActivity.SkipDirection.SKIP_REWIND);
- return true;
- });
- ui.findPreference(PreferenceController.PREF_PLAYBACK_FAST_FORWARD_DELTA_LAUNCHER)
- .setOnPreferenceClickListener(preference -> {
- MediaplayerActivity.showSkipPreference(activity, MediaplayerActivity.SkipDirection.SKIP_FORWARD);
- return true;
- });
- if (!PictureInPictureUtil.supportsPictureInPicture(activity)) {
- ListPreference behaviour = (ListPreference) ui.findPreference(UserPreferences.PREF_VIDEO_BEHAVIOR);
- behaviour.setEntries(R.array.video_background_behavior_options_without_pip);
- behaviour.setEntryValues(R.array.video_background_behavior_values_without_pip);
- }
- }
-
- private void setupAutoDownloadScreen() {
- ui.findPreference(UserPreferences.PREF_ENABLE_AUTODL).setOnPreferenceChangeListener(
- (preference, newValue) -> {
- if (newValue instanceof Boolean) {
- checkAutodownloadItemVisibility((Boolean) newValue);
- }
- return true;
- });
- ui.findPreference(UserPreferences.PREF_ENABLE_AUTODL_WIFI_FILTER)
- .setOnPreferenceChangeListener(
- (preference, newValue) -> {
- if (newValue instanceof Boolean) {
- setSelectedNetworksEnabled((Boolean) newValue);
- return true;
- } else {
- return false;
- }
- }
- );
- ui.findPreference(UserPreferences.PREF_EPISODE_CACHE_SIZE)
- .setOnPreferenceChangeListener(
- (preference, o) -> {
- if (o instanceof String) {
- setEpisodeCacheSizeText(UserPreferences.readEpisodeCacheSize((String) o));
- }
- return true;
- }
- );
- }
-
- private void setupNetworkScreen() {
- final AppCompatActivity activity = ui.getActivity();
- ui.findPreference(PREF_SCREEN_AUTODL).setOnPreferenceClickListener(preference -> {
- openScreen(R.xml.preferences_autodownload, activity);
- return true;
- });
- ui.findPreference(UserPreferences.PREF_UPDATE_INTERVAL)
- .setOnPreferenceClickListener(preference -> {
- showUpdateIntervalTimePreferencesDialog();
- return true;
- });
- ui.findPreference(UserPreferences.PREF_PARALLEL_DOWNLOADS)
- .setOnPreferenceChangeListener(
- (preference, o) -> {
- if (o instanceof Integer) {
- setParallelDownloadsText((Integer) o);
- }
- return true;
- }
- );
- // validate and set correct value: number of downloads between 1 and 50 (inclusive)
- ui.findPreference(PREF_PROXY).setOnPreferenceClickListener(preference -> {
- ProxyDialog dialog = new ProxyDialog(ui.getActivity());
- dialog.createDialog().show();
- return true;
- });
- }
-
- private void setupMainScreen() {
- final AppCompatActivity activity = ui.getActivity();
- setupSearch();
- ui.findPreference(PREF_SCREEN_USER_INTERFACE).setOnPreferenceClickListener(preference -> {
- openScreen(R.xml.preferences_user_interface, activity);
- return true;
- });
- ui.findPreference(PREF_SCREEN_PLAYBACK).setOnPreferenceClickListener(preference -> {
- openScreen(R.xml.preferences_playback, activity);
- return true;
- });
- ui.findPreference(PREF_SCREEN_NETWORK).setOnPreferenceClickListener(preference -> {
- openScreen(R.xml.preferences_network, activity);
- return true;
- });
- ui.findPreference(PREF_SCREEN_INTEGRATIONS).setOnPreferenceClickListener(preference -> {
- openScreen(R.xml.preferences_integrations, activity);
- return true;
- });
- ui.findPreference(PREF_SCREEN_STORAGE).setOnPreferenceClickListener(preference -> {
- openScreen(R.xml.preferences_storage, activity);
- return true;
- });
-
- ui.findPreference(PreferenceController.PREF_ABOUT).setOnPreferenceClickListener(
- preference -> {
- activity.startActivity(new Intent(activity, AboutActivity.class));
- return true;
- }
- );
- ui.findPreference(PreferenceController.STATISTICS).setOnPreferenceClickListener(
- preference -> {
- activity.startActivity(new Intent(activity, StatisticsActivity.class));
- return true;
- }
- );
- ui.findPreference(PREF_KNOWN_ISSUES).setOnPreferenceClickListener(preference -> {
- openInBrowser("https://github.com/AntennaPod/AntennaPod/labels/bug");
- return true;
- });
- ui.findPreference(PREF_FAQ).setOnPreferenceClickListener(preference -> {
- openInBrowser("http://antennapod.org/faq.html");
- return true;
- });
- ui.findPreference(PREF_SEND_CRASH_REPORT).setOnPreferenceClickListener(preference -> {
- Context context = ui.getActivity().getApplicationContext();
- Intent emailIntent = new Intent(Intent.ACTION_SEND);
- emailIntent.setType("text/plain");
- emailIntent.putExtra(Intent.EXTRA_EMAIL, new String[]{"Martin.Fietz@gmail.com"});
- emailIntent.putExtra(Intent.EXTRA_SUBJECT, "AntennaPod Crash Report");
- emailIntent.putExtra(Intent.EXTRA_TEXT, "Please describe what you were doing when the app crashed");
- // the attachment
- Uri fileUri = FileProvider.getUriForFile(context, context.getString(R.string.provider_authority),
- CrashReportWriter.getFile());
- emailIntent.putExtra(Intent.EXTRA_STREAM, fileUri);
- emailIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
- String intentTitle = ui.getActivity().getString(R.string.send_email);
- if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) {
- List<ResolveInfo> resInfoList = context.getPackageManager().queryIntentActivities(emailIntent, PackageManager.MATCH_DEFAULT_ONLY);
- for (ResolveInfo resolveInfo : resInfoList) {
- String packageName = resolveInfo.activityInfo.packageName;
- context.grantUriPermission(packageName, fileUri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
- }
- }
- ui.getActivity().startActivity(Intent.createChooser(emailIntent, intentTitle));
- return true;
- });
- }
-
- private void setupSearch() {
- final AppCompatActivity activity = ui.getActivity();
-
- SearchPreference searchPreference = (SearchPreference) ui.findPreference("searchPreference");
- SearchConfiguration config = searchPreference.getSearchConfiguration();
- config.setActivity(activity);
- config.setFragmentContainerViewId(R.id.content);
- config.setBreadcrumbsEnabled(true);
-
- config.index()
- .addBreadcrumb(getTitleOfPage(R.xml.preferences_user_interface))
- .addFile(R.xml.preferences_user_interface);
- config.index()
- .addBreadcrumb(getTitleOfPage(R.xml.preferences_playback))
- .addFile(R.xml.preferences_playback);
- config.index()
- .addBreadcrumb(getTitleOfPage(R.xml.preferences_network))
- .addFile(R.xml.preferences_network);
- config.index()
- .addBreadcrumb(getTitleOfPage(R.xml.preferences_storage))
- .addFile(R.xml.preferences_storage);
- config.index()
- .addBreadcrumb(getTitleOfPage(R.xml.preferences_network))
- .addBreadcrumb(R.string.automation)
- .addBreadcrumb(getTitleOfPage(R.xml.preferences_autodownload))
- .addFile(R.xml.preferences_autodownload);
- config.index()
- .addBreadcrumb(getTitleOfPage(R.xml.preferences_integrations))
- .addBreadcrumb(getTitleOfPage(R.xml.preferences_gpodder))
- .addFile(R.xml.preferences_gpodder);
- config.index()
- .addBreadcrumb(getTitleOfPage(R.xml.preferences_integrations))
- .addBreadcrumb(getTitleOfPage(R.xml.preferences_flattr))
- .addFile(R.xml.preferences_flattr);
- }
-
- public PreferenceFragmentCompat openScreen(int preferences, AppCompatActivity activity) {
- PreferenceFragmentCompat prefFragment = new PreferenceActivity.MainFragment();
- Bundle args = new Bundle();
- args.putInt(PARAM_RESOURCE, preferences);
- prefFragment.setArguments(args);
- activity.getSupportFragmentManager().beginTransaction()
- .replace(R.id.content, prefFragment)
- .addToBackStack(TAG).commit();
- return prefFragment;
- }
-
- public static int getTitleOfPage(int preferences) {
- switch (preferences) {
- case R.xml.preferences_network:
- return R.string.network_pref;
- case R.xml.preferences_autodownload:
- return R.string.pref_automatic_download_title;
- case R.xml.preferences_playback:
- return R.string.playback_pref;
- case R.xml.preferences_storage:
- return R.string.storage_pref;
- case R.xml.preferences_user_interface:
- return R.string.user_interface_label;
- case R.xml.preferences_integrations:
- return R.string.integrations_label;
- case R.xml.preferences_flattr:
- return R.string.flattr_label;
- case R.xml.preferences_gpodder:
- return R.string.gpodnet_main_label;
- default:
- return R.string.settings_label;
- }
- }
-
- private boolean export(ExportWriter exportWriter) {
- Context context = ui.getActivity();
- final ProgressDialog progressDialog = new ProgressDialog(context);
- progressDialog.setMessage(context.getString(R.string.exporting_label));
- progressDialog.setIndeterminate(true);
- progressDialog.show();
- final AlertDialog.Builder alert = new AlertDialog.Builder(context)
- .setNeutralButton(android.R.string.ok, (dialog, which) -> dialog.dismiss());
- Observable<File> observable = new ExportWorker(exportWriter).exportObservable();
- disposable = observable.subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe(output -> {
- alert.setTitle(R.string.export_success_title);
- String message = context.getString(R.string.export_success_sum, output.toString());
- alert.setMessage(message);
- alert.setPositiveButton(R.string.send_label, (dialog, which) -> {
- Uri fileUri = FileProvider.getUriForFile(context.getApplicationContext(),
- "de.danoeh.antennapod.provider", output);
- Intent sendIntent = new Intent(Intent.ACTION_SEND);
- sendIntent.putExtra(Intent.EXTRA_SUBJECT,
- context.getResources().getText(R.string.opml_export_label));
- sendIntent.putExtra(Intent.EXTRA_STREAM, fileUri);
- sendIntent.setType("text/plain");
- sendIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
- if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) {
- List<ResolveInfo> resInfoList = context.getPackageManager().queryIntentActivities(sendIntent, PackageManager.MATCH_DEFAULT_ONLY);
- for (ResolveInfo resolveInfo : resInfoList) {
- String packageName = resolveInfo.activityInfo.packageName;
- context.grantUriPermission(packageName, fileUri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
- }
- }
- context.startActivity(Intent.createChooser(sendIntent,
- context.getResources().getText(R.string.send_label)));
- });
- alert.create().show();
- }, error -> {
- alert.setTitle(R.string.export_error_label);
- alert.setMessage(error.getMessage());
- alert.show();
- }, progressDialog::dismiss);
- return true;
- }
-
- private void openInBrowser(String url) {
- try {
- Intent myIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
- ui.getActivity().startActivity(myIntent);
- } catch (ActivityNotFoundException e) {
- Toast.makeText(ui.getActivity(), R.string.pref_no_browser_found, Toast.LENGTH_LONG).show();
- Log.e(TAG, Log.getStackTraceString(e));
- }
- }
-
- public void onResume(int screen) {
- switch (screen) {
- case R.xml.preferences_network:
- setUpdateIntervalText();
- setParallelDownloadsText(UserPreferences.getParallelDownloads());
- break;
- case R.xml.preferences_autodownload:
- setEpisodeCacheSizeText(UserPreferences.getEpisodeCacheSize());
- checkAutodownloadItemVisibility(UserPreferences.isEnableAutodownload());
- break;
- case R.xml.preferences_storage:
- setDataFolderText();
- break;
- case R.xml.preferences_integrations:
- setIntegrationsItemVisibility();
- return;
- case R.xml.preferences_flattr:
- checkFlattrItemVisibility();
- break;
- case R.xml.preferences_gpodder:
- GpodnetPreferences.registerOnSharedPreferenceChangeListener(gpoddernetListener);
- updateGpodnetPreferenceScreen();
- break;
- case R.xml.preferences_playback:
- checkSonicItemVisibility();
- break;
- }
- }
-
- public void unregisterGpodnet() {
- GpodnetPreferences.unregisterOnSharedPreferenceChangeListener(gpoddernetListener);
- }
-
- public void unsubscribeExportSubscription() {
- if (disposable != null) {
- disposable.dispose();
- }
- }
-
- @SuppressLint("NewApi")
- public void onActivityResult(int requestCode, int resultCode, Intent data) {
- if (resultCode == Activity.RESULT_OK &&
- requestCode == DirectoryChooserActivity.RESULT_CODE_DIR_SELECTED) {
- String dir = data.getStringExtra(DirectoryChooserActivity.RESULT_SELECTED_DIR);
-
- File path;
- if(dir != null) {
- path = new File(dir);
- } else {
- path = ui.getActivity().getExternalFilesDir(null);
- }
- String message = null;
- final Context context= ui.getActivity().getApplicationContext();
- if(!path.exists()) {
- message = String.format(context.getString(R.string.folder_does_not_exist_error), dir);
- } else if(!path.canRead()) {
- message = String.format(context.getString(R.string.folder_not_readable_error), dir);
- } else if(!path.canWrite()) {
- message = String.format(context.getString(R.string.folder_not_writable_error), dir);
- }
-
- if(message == null) {
- Log.d(TAG, "Setting data folder: " + dir);
- UserPreferences.setDataFolder(dir);
- setDataFolderText();
- } else {
- AlertDialog.Builder ab = new AlertDialog.Builder(ui.getActivity());
- ab.setMessage(message);
- ab.setPositiveButton(android.R.string.ok, null);
- ab.show();
- }
- }
- }
-
-
- private void updateGpodnetPreferenceScreen() {
- final boolean loggedIn = GpodnetPreferences.loggedIn();
- ui.findPreference(PreferenceController.PREF_GPODNET_LOGIN).setEnabled(!loggedIn);
- ui.findPreference(PreferenceController.PREF_GPODNET_SETLOGIN_INFORMATION).setEnabled(loggedIn);
- ui.findPreference(PreferenceController.PREF_GPODNET_SYNC).setEnabled(loggedIn);
- ui.findPreference(PreferenceController.PREF_GPODNET_FORCE_FULL_SYNC).setEnabled(loggedIn);
- ui.findPreference(PreferenceController.PREF_GPODNET_LOGOUT).setEnabled(loggedIn);
- ui.findPreference(PREF_GPODNET_NOTIFICATIONS).setEnabled(loggedIn);
- if(loggedIn) {
- String format = ui.getActivity().getString(R.string.pref_gpodnet_login_status);
- String summary = String.format(format, GpodnetPreferences.getUsername(),
- GpodnetPreferences.getDeviceID());
- ui.findPreference(PreferenceController.PREF_GPODNET_LOGOUT).setSummary(Html.fromHtml(summary));
- updateLastGpodnetSyncReport(GpodnetPreferences.getLastSyncAttemptResult(),
- GpodnetPreferences.getLastSyncAttemptTimestamp());
- } else {
- ui.findPreference(PreferenceController.PREF_GPODNET_LOGOUT).setSummary(null);
- updateLastGpodnetSyncReport(false, 0);
- }
- ui.findPreference(PreferenceController.PREF_GPODNET_HOSTNAME).setSummary(GpodnetPreferences.getHostname());
- }
-
- private void updateLastGpodnetSyncReport(boolean successful, long lastTime) {
- Preference sync = ui.findPreference(PREF_GPODNET_SYNC);
- if (lastTime != 0) {
- sync.setSummary(ui.getActivity().getString(R.string.pref_gpodnet_sync_changes_sum) + "\n" +
- ui.getActivity().getString(R.string.pref_gpodnet_sync_sum_last_sync_line,
- ui.getActivity().getString(successful ?
- R.string.gpodnetsync_pref_report_successful :
- R.string.gpodnetsync_pref_report_failed),
- DateUtils.getRelativeDateTimeString(ui.getActivity(),
- lastTime,
- DateUtils.MINUTE_IN_MILLIS,
- DateUtils.WEEK_IN_MILLIS,
- DateUtils.FORMAT_SHOW_TIME)));
- } else {
- sync.setSummary(ui.getActivity().getString(R.string.pref_gpodnet_sync_changes_sum));
- }
- }
-
- private String[] getUpdateIntervalEntries(final String[] values) {
- final Resources res = ui.getActivity().getResources();
- String[] entries = new String[values.length];
- for (int x = 0; x < values.length; x++) {
- Integer v = Integer.parseInt(values[x]);
- switch (v) {
- case 0:
- entries[x] = res.getString(R.string.pref_update_interval_hours_manual);
- break;
- case 1:
- entries[x] = v + " " + res.getString(R.string.pref_update_interval_hours_singular);
- break;
- default:
- entries[x] = v + " " + res.getString(R.string.pref_update_interval_hours_plural);
- break;
-
- }
- }
- return entries;
- }
-
- private void buildEpisodeCleanupPreference() {
- final Resources res = ui.getActivity().getResources();
-
- ListPreference pref = (ListPreference) ui.findPreference(UserPreferences.PREF_EPISODE_CLEANUP);
- String[] values = res.getStringArray(
- R.array.episode_cleanup_values);
- String[] entries = new String[values.length];
- for (int x = 0; x < values.length; x++) {
- int v = Integer.parseInt(values[x]);
- if (v == UserPreferences.EPISODE_CLEANUP_QUEUE) {
- entries[x] = res.getString(R.string.episode_cleanup_queue_removal);
- } else if (v == UserPreferences.EPISODE_CLEANUP_NULL){
- entries[x] = res.getString(R.string.episode_cleanup_never);
- } else if (v == 0) {
- entries[x] = res.getString(R.string.episode_cleanup_after_listening);
- } else {
- entries[x] = res.getQuantityString(R.plurals.episode_cleanup_days_after_listening, v, v);
- }
- }
- pref.setEntries(entries);
- }
-
- private void buildSmartMarkAsPlayedPreference() {
- final Resources res = ui.getActivity().getResources();
-
- ListPreference pref = (ListPreference) ui.findPreference(UserPreferences.PREF_SMART_MARK_AS_PLAYED_SECS);
- String[] values = res.getStringArray(R.array.smart_mark_as_played_values);
- String[] entries = new String[values.length];
- for (int x = 0; x < values.length; x++) {
- if(x == 0) {
- entries[x] = res.getString(R.string.pref_smart_mark_as_played_disabled);
- } else {
- Integer v = Integer.parseInt(values[x]);
- if(v < 60) {
- entries[x] = res.getQuantityString(R.plurals.time_seconds_quantified, v, v);
- } else {
- v /= 60;
- entries[x] = res.getQuantityString(R.plurals.time_minutes_quantified, v, v);
- }
- }
- }
- pref.setEntries(entries);
- }
-
- private void setSelectedNetworksEnabled(boolean b) {
- if (selectedNetworks != null) {
- for (Preference p : selectedNetworks) {
- p.setEnabled(b);
- }
- }
- }
-
- private void setIntegrationsItemVisibility() {
- ui.findPreference(PreferenceController.PREF_SCREEN_FLATTR).setEnabled(FlattrUtils.hasAPICredentials());
- }
-
- @SuppressWarnings("deprecation")
- private void checkFlattrItemVisibility() {
- boolean hasFlattrToken = FlattrUtils.hasToken();
- ui.findPreference(PreferenceController.PREF_FLATTR_AUTH).setEnabled(!hasFlattrToken);
- ui.findPreference(PreferenceController.PREF_FLATTR_REVOKE).setEnabled(hasFlattrToken);
- ui.findPreference(PreferenceController.PREF_AUTO_FLATTR_PREFS).setEnabled(hasFlattrToken);
- }
-
- private void checkAutodownloadItemVisibility(boolean autoDownload) {
- ui.findPreference(UserPreferences.PREF_EPISODE_CACHE_SIZE).setEnabled(autoDownload);
- ui.findPreference(UserPreferences.PREF_ENABLE_AUTODL_ON_BATTERY).setEnabled(autoDownload);
- ui.findPreference(UserPreferences.PREF_ENABLE_AUTODL_WIFI_FILTER).setEnabled(autoDownload);
- ui.findPreference(UserPreferences.PREF_EPISODE_CLEANUP).setEnabled(autoDownload);
- ui.findPreference(UserPreferences.PREF_ENABLE_AUTODL_ON_MOBILE).setEnabled(autoDownload);
- setSelectedNetworksEnabled(autoDownload && UserPreferences.isEnableAutodownloadWifiFilter());
- }
-
- private void checkSonicItemVisibility() {
- if (Build.VERSION.SDK_INT < 16) {
- ListPreference p = (ListPreference) ui.findPreference(UserPreferences.PREF_MEDIA_PLAYER);
- p.setEntries(R.array.media_player_options_no_sonic);
- p.setEntryValues(R.array.media_player_values_no_sonic);
- }
- }
-
- private void setUpdateIntervalText() {
- Context context = ui.getActivity().getApplicationContext();
- String val;
- long interval = UserPreferences.getUpdateInterval();
- if(interval > 0) {
- int hours = (int) TimeUnit.MILLISECONDS.toHours(interval);
- String hoursStr = context.getResources().getQuantityString(R.plurals.time_hours_quantified, hours, hours);
- val = String.format(context.getString(R.string.pref_autoUpdateIntervallOrTime_every), hoursStr);
- } else {
- int[] timeOfDay = UserPreferences.getUpdateTimeOfDay();
- if(timeOfDay.length == 2) {
- Calendar cal = new GregorianCalendar();
- cal.set(Calendar.HOUR_OF_DAY, timeOfDay[0]);
- cal.set(Calendar.MINUTE, timeOfDay[1]);
- String timeOfDayStr = DateFormat.getTimeFormat(context).format(cal.getTime());
- val = String.format(context.getString(R.string.pref_autoUpdateIntervallOrTime_at),
- timeOfDayStr);
- } else {
- val = context.getString(R.string.pref_smart_mark_as_played_disabled); // TODO: Is this a bug? Otherwise document why is this related to smart mark???
- }
- }
- String summary = context.getString(R.string.pref_autoUpdateIntervallOrTime_sum) + "\n"
- + String.format(context.getString(R.string.pref_current_value), val);
- ui.findPreference(UserPreferences.PREF_UPDATE_INTERVAL).setSummary(summary);
- }
-
- private void setParallelDownloadsText(int downloads) {
- final Resources res = ui.getActivity().getResources();
-
- String s = Integer.toString(downloads)
- + res.getString(R.string.parallel_downloads_suffix);
- ui.findPreference(UserPreferences.PREF_PARALLEL_DOWNLOADS).setSummary(s);
- }
-
- private void setEpisodeCacheSizeText(int cacheSize) {
- final Resources res = ui.getActivity().getResources();
-
- String s;
- if (cacheSize == res.getInteger(
- R.integer.episode_cache_size_unlimited)) {
- s = res.getString(R.string.pref_episode_cache_unlimited);
- } else {
- s = Integer.toString(cacheSize)
- + res.getString(R.string.episodes_suffix);
- }
- ui.findPreference(UserPreferences.PREF_EPISODE_CACHE_SIZE).setSummary(s);
- }
-
- private void setDataFolderText() {
- File f = UserPreferences.getDataFolder(null);
- if (f != null) {
- ui.findPreference(PreferenceController.PREF_CHOOSE_DATA_DIR)
- .setSummary(f.getAbsolutePath());
- }
- }
-
- private static String blankIfNull(String val) {
- return val == null ? "" : val;
- }
-
- private void buildAutodownloadSelectedNetworksPreference() {
- final Activity activity = ui.getActivity();
-
- if (selectedNetworks != null) {
- clearAutodownloadSelectedNetworsPreference();
- }
- // get configured networks
- WifiManager wifiservice = (WifiManager) activity.getApplicationContext().getSystemService(Context.WIFI_SERVICE);
- List<WifiConfiguration> networks = wifiservice.getConfiguredNetworks();
-
- if (networks == null) {
- Log.e(TAG, "Couldn't get list of configure Wi-Fi networks");
- return;
- }
- Collections.sort(networks, (x, y) ->
- blankIfNull(x.SSID).compareTo(blankIfNull(y.SSID)));
- selectedNetworks = new CheckBoxPreference[networks.size()];
- List<String> prefValues = Arrays.asList(UserPreferences
- .getAutodownloadSelectedNetworks());
- PreferenceScreen prefScreen = ui.getPreferenceScreen();
- Preference.OnPreferenceClickListener clickListener = preference -> {
- if (preference instanceof CheckBoxPreference) {
- String key = preference.getKey();
- List<String> prefValuesList = new ArrayList<>(
- Arrays.asList(UserPreferences
- .getAutodownloadSelectedNetworks())
- );
- boolean newValue = ((CheckBoxPreference) preference)
- .isChecked();
- Log.d(TAG, "Selected network " + key + ". New state: " + newValue);
-
- int index = prefValuesList.indexOf(key);
- if (index >= 0 && !newValue) {
- // remove network
- prefValuesList.remove(index);
- } else if (index < 0 && newValue) {
- prefValuesList.add(key);
- }
-
- UserPreferences.setAutodownloadSelectedNetworks(
- prefValuesList.toArray(new String[prefValuesList.size()])
- );
- return true;
- } else {
- return false;
- }
- };
- // create preference for each known network. attach listener and set
- // value
- for (int i = 0; i < networks.size(); i++) {
- WifiConfiguration config = networks.get(i);
-
- CheckBoxPreference pref = new CheckBoxPreference(activity);
- String key = Integer.toString(config.networkId);
- pref.setTitle(config.SSID);
- pref.setKey(key);
- pref.setOnPreferenceClickListener(clickListener);
- pref.setPersistent(false);
- pref.setChecked(prefValues.contains(key));
- selectedNetworks[i] = pref;
- prefScreen.addPreference(pref);
- }
- }
-
- private void clearAutodownloadSelectedNetworsPreference() {
- if (selectedNetworks != null) {
- PreferenceScreen prefScreen = ui.getPreferenceScreen();
-
- for (CheckBoxPreference network : selectedNetworks) {
- if (network != null) {
- prefScreen.removePreference(network);
- }
- }
- }
- }
-
- private void showDrawerPreferencesDialog() {
- final Context context = ui.getActivity();
- final List<String> hiddenDrawerItems = UserPreferences.getHiddenDrawerItems();
- final String[] navTitles = context.getResources().getStringArray(R.array.nav_drawer_titles);
- final String[] NAV_DRAWER_TAGS = MainActivity.NAV_DRAWER_TAGS;
- boolean[] checked = new boolean[MainActivity.NAV_DRAWER_TAGS.length];
- for(int i=0; i < NAV_DRAWER_TAGS.length; i++) {
- String tag = NAV_DRAWER_TAGS[i];
- if(!hiddenDrawerItems.contains(tag)) {
- checked[i] = true;
- }
- }
- AlertDialog.Builder builder = new AlertDialog.Builder(context);
- builder.setTitle(R.string.drawer_preferences);
- builder.setMultiChoiceItems(navTitles, checked, (dialog, which, isChecked) -> {
- if (isChecked) {
- hiddenDrawerItems.remove(NAV_DRAWER_TAGS[which]);
- } else {
- hiddenDrawerItems.add(NAV_DRAWER_TAGS[which]);
- }
- });
- builder.setPositiveButton(R.string.confirm_label, (dialog, which) ->
- UserPreferences.setHiddenDrawerItems(hiddenDrawerItems));
- builder.setNegativeButton(R.string.cancel_label, null);
- builder.create().show();
- }
-
- private void showNotificationButtonsDialog() {
- final Context context = ui.getActivity();
- final List<Integer> preferredButtons = UserPreferences.getCompactNotificationButtons();
- final String[] allButtonNames = context.getResources().getStringArray(
- R.array.compact_notification_buttons_options);
- boolean[] checked = new boolean[allButtonNames.length]; // booleans default to false in java
-
- for(int i=0; i < checked.length; i++) {
- if(preferredButtons.contains(i)) {
- checked[i] = true;
- }
- }
-
- AlertDialog.Builder builder = new AlertDialog.Builder(context);
- builder.setTitle(String.format(context.getResources().getString(
- R.string.pref_compact_notification_buttons_dialog_title), 2));
- builder.setMultiChoiceItems(allButtonNames, checked, (dialog, which, isChecked) -> {
- checked[which] = isChecked;
-
- if (isChecked) {
- if (preferredButtons.size() < 2) {
- preferredButtons.add(which);
- } else {
- // Only allow a maximum of two selections. This is because the notification
- // on the lock screen can only display 3 buttons, and the play/pause button
- // is always included.
- checked[which] = false;
- ListView selectionView = ((AlertDialog) dialog).getListView();
- selectionView.setItemChecked(which, false);
- Snackbar.make(
- selectionView,
- String.format(context.getResources().getString(
- R.string.pref_compact_notification_buttons_dialog_error), 2),
- Snackbar.LENGTH_SHORT).show();
- }
- } else {
- preferredButtons.remove((Integer) which);
- }
- });
- builder.setPositiveButton(R.string.confirm_label, (dialog, which) ->
- UserPreferences.setCompactNotificationButtons(preferredButtons));
- builder.setNegativeButton(R.string.cancel_label, null);
- builder.create().show();
- }
-
- // CHOOSE DATA FOLDER
-
- private void requestPermission() {
- ActivityCompat.requestPermissions(ui.getActivity(), EXTERNAL_STORAGE_PERMISSIONS,
- PERMISSION_REQUEST_EXTERNAL_STORAGE);
- }
-
- private void openDirectoryChooser() {
- Activity activity = ui.getActivity();
- Intent intent = new Intent(activity, DirectoryChooserActivity.class);
- activity.startActivityForResult(intent, DirectoryChooserActivity.RESULT_CODE_DIR_SELECTED);
- }
-
- private void showChooseDataFolderDialog() {
- ChooseDataFolderDialog.showDialog(
- ui.getActivity(), new ChooseDataFolderDialog.RunnableWithString() {
- @Override
- public void run(final String folder) {
- UserPreferences.setDataFolder(folder);
- setDataFolderText();
- }
- });
- }
-
- // UPDATE TIME/INTERVAL DIALOG
-
- private void showUpdateIntervalTimePreferencesDialog() {
- final Context context = ui.getActivity();
-
- MaterialDialog.Builder builder = new MaterialDialog.Builder(context);
- builder.title(R.string.pref_autoUpdateIntervallOrTime_title);
- builder.content(R.string.pref_autoUpdateIntervallOrTime_message);
- builder.positiveText(R.string.pref_autoUpdateIntervallOrTime_Interval);
- builder.negativeText(R.string.pref_autoUpdateIntervallOrTime_TimeOfDay);
- builder.neutralText(R.string.pref_autoUpdateIntervallOrTime_Disable);
- builder.onPositive((dialog, which) -> {
- AlertDialog.Builder builder1 = new AlertDialog.Builder(context);
- builder1.setTitle(context.getString(R.string.pref_autoUpdateIntervallOrTime_Interval));
- final String[] values = context.getResources().getStringArray(R.array.update_intervall_values);
- final String[] entries = getUpdateIntervalEntries(values);
- long currInterval = UserPreferences.getUpdateInterval();
- int checkedItem = -1;
- if(currInterval > 0) {
- String currIntervalStr = String.valueOf(TimeUnit.MILLISECONDS.toHours(currInterval));
- checkedItem = ArrayUtils.indexOf(values, currIntervalStr);
- }
- builder1.setSingleChoiceItems(entries, checkedItem, (dialog1, which1) -> {
- int hours = Integer.parseInt(values[which1]);
- UserPreferences.setUpdateInterval(hours);
- dialog1.dismiss();
- setUpdateIntervalText();
- });
- builder1.setNegativeButton(context.getString(R.string.cancel_label), null);
- builder1.show();
- });
- builder.onNegative((dialog, which) -> {
- int hourOfDay = 7, minute = 0;
- int[] updateTime = UserPreferences.getUpdateTimeOfDay();
- if (updateTime.length == 2) {
- hourOfDay = updateTime[0];
- minute = updateTime[1];
- }
- TimePickerDialog timePickerDialog = new TimePickerDialog(context,
- (view, selectedHourOfDay, selectedMinute) -> {
- if (view.getTag() == null) { // onTimeSet() may get called twice!
- view.setTag("TAGGED");
- UserPreferences.setUpdateTimeOfDay(selectedHourOfDay, selectedMinute);
- setUpdateIntervalText();
- }
- }, hourOfDay, minute, DateFormat.is24HourFormat(context));
- timePickerDialog.setTitle(context.getString(R.string.pref_autoUpdateIntervallOrTime_TimeOfDay));
- timePickerDialog.show();
- });
- builder.onNeutral((dialog, which) -> {
- UserPreferences.setUpdateInterval(0);
- setUpdateIntervalText();
- });
- builder.show();
- }
-
-
- public interface PreferenceUI {
-
- void setFragment(PreferenceFragmentCompat fragment);
- PreferenceFragmentCompat getFragment();
-
- /**
- * Finds a preference based on its key.
- */
- Preference findPreference(CharSequence key);
-
- PreferenceScreen getPreferenceScreen();
-
- AppCompatActivity getActivity();
- }
-}
diff --git a/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceUpgrader.java b/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceUpgrader.java
new file mode 100644
index 000000000..e56703598
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceUpgrader.java
@@ -0,0 +1,69 @@
+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.core.preferences.UserPreferences;
+import de.danoeh.antennapod.core.util.gui.NotificationUtils;
+
+public class PreferenceUpgrader {
+ private static final String PREF_CONFIGURED_VERSION = "configuredVersion";
+ private static final String PREF_NAME = "PreferenceUpgrader";
+
+ private static SharedPreferences prefs;
+
+ public static void checkUpgrades(Context context) {
+ prefs = PreferenceManager.getDefaultSharedPreferences(context);
+ SharedPreferences upgraderPrefs = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);
+ int oldVersion = upgraderPrefs.getInt(PREF_CONFIGURED_VERSION, 1070200);
+ int newVersion = BuildConfig.VERSION_CODE;
+
+ if (oldVersion != newVersion) {
+ NotificationUtils.createChannels(context);
+
+ upgraderPrefs.edit().putInt(PREF_CONFIGURED_VERSION, newVersion).apply();
+ upgrade(oldVersion);
+ }
+ }
+
+ private static void upgrade(int oldVersion) {
+ if (oldVersion < 1070196) {
+ // migrate episode cleanup value (unit changed from days to hours)
+ int oldValueInDays = UserPreferences.getEpisodeCleanupValue();
+ if (oldValueInDays > 0) {
+ UserPreferences.setEpisodeCleanupValue(oldValueInDays * 24);
+ } // else 0 or special negative values, no change needed
+ }
+ if (oldVersion < 1070197) {
+ if (prefs.getBoolean("prefMobileUpdate", false)) {
+ prefs.edit().putString("prefMobileUpdateAllowed", "everything").apply();
+ }
+ }
+ if (oldVersion < 1070300) {
+ UserPreferences.restartUpdateAlarm();
+
+ if (UserPreferences.getMediaPlayer().equals("builtin")) {
+ prefs.edit().putString(UserPreferences.PREF_MEDIA_PLAYER,
+ UserPreferences.PREF_MEDIA_PLAYER_EXOPLAYER).apply();
+ }
+
+ if (prefs.getBoolean("prefEnableAutoDownloadOnMobile", false)) {
+ UserPreferences.setAllowMobileAutoDownload(true);
+ }
+ switch (prefs.getString("prefMobileUpdateAllowed", "images")) {
+ case "everything":
+ UserPreferences.setAllowMobileFeedRefresh(true);
+ UserPreferences.setAllowMobileEpisodeDownload(true);
+ UserPreferences.setAllowMobileImages(true);
+ break;
+ case "images":
+ UserPreferences.setAllowMobileImages(true);
+ break;
+ case "nothing":
+ UserPreferences.setAllowMobileImages(false);
+ break;
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/view/EmptyViewHandler.java b/app/src/main/java/de/danoeh/antennapod/view/EmptyViewHandler.java
new file mode 100644
index 000000000..8b886e699
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/view/EmptyViewHandler.java
@@ -0,0 +1,112 @@
+package de.danoeh.antennapod.view;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.support.annotation.AttrRes;
+import android.support.v4.content.ContextCompat;
+import android.support.v7.widget.RecyclerView;
+import android.util.TypedValue;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.ListView;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
+
+import de.danoeh.antennapod.R;
+
+public class EmptyViewHandler {
+ private boolean layoutAdded = false;
+ private RecyclerView recyclerView;
+ private RecyclerView.Adapter adapter;
+
+ private final Context context;
+ private final View emptyView;
+ private final TextView tvTitle;
+ private final TextView tvMessage;
+ private final ImageView ivIcon;
+
+ public EmptyViewHandler(Context context) {
+ emptyView = View.inflate(context, R.layout.empty_view_layout, null);
+ this.context = context;
+ tvTitle = emptyView.findViewById(R.id.emptyViewTitle);
+ tvMessage = emptyView.findViewById(R.id.emptyViewMessage);
+ ivIcon = emptyView.findViewById(R.id.emptyViewIcon);
+ }
+
+ public void setTitle(int title) {
+ tvTitle.setText(title);
+ }
+
+ public void setMessage(int message) {
+ tvMessage.setText(message);
+ }
+
+ public void setIcon(@AttrRes int iconAttr) {
+ TypedValue typedValue = new TypedValue();
+ context.getTheme().resolveAttribute(iconAttr, typedValue, true);
+ Drawable d = ContextCompat.getDrawable(context, typedValue.resourceId);
+ ivIcon.setImageDrawable(d);
+ ivIcon.setVisibility(View.VISIBLE);
+ }
+
+ public void hide() {
+ emptyView.setVisibility(View.GONE);
+ }
+
+ public void attachToListView(ListView listView) {
+ if (layoutAdded) {
+ throw new IllegalStateException("Can not attach to ListView multiple times");
+ }
+ layoutAdded = true;
+ ((ViewGroup) listView.getParent()).addView(emptyView);
+ listView.setEmptyView(emptyView);
+ }
+
+ public void attachToRecyclerView(RecyclerView recyclerView) {
+ if (layoutAdded) {
+ throw new IllegalStateException("Can not attach to ListView multiple times");
+ }
+ layoutAdded = true;
+ this.recyclerView = recyclerView;
+ ViewGroup parent = ((ViewGroup) recyclerView.getParent());
+ parent.addView(emptyView);
+ updateAdapter(recyclerView.getAdapter());
+
+ if (parent instanceof RelativeLayout) {
+ RelativeLayout.LayoutParams layoutParams =
+ (RelativeLayout.LayoutParams)emptyView.getLayoutParams();
+ layoutParams.addRule(RelativeLayout.CENTER_IN_PARENT, RelativeLayout.TRUE);
+ emptyView.setLayoutParams(layoutParams);
+ }
+ }
+
+ public void updateAdapter(RecyclerView.Adapter adapter) {
+ if (this.adapter != null) {
+ this.adapter.unregisterAdapterDataObserver(adapterObserver);
+ }
+ this.adapter = adapter;
+ if (adapter != null) {
+ adapter.registerAdapterDataObserver(adapterObserver);
+ }
+ updateVisibility();
+ }
+
+ private final SimpleAdapterDataObserver adapterObserver = new SimpleAdapterDataObserver() {
+ @Override
+ public void anythingChanged() {
+ updateVisibility();
+ }
+ };
+
+ private void updateVisibility() {
+ boolean empty;
+ if (adapter == null) {
+ empty = true;
+ } else {
+ empty = adapter.getItemCount() == 0;
+ }
+ recyclerView.setVisibility(empty ? View.GONE : View.VISIBLE);
+ emptyView.setVisibility(empty ? View.VISIBLE : View.GONE);
+ }
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/view/SimpleAdapterDataObserver.java b/app/src/main/java/de/danoeh/antennapod/view/SimpleAdapterDataObserver.java
new file mode 100644
index 000000000..45c3a35bd
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/view/SimpleAdapterDataObserver.java
@@ -0,0 +1,41 @@
+package de.danoeh.antennapod.view;
+
+import android.support.annotation.Nullable;
+import android.support.v7.widget.RecyclerView;
+
+/**
+ * AdapterDataObserver that relays all events to the method anythingChanged().
+ */
+public abstract class SimpleAdapterDataObserver extends RecyclerView.AdapterDataObserver {
+ public abstract void anythingChanged();
+
+ @Override
+ public void onChanged() {
+ anythingChanged();
+ }
+
+ @Override
+ public void onItemRangeChanged(int positionStart, int itemCount) {
+ anythingChanged();
+ }
+
+ @Override
+ public void onItemRangeChanged(int positionStart, int itemCount, @Nullable Object payload) {
+ anythingChanged();
+ }
+
+ @Override
+ public void onItemRangeInserted(int positionStart, int itemCount) {
+ anythingChanged();
+ }
+
+ @Override
+ public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
+ anythingChanged();
+ }
+
+ @Override
+ public void onItemRangeRemoved(int positionStart, int itemCount) {
+ anythingChanged();
+ }
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/view/WrappingGridView.java b/app/src/main/java/de/danoeh/antennapod/view/WrappingGridView.java
new file mode 100644
index 000000000..37792b4d1
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/view/WrappingGridView.java
@@ -0,0 +1,35 @@
+package de.danoeh.antennapod.view;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.GridView;
+
+/**
+ * Source: https://stackoverflow.com/a/46350213/
+ */
+public class WrappingGridView extends GridView {
+
+ public WrappingGridView(Context context) {
+ super(context);
+ }
+
+ public WrappingGridView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public WrappingGridView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ int heightSpec = heightMeasureSpec;
+ if (getLayoutParams().height == LayoutParams.WRAP_CONTENT) {
+ // The great Android "hackatlon", the love, the magic.
+ // The two leftmost bits in the height measure spec have
+ // a special meaning, hence we can't use them to describe height.
+ heightSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);
+ }
+ super.onMeasure(widthMeasureSpec, heightSpec);
+ }
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/viewmodel/FeedSettingsViewModel.java b/app/src/main/java/de/danoeh/antennapod/viewmodel/FeedSettingsViewModel.java
new file mode 100644
index 000000000..fe11a645c
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/viewmodel/FeedSettingsViewModel.java
@@ -0,0 +1,30 @@
+package de.danoeh.antennapod.viewmodel;
+
+import android.arch.lifecycle.ViewModel;
+import de.danoeh.antennapod.core.feed.Feed;
+import de.danoeh.antennapod.core.storage.DBReader;
+import io.reactivex.Maybe;
+
+public class FeedSettingsViewModel extends ViewModel {
+ private Feed feed;
+
+ public Maybe<Feed> getFeed(long feedId) {
+ if (feed == null) {
+ return loadFeed(feedId);
+ } else {
+ return Maybe.just(feed);
+ }
+ }
+
+ private Maybe<Feed> loadFeed(long feedId) {
+ return Maybe.create(emitter -> {
+ Feed feed = DBReader.getFeed(feedId);
+ if (feed != null) {
+ this.feed = feed;
+ emitter.onSuccess(feed);
+ } else {
+ emitter.onComplete();
+ }
+ });
+ }
+}