summaryrefslogtreecommitdiff
path: root/app/src/main/java/de/danoeh
diff options
context:
space:
mode:
Diffstat (limited to 'app/src/main/java/de/danoeh')
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/DownloadAuthenticationActivity.java111
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java71
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java115
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java107
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/VideoplayerActivity.java17
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/WidgetConfigActivity.java52
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/gpoddernet/GpodnetAuthenticationActivity.java395
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/ChaptersListAdapter.java31
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/DownloadLogAdapter.java10
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/DownloadlistAdapter.java10
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistDescriptionAdapter.java3
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/FeedSearchResultAdapter.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/NavListAdapter.java6
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/StatisticsListAdapter.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/SubscriptionsAdapter.java5
-rw-r--r--app/src/main/java/de/danoeh/antennapod/config/ClientConfigurator.java6
-rw-r--r--app/src/main/java/de/danoeh/antennapod/config/DownloadServiceCallbacksImpl.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/config/PlaybackServiceCallbacksImpl.java24
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/AuthenticationDialog.java35
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/EpisodesApplyActionFragment.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/FilterDialog.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/GpodnetSetHostnameDialog.java59
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/PlaybackControlsDialog.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/SleepTimerDialog.java4
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/SubscriptionsFilterDialog.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/VariableSpeedDialog.java4
-rw-r--r--app/src/main/java/de/danoeh/antennapod/discovery/GpodnetPodcastSearcher.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/discovery/PodcastSearcherRegistry.java8
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/AddFeedFragment.java20
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java5
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/AudioPlayerFragment.java114
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ChaptersFragment.java16
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/CoverFragment.java46
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/DownloadsFragment.java17
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/EpisodesFragment.java14
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java8
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java31
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/FeedInfoFragment.java16
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/FeedItemlistFragment.java59
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/FeedSettingsFragment.java32
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java21
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java32
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/NavDrawerFragment.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java14
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java15
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/SubscriptionFragment.java33
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/PodcastListFragment.java104
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/TagListFragment.java85
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/AutoDownloadPreferencesFragment.java4
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/GpodderAuthenticationFragment.java307
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/GpodderPreferencesFragment.java14
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/NetworkPreferencesFragment.java8
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/UserInterfacePreferencesFragment.java16
-rw-r--r--app/src/main/java/de/danoeh/antennapod/menuhandler/MenuItemUtils.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/preferences/PreferenceUpgrader.java12
-rw-r--r--app/src/main/java/de/danoeh/antennapod/view/ChapterSeekBar.java129
-rw-r--r--app/src/main/java/de/danoeh/antennapod/view/CircularProgressBar.java93
-rw-r--r--app/src/main/java/de/danoeh/antennapod/view/EpisodeItemListRecyclerView.java4
-rw-r--r--app/src/main/java/de/danoeh/antennapod/view/PlaybackSpeedIndicatorView.java113
-rw-r--r--app/src/main/java/de/danoeh/antennapod/view/RecursiveRadioGroup.java67
-rw-r--r--app/src/main/java/de/danoeh/antennapod/view/SquareImageView.java62
-rw-r--r--app/src/main/java/de/danoeh/antennapod/view/WrappingGridView.java35
-rw-r--r--app/src/main/java/de/danoeh/antennapod/view/viewholder/DownloadItemViewHolder.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/view/viewholder/EpisodeItemViewHolder.java41
64 files changed, 1236 insertions, 1446 deletions
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/DownloadAuthenticationActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/DownloadAuthenticationActivity.java
index 912038e4c..0f1d38db6 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/DownloadAuthenticationActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/DownloadAuthenticationActivity.java
@@ -1,95 +1,76 @@
package de.danoeh.antennapod.activity;
-import android.app.Activity;
-import android.content.Intent;
import android.os.Bundle;
-
-import androidx.annotation.NonNull;
+import android.text.TextUtils;
import androidx.appcompat.app.AppCompatActivity;
-import android.widget.Button;
-import android.widget.EditText;
-import android.widget.TextView;
-
-import org.apache.commons.lang3.Validate;
-
import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.core.feed.FeedMedia;
+import de.danoeh.antennapod.core.feed.FeedPreferences;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.download.DownloadRequest;
+import de.danoeh.antennapod.core.storage.DBReader;
+import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.storage.DownloadRequester;
+import de.danoeh.antennapod.dialog.AuthenticationDialog;
+import io.reactivex.Completable;
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.schedulers.Schedulers;
+import org.apache.commons.lang3.Validate;
+
/**
* Shows a username and a password text field.
* The activity MUST be started with the ARG_DOWNlOAD_REQUEST argument set to a non-null value.
- * Other arguments are optional.
- * The activity's result will be the same DownloadRequest with the entered username and password.
*/
public class DownloadAuthenticationActivity extends AppCompatActivity {
/**
- * The download request object that contains information about the resource that requires a username and a password
+ * The download request object that contains information about the resource that requires a username and a password.
*/
public static final String ARG_DOWNLOAD_REQUEST = "request";
- /**
- * True if the request should be sent to the DownloadRequester when this activity is finished, false otherwise.
- * The default value is false.
- */
- public static final String ARG_SEND_TO_DOWNLOAD_REQUESTER_BOOL = "send_to_downloadrequester";
-
- private static final String RESULT_REQUEST = "request";
-
- private EditText etxtUsername;
- private EditText etxtPassword;
@Override
protected void onCreate(Bundle savedInstanceState) {
- setTheme(UserPreferences.getNoTitleTheme());
+ setTheme(UserPreferences.getTranslucentTheme());
super.onCreate(savedInstanceState);
- setContentView(R.layout.download_authentication_activity);
- TextView txtvDescription = findViewById(R.id.txtvDescription);
- etxtUsername = findViewById(R.id.etxtUsername);
- etxtPassword = findViewById(R.id.etxtPassword);
- Button butConfirm = findViewById(R.id.butConfirm);
- Button butCancel = findViewById(R.id.butCancel);
-
Validate.isTrue(getIntent().hasExtra(ARG_DOWNLOAD_REQUEST), "Download request missing");
DownloadRequest request = getIntent().getParcelableExtra(ARG_DOWNLOAD_REQUEST);
- boolean sendToDownloadRequester = getIntent().getBooleanExtra(ARG_SEND_TO_DOWNLOAD_REQUESTER_BOOL, false);
-
- String newDescription = txtvDescription.getText() + ":\n\n" + request.getTitle();
- txtvDescription.setText(newDescription);
-
- if (savedInstanceState != null) {
- etxtUsername.setText(savedInstanceState.getString("username"));
- etxtPassword.setText(savedInstanceState.getString("password"));
- }
- butConfirm.setOnClickListener(v -> {
- String username = etxtUsername.getText().toString();
- String password = etxtPassword.getText().toString();
- request.setUsername(username);
- request.setPassword(password);
- Intent result = new Intent();
- result.putExtra(RESULT_REQUEST, request);
- setResult(Activity.RESULT_OK, result);
-
- if (sendToDownloadRequester) {
- DownloadRequester.getInstance().download(DownloadAuthenticationActivity.this, request);
+ new AuthenticationDialog(this, R.string.authentication_label, true, "", "") {
+ @Override
+ protected void onConfirmed(String username, String password) {
+ Completable.fromAction(
+ () -> {
+ request.setUsername(username);
+ request.setPassword(password);
+
+ if (request.getFeedfileType() == FeedMedia.FEEDFILETYPE_FEEDMEDIA) {
+ long mediaId = request.getFeedfileId();
+ FeedMedia media = DBReader.getFeedMedia(mediaId);
+ if (media != null) {
+ FeedPreferences preferences = media.getItem().getFeed().getPreferences();
+ if (TextUtils.isEmpty(preferences.getPassword())
+ || TextUtils.isEmpty(preferences.getUsername())) {
+ preferences.setUsername(username);
+ preferences.setPassword(password);
+ DBWriter.setFeedPreferences(preferences);
+ }
+ }
+ }
+ })
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(() -> {
+ DownloadRequester.getInstance().download(DownloadAuthenticationActivity.this, request);
+ finish();
+ });
}
- finish();
- });
-
- butCancel.setOnClickListener(v -> {
- setResult(Activity.RESULT_CANCELED);
- finish();
- });
- }
-
- @Override
- protected void onSaveInstanceState(@NonNull Bundle outState) {
- super.onSaveInstanceState(outState);
- outState.putString("username", etxtUsername.getText().toString());
- outState.putString("password", etxtPassword.getText().toString());
+ @Override
+ protected void onCancelled() {
+ finish();
+ }
+ }.show();
}
}
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 d1716e009..b5edcc878 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java
@@ -6,6 +6,7 @@ import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.media.AudioManager;
+import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
@@ -40,7 +41,7 @@ import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.receiver.MediaButtonReceiver;
import de.danoeh.antennapod.core.service.playback.PlaybackService;
import de.danoeh.antennapod.core.util.StorageUtils;
-import de.danoeh.antennapod.core.util.ThemeUtils;
+import de.danoeh.antennapod.ui.common.ThemeUtils;
import de.danoeh.antennapod.core.util.download.AutoUpdateManager;
import de.danoeh.antennapod.dialog.RatingDialog;
import de.danoeh.antennapod.fragment.AddFeedFragment;
@@ -51,9 +52,11 @@ import de.danoeh.antennapod.fragment.FeedItemlistFragment;
import de.danoeh.antennapod.fragment.NavDrawerFragment;
import de.danoeh.antennapod.fragment.PlaybackHistoryFragment;
import de.danoeh.antennapod.fragment.QueueFragment;
+import de.danoeh.antennapod.fragment.SearchFragment;
import de.danoeh.antennapod.fragment.SubscriptionFragment;
import de.danoeh.antennapod.fragment.TransitionEffect;
import de.danoeh.antennapod.preferences.PreferenceUpgrader;
+import de.danoeh.antennapod.ui.appstartintent.MainActivityStarter;
import de.danoeh.antennapod.view.LockableBottomSheetBehavior;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.Validate;
@@ -75,7 +78,6 @@ public class MainActivity extends CastEnabledActivity {
public static final String EXTRA_FRAGMENT_TAG = "fragment_tag";
public static final String EXTRA_FRAGMENT_ARGS = "fragment_args";
public static final String EXTRA_FEED_ID = "fragment_feed_id";
- public static final String EXTRA_OPEN_PLAYER = "open_player";
public static final String EXTRA_REFRESH_ON_START = "refresh_on_start";
public static final String EXTRA_STARTED_FROM_SEARCH = "started_from_search";
public static final String KEY_GENERATED_VIEW_ID = "generated_view_id";
@@ -184,16 +186,16 @@ public class MainActivity extends CastEnabledActivity {
}
};
- public void setupToolbarToggle(@Nullable Toolbar toolbar) {
+ public void setupToolbarToggle(@NonNull Toolbar toolbar, boolean displayUpArrow) {
if (drawerLayout != null) { // Tablet layout does not have a drawer
drawerLayout.removeDrawerListener(drawerToggle);
drawerToggle = new ActionBarDrawerToggle(this, drawerLayout, toolbar,
R.string.drawer_open, R.string.drawer_close);
drawerLayout.addDrawerListener(drawerToggle);
drawerToggle.syncState();
- drawerToggle.setDrawerIndicatorEnabled(getSupportFragmentManager().getBackStackEntryCount() == 0);
+ drawerToggle.setDrawerIndicatorEnabled(!displayUpArrow);
drawerToggle.setToolbarNavigationClickListener(v -> getSupportFragmentManager().popBackStack());
- } else if (getSupportFragmentManager().getBackStackEntryCount() == 0) {
+ } else if (!displayUpArrow) {
toolbar.setNavigationIcon(null);
} else {
toolbar.setNavigationIcon(ThemeUtils.getDrawableFromAttr(this, R.attr.homeAsUpIndicator));
@@ -508,9 +510,11 @@ public class MainActivity extends CastEnabledActivity {
}
}
sheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
- } else if (intent.getBooleanExtra(EXTRA_OPEN_PLAYER, false)) {
+ } else if (intent.getBooleanExtra(MainActivityStarter.EXTRA_OPEN_PLAYER, false)) {
sheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
bottomSheetCallback.onSlide(null, 1.0f);
+ } else if (Intent.ACTION_VIEW.equals(intent.getAction())) {
+ handleDeeplink(intent.getData());
}
// to avoid handling the intent twice when the configuration changes
setIntent(new Intent(MainActivity.this, MainActivity.class));
@@ -520,6 +524,7 @@ public class MainActivity extends CastEnabledActivity {
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
setIntent(intent);
+ handleNavIntent();
}
public Snackbar showSnackbarAbovePlayer(CharSequence text, int duration) {
@@ -540,6 +545,59 @@ public class MainActivity extends CastEnabledActivity {
return showSnackbarAbovePlayer(getResources().getText(text), duration);
}
+ /**
+ * Handles the deep link incoming via App Actions.
+ * Performs an in-app search or opens the relevant feature of the app
+ * depending on the query.
+ *
+ * @param uri incoming deep link
+ */
+ private void handleDeeplink(Uri uri) {
+ if (uri == null || uri.getPath() == null) {
+ return;
+ }
+ Log.d(TAG, "Handling deeplink: " + uri.toString());
+ switch (uri.getPath()) {
+ case "/deeplink/search":
+ String query = uri.getQueryParameter("query");
+ if (query == null) {
+ return;
+ }
+
+ this.loadChildFragment(SearchFragment.newInstance(query));
+ break;
+ case "/deeplink/main":
+ String feature = uri.getQueryParameter("page");
+ if (feature == null) {
+ return;
+ }
+ switch (feature) {
+ case "DOWNLOADS":
+ loadFragment(DownloadsFragment.TAG, null);
+ break;
+ case "HISTORY":
+ loadFragment(PlaybackHistoryFragment.TAG, null);
+ break;
+ case "EPISODES":
+ loadFragment(EpisodesFragment.TAG, null);
+ break;
+ case "QUEUE":
+ loadFragment(QueueFragment.TAG, null);
+ break;
+ case "SUBSCRIPTIONS":
+ loadFragment(SubscriptionFragment.TAG, null);
+ break;
+ default:
+ showSnackbarAbovePlayer(getString(R.string.app_action_not_found, feature),
+ Snackbar.LENGTH_LONG);
+ return;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
//Hardware keyboard support
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
@@ -592,5 +650,4 @@ public class MainActivity extends CastEnabledActivity {
}
return super.onKeyUp(keyCode, event);
}
-
}
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 deb2fe0db..56a66ba93 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java
@@ -1,10 +1,8 @@
package de.danoeh.antennapod.activity;
-import android.Manifest;
import android.annotation.TargetApi;
import android.content.Intent;
import android.content.SharedPreferences;
-import android.content.pm.PackageManager;
import android.graphics.PixelFormat;
import android.os.Build;
import android.os.Bundle;
@@ -17,7 +15,6 @@ import android.widget.ImageButton;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
import android.widget.TextView;
-import android.widget.Toast;
import com.bumptech.glide.Glide;
@@ -28,17 +25,16 @@ import org.greenrobot.eventbus.ThreadMode;
import java.text.NumberFormat;
-import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
-import androidx.core.app.ActivityCompat;
+import androidx.cardview.widget.CardView;
import androidx.core.app.ActivityOptionsCompat;
-import androidx.core.content.ContextCompat;
+import androidx.interpolator.view.animation.FastOutSlowInInterpolator;
+
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
-import de.danoeh.antennapod.core.feed.MediaType;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.playback.PlaybackService;
import de.danoeh.antennapod.core.storage.DBReader;
@@ -50,11 +46,9 @@ import de.danoeh.antennapod.core.util.ShareUtils;
import de.danoeh.antennapod.core.util.StorageUtils;
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;
import de.danoeh.antennapod.core.util.playback.Playable;
import de.danoeh.antennapod.core.util.playback.PlaybackController;
-import de.danoeh.antennapod.core.util.playback.PlaybackServiceStarter;
import de.danoeh.antennapod.dialog.PlaybackControlsDialog;
import de.danoeh.antennapod.dialog.ShareDialog;
import de.danoeh.antennapod.dialog.SkipPreferenceDialog;
@@ -64,7 +58,6 @@ import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
-
/**
* Provides general features which are both needed for playing audio and video
* files.
@@ -72,9 +65,6 @@ import io.reactivex.schedulers.Schedulers;
public abstract class MediaplayerActivity extends CastEnabledActivity implements OnSeekBarChangeListener {
private static final String TAG = "MediaplayerActivity";
private static final String PREFS = "MediaPlayerActivityPreferences";
- private static final String PREF_SHOW_TIME_LEFT = "showTimeLeft";
- private static final int REQUEST_CODE_STORAGE_PLAY_VIDEO = 42;
- private static final int REQUEST_CODE_STORAGE_PLAY_AUDIO = 43;
PlaybackController controller;
@@ -87,6 +77,8 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
private ImageButton butFF;
private TextView txtvFF;
private ImageButton butSkip;
+ private CardView cardViewSeek;
+ private TextView txtvSeek;
private boolean showTimeLeft = false;
@@ -96,12 +88,6 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
private PlaybackController newPlaybackController() {
return new PlaybackController(this) {
-
- @Override
- public void setupGUI() {
- MediaplayerActivity.this.setupGUI();
- }
-
@Override
public void onPositionObserverUpdate() {
MediaplayerActivity.this.onPositionObserverUpdate();
@@ -143,8 +129,8 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
}
@Override
- public boolean loadMediaInfo() {
- return MediaplayerActivity.this.loadMediaInfo();
+ public void loadMediaInfo() {
+ MediaplayerActivity.this.loadMediaInfo();
}
@Override
@@ -467,17 +453,15 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
* to the PlaybackService to ensure that the activity has the right
* FeedMedia object.
*/
- boolean loadMediaInfo() {
+ void loadMediaInfo() {
Log.d(TAG, "loadMediaInfo()");
- if(controller == null || controller.getMedia() == null) {
- return false;
+ if (controller == null || controller.getMedia() == null) {
+ return;
}
- SharedPreferences prefs = getSharedPreferences(PREFS, MODE_PRIVATE);
- showTimeLeft = prefs.getBoolean(PREF_SHOW_TIME_LEFT, false);
+ showTimeLeft = UserPreferences.shouldShowRemainingTime();
onPositionObserverUpdate();
checkFavorite();
updatePlaybackSpeedButton();
- return true;
}
void updatePlaybackSpeedButton() {
@@ -492,9 +476,11 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
setContentView(getContentViewResourceId());
sbPosition = findViewById(R.id.sbPosition);
txtvPosition = findViewById(R.id.txtvPosition);
+ cardViewSeek = findViewById(R.id.cardViewSeek);
+ txtvSeek = findViewById(R.id.txtvSeek);
SharedPreferences prefs = getSharedPreferences(PREFS, MODE_PRIVATE);
- showTimeLeft = prefs.getBoolean(PREF_SHOW_TIME_LEFT, false);
+ showTimeLeft = UserPreferences.shouldShowRemainingTime();
Log.d("timeleft", showTimeLeft ? "true" : "false");
txtvLength = findViewById(R.id.txtvLength);
if (txtvLength != null) {
@@ -518,9 +504,7 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
}
txtvLength.setText(length);
- SharedPreferences.Editor editor = prefs.edit();
- editor.putBoolean(PREF_SHOW_TIME_LEFT, showTimeLeft);
- editor.apply();
+ UserPreferences.setShowRemainTimeSetting(showTimeLeft);
Log.d("timeleft on click", showTimeLeft ? "true" : "false");
});
}
@@ -618,21 +602,21 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
}
if (fromUser) {
prog = progress / ((float) seekBar.getMax());
- int duration = controller.getDuration();
TimeSpeedConverter converter = new TimeSpeedConverter(controller.getCurrentPlaybackSpeedMultiplier());
- int position = converter.convert((int) (prog * duration));
- txtvPosition.setText(Converter.getDurationStringLong(position));
-
- if (showTimeLeft) {
- int timeLeft = converter.convert(duration - (int) (prog * duration));
- txtvLength.setText("-" + Converter.getDurationStringLong(timeLeft));
- }
+ int position = converter.convert((int) (prog * controller.getDuration()));
+ txtvSeek.setText(Converter.getDurationStringLong(position));
}
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
-
+ cardViewSeek.setScaleX(.8f);
+ cardViewSeek.setScaleY(.8f);
+ cardViewSeek.animate()
+ .setInterpolator(new FastOutSlowInInterpolator())
+ .alpha(1f).scaleX(1f).scaleY(1f)
+ .setDuration(200)
+ .start();
}
@Override
@@ -640,6 +624,13 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
if (controller != null) {
controller.seekTo((int) (prog * controller.getDuration()));
}
+ cardViewSeek.setScaleX(1f);
+ cardViewSeek.setScaleY(1f);
+ cardViewSeek.animate()
+ .setInterpolator(new FastOutSlowInInterpolator())
+ .alpha(0f).scaleX(.8f).scaleY(.8f)
+ .setDuration(200)
+ .start();
}
private void checkFavorite() {
@@ -663,50 +654,6 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
}, error -> Log.e(TAG, Log.getStackTraceString(error)));
}
- void playExternalMedia(Intent intent, MediaType type) {
- if (intent == null || intent.getData() == null) {
- return;
- }
- if (Build.VERSION.SDK_INT >= 23
- && ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE)
- != PackageManager.PERMISSION_GRANTED) {
-
- if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.READ_EXTERNAL_STORAGE)) {
- Toast.makeText(this, R.string.needs_storage_permission, Toast.LENGTH_LONG).show();
- }
-
- int code = REQUEST_CODE_STORAGE_PLAY_AUDIO;
- if (type == MediaType.VIDEO) {
- code = REQUEST_CODE_STORAGE_PLAY_VIDEO;
- }
- ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, code);
- return;
- }
-
- Log.d(TAG, "Received VIEW intent: " + intent.getData().getPath());
- ExternalMedia media = new ExternalMedia(intent.getData().getPath(), type);
-
- new PlaybackServiceStarter(this, media)
- .callEvenIfRunning(true)
- .startWhenPrepared(true)
- .shouldStream(false)
- .prepareImmediately(true)
- .start();
- }
-
- @Override
- public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, int[] grantResults) {
- if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
- if (requestCode == REQUEST_CODE_STORAGE_PLAY_AUDIO) {
- playExternalMedia(getIntent(), MediaType.AUDIO);
- } else if (requestCode == REQUEST_CODE_STORAGE_PLAY_VIDEO) {
- playExternalMedia(getIntent(), MediaType.VIDEO);
- }
- } else {
- Toast.makeText(this, R.string.needs_storage_permission, Toast.LENGTH_LONG).show();
- }
- }
-
@Nullable
private static FeedItem getFeedItem(@Nullable Playable playable) {
if (playable instanceof FeedMedia) {
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 18620a56a..a5883ca14 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java
@@ -4,10 +4,14 @@ import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
+import android.content.SharedPreferences;
import android.graphics.LightingColorFilter;
import android.os.Build;
import android.os.Bundle;
+import android.text.Spannable;
+import android.text.SpannableString;
import android.text.TextUtils;
+import android.text.style.ForegroundColorSpan;
import android.util.Log;
import android.view.MenuItem;
import android.view.View;
@@ -15,6 +19,7 @@ import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.TextView;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
@@ -28,7 +33,6 @@ import de.danoeh.antennapod.core.event.DownloadEvent;
import de.danoeh.antennapod.core.event.FeedListUpdateEvent;
import de.danoeh.antennapod.core.event.PlayerStatusEvent;
import de.danoeh.antennapod.core.feed.Feed;
-import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedPreferences;
import de.danoeh.antennapod.core.feed.VolumeAdaptionSetting;
import de.danoeh.antennapod.core.glide.ApGlideSettings;
@@ -41,6 +45,7 @@ import de.danoeh.antennapod.core.service.download.Downloader;
import de.danoeh.antennapod.core.service.download.HttpDownloader;
import de.danoeh.antennapod.core.service.playback.PlaybackService;
import de.danoeh.antennapod.core.storage.DBReader;
+import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.storage.DownloadRequestException;
import de.danoeh.antennapod.core.storage.DownloadRequester;
import de.danoeh.antennapod.core.syndication.handler.FeedHandler;
@@ -49,7 +54,6 @@ import de.danoeh.antennapod.core.syndication.handler.UnsupportedFeedtypeExceptio
import de.danoeh.antennapod.core.util.DownloadError;
import de.danoeh.antennapod.core.util.FileNameGenerator;
import de.danoeh.antennapod.core.util.IntentUtils;
-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.playback.RemoteMedia;
@@ -58,9 +62,11 @@ import de.danoeh.antennapod.core.util.syndication.HtmlToPlainText;
import de.danoeh.antennapod.databinding.OnlinefeedviewActivityBinding;
import de.danoeh.antennapod.dialog.AuthenticationDialog;
import de.danoeh.antennapod.discovery.PodcastSearcherRegistry;
+import io.reactivex.Maybe;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
+import io.reactivex.observers.DisposableMaybeObserver;
import io.reactivex.schedulers.Schedulers;
import org.apache.commons.lang3.StringUtils;
import org.greenrobot.eventbus.EventBus;
@@ -87,6 +93,9 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
// Optional argument: specify a title for the actionbar.
private static final int RESULT_ERROR = 2;
private static final String TAG = "OnlineFeedViewActivity";
+ private static final String PREFS = "OnlineFeedViewActivityPreferences";
+ private static final String PREF_LAST_AUTO_DOWNLOAD = "lastAutoDownload";
+
private volatile List<Feed> feeds;
private Feed feed;
private String selectedDownloadUrl;
@@ -248,7 +257,8 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
url = URLChecker.prepareURL(url);
feed = new Feed(url, null);
if (username != null && password != null) {
- feed.setPreferences(new FeedPreferences(0, false, FeedPreferences.AutoDeleteAction.GLOBAL, VolumeAdaptionSetting.OFF, username, password));
+ feed.setPreferences(new FeedPreferences(0, false, FeedPreferences.AutoDeleteAction.GLOBAL,
+ VolumeAdaptionSetting.OFF, username, password));
}
String fileUrl = new File(getExternalCacheDir(),
FileNameGenerator.generateFileName(feed.getDownload_url())).toString();
@@ -283,11 +293,7 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
dialog.show();
}
} else {
- String errorMsg = status.getReason().getErrorString(OnlineFeedViewActivity.this);
- if (status.getReasonDetailed() != null) {
- errorMsg += " (" + status.getReasonDetailed() + ")";
- }
- showErrorDialog(errorMsg);
+ showErrorDialog(status.getReason().getErrorString(OnlineFeedViewActivity.this), status.getReasonDetailed());
}
}
@@ -316,37 +322,47 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
}
Log.d(TAG, "Parsing feed");
- parser = Observable.fromCallable(this::doParseFeed)
+ parser = Maybe.fromCallable(this::doParseFeed)
.subscribeOn(Schedulers.computation())
.observeOn(AndroidSchedulers.mainThread())
- .subscribe(optionalResult -> {
- if(optionalResult.isPresent()) {
- FeedHandlerResult result = optionalResult.get();
- beforeShowFeedInformation(result.feed);
+ .subscribeWith(new DisposableMaybeObserver<FeedHandlerResult>() {
+ @Override
+ public void onSuccess(@NonNull FeedHandlerResult result) {
showFeedInformation(result.feed, result.alternateFeedUrls);
}
- }, error -> {
- String errorMsg = DownloadError.ERROR_PARSER_EXCEPTION.getErrorString(
- OnlineFeedViewActivity.this) + " (" + error.getMessage() + ")";
- showErrorDialog(errorMsg);
- Log.d(TAG, "Feed parser exception: " + Log.getStackTraceString(error));
+
+ @Override
+ public void onComplete() {
+ // Ignore null result: We showed the discovery dialog.
+ }
+
+ @Override
+ public void onError(@NonNull Throwable error) {
+ showErrorDialog(error.getMessage(), "");
+ Log.d(TAG, "Feed parser exception: " + Log.getStackTraceString(error));
+ }
});
}
- @NonNull
- private Optional<FeedHandlerResult> doParseFeed() throws Exception {
+ /**
+ * Try to parse the feed.
+ * @return The FeedHandlerResult if successful.
+ * Null if unsuccessful but we started another attempt.
+ * @throws Exception If unsuccessful but we do not know a resolution.
+ */
+ @Nullable
+ private FeedHandlerResult doParseFeed() throws Exception {
FeedHandler handler = new FeedHandler();
try {
- return Optional.of(handler.parseFeed(feed));
+ return 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();
+ return null; // Should not display an error message
} else {
- Log.d(TAG, "Supplied feed is an HTML web page that has no references to any feed");
- throw e;
+ throw new UnsupportedFeedtypeException(getString(R.string.download_error_unsupported_type_html));
}
} else {
throw e;
@@ -361,23 +377,6 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
}
/**
- * Called after the feed has been downloaded and parsed and before showFeedInformation is called.
- * This method is executed on a background thread
- */
- private void beforeShowFeedInformation(Feed feed) {
- Log.d(TAG, "Removing HTML from feed description");
-
- feed.setDescription(HtmlToPlainText.getPlainText(feed.getDescription()));
-
- Log.d(TAG, "Removing HTML from shownotes");
- if (feed.getItems() != null) {
- for (FeedItem item : feed.getItems()) {
- item.setDescription(HtmlToPlainText.getPlainText(item.getDescription()));
- }
- }
- }
-
- /**
* Called when feed parsed successfully.
* This method is executed on the GUI thread.
*/
@@ -420,7 +419,7 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
viewBinding.titleLabel.setText(feed.getTitle());
viewBinding.authorLabel.setText(feed.getAuthor());
- description.setText(feed.getDescription());
+ description.setText(HtmlToPlainText.getPlainText(feed.getDescription()));
viewBinding.subscribeButton.setOnClickListener(v -> {
if (feedInFeedlist(feed)) {
@@ -445,6 +444,11 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
IntentUtils.sendLocalBroadcast(this, PlaybackService.ACTION_SHUTDOWN_PLAYBACK_SERVICE);
});
+ if (UserPreferences.isEnableAutodownload()) {
+ SharedPreferences preferences = getSharedPreferences(PREFS, MODE_PRIVATE);
+ viewBinding.autoDownloadCheckBox.setChecked(preferences.getBoolean(PREF_LAST_AUTO_DOWNLOAD, true));
+ }
+
final int MAX_LINES_COLLAPSED = 10;
description.setMaxLines(MAX_LINES_COLLAPSED);
description.setOnClickListener(v -> {
@@ -511,10 +515,17 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
if (didPressSubscribe) {
didPressSubscribe = false;
if (UserPreferences.isEnableAutodownload()) {
+ boolean autoDownload = viewBinding.autoDownloadCheckBox.isChecked();
+
Feed feed1 = DBReader.getFeed(getFeedId(feed));
FeedPreferences feedPreferences = feed1.getPreferences();
- feedPreferences.setAutoDownload(viewBinding.autoDownloadCheckBox.isChecked());
- feed1.savePreferences();
+ feedPreferences.setAutoDownload(autoDownload);
+ DBWriter.setFeedPreferences(feedPreferences);
+
+ SharedPreferences preferences = getSharedPreferences(PREFS, MODE_PRIVATE);
+ SharedPreferences.Editor editor = preferences.edit();
+ editor.putBoolean(PREF_LAST_AUTO_DOWNLOAD, autoDownload);
+ editor.apply();
}
openFeed();
}
@@ -553,12 +564,16 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
}
@UiThread
- private void showErrorDialog(String errorMsg) {
+ private void showErrorDialog(String errorMsg, String details) {
if (!isFinishing() && !isPaused) {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.error_label);
if (errorMsg != null) {
- builder.setMessage(errorMsg);
+ String total = errorMsg + "\n\n" + details;
+ SpannableString errorMessage = new SpannableString(total);
+ errorMessage.setSpan(new ForegroundColorSpan(0x88888888),
+ errorMsg.length(), total.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ builder.setMessage(errorMessage);
} else {
builder.setMessage(R.string.download_error_error_unknown);
}
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/VideoplayerActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/VideoplayerActivity.java
index 1c8619e99..15d0bec4a 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/VideoplayerActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/VideoplayerActivity.java
@@ -17,7 +17,6 @@ import android.widget.ImageView;
import androidx.core.view.WindowCompat;
import androidx.appcompat.app.ActionBar;
-import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;
import android.view.Menu;
@@ -37,12 +36,12 @@ import java.lang.ref.WeakReference;
import java.util.concurrent.atomic.AtomicBoolean;
import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.core.feed.MediaType;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.playback.PlaybackService;
import de.danoeh.antennapod.core.service.playback.PlayerStatus;
import de.danoeh.antennapod.core.util.gui.PictureInPictureUtil;
import de.danoeh.antennapod.core.util.playback.Playable;
+import de.danoeh.antennapod.ui.appstartintent.MainActivityStarter;
import de.danoeh.antennapod.view.AspectRatioVideoView;
/**
@@ -88,9 +87,7 @@ public class VideoplayerActivity extends MediaplayerActivity {
@Override
protected void onResume() {
super.onResume();
- if (TextUtils.equals(getIntent().getAction(), Intent.ACTION_VIEW)) {
- playExternalMedia(getIntent(), MediaType.VIDEO);
- } else if (PlaybackService.isCasting()) {
+ if (PlaybackService.isCasting()) {
Intent intent = PlaybackService.getPlayerActivityIntent(this);
if (!intent.getComponent().getClassName().equals(VideoplayerActivity.class.getName())) {
destroyingDueToReload = true;
@@ -135,17 +132,13 @@ public class VideoplayerActivity extends MediaplayerActivity {
}
@Override
- protected boolean loadMediaInfo() {
- if (!super.loadMediaInfo() || controller == null) {
- return false;
- }
+ protected void loadMediaInfo() {
+ super.loadMediaInfo();
Playable media = controller.getMedia();
if (media != null) {
getSupportActionBar().setSubtitle(media.getEpisodeTitle());
getSupportActionBar().setTitle(media.getFeedTitle());
- return true;
}
- return false;
}
@Override
@@ -347,7 +340,7 @@ public class VideoplayerActivity extends MediaplayerActivity {
Log.d(TAG, "ReloadNotification received, switching to Castplayer now");
destroyingDueToReload = true;
finish();
- startActivity(new Intent(this, MainActivity.class).putExtra(MainActivity.EXTRA_OPEN_PLAYER, true));
+ new MainActivityStarter(this).withOpenPlayer().start();
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/WidgetConfigActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/WidgetConfigActivity.java
index 4805dba10..3020aba43 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/WidgetConfigActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/WidgetConfigActivity.java
@@ -2,33 +2,34 @@ package de.danoeh.antennapod.activity;
import android.Manifest;
import android.app.WallpaperManager;
-import android.content.pm.PackageManager;
-import android.graphics.drawable.Drawable;
-import android.os.Build;
-import android.widget.ImageView;
-import androidx.appcompat.app.AppCompatActivity;
-
import android.appwidget.AppWidgetManager;
import android.content.Intent;
import android.content.SharedPreferences;
+import android.content.pm.PackageManager;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
import android.os.Bundle;
import android.view.View;
-import android.widget.RelativeLayout;
+import android.widget.CheckBox;
+import android.widget.ImageView;
import android.widget.SeekBar;
import android.widget.TextView;
-
+import androidx.appcompat.app.AppCompatActivity;
import androidx.core.content.ContextCompat;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.receiver.PlayerWidget;
-import de.danoeh.antennapod.core.service.PlayerWidgetJobService;
+import de.danoeh.antennapod.core.widget.WidgetUpdaterJobService;
public class WidgetConfigActivity extends AppCompatActivity {
private int appWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID;
private SeekBar opacitySeekBar;
private TextView opacityTextView;
- private RelativeLayout widgetPreview;
+ private View widgetPreview;
+ private CheckBox ckRewind;
+ private CheckBox ckFastForward;
+ private CheckBox ckSkip;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -73,6 +74,32 @@ public class WidgetConfigActivity extends AppCompatActivity {
}
});
+
+ widgetPreview.findViewById(R.id.txtNoPlaying).setVisibility(View.GONE);
+ TextView title = widgetPreview.findViewById(R.id.txtvTitle);
+ title.setVisibility(View.VISIBLE);
+ title.setText(R.string.app_name);
+ TextView progress = widgetPreview.findViewById(R.id.txtvProgress);
+ progress.setVisibility(View.VISIBLE);
+ progress.setText(R.string.position_default_label);
+
+ ckRewind = findViewById(R.id.ckRewind);
+ ckRewind.setOnClickListener(v -> displayPreviewPanel());
+ ckFastForward = findViewById(R.id.ckFastForward);
+ ckFastForward.setOnClickListener(v -> displayPreviewPanel());
+ ckSkip = findViewById(R.id.ckSkip);
+ ckSkip.setOnClickListener(v -> displayPreviewPanel());
+ }
+
+ private void displayPreviewPanel() {
+ boolean showExtendedPreview = ckRewind.isChecked() || ckFastForward.isChecked() || ckSkip.isChecked();
+ widgetPreview.findViewById(R.id.extendedButtonsContainer)
+ .setVisibility(showExtendedPreview ? View.VISIBLE : View.GONE);
+ widgetPreview.findViewById(R.id.butPlay).setVisibility(showExtendedPreview ? View.GONE : View.VISIBLE);
+ widgetPreview.findViewById(R.id.butFastForward)
+ .setVisibility(ckFastForward.isChecked() ? View.VISIBLE : View.GONE);
+ widgetPreview.findViewById(R.id.butSkip).setVisibility(ckSkip.isChecked() ? View.VISIBLE : View.GONE);
+ widgetPreview.findViewById(R.id.butRew).setVisibility(ckRewind.isChecked() ? View.VISIBLE : View.GONE);
}
private void displayDeviceBackground() {
@@ -91,13 +118,16 @@ public class WidgetConfigActivity extends AppCompatActivity {
SharedPreferences prefs = getSharedPreferences(PlayerWidget.PREFS_NAME, MODE_PRIVATE);
SharedPreferences.Editor editor = prefs.edit();
editor.putInt(PlayerWidget.KEY_WIDGET_COLOR + appWidgetId, backgroundColor);
+ editor.putBoolean(PlayerWidget.KEY_WIDGET_SKIP + appWidgetId, ckSkip.isChecked());
+ editor.putBoolean(PlayerWidget.KEY_WIDGET_REWIND + appWidgetId, ckRewind.isChecked());
+ editor.putBoolean(PlayerWidget.KEY_WIDGET_FAST_FORWARD + appWidgetId, ckFastForward.isChecked());
editor.apply();
Intent resultValue = new Intent();
resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
setResult(RESULT_OK, resultValue);
finish();
- PlayerWidgetJobService.updateWidget(this);
+ WidgetUpdaterJobService.performBackgroundUpdate(this);
}
private int getColorWithAlpha(int color, int opacity) {
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
deleted file mode 100644
index cfd6ec702..000000000
--- a/app/src/main/java/de/danoeh/antennapod/activity/gpoddernet/GpodnetAuthenticationActivity.java
+++ /dev/null
@@ -1,395 +0,0 @@
-package de.danoeh.antennapod.activity.gpoddernet;
-
-import android.content.Context;
-import android.content.Intent;
-import android.os.AsyncTask;
-import android.os.Build;
-import android.os.Bundle;
-import androidx.appcompat.app.AppCompatActivity;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.inputmethod.EditorInfo;
-import android.view.inputmethod.InputMethodManager;
-import android.widget.AdapterView;
-import android.widget.ArrayAdapter;
-import android.widget.Button;
-import android.widget.EditText;
-import android.widget.ProgressBar;
-import android.widget.Spinner;
-import android.widget.TextView;
-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;
-import de.danoeh.antennapod.activity.MainActivity;
-import de.danoeh.antennapod.core.preferences.GpodnetPreferences;
-import de.danoeh.antennapod.core.preferences.UserPreferences;
-import de.danoeh.antennapod.core.service.download.AntennapodHttpClient;
-import de.danoeh.antennapod.core.sync.SyncService;
-import de.danoeh.antennapod.core.sync.gpoddernet.GpodnetService;
-import de.danoeh.antennapod.core.sync.gpoddernet.GpodnetServiceException;
-import de.danoeh.antennapod.core.sync.gpoddernet.model.GpodnetDevice;
-
-/**
- * Guides the user through the authentication process
- * Step 1: Request username and password from user
- * Step 2: Choose device from a list of available devices or create a new one
- * Step 3: Choose from a list of actions
- */
-public class GpodnetAuthenticationActivity extends AppCompatActivity {
- private static final String TAG = "GpodnetAuthActivity";
-
- private ViewFlipper viewFlipper;
-
- private static final int STEP_DEFAULT = -1;
- private static final int STEP_LOGIN = 0;
- private static final int STEP_DEVICE = 1;
- private static final int STEP_FINISH = 2;
-
- private int currentStep = -1;
-
- private GpodnetService service;
- private volatile String username;
- private volatile String password;
- private volatile GpodnetDevice selectedDevice;
-
- private View[] views;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- setTheme(UserPreferences.getTheme());
- super.onCreate(savedInstanceState);
- getSupportActionBar().setDisplayHomeAsUpEnabled(true);
-
- setContentView(R.layout.gpodnetauth_activity);
- service = new GpodnetService(AntennapodHttpClient.getHttpClient(), GpodnetPreferences.getHostname());
-
- viewFlipper = findViewById(R.id.viewflipper);
- LayoutInflater inflater = (LayoutInflater)
- getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- views = new View[]{
- inflater.inflate(R.layout.gpodnetauth_credentials, viewFlipper, false),
- inflater.inflate(R.layout.gpodnetauth_device, viewFlipper, false),
- inflater.inflate(R.layout.gpodnetauth_finish, viewFlipper, false)
- };
- for (View view : views) {
- viewFlipper.addView(view);
- }
- advance();
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- if (item.getItemId() == android.R.id.home) {
- finish();
- return true;
- }
- return super.onOptionsItemSelected(item);
- }
-
- private void setupLoginView(View view) {
- final EditText username = view.findViewById(R.id.etxtUsername);
- final EditText password = view.findViewById(R.id.etxtPassword);
- final Button login = view.findViewById(R.id.butLogin);
- final TextView txtvError = view.findViewById(R.id.txtvError);
- final ProgressBar progressBar = view.findViewById(R.id.progBarLogin);
-
- password.setOnEditorActionListener((v, actionID, event) ->
- actionID == EditorInfo.IME_ACTION_GO && login.performClick());
-
- login.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
-
- 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>() {
-
- volatile Exception exception;
-
- @Override
- protected void onPreExecute() {
- super.onPreExecute();
- login.setEnabled(false);
- progressBar.setVisibility(View.VISIBLE);
- txtvError.setVisibility(View.GONE);
- // hide the keyboard
- InputMethodManager inputManager = (InputMethodManager)
- getSystemService(Context.INPUT_METHOD_SERVICE);
- inputManager.hideSoftInputFromWindow(login.getWindowToken(),
- InputMethodManager.HIDE_NOT_ALWAYS);
-
- }
-
- @Override
- protected void onPostExecute(Void aVoid) {
- super.onPostExecute(aVoid);
- login.setEnabled(true);
- progressBar.setVisibility(View.GONE);
-
- if (exception == null) {
- advance();
- } else {
- txtvError.setText(exception.getCause().getMessage());
- txtvError.setVisibility(View.VISIBLE);
- }
- }
-
- @Override
- protected Void doInBackground(GpodnetService... params) {
- try {
- params[0].authenticate(usernameStr, passwordStr);
- GpodnetAuthenticationActivity.this.username = usernameStr;
- GpodnetAuthenticationActivity.this.password = passwordStr;
- } catch (GpodnetServiceException e) {
- e.printStackTrace();
- exception = e;
- }
- return null;
- }
- };
- authTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, service);
- }
- });
- }
-
- private void setupDeviceView(View view) {
- final EditText deviceID = view.findViewById(R.id.etxtDeviceID);
- final EditText caption = view.findViewById(R.id.etxtCaption);
- final Button createNewDevice = view.findViewById(R.id.butCreateNewDevice);
- final Button chooseDevice = view.findViewById(R.id.butChooseExistingDevice);
- final TextView txtvError = view.findViewById(R.id.txtvError);
- final ProgressBar progBarCreateDevice = view.findViewById(R.id.progbarCreateDevice);
- final Spinner spinnerDevices = view.findViewById(R.id.spinnerChooseDevice);
-
-
- // load device list
- final AtomicReference<List<GpodnetDevice>> devices = new AtomicReference<>();
- new AsyncTask<GpodnetService, Void, List<GpodnetDevice>>() {
-
- @Override
- protected void onPreExecute() {
- super.onPreExecute();
- chooseDevice.setEnabled(false);
- spinnerDevices.setEnabled(false);
- createNewDevice.setEnabled(false);
- }
-
- @Override
- protected void onPostExecute(List<GpodnetDevice> gpodnetDevices) {
- super.onPostExecute(gpodnetDevices);
- if (gpodnetDevices != null) {
- List<String> deviceNames = new ArrayList<>();
- for (GpodnetDevice device : gpodnetDevices) {
- deviceNames.add(device.getCaption());
- }
- spinnerDevices.setAdapter(new ArrayAdapter<>(GpodnetAuthenticationActivity.this,
- android.R.layout.simple_spinner_dropdown_item, deviceNames));
- spinnerDevices.setEnabled(true);
- if (!deviceNames.isEmpty()) {
- chooseDevice.setEnabled(true);
- }
- devices.set(gpodnetDevices);
- deviceID.setText(generateDeviceID(gpodnetDevices));
- createNewDevice.setEnabled(true);
- }
- }
-
- @Override
- protected List<GpodnetDevice> doInBackground(GpodnetService... params) {
- try {
- return params[0].getDevices();
- } catch (GpodnetServiceException e) {
- e.printStackTrace();
- return null;
- }
- }
- }.execute(service);
-
-
- createNewDevice.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- if (checkDeviceIDText(deviceID, caption, txtvError, devices.get())) {
- final String deviceStr = deviceID.getText().toString();
- final String captionStr = caption.getText().toString();
-
- new AsyncTask<GpodnetService, Void, GpodnetDevice>() {
-
- private volatile Exception exception;
-
- @Override
- protected void onPreExecute() {
- super.onPreExecute();
- createNewDevice.setEnabled(false);
- chooseDevice.setEnabled(false);
- progBarCreateDevice.setVisibility(View.VISIBLE);
- txtvError.setVisibility(View.GONE);
- }
-
- @Override
- protected void onPostExecute(GpodnetDevice result) {
- super.onPostExecute(result);
- createNewDevice.setEnabled(true);
- chooseDevice.setEnabled(true);
- progBarCreateDevice.setVisibility(View.GONE);
- if (exception == null) {
- selectedDevice = result;
- advance();
- } else {
- txtvError.setText(exception.getMessage());
- txtvError.setVisibility(View.VISIBLE);
- }
- }
-
- @Override
- protected GpodnetDevice doInBackground(GpodnetService... params) {
- try {
- params[0].configureDevice(deviceStr, captionStr, GpodnetDevice.DeviceType.MOBILE);
- return new GpodnetDevice(deviceStr, captionStr, GpodnetDevice.DeviceType.MOBILE.toString(), 0);
- } catch (GpodnetServiceException e) {
- e.printStackTrace();
- exception = e;
- }
- return null;
- }
- }.execute(service);
- }
- }
- });
-
- chooseDevice.setOnClickListener(v -> {
- final int position = spinnerDevices.getSelectedItemPosition();
- if (position != AdapterView.INVALID_POSITION) {
- selectedDevice = devices.get().get(position);
- advance();
- }
- });
- }
-
-
- private String generateDeviceID(List<GpodnetDevice> gpodnetDevices) {
- // devices names must be of a certain form:
- // https://gpoddernet.readthedocs.org/en/latest/api/reference/general.html#devices
- // This is more restrictive than needed, but I think it makes for more readable names.
- String baseId = Build.MODEL.replaceAll("\\W", "");
- String id = baseId;
- int num = 0;
-
- while (isDeviceWithIdInList(id, gpodnetDevices)) {
- id = baseId + "_" + num;
- num++;
- }
-
- return id;
- }
-
- private boolean isDeviceWithIdInList(String id, List<GpodnetDevice> gpodnetDevices) {
- if (gpodnetDevices == null) {
- return false;
- }
- for (GpodnetDevice device : gpodnetDevices) {
- if (device.getId().equals(id)) {
- return true;
- }
- }
- return false;
- }
-
- private boolean checkDeviceIDText(EditText deviceID, EditText caption, TextView txtvError, List<GpodnetDevice> devices) {
- String text = deviceID.getText().toString();
- if (text.length() == 0) {
- txtvError.setText(R.string.gpodnetauth_device_errorEmpty);
- txtvError.setVisibility(View.VISIBLE);
- return false;
- } else if (caption.length() == 0) {
- txtvError.setText(R.string.gpodnetauth_device_caption_errorEmpty);
- txtvError.setVisibility(View.VISIBLE);
- return false;
- } else {
- if (devices != null) {
- if (isDeviceWithIdInList(text, devices)) {
- txtvError.setText(R.string.gpodnetauth_device_errorAlreadyUsed);
- txtvError.setVisibility(View.VISIBLE);
- return false;
- }
- txtvError.setVisibility(View.GONE);
- return true;
- }
- return true;
- }
-
- }
-
- private void setupFinishView(View view) {
- final Button sync = view.findViewById(R.id.butSyncNow);
- final Button back = view.findViewById(R.id.butGoMainscreen);
-
- sync.setOnClickListener(v -> {
- finish();
- SyncService.sync(getApplicationContext());
- });
- back.setOnClickListener(v -> {
- Intent intent = new Intent(GpodnetAuthenticationActivity.this, MainActivity.class);
- intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
- startActivity(intent);
- });
- }
-
- private void writeLoginCredentials() {
- if (BuildConfig.DEBUG) Log.d(TAG, "Writing login credentials");
- GpodnetPreferences.setUsername(username);
- GpodnetPreferences.setPassword(password);
- GpodnetPreferences.setDeviceID(selectedDevice.getId());
- }
-
- private void advance() {
- if (currentStep < STEP_FINISH) {
-
- View view = views[currentStep + 1];
- if (currentStep == STEP_DEFAULT) {
- setupLoginView(view);
- } else if (currentStep == STEP_LOGIN) {
- if (username == null || password == null) {
- throw new IllegalStateException("Username and password must not be null here");
- } else {
- setupDeviceView(view);
- }
- } else if (currentStep == STEP_DEVICE) {
- if (selectedDevice == null) {
- throw new IllegalStateException("Device must not be null here");
- } else {
- writeLoginCredentials();
- setupFinishView(view);
- }
- }
- if (currentStep != STEP_DEFAULT) {
- viewFlipper.showNext();
- }
- currentStep++;
- } else {
- 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/ChaptersListAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/ChaptersListAdapter.java
index 4fa8acc43..d4b32ee06 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/ChaptersListAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/ChaptersListAdapter.java
@@ -20,9 +20,9 @@ import de.danoeh.antennapod.core.glide.ApGlideSettings;
import de.danoeh.antennapod.core.util.Converter;
import de.danoeh.antennapod.core.util.EmbeddedChapterImage;
import de.danoeh.antennapod.core.util.IntentUtils;
-import de.danoeh.antennapod.core.util.ThemeUtils;
+import de.danoeh.antennapod.ui.common.ThemeUtils;
import de.danoeh.antennapod.core.util.playback.Playable;
-import de.danoeh.antennapod.view.CircularProgressBar;
+import de.danoeh.antennapod.ui.common.CircularProgressBar;
public class ChaptersListAdapter extends RecyclerView.Adapter<ChaptersListAdapter.ChapterHolder> {
private Playable media;
@@ -42,7 +42,7 @@ public class ChaptersListAdapter extends RecyclerView.Adapter<ChaptersListAdapte
hasImages = false;
if (media.getChapters() != null) {
for (Chapter chapter : media.getChapters()) {
- if (!ignoreChapter(chapter) && !TextUtils.isEmpty(chapter.getImageUrl())) {
+ if (!TextUtils.isEmpty(chapter.getImageUrl())) {
hasImages = true;
}
}
@@ -125,14 +125,7 @@ public class ChaptersListAdapter extends RecyclerView.Adapter<ChaptersListAdapte
if (media == null || media.getChapters() == null) {
return 0;
}
- // ignore invalid chapters
- int counter = 0;
- for (Chapter chapter : media.getChapters()) {
- if (!ignoreChapter(chapter)) {
- counter++;
- }
- }
- return counter;
+ return media.getChapters().size();
}
static class ChapterHolder extends RecyclerView.ViewHolder {
@@ -171,22 +164,8 @@ public class ChaptersListAdapter extends RecyclerView.Adapter<ChaptersListAdapte
notifyItemChanged(currentChapterIndex, "foo");
}
- private boolean ignoreChapter(Chapter c) {
- return media.getDuration() > 0 && media.getDuration() < c.getStart();
- }
-
public Chapter getItem(int position) {
- int i = 0;
- for (Chapter chapter : media.getChapters()) {
- if (!ignoreChapter(chapter)) {
- if (i == position) {
- return chapter;
- } else {
- i++;
- }
- }
- }
- return null;
+ return media.getChapters().get(position);
}
public interface Callback {
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/DownloadLogAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/DownloadLogAdapter.java
index 0c4aaf6ed..811e1e31b 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/DownloadLogAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/DownloadLogAdapter.java
@@ -20,7 +20,7 @@ import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBTasks;
import de.danoeh.antennapod.core.storage.DownloadRequestException;
import de.danoeh.antennapod.core.storage.DownloadRequester;
-import de.danoeh.antennapod.core.util.ThemeUtils;
+import de.danoeh.antennapod.ui.common.ThemeUtils;
import de.danoeh.antennapod.view.viewholder.DownloadItemViewHolder;
/**
@@ -68,16 +68,14 @@ public class DownloadLogAdapter extends BaseAdapter {
holder.icon.setContentDescription(context.getString(R.string.download_successful));
holder.secondaryActionButton.setVisibility(View.INVISIBLE);
holder.reason.setVisibility(View.GONE);
+ holder.tapForDetails.setVisibility(View.GONE);
} else {
holder.icon.setTextColor(ContextCompat.getColor(context, R.color.download_failed_red));
holder.icon.setText("{fa-times-circle}");
holder.icon.setContentDescription(context.getString(R.string.error_label));
- String reasonText = status.getReason().getErrorString(context);
- if (status.getReasonDetailed() != null) {
- reasonText += ": " + status.getReasonDetailed();
- }
- holder.reason.setText(reasonText);
+ holder.reason.setText(status.getReason().getErrorString(context));
holder.reason.setVisibility(View.VISIBLE);
+ holder.tapForDetails.setVisibility(View.VISIBLE);
if (newerWasSuccessful(position, status.getFeedfileType(), status.getFeedfileId())) {
holder.secondaryActionButton.setVisibility(View.INVISIBLE);
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/DownloadlistAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/DownloadlistAdapter.java
index 268a21409..9363edc9f 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/DownloadlistAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/DownloadlistAdapter.java
@@ -15,8 +15,8 @@ import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.service.download.DownloadRequest;
import de.danoeh.antennapod.core.service.download.DownloadStatus;
import de.danoeh.antennapod.core.service.download.Downloader;
-import de.danoeh.antennapod.core.util.ThemeUtils;
-import de.danoeh.antennapod.view.CircularProgressBar;
+import de.danoeh.antennapod.ui.common.ThemeUtils;
+import de.danoeh.antennapod.ui.common.CircularProgressBar;
public class DownloadlistAdapter extends BaseAdapter {
@@ -68,8 +68,8 @@ public class DownloadlistAdapter extends BaseAdapter {
holder.secondaryActionButton.setContentDescription(context.getString(R.string.cancel_download_label));
holder.secondaryActionButton.setTag(downloader);
holder.secondaryActionButton.setOnClickListener(butSecondaryListener);
- holder.secondaryActionProgress.setPercentage(0, request);
+ boolean percentageWasSet = false;
String status = "";
if (request.getFeedfileType() == Feed.FEEDFILETYPE_FEED) {
status += context.getString(R.string.download_type_feed);
@@ -85,8 +85,12 @@ public class DownloadlistAdapter extends BaseAdapter {
status += " / " + Formatter.formatShortFileSize(context, request.getSize());
holder.secondaryActionProgress.setPercentage(
0.01f * Math.max(1, request.getProgressPercent()), request);
+ percentageWasSet = true;
}
}
+ if (!percentageWasSet) {
+ holder.secondaryActionProgress.setPercentage(0, request);
+ }
holder.status.setText(status);
return convertView;
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistDescriptionAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistDescriptionAdapter.java
index 8cb0fd30a..50b924e49 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistDescriptionAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistDescriptionAdapter.java
@@ -20,6 +20,7 @@ import de.danoeh.antennapod.core.service.playback.PlaybackService;
import de.danoeh.antennapod.core.util.DateUtils;
import de.danoeh.antennapod.core.util.playback.Playable;
import de.danoeh.antennapod.core.util.playback.PlaybackServiceStarter;
+import de.danoeh.antennapod.core.util.syndication.HtmlToPlainText;
import de.danoeh.antennapod.dialog.StreamingConfirmationDialog;
import java.util.List;
@@ -59,7 +60,7 @@ public class FeedItemlistDescriptionAdapter extends ArrayAdapter<FeedItem> {
holder.title.setText(item.getTitle());
holder.pubDate.setText(DateUtils.formatAbbrev(getContext(), item.getPubDate()));
if (item.getDescription() != null) {
- String description = item.getDescription()
+ String description = HtmlToPlainText.getPlainText(item.getDescription())
.replaceAll("\n", " ")
.replaceAll("\\s+", " ")
.trim();
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/FeedSearchResultAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/FeedSearchResultAdapter.java
index 2e5ba31c9..dbb9ce0d0 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/FeedSearchResultAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/FeedSearchResultAdapter.java
@@ -10,7 +10,7 @@ import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.fragment.FeedItemlistFragment;
-import de.danoeh.antennapod.view.SquareImageView;
+import de.danoeh.antennapod.ui.common.SquareImageView;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
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 5533197b9..8bfcf66cc 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/NavListAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/NavListAdapter.java
@@ -24,7 +24,6 @@ import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.glide.ApGlideSettings;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.storage.NavDrawerData;
-import de.danoeh.antennapod.core.util.ThemeUtils;
import de.danoeh.antennapod.fragment.AddFeedFragment;
import de.danoeh.antennapod.fragment.DownloadsFragment;
import de.danoeh.antennapod.fragment.EpisodesFragment;
@@ -32,6 +31,7 @@ import de.danoeh.antennapod.fragment.NavDrawerFragment;
import de.danoeh.antennapod.fragment.PlaybackHistoryFragment;
import de.danoeh.antennapod.fragment.QueueFragment;
import de.danoeh.antennapod.fragment.SubscriptionFragment;
+import de.danoeh.antennapod.ui.common.ThemeUtils;
import org.apache.commons.lang3.ArrayUtils;
import java.lang.ref.WeakReference;
@@ -76,7 +76,7 @@ public class NavListAdapter extends RecyclerView.Adapter<NavListAdapter.Holder>
}
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
- if (key.equals(UserPreferences.PREF_HIDDEN_DRAWER_ITEMS)) {
+ if (UserPreferences.PREF_HIDDEN_DRAWER_ITEMS.equals(key)) {
loadItems();
}
}
@@ -314,7 +314,7 @@ public class NavListAdapter extends RecyclerView.Adapter<NavListAdapter.Holder>
}
Glide.with(context)
- .load(feed.getImageLocation())
+ .load(feed.getImageUrl())
.apply(new RequestOptions()
.placeholder(R.color.light_gray)
.error(R.color.light_gray)
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/StatisticsListAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/StatisticsListAdapter.java
index 72482b06d..23b5cfdce 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/StatisticsListAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/StatisticsListAdapter.java
@@ -66,7 +66,7 @@ public abstract class StatisticsListAdapter extends RecyclerView.Adapter<Recycle
StatisticsHolder holder = (StatisticsHolder) h;
StatisticsItem statsItem = statisticsData.get(position - 1);
Glide.with(context)
- .load(statsItem.feed.getImageLocation())
+ .load(statsItem.feed.getImageUrl())
.apply(new RequestOptions()
.placeholder(R.color.light_gray)
.error(R.color.light_gray)
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/SubscriptionsAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/SubscriptionsAdapter.java
index 5c0ecfa3c..1d85cdaff 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/SubscriptionsAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/SubscriptionsAdapter.java
@@ -22,8 +22,8 @@ import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.LocalFeedUpdater;
import de.danoeh.antennapod.core.storage.NavDrawerData;
-import de.danoeh.antennapod.core.util.ThemeUtils;
import de.danoeh.antennapod.fragment.FeedItemlistFragment;
+import de.danoeh.antennapod.ui.common.ThemeUtils;
import jp.shts.android.library.TriangleLabelView;
/**
@@ -112,7 +112,7 @@ public class SubscriptionsAdapter extends BaseAdapter implements AdapterView.OnI
boolean textAndImageCombined = feed.isLocalFeed()
&& LocalFeedUpdater.getDefaultIconUrl(convertView.getContext()).equals(feed.getImageUrl());
new CoverLoader(mainActivityRef.get())
- .withUri(feed.getImageLocation())
+ .withUri(feed.getImageUrl())
.withPlaceholderView(holder.feedTitle, textAndImageCombined)
.withCoverView(holder.imageView)
.load();
@@ -123,7 +123,6 @@ public class SubscriptionsAdapter extends BaseAdapter implements AdapterView.OnI
.withCoverView(holder.imageView)
.load();
}
-
return convertView;
}
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 7a5cf431f..a45eb5199 100644
--- a/app/src/main/java/de/danoeh/antennapod/config/ClientConfigurator.java
+++ b/app/src/main/java/de/danoeh/antennapod/config/ClientConfigurator.java
@@ -2,21 +2,19 @@ package de.danoeh.antennapod.config;
import de.danoeh.antennapod.BuildConfig;
import de.danoeh.antennapod.core.ClientConfig;
-import de.danoeh.antennapod.core.storage.APDownloadAlgorithm;
/**
* Configures the ClientConfig class of the core package.
*/
class ClientConfigurator {
- private ClientConfigurator(){}
+ private ClientConfigurator() {
+ }
static {
ClientConfig.USER_AGENT = "AntennaPod/" + BuildConfig.VERSION_NAME;
ClientConfig.applicationCallbacks = new ApplicationCallbacksImpl();
ClientConfig.downloadServiceCallbacks = new DownloadServiceCallbacksImpl();
- ClientConfig.playbackServiceCallbacks = new PlaybackServiceCallbacksImpl();
- ClientConfig.automaticDownloadAlgorithm = new APDownloadAlgorithm();
ClientConfig.castCallbacks = new CastCallbackImpl();
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/config/DownloadServiceCallbacksImpl.java b/app/src/main/java/de/danoeh/antennapod/config/DownloadServiceCallbacksImpl.java
index 55bf05e43..f782308d1 100644
--- a/app/src/main/java/de/danoeh/antennapod/config/DownloadServiceCallbacksImpl.java
+++ b/app/src/main/java/de/danoeh/antennapod/config/DownloadServiceCallbacksImpl.java
@@ -30,8 +30,8 @@ public class DownloadServiceCallbacksImpl implements DownloadServiceCallbacks {
@Override
public PendingIntent getAuthentificationNotificationContentIntent(Context context, DownloadRequest request) {
final Intent activityIntent = new Intent(context.getApplicationContext(), DownloadAuthenticationActivity.class);
+ activityIntent.setAction("request" + request.getFeedfileId());
activityIntent.putExtra(DownloadAuthenticationActivity.ARG_DOWNLOAD_REQUEST, request);
- activityIntent.putExtra(DownloadAuthenticationActivity.ARG_SEND_TO_DOWNLOAD_REQUESTER_BOOL, true);
return PendingIntent.getActivity(context.getApplicationContext(),
R.id.pending_intent_download_service_auth, activityIntent, PendingIntent.FLAG_ONE_SHOT);
}
diff --git a/app/src/main/java/de/danoeh/antennapod/config/PlaybackServiceCallbacksImpl.java b/app/src/main/java/de/danoeh/antennapod/config/PlaybackServiceCallbacksImpl.java
deleted file mode 100644
index f70cb26ff..000000000
--- a/app/src/main/java/de/danoeh/antennapod/config/PlaybackServiceCallbacksImpl.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package de.danoeh.antennapod.config;
-
-import android.content.Context;
-import android.content.Intent;
-import android.os.Build;
-import de.danoeh.antennapod.activity.MainActivity;
-import de.danoeh.antennapod.activity.VideoplayerActivity;
-import de.danoeh.antennapod.core.PlaybackServiceCallbacks;
-import de.danoeh.antennapod.core.feed.MediaType;
-
-public class PlaybackServiceCallbacksImpl implements PlaybackServiceCallbacks {
- @Override
- public Intent getPlayerActivityIntent(Context context, MediaType mediaType, boolean remotePlayback) {
- if (mediaType == MediaType.AUDIO || remotePlayback) {
- return new Intent(context, MainActivity.class).putExtra(MainActivity.EXTRA_OPEN_PLAYER, true);
- } else {
- Intent i = new Intent(context, VideoplayerActivity.class);
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
- i.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
- }
- return i;
- }
- }
-}
diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/AuthenticationDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/AuthenticationDialog.java
index 39d321f18..d7b2dc536 100644
--- a/app/src/main/java/de/danoeh/antennapod/dialog/AuthenticationDialog.java
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/AuthenticationDialog.java
@@ -1,37 +1,50 @@
package de.danoeh.antennapod.dialog;
import android.content.Context;
-import android.view.View;
-import android.widget.EditText;
+import android.text.method.HideReturnsTransformationMethod;
+import android.text.method.PasswordTransformationMethod;
+import android.view.LayoutInflater;
import androidx.appcompat.app.AlertDialog;
import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.databinding.AuthenticationDialogBinding;
/**
* Displays a dialog with a username and password text field and an optional checkbox to save username and preferences.
*/
public abstract class AuthenticationDialog extends AlertDialog.Builder {
+ boolean passwordHidden = true;
public AuthenticationDialog(Context context, int titleRes, boolean enableUsernameField,
String usernameInitialValue, String passwordInitialValue) {
super(context);
setTitle(titleRes);
- View rootView = View.inflate(context, R.layout.authentication_dialog, null);
- setView(rootView);
+ AuthenticationDialogBinding viewBinding = AuthenticationDialogBinding.inflate(LayoutInflater.from(context));
+ setView(viewBinding.getRoot());
- final EditText etxtUsername = rootView.findViewById(R.id.etxtUsername);
- final EditText etxtPassword = rootView.findViewById(R.id.etxtPassword);
-
- etxtUsername.setEnabled(enableUsernameField);
+ viewBinding.usernameEditText.setEnabled(enableUsernameField);
if (usernameInitialValue != null) {
- etxtUsername.setText(usernameInitialValue);
+ viewBinding.usernameEditText.setText(usernameInitialValue);
}
if (passwordInitialValue != null) {
- etxtPassword.setText(passwordInitialValue);
+ viewBinding.passwordEditText.setText(passwordInitialValue);
}
+ viewBinding.showPasswordButton.setOnClickListener(v -> {
+ if (passwordHidden) {
+ viewBinding.passwordEditText.setTransformationMethod(HideReturnsTransformationMethod.getInstance());
+ viewBinding.showPasswordButton.setAlpha(1.0f);
+ } else {
+ viewBinding.passwordEditText.setTransformationMethod(PasswordTransformationMethod.getInstance());
+ viewBinding.showPasswordButton.setAlpha(0.6f);
+ }
+ passwordHidden = !passwordHidden;
+ });
+
setOnCancelListener(dialog -> onCancelled());
+ setOnDismissListener(dialog -> onCancelled());
setNegativeButton(R.string.cancel_label, null);
setPositiveButton(R.string.confirm_label, (dialog, which)
- -> onConfirmed(etxtUsername.getText().toString(), etxtPassword.getText().toString()));
+ -> onConfirmed(viewBinding.usernameEditText.getText().toString(),
+ viewBinding.passwordEditText.getText().toString()));
}
protected void onCancelled() {
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 efaff1da3..e1e8f1c2e 100644
--- a/app/src/main/java/de/danoeh/antennapod/dialog/EpisodesApplyActionFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/EpisodesApplyActionFragment.java
@@ -28,7 +28,7 @@ import de.danoeh.antennapod.core.storage.DownloadRequester;
import de.danoeh.antennapod.core.util.FeedItemPermutors;
import de.danoeh.antennapod.core.util.LongList;
import de.danoeh.antennapod.core.util.SortOrder;
-import de.danoeh.antennapod.core.util.ThemeUtils;
+import de.danoeh.antennapod.ui.common.ThemeUtils;
import java.util.ArrayList;
import java.util.Arrays;
diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/FilterDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/FilterDialog.java
index 80df87891..779248e2f 100644
--- a/app/src/main/java/de/danoeh/antennapod/dialog/FilterDialog.java
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/FilterDialog.java
@@ -16,7 +16,7 @@ import java.util.Set;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.feed.FeedItemFilter;
import de.danoeh.antennapod.core.feed.FeedItemFilterGroup;
-import de.danoeh.antennapod.view.RecursiveRadioGroup;
+import de.danoeh.antennapod.ui.common.RecursiveRadioGroup;
public abstract class FilterDialog {
diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/GpodnetSetHostnameDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/GpodnetSetHostnameDialog.java
deleted file mode 100644
index 8119dffcb..000000000
--- a/app/src/main/java/de/danoeh/antennapod/dialog/GpodnetSetHostnameDialog.java
+++ /dev/null
@@ -1,59 +0,0 @@
-package de.danoeh.antennapod.dialog;
-
-import android.content.Context;
-import androidx.appcompat.app.AlertDialog;
-import android.text.Editable;
-import android.text.InputType;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.EditText;
-import android.widget.LinearLayout;
-
-import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.core.preferences.GpodnetPreferences;
-import de.danoeh.antennapod.core.sync.gpoddernet.GpodnetService;
-
-/**
- * Creates a dialog that lets the user change the hostname for the gpodder.net service.
- */
-public class GpodnetSetHostnameDialog {
-
- private GpodnetSetHostnameDialog(){}
-
- private static final String TAG = "GpodnetSetHostnameDialog";
-
- public static AlertDialog createDialog(final Context context) {
- AlertDialog.Builder dialog = new AlertDialog.Builder(context);
- final EditText et = new EditText(context);
- et.setText(GpodnetPreferences.getHostname());
- et.setInputType(InputType.TYPE_TEXT_VARIATION_URI);
- dialog.setTitle(R.string.pref_gpodnet_sethostname_title)
- .setView(setupContentView(context, et))
- .setPositiveButton(R.string.confirm_label, (dialog1, which) -> {
- final Editable e = et.getText();
- if (e != null) {
- GpodnetPreferences.setHostname(e.toString());
- }
- dialog1.dismiss();
- })
- .setNegativeButton(R.string.cancel_label, (dialog1, which) -> dialog1.cancel())
- .setNeutralButton(R.string.pref_gpodnet_sethostname_use_default_host, (dialog1, which) -> {
- GpodnetPreferences.setHostname(GpodnetService.DEFAULT_BASE_HOST);
- dialog1.dismiss();
- })
- .setCancelable(true);
- return dialog.show();
- }
-
- private static View setupContentView(Context context, EditText et) {
- LinearLayout ll = new LinearLayout(context);
- ll.addView(et);
- LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) et.getLayoutParams();
- if (params != null) {
- params.setMargins(8, 8, 8, 8);
- params.width = ViewGroup.LayoutParams.MATCH_PARENT;
- params.height = ViewGroup.LayoutParams.MATCH_PARENT;
- }
- return ll;
- }
-}
diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/PlaybackControlsDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/PlaybackControlsDialog.java
index 98f6cc117..195891499 100644
--- a/app/src/main/java/de/danoeh/antennapod/dialog/PlaybackControlsDialog.java
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/PlaybackControlsDialog.java
@@ -41,7 +41,7 @@ public class PlaybackControlsDialog extends DialogFragment {
super.onStart();
controller = new PlaybackController(getActivity()) {
@Override
- public void setupGUI() {
+ public void loadMediaInfo() {
setupUi();
setupAudioTracks();
}
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 f1a41d753..fa5c2d8c3 100644
--- a/app/src/main/java/de/danoeh/antennapod/dialog/SleepTimerDialog.java
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/SleepTimerDialog.java
@@ -47,12 +47,12 @@ public class SleepTimerDialog extends DialogFragment {
super.onStart();
controller = new PlaybackController(getActivity()) {
@Override
- public void setupGUI() {
+ public void onSleepTimerUpdate() {
updateTime();
}
@Override
- public void onSleepTimerUpdate() {
+ public void loadMediaInfo() {
updateTime();
}
};
diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/SubscriptionsFilterDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/SubscriptionsFilterDialog.java
index 8a87fef25..29172bb5e 100644
--- a/app/src/main/java/de/danoeh/antennapod/dialog/SubscriptionsFilterDialog.java
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/SubscriptionsFilterDialog.java
@@ -20,7 +20,7 @@ import de.danoeh.antennapod.core.event.UnreadItemsUpdateEvent;
import de.danoeh.antennapod.core.feed.SubscriptionsFilter;
import de.danoeh.antennapod.core.feed.SubscriptionsFilterGroup;
import de.danoeh.antennapod.core.preferences.UserPreferences;
-import de.danoeh.antennapod.view.RecursiveRadioGroup;
+import de.danoeh.antennapod.ui.common.RecursiveRadioGroup;
public class SubscriptionsFilterDialog {
public static void showDialog(Context context) {
diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/VariableSpeedDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/VariableSpeedDialog.java
index 1fc7a77b2..65e7c4424 100644
--- a/app/src/main/java/de/danoeh/antennapod/dialog/VariableSpeedDialog.java
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/VariableSpeedDialog.java
@@ -56,12 +56,12 @@ public class VariableSpeedDialog extends DialogFragment {
super.onStart();
controller = new PlaybackController(getActivity()) {
@Override
- public void setupGUI() {
+ public void onPlaybackSpeedChange() {
updateSpeed();
}
@Override
- public void onPlaybackSpeedChange() {
+ public void loadMediaInfo() {
updateSpeed();
}
};
diff --git a/app/src/main/java/de/danoeh/antennapod/discovery/GpodnetPodcastSearcher.java b/app/src/main/java/de/danoeh/antennapod/discovery/GpodnetPodcastSearcher.java
index 53237579f..6de2186e0 100644
--- a/app/src/main/java/de/danoeh/antennapod/discovery/GpodnetPodcastSearcher.java
+++ b/app/src/main/java/de/danoeh/antennapod/discovery/GpodnetPodcastSearcher.java
@@ -18,7 +18,7 @@ public class GpodnetPodcastSearcher implements PodcastSearcher {
return Single.create((SingleOnSubscribe<List<PodcastSearchResult>>) subscriber -> {
try {
GpodnetService service = new GpodnetService(AntennapodHttpClient.getHttpClient(),
- GpodnetPreferences.getHostname());
+ GpodnetPreferences.getHosturl());
List<GpodnetPodcast> gpodnetPodcasts = service.searchPodcasts(query, 0);
List<PodcastSearchResult> results = new ArrayList<>();
for (GpodnetPodcast podcast : gpodnetPodcasts) {
diff --git a/app/src/main/java/de/danoeh/antennapod/discovery/PodcastSearcherRegistry.java b/app/src/main/java/de/danoeh/antennapod/discovery/PodcastSearcherRegistry.java
index ad574cab6..16c5548be 100644
--- a/app/src/main/java/de/danoeh/antennapod/discovery/PodcastSearcherRegistry.java
+++ b/app/src/main/java/de/danoeh/antennapod/discovery/PodcastSearcherRegistry.java
@@ -15,11 +15,11 @@ public class PodcastSearcherRegistry {
public static List<SearcherInfo> getSearchProviders() {
if (searchProviders == null) {
searchProviders = new ArrayList<>();
- searchProviders.add(new SearcherInfo(new CombinedSearcher(), 1.f));
- searchProviders.add(new SearcherInfo(new ItunesPodcastSearcher(), 1.f));
- searchProviders.add(new SearcherInfo(new FyydPodcastSearcher(), 1.f));
+ searchProviders.add(new SearcherInfo(new CombinedSearcher(), 1.0f));
searchProviders.add(new SearcherInfo(new GpodnetPodcastSearcher(), 0.0f));
- searchProviders.add(new SearcherInfo(new PodcastIndexPodcastSearcher(), 0.0f));
+ searchProviders.add(new SearcherInfo(new FyydPodcastSearcher(), 1.0f));
+ searchProviders.add(new SearcherInfo(new ItunesPodcastSearcher(), 1.0f));
+ searchProviders.add(new SearcherInfo(new PodcastIndexPodcastSearcher(), 1.0f));
}
return searchProviders;
}
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 06a974dfd..08e23fc7f 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/AddFeedFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/AddFeedFragment.java
@@ -50,9 +50,11 @@ public class AddFeedFragment extends Fragment {
public static final String TAG = "AddFeedFragment";
private static final int REQUEST_CODE_CHOOSE_OPML_IMPORT_PATH = 1;
private static final int REQUEST_CODE_ADD_LOCAL_FOLDER = 2;
+ private static final String KEY_UP_ARROW = "up_arrow";
private AddfeedBinding viewBinding;
private MainActivity activity;
+ private boolean displayUpArrow;
@Override
@Nullable
@@ -64,7 +66,11 @@ public class AddFeedFragment extends Fragment {
activity = (MainActivity) getActivity();
Toolbar toolbar = viewBinding.toolbar;
- ((MainActivity) getActivity()).setupToolbarToggle(toolbar);
+ displayUpArrow = getParentFragmentManager().getBackStackEntryCount() != 0;
+ if (savedInstanceState != null) {
+ displayUpArrow = savedInstanceState.getBoolean(KEY_UP_ARROW);
+ }
+ ((MainActivity) getActivity()).setupToolbarToggle(toolbar, displayUpArrow);
viewBinding.searchItunesButton.setOnClickListener(v
-> activity.loadChildFragment(OnlineSearchFragment.newInstance(ItunesPodcastSearcher.class)));
@@ -119,6 +125,12 @@ public class AddFeedFragment extends Fragment {
return viewBinding.getRoot();
}
+ @Override
+ public void onSaveInstanceState(@NonNull Bundle outState) {
+ outState.putBoolean(KEY_UP_ARROW, displayUpArrow);
+ super.onSaveInstanceState(outState);
+ }
+
private void showAddViaUrlDialog() {
AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
builder.setTitle(R.string.add_podcast_by_url);
@@ -196,7 +208,11 @@ public class AddFeedFragment extends Fragment {
if (documentFile == null) {
throw new IllegalArgumentException("Unable to retrieve document tree");
}
- Feed dirFeed = new Feed(Feed.PREFIX_LOCAL_FOLDER + uri.toString(), null, documentFile.getName());
+ String title = documentFile.getName();
+ if (title == null) {
+ title = getString(R.string.local_folder);
+ }
+ Feed dirFeed = new Feed(Feed.PREFIX_LOCAL_FOLDER + uri.toString(), null, title);
dirFeed.setItems(Collections.emptyList());
dirFeed.setSortOrder(SortOrder.EPISODE_TITLE_A_Z);
Feed fromDatabase = DBTasks.updateFeed(getContext(), dirFeed, false);
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 4423a2ebe..612959c04 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java
@@ -104,13 +104,12 @@ public class AllEpisodesFragment extends EpisodesListFragment {
@NonNull
@Override
protected List<FeedItem> loadData() {
- return feedItemFilter.filter(DBReader.getRecentlyPublishedEpisodes(0, page * EPISODES_PER_PAGE));
+ return DBReader.getRecentlyPublishedEpisodes(0, page * EPISODES_PER_PAGE, feedItemFilter);
}
@NonNull
@Override
protected List<FeedItem> loadMoreData() {
- return feedItemFilter.filter(DBReader.getRecentlyPublishedEpisodes((page - 1) * EPISODES_PER_PAGE,
- EPISODES_PER_PAGE));
+ return DBReader.getRecentlyPublishedEpisodes((page - 1) * EPISODES_PER_PAGE, EPISODES_PER_PAGE, feedItemFilter);
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/AudioPlayerFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/AudioPlayerFragment.java
index 82e2b3a6a..51f264e56 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/AudioPlayerFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/AudioPlayerFragment.java
@@ -1,8 +1,6 @@
package de.danoeh.antennapod.fragment;
-import android.content.Context;
import android.content.Intent;
-import android.content.SharedPreferences;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
@@ -17,7 +15,9 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.widget.Toolbar;
+import androidx.cardview.widget.CardView;
import androidx.fragment.app.Fragment;
+import androidx.interpolator.view.animation.FastOutSlowInInterpolator;
import androidx.viewpager2.adapter.FragmentStateAdapter;
import androidx.viewpager2.widget.ViewPager2;
import com.google.android.material.bottomsheet.BottomSheetBehavior;
@@ -29,11 +29,14 @@ import de.danoeh.antennapod.activity.CastEnabledActivity;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.core.event.FavoritesEvent;
import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
+import de.danoeh.antennapod.core.feed.Chapter;
+import de.danoeh.antennapod.core.event.UnreadItemsUpdateEvent;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.feed.util.PlaybackSpeedUtils;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.playback.PlaybackService;
+import de.danoeh.antennapod.core.util.ChapterUtils;
import de.danoeh.antennapod.core.util.Converter;
import de.danoeh.antennapod.core.util.IntentUtils;
import de.danoeh.antennapod.core.util.TimeSpeedConverter;
@@ -45,7 +48,8 @@ import de.danoeh.antennapod.dialog.SkipPreferenceDialog;
import de.danoeh.antennapod.dialog.SleepTimerDialog;
import de.danoeh.antennapod.dialog.VariableSpeedDialog;
import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler;
-import de.danoeh.antennapod.view.PlaybackSpeedIndicatorView;
+import de.danoeh.antennapod.view.ChapterSeekBar;
+import de.danoeh.antennapod.ui.common.PlaybackSpeedIndicatorView;
import io.reactivex.Maybe;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
@@ -62,14 +66,13 @@ import java.util.List;
* Shows the audio player.
*/
public class AudioPlayerFragment extends Fragment implements
- SeekBar.OnSeekBarChangeListener, Toolbar.OnMenuItemClickListener {
+ ChapterSeekBar.OnSeekBarChangeListener, Toolbar.OnMenuItemClickListener {
public static final String TAG = "AudioPlayerFragment";
private static final int POS_COVER = 0;
private static final int POS_DESCR = 1;
private static final int POS_CHAPTERS = 2;
private static final int NUM_CONTENT_FRAGMENTS = 3;
- private static final String PREFS = "AudioPlayerFragmentPreferences";
- private static final String PREF_SHOW_TIME_LEFT = "showTimeLeft";
+ public static final String PREFS = "AudioPlayerFragmentPreferences";
private static final float EPSILON = 0.001f;
PlaybackSpeedIndicatorView butPlaybackSpeed;
@@ -77,7 +80,7 @@ public class AudioPlayerFragment extends Fragment implements
private ViewPager2 pager;
private TextView txtvPosition;
private TextView txtvLength;
- private SeekBar sbPosition;
+ private ChapterSeekBar sbPosition;
private ImageButton butRev;
private TextView txtvRev;
private ImageButton butPlay;
@@ -86,6 +89,8 @@ public class AudioPlayerFragment extends Fragment implements
private ImageButton butSkip;
private Toolbar toolbar;
private ProgressBar progressIndicator;
+ private CardView cardViewSeek;
+ private TextView txtvSeek;
private PlaybackController controller;
private Disposable disposable;
@@ -122,6 +127,8 @@ public class AudioPlayerFragment extends Fragment implements
txtvFF = root.findViewById(R.id.txtvFF);
butSkip = root.findViewById(R.id.butSkip);
progressIndicator = root.findViewById(R.id.progLoading);
+ cardViewSeek = root.findViewById(R.id.cardViewSeek);
+ txtvSeek = root.findViewById(R.id.txtvSeek);
setupLengthTextView();
setupControlButtons();
@@ -168,12 +175,33 @@ public class AudioPlayerFragment extends Fragment implements
return root;
}
- public void setHasChapters(boolean hasChapters) {
+ private void setHasChapters(boolean hasChapters) {
this.hasChapters = hasChapters;
tabLayoutMediator.detach();
tabLayoutMediator.attach();
}
+ private void setChapterDividers(Playable media) {
+
+ if (media == null) {
+ return;
+ }
+
+ float[] dividerPos = null;
+
+ if (hasChapters) {
+ List<Chapter> chapters = media.getChapters();
+ dividerPos = new float[chapters.size()];
+ float duration = media.getDuration();
+
+ for (int i = 0; i < chapters.size(); i++) {
+ dividerPos[i] = chapters.get(i).getStart() / duration;
+ }
+ }
+
+ sbPosition.setDividerPos(dividerPos);
+ }
+
public View getExternalPlayerHolder() {
return getView().findViewById(R.id.playerFragment);
}
@@ -211,16 +239,25 @@ public class AudioPlayerFragment extends Fragment implements
IntentUtils.sendLocalBroadcast(getActivity(), PlaybackService.ACTION_SKIP_CURRENT_EPISODE));
}
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ public void onUnreadItemsUpdate(UnreadItemsUpdateEvent event) {
+ if (controller == null) {
+ return;
+ }
+ updatePosition(new PlaybackPositionEvent(controller.getPosition(),
+ controller.getDuration()));
+ }
+
private void setupLengthTextView() {
- SharedPreferences prefs = getContext().getSharedPreferences(PREFS, Context.MODE_PRIVATE);
- showTimeLeft = prefs.getBoolean(PREF_SHOW_TIME_LEFT, false);
+ showTimeLeft = UserPreferences.shouldShowRemainingTime();
txtvLength.setOnClickListener(v -> {
if (controller == null) {
return;
}
showTimeLeft = !showTimeLeft;
- prefs.edit().putBoolean(PREF_SHOW_TIME_LEFT, showTimeLeft).apply();
- updatePosition(new PlaybackPositionEvent(controller.getPosition(), controller.getDuration()));
+ UserPreferences.setShowRemainTimeSetting(showTimeLeft);
+ updatePosition(new PlaybackPositionEvent(controller.getPosition(),
+ controller.getDuration()));
});
}
@@ -285,26 +322,21 @@ public class AudioPlayerFragment extends Fragment implements
disposable = Maybe.create(emitter -> {
Playable media = controller.getMedia();
if (media != null) {
+ ChapterUtils.loadChapters(media, getContext());
emitter.onSuccess(media);
} else {
emitter.onComplete();
}
})
- .subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe(media -> updateUi((Playable) media),
- error -> Log.e(TAG, Log.getStackTraceString(error)),
- () -> updateUi(null));
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(media -> updateUi((Playable) media),
+ error -> Log.e(TAG, Log.getStackTraceString(error)),
+ () -> updateUi(null));
}
private PlaybackController newPlaybackController() {
return new PlaybackController(getActivity()) {
-
- @Override
- public void setupGUI() {
- AudioPlayerFragment.this.loadMediaInfo();
- }
-
@Override
public void onBufferStart() {
progressIndicator.setVisibility(View.VISIBLE);
@@ -352,9 +384,8 @@ public class AudioPlayerFragment extends Fragment implements
}
@Override
- public boolean loadMediaInfo() {
+ public void loadMediaInfo() {
AudioPlayerFragment.this.loadMediaInfo();
- return true;
}
@Override
@@ -383,8 +414,15 @@ public class AudioPlayerFragment extends Fragment implements
if (controller == null) {
return;
}
+
+ if (media != null && media.getChapters() != null) {
+ setHasChapters(media.getChapters().size() > 0);
+ } else {
+ setHasChapters(false);
+ }
updatePosition(new PlaybackPositionEvent(controller.getPosition(), controller.getDuration()));
updatePlaybackSpeedButton(media);
+ setChapterDividers(media);
setupOptionsMenu(media);
}
@@ -433,6 +471,7 @@ public class AudioPlayerFragment extends Fragment implements
return;
}
txtvPosition.setText(Converter.getDurationStringLong(currentPosition));
+ showTimeLeft = UserPreferences.shouldShowRemainingTime();
if (showTimeLeft) {
txtvLength.setText("-" + Converter.getDurationStringLong(remainingTime));
} else {
@@ -454,22 +493,22 @@ public class AudioPlayerFragment extends Fragment implements
}
if (fromUser) {
float prog = progress / ((float) seekBar.getMax());
- int duration = controller.getDuration();
TimeSpeedConverter converter = new TimeSpeedConverter(controller.getCurrentPlaybackSpeedMultiplier());
- int position = converter.convert((int) (prog * duration));
- txtvPosition.setText(Converter.getDurationStringLong(position));
-
- if (showTimeLeft && prog != 0) {
- int timeLeft = converter.convert(duration - (int) (prog * duration));
- String length = "-" + Converter.getDurationStringLong(timeLeft);
- txtvLength.setText(length);
- }
+ int position = converter.convert((int) (prog * controller.getDuration()));
+ txtvSeek.setText(Converter.getDurationStringLong(position));
}
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
// interrupt position Observer, restart later
+ cardViewSeek.setScaleX(.8f);
+ cardViewSeek.setScaleY(.8f);
+ cardViewSeek.animate()
+ .setInterpolator(new FastOutSlowInInterpolator())
+ .alpha(1f).scaleX(1f).scaleY(1f)
+ .setDuration(200)
+ .start();
}
@Override
@@ -478,6 +517,13 @@ public class AudioPlayerFragment extends Fragment implements
float prog = seekBar.getProgress() / ((float) seekBar.getMax());
controller.seekTo((int) (prog * controller.getDuration()));
}
+ cardViewSeek.setScaleX(1f);
+ cardViewSeek.setScaleY(1f);
+ cardViewSeek.animate()
+ .setInterpolator(new FastOutSlowInInterpolator())
+ .alpha(0f).scaleX(.8f).scaleY(.8f)
+ .setDuration(200)
+ .start();
}
public void setupOptionsMenu(Playable media) {
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 d781d0774..acda462bd 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/ChaptersFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/ChaptersFragment.java
@@ -8,9 +8,9 @@ import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
+import androidx.recyclerview.widget.DividerItemDecoration;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
-import com.yqritc.recyclerviewflexibledivider.HorizontalDividerItemDecoration;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.adapter.ChaptersListAdapter;
import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
@@ -45,7 +45,8 @@ public class ChaptersFragment extends Fragment {
RecyclerView recyclerView = root.findViewById(R.id.recyclerView);
layoutManager = new LinearLayoutManager(getActivity());
recyclerView.setLayoutManager(layoutManager);
- recyclerView.addItemDecoration(new HorizontalDividerItemDecoration.Builder(getActivity()).build());
+ recyclerView.addItemDecoration(new DividerItemDecoration(recyclerView.getContext(),
+ layoutManager.getOrientation()));
adapter = new ChaptersListAdapter(getActivity(), pos -> {
if (controller.getStatus() != PlayerStatus.PLAYING) {
@@ -71,13 +72,7 @@ public class ChaptersFragment extends Fragment {
super.onStart();
controller = new PlaybackController(getActivity()) {
@Override
- public boolean loadMediaInfo() {
- ChaptersFragment.this.loadMediaInfo();
- return true;
- }
-
- @Override
- public void setupGUI() {
+ public void loadMediaInfo() {
ChaptersFragment.this.loadMediaInfo();
}
@@ -123,7 +118,7 @@ public class ChaptersFragment extends Fragment {
disposable = Maybe.create(emitter -> {
Playable media = controller.getMedia();
if (media != null) {
- media.loadChapterMarks(getContext());
+ ChapterUtils.loadChapters(media, getContext());
emitter.onSuccess(media);
} else {
emitter.onComplete();
@@ -142,7 +137,6 @@ public class ChaptersFragment extends Fragment {
return;
}
adapter.setMedia(media);
- ((AudioPlayerFragment) getParentFragment()).setHasChapters(adapter.getItemCount() > 0);
int positionOfCurrentChapter = getCurrentChapter(media);
updateChapterSelection(positionOfCurrentChapter);
}
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 648fc614a..d8c382cb2 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/CoverFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/CoverFragment.java
@@ -13,11 +13,9 @@ import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
-
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
-
import com.bumptech.glide.Glide;
import com.bumptech.glide.load.resource.bitmap.FitCenter;
import com.bumptech.glide.load.resource.bitmap.RoundedCorners;
@@ -25,9 +23,11 @@ import com.bumptech.glide.RequestBuilder;
import com.bumptech.glide.request.RequestOptions;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
+import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.feed.util.ImageResourceUtils;
import de.danoeh.antennapod.core.glide.ApGlideSettings;
import de.danoeh.antennapod.core.util.ChapterUtils;
+import de.danoeh.antennapod.core.util.DateUtils;
import de.danoeh.antennapod.core.util.EmbeddedChapterImage;
import de.danoeh.antennapod.core.util.playback.Playable;
import de.danoeh.antennapod.core.util.playback.PlaybackController;
@@ -35,6 +35,7 @@ import io.reactivex.Maybe;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
+import org.apache.commons.lang3.StringUtils;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
@@ -93,7 +94,12 @@ public class CoverFragment extends Fragment {
}
private void displayMediaInfo(@NonNull Playable media) {
- txtvPodcastTitle.setText(media.getFeedTitle());
+ String pubDateStr = DateUtils.formatAbbrev(getActivity(), ((FeedMedia) media).getPubDate());
+ txtvPodcastTitle.setText(StringUtils.stripToEmpty(media.getFeedTitle())
+ + "\u00A0"
+ + "・"
+ + "\u00A0"
+ + StringUtils.replace(StringUtils.stripToEmpty(pubDateStr), " ", "\u00A0"));
txtvEpisodeTitle.setText(media.getEpisodeTitle());
displayedChapterIndex = -2; // Force refresh
displayCoverImage(media.getPosition());
@@ -111,13 +117,7 @@ public class CoverFragment extends Fragment {
super.onStart();
controller = new PlaybackController(getActivity()) {
@Override
- public boolean loadMediaInfo() {
- CoverFragment.this.loadMediaInfo();
- return true;
- }
-
- @Override
- public void setupGUI() {
+ public void loadMediaInfo() {
CoverFragment.this.loadMediaInfo();
}
};
@@ -151,23 +151,25 @@ public class CoverFragment extends Fragment {
if (chapter != displayedChapterIndex) {
displayedChapterIndex = chapter;
+ RequestOptions options = new RequestOptions()
+ .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
+ .dontAnimate()
+ .transforms(new FitCenter(),
+ new RoundedCorners((int) (16 * getResources().getDisplayMetrics().density)));
+
RequestBuilder<Drawable> cover = Glide.with(this)
- .load(ImageResourceUtils.getImageLocation(media))
- .apply(new RequestOptions()
- .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
- .dontAnimate()
- .transforms(new FitCenter(),
- new RoundedCorners((int) (16 * getResources().getDisplayMetrics().density))));
+ .load(media.getImageLocation())
+ .error(Glide.with(this)
+ .load(ImageResourceUtils.getFallbackImageLocation(media))
+ .apply(options))
+ .apply(options);
+
if (chapter == -1 || TextUtils.isEmpty(media.getChapters().get(chapter).getImageUrl())) {
cover.into(imgvCover);
} else {
Glide.with(this)
.load(EmbeddedChapterImage.getModelFor(media, chapter))
- .apply(new RequestOptions()
- .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
- .dontAnimate()
- .transforms(new FitCenter(),
- new RoundedCorners((int) (16 * getResources().getDisplayMetrics().density))))
+ .apply(options)
.thumbnail(cover)
.error(cover)
.into(imgvCover);
@@ -208,7 +210,7 @@ public class CoverFragment extends Fragment {
imgvCover.setLayoutParams(params);
}
} else {
- double percentageHeight = ratio * 0.8;
+ double percentageHeight = ratio * 0.6;
mainContainer.setOrientation(LinearLayout.HORIZONTAL);
if (newConfig.screenHeightDp > 0) {
params.height = (int) (convertDpToPixel(newConfig.screenHeightDp) * percentageHeight);
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/DownloadsFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/DownloadsFragment.java
index ffb3e71fa..5c83cee57 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/DownloadsFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/DownloadsFragment.java
@@ -28,16 +28,17 @@ public class DownloadsFragment extends PagedToolbarFragment {
public static final String TAG = "DownloadsFragment";
public static final String ARG_SELECTED_TAB = "selected_tab";
+ private static final String PREF_LAST_TAB_POSITION = "tab_position";
+ private static final String KEY_UP_ARROW = "up_arrow";
public static final int POS_RUNNING = 0;
private static final int POS_COMPLETED = 1;
public static final int POS_LOG = 2;
private static final int TOTAL_COUNT = 3;
- private static final String PREF_LAST_TAB_POSITION = "tab_position";
-
private ViewPager2 viewPager;
private TabLayout tabLayout;
+ private boolean displayUpArrow;
@Override
public View onCreateView(@NonNull LayoutInflater inflater,
@@ -48,7 +49,11 @@ public class DownloadsFragment extends PagedToolbarFragment {
Toolbar toolbar = root.findViewById(R.id.toolbar);
toolbar.setTitle(R.string.downloads_label);
toolbar.inflateMenu(R.menu.downloads);
- ((MainActivity) getActivity()).setupToolbarToggle(toolbar);
+ displayUpArrow = getParentFragmentManager().getBackStackEntryCount() != 0;
+ if (savedInstanceState != null) {
+ displayUpArrow = savedInstanceState.getBoolean(KEY_UP_ARROW);
+ }
+ ((MainActivity) getActivity()).setupToolbarToggle(toolbar, displayUpArrow);
viewPager = root.findViewById(R.id.viewpager);
viewPager.setAdapter(new DownloadsPagerAdapter(this));
@@ -82,6 +87,12 @@ public class DownloadsFragment extends PagedToolbarFragment {
}
@Override
+ public void onSaveInstanceState(@NonNull Bundle outState) {
+ outState.putBoolean(KEY_UP_ARROW, displayUpArrow);
+ super.onSaveInstanceState(outState);
+ }
+
+ @Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
if (getArguments() != null) {
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesFragment.java
index eff23f7a3..1ca5d524b 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesFragment.java
@@ -24,6 +24,7 @@ public class EpisodesFragment extends PagedToolbarFragment {
public static final String TAG = "EpisodesFragment";
private static final String PREF_LAST_TAB_POSITION = "tab_position";
+ private static final String KEY_UP_ARROW = "up_arrow";
private static final int POS_NEW_EPISODES = 0;
private static final int POS_ALL_EPISODES = 1;
@@ -31,6 +32,7 @@ public class EpisodesFragment extends PagedToolbarFragment {
private static final int TOTAL_COUNT = 3;
private TabLayout tabLayout;
+ private boolean displayUpArrow;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -44,7 +46,11 @@ public class EpisodesFragment extends PagedToolbarFragment {
toolbar.setTitle(R.string.episodes_label);
toolbar.inflateMenu(R.menu.episodes);
MenuItemUtils.setupSearchItem(toolbar.getMenu(), (MainActivity) getActivity(), 0, "");
- ((MainActivity) getActivity()).setupToolbarToggle(toolbar);
+ displayUpArrow = getParentFragmentManager().getBackStackEntryCount() != 0;
+ if (savedInstanceState != null) {
+ displayUpArrow = savedInstanceState.getBoolean(KEY_UP_ARROW);
+ }
+ ((MainActivity) getActivity()).setupToolbarToggle(toolbar, displayUpArrow);
ViewPager2 viewPager = rootView.findViewById(R.id.viewpager);
viewPager.setAdapter(new EpisodesPagerAdapter(this));
@@ -88,6 +94,12 @@ public class EpisodesFragment extends PagedToolbarFragment {
editor.apply();
}
+ @Override
+ public void onSaveInstanceState(@NonNull Bundle outState) {
+ outState.putBoolean(KEY_UP_ARROW, displayUpArrow);
+ super.onSaveInstanceState(outState);
+ }
+
static class EpisodesPagerAdapter extends FragmentStateAdapter {
EpisodesPagerAdapter(@NonNull Fragment fragment) {
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java
index 8dae310ba..39f935bbe 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java
@@ -383,6 +383,14 @@ public abstract class EpisodesListFragment extends Fragment {
@NonNull
protected abstract List<FeedItem> loadData();
+ /**
+ * Load a new page of data as defined by {@link #page} and {@link #EPISODES_PER_PAGE}.
+ * If the number of items returned is less than {@link #EPISODES_PER_PAGE},
+ * it will be assumed that the underlying data is exhausted
+ * and this method will not be called again.
+ *
+ * @return The items from the next page of data
+ */
@NonNull
protected abstract List<FeedItem> loadMoreData();
}
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 5d701472f..d77935910 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java
@@ -108,12 +108,7 @@ public class ExternalPlayerFragment extends Fragment {
}
@Override
- public boolean loadMediaInfo() {
- return ExternalPlayerFragment.this.loadMediaInfo();
- }
-
- @Override
- public void setupGUI() {
+ public void loadMediaInfo() {
ExternalPlayerFragment.this.loadMediaInfo();
}
@@ -170,11 +165,11 @@ public class ExternalPlayerFragment extends Fragment {
}
}
- private boolean loadMediaInfo() {
+ private void loadMediaInfo() {
Log.d(TAG, "Loading media info");
if (controller == null) {
Log.w(TAG, "loadMediaInfo was called while PlaybackController was null!");
- return false;
+ return;
}
if (disposable != null) {
@@ -186,7 +181,6 @@ public class ExternalPlayerFragment extends Fragment {
.subscribe(this::updateUi,
error -> Log.e(TAG, Log.getStackTraceString(error)),
() -> ((MainActivity) getActivity()).setPlayerVisible(false));
- return true;
}
private void updateUi(Playable media) {
@@ -198,14 +192,19 @@ public class ExternalPlayerFragment extends Fragment {
feedName.setText(media.getFeedTitle());
onPositionObserverUpdate();
+ RequestOptions options = new RequestOptions()
+ .placeholder(R.color.light_gray)
+ .error(R.color.light_gray)
+ .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
+ .fitCenter()
+ .dontAnimate();
+
Glide.with(getActivity())
- .load(ImageResourceUtils.getImageLocation(media))
- .apply(new RequestOptions()
- .placeholder(R.color.light_gray)
- .error(R.color.light_gray)
- .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
- .fitCenter()
- .dontAnimate())
+ .load(ImageResourceUtils.getEpisodeListImageLocation(media))
+ .error(Glide.with(getActivity())
+ .load(ImageResourceUtils.getFallbackImageLocation(media))
+ .apply(options))
+ .apply(options)
.into(imgvCover);
if (controller != null && controller.isPlayingVideoLocally()) {
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/FeedInfoFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/FeedInfoFragment.java
index abb597e60..25ab925eb 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/FeedInfoFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/FeedInfoFragment.java
@@ -45,7 +45,7 @@ import de.danoeh.antennapod.core.storage.DownloadRequestException;
import de.danoeh.antennapod.core.storage.StatisticsItem;
import de.danoeh.antennapod.core.util.Converter;
import de.danoeh.antennapod.core.util.IntentUtils;
-import de.danoeh.antennapod.core.util.ThemeUtils;
+import de.danoeh.antennapod.ui.common.ThemeUtils;
import de.danoeh.antennapod.core.util.syndication.HtmlToPlainText;
import de.danoeh.antennapod.fragment.preferences.StatisticsFragment;
import de.danoeh.antennapod.menuhandler.FeedMenuHandler;
@@ -130,6 +130,8 @@ public class FeedInfoFragment extends Fragment implements Toolbar.OnMenuItemClic
protected void doTint(Context themedContext) {
toolbar.getMenu().findItem(R.id.visit_website_item)
.setIcon(ThemeUtils.getDrawableFromAttr(themedContext, R.attr.location_web_site));
+ toolbar.getMenu().findItem(R.id.share_parent)
+ .setIcon(ThemeUtils.getDrawableFromAttr(themedContext, R.attr.ic_share));
}
};
iconTintManager.updateTint();
@@ -201,7 +203,7 @@ public class FeedInfoFragment extends Fragment implements Toolbar.OnMenuItemClic
Log.d(TAG, "Author is " + feed.getAuthor());
Log.d(TAG, "URL is " + feed.getDownload_url());
Glide.with(getContext())
- .load(feed.getImageLocation())
+ .load(feed.getImageUrl())
.apply(new RequestOptions()
.placeholder(R.color.light_gray)
.error(R.color.light_gray)
@@ -210,7 +212,7 @@ public class FeedInfoFragment extends Fragment implements Toolbar.OnMenuItemClic
.dontAnimate())
.into(imgvCover);
Glide.with(getContext())
- .load(feed.getImageLocation())
+ .load(feed.getImageUrl())
.apply(new RequestOptions()
.placeholder(R.color.image_readability_tint)
.error(R.color.image_readability_tint)
@@ -284,9 +286,13 @@ public class FeedInfoFragment extends Fragment implements Toolbar.OnMenuItemClic
}
private void refreshToolbarState() {
+ boolean shareLinkVisible = feed != null && feed.getLink() != null;
+ boolean downloadUrlVisible = feed != null && !feed.isLocalFeed();
+
toolbar.getMenu().findItem(R.id.reconnect_local_folder).setVisible(feed != null && feed.isLocalFeed());
- toolbar.getMenu().findItem(R.id.share_download_url_item).setVisible(feed != null && !feed.isLocalFeed());
- toolbar.getMenu().findItem(R.id.share_link_item).setVisible(feed != null && feed.getLink() != null);
+ toolbar.getMenu().findItem(R.id.share_download_url_item).setVisible(downloadUrlVisible);
+ toolbar.getMenu().findItem(R.id.share_link_item).setVisible(shareLinkVisible);
+ toolbar.getMenu().findItem(R.id.share_parent).setVisible(downloadUrlVisible || shareLinkVisible);
toolbar.getMenu().findItem(R.id.visit_website_item).setVisible(feed != null && feed.getLink() != null
&& IntentUtils.isCallable(getContext(), new Intent(Intent.ACTION_VIEW, Uri.parse(feed.getLink()))));
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/FeedItemlistFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/FeedItemlistFragment.java
index 8e14214d2..acb929dd2 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/FeedItemlistFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/FeedItemlistFragment.java
@@ -16,7 +16,6 @@ import android.widget.AdapterView;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.ProgressBar;
-import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
@@ -57,8 +56,7 @@ import de.danoeh.antennapod.core.storage.DownloadRequestException;
import de.danoeh.antennapod.core.storage.DownloadRequester;
import de.danoeh.antennapod.core.util.FeedItemPermutors;
import de.danoeh.antennapod.core.util.FeedItemUtil;
-import de.danoeh.antennapod.core.util.Optional;
-import de.danoeh.antennapod.core.util.ThemeUtils;
+import de.danoeh.antennapod.ui.common.ThemeUtils;
import de.danoeh.antennapod.core.util.gui.MoreContentListFooterUtil;
import de.danoeh.antennapod.dialog.EpisodesApplyActionFragment;
import de.danoeh.antennapod.dialog.FilterDialog;
@@ -89,6 +87,7 @@ public class FeedItemlistFragment extends Fragment implements AdapterView.OnItem
Toolbar.OnMenuItemClickListener {
private static final String TAG = "ItemlistFragment";
private static final String ARGUMENT_FEED_ID = "argument.de.danoeh.antennapod.feed_id";
+ private static final String KEY_UP_ARROW = "up_arrow";
private FeedItemListAdapter adapter;
private MoreContentListFooterUtil nextPageLoader;
@@ -106,6 +105,7 @@ public class FeedItemlistFragment extends Fragment implements AdapterView.OnItem
private View header;
private Toolbar toolbar;
private ToolbarIconTintManager iconTintManager;
+ private boolean displayUpArrow;
private long feedID;
private Feed feed;
@@ -146,7 +146,11 @@ public class FeedItemlistFragment extends Fragment implements AdapterView.OnItem
toolbar = root.findViewById(R.id.toolbar);
toolbar.inflateMenu(R.menu.feedlist);
toolbar.setOnMenuItemClickListener(this);
- ((MainActivity) getActivity()).setupToolbarToggle(toolbar);
+ displayUpArrow = getParentFragmentManager().getBackStackEntryCount() != 0;
+ if (savedInstanceState != null) {
+ displayUpArrow = savedInstanceState.getBoolean(KEY_UP_ARROW);
+ }
+ ((MainActivity) getActivity()).setupToolbarToggle(toolbar, displayUpArrow);
refreshToolbarState();
recyclerView = root.findViewById(R.id.recyclerView);
@@ -231,6 +235,12 @@ public class FeedItemlistFragment extends Fragment implements AdapterView.OnItem
adapter = null;
}
+ @Override
+ public void onSaveInstanceState(@NonNull Bundle outState) {
+ outState.putBoolean(KEY_UP_ARROW, displayUpArrow);
+ super.onSaveInstanceState(outState);
+ }
+
private final MenuItemUtils.UpdateRefreshMenuItemChecker updateRefreshMenuItemChecker = new MenuItemUtils.UpdateRefreshMenuItemChecker() {
@Override
public boolean isRefreshing() {
@@ -451,10 +461,6 @@ public class FeedItemlistFragment extends Fragment implements AdapterView.OnItem
if (feed.getItemFilter() != null) {
FeedItemFilter filter = feed.getItemFilter();
if (filter.getValues().length > 0) {
- if (feed.hasLastUpdateFailed()) {
- RelativeLayout.LayoutParams p = (RelativeLayout.LayoutParams) txtvInformation.getLayoutParams();
- p.addRule(RelativeLayout.BELOW, R.id.txtvFailure);
- }
txtvInformation.setText("{md-info-outline} " + this.getString(R.string.filtered_label));
Iconify.addIcons(txtvInformation);
txtvInformation.setOnClickListener((l) -> {
@@ -514,7 +520,7 @@ public class FeedItemlistFragment extends Fragment implements AdapterView.OnItem
private void loadFeedImage() {
Glide.with(getActivity())
- .load(feed.getImageLocation())
+ .load(feed.getImageUrl())
.apply(new RequestOptions()
.placeholder(R.color.image_readability_tint)
.error(R.color.image_readability_tint)
@@ -524,7 +530,7 @@ public class FeedItemlistFragment extends Fragment implements AdapterView.OnItem
.into(imgvBackground);
Glide.with(getActivity())
- .load(feed.getImageLocation())
+ .load(feed.getImageUrl())
.apply(new RequestOptions()
.placeholder(R.color.light_gray)
.error(R.color.light_gray)
@@ -542,27 +548,32 @@ public class FeedItemlistFragment extends Fragment implements AdapterView.OnItem
disposable = Observable.fromCallable(this::loadData)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
- .subscribe(result -> {
- feed = result.orElse(null);
- refreshHeaderView();
- displayList();
- }, error -> Log.e(TAG, Log.getStackTraceString(error)));
+ .subscribe(
+ result -> {
+ feed = result;
+ refreshHeaderView();
+ displayList();
+ }, error -> {
+ feed = null;
+ refreshHeaderView();
+ displayList();
+ Log.e(TAG, Log.getStackTraceString(error));
+ });
}
- @NonNull
- private Optional<Feed> loadData() {
- Feed feed = DBReader.getFeed(feedID);
- if (feed != null && feed.getItemFilter() != null) {
- DBReader.loadAdditionalFeedItemListData(feed.getItems());
- FeedItemFilter filter = feed.getItemFilter();
- feed.setItems(filter.filter(feed.getItems()));
+ @Nullable
+ private Feed loadData() {
+ Feed feed = DBReader.getFeed(feedID, true);
+ if (feed == null) {
+ return null;
}
- if (feed != null && feed.getSortOrder() != null) {
+ DBReader.loadAdditionalFeedItemListData(feed.getItems());
+ if (feed.getSortOrder() != null) {
List<FeedItem> feedItems = feed.getItems();
FeedItemPermutors.getPermutor(feed.getSortOrder()).reorder(feedItems);
feed.setItems(feedItems);
}
- return Optional.ofNullable(feed);
+ return feed;
}
private static class FeedItemListAdapter extends EpisodeItemListAdapter {
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/FeedSettingsFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/FeedSettingsFragment.java
index 1253a8ad2..c000107a7 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/FeedSettingsFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/FeedSettingsFragment.java
@@ -164,6 +164,7 @@ public class FeedSettingsFragment extends Fragment {
setupEpisodeFilterPreference();
setupPlaybackSpeedPreference();
setupFeedAutoSkipPreference();
+ setupEpisodeNotificationPreference();
setupTags();
updateAutoDeleteSummary();
@@ -198,7 +199,7 @@ public class FeedSettingsFragment extends Fragment {
protected void onConfirmed(int skipIntro, int skipEnding) {
feedPreferences.setFeedSkipIntro(skipIntro);
feedPreferences.setFeedSkipEnding(skipEnding);
- feed.savePreferences();
+ DBWriter.setFeedPreferences(feedPreferences);
EventBus.getDefault().post(
new SkipIntroEndingChangedEvent(feedPreferences.getFeedSkipIntro(),
feedPreferences.getFeedSkipEnding(),
@@ -226,7 +227,7 @@ public class FeedSettingsFragment extends Fragment {
feedPlaybackSpeedPreference.setEntries(entries);
feedPlaybackSpeedPreference.setOnPreferenceChangeListener((preference, newValue) -> {
feedPreferences.setFeedPlaybackSpeed(Float.parseFloat((String) newValue));
- feed.savePreferences();
+ DBWriter.setFeedPreferences(feedPreferences);
updatePlaybackSpeedPreference();
EventBus.getDefault().post(
new SpeedPresetChangedEvent(feedPreferences.getFeedPlaybackSpeed(), feed.getId()));
@@ -240,7 +241,7 @@ public class FeedSettingsFragment extends Fragment {
@Override
protected void onConfirmed(FeedFilter filter) {
feedPreferences.setFilter(filter);
- feed.savePreferences();
+ DBWriter.setFeedPreferences(feedPreferences);
}
}.show();
return false;
@@ -256,7 +257,7 @@ public class FeedSettingsFragment extends Fragment {
protected void onConfirmed(String username, String password) {
feedPreferences.setUsername(username);
feedPreferences.setPassword(password);
- feed.savePreferences();
+ DBWriter.setFeedPreferences(feedPreferences);
}
}.show();
return false;
@@ -276,7 +277,7 @@ public class FeedSettingsFragment extends Fragment {
feedPreferences.setAutoDeleteAction(FeedPreferences.AutoDeleteAction.NO);
break;
}
- feed.savePreferences();
+ DBWriter.setFeedPreferences(feedPreferences);
updateAutoDeleteSummary();
return false;
});
@@ -322,7 +323,7 @@ public class FeedSettingsFragment extends Fragment {
feedPreferences.setVolumeAdaptionSetting(VolumeAdaptionSetting.HEAVY_REDUCTION);
break;
}
- feed.savePreferences();
+ DBWriter.setFeedPreferences(feedPreferences);
updateVolumeReductionValue();
EventBus.getDefault().post(
new VolumeAdaptionChangedEvent(feedPreferences.getVolumeAdaptionSetting(), feed.getId()));
@@ -353,7 +354,7 @@ public class FeedSettingsFragment extends Fragment {
pref.setOnPreferenceChangeListener((preference, newValue) -> {
boolean checked = newValue == Boolean.TRUE;
feedPreferences.setKeepUpdated(checked);
- feed.savePreferences();
+ DBWriter.setFeedPreferences(feedPreferences);
pref.setChecked(checked);
return false;
});
@@ -384,7 +385,7 @@ public class FeedSettingsFragment extends Fragment {
boolean checked = newValue == Boolean.TRUE;
feedPreferences.setAutoDownload(checked);
- feed.savePreferences();
+ DBWriter.setFeedPreferences(feedPreferences);
updateAutoDownloadEnabled();
ApplyToEpisodesDialog dialog = new ApplyToEpisodesDialog(getActivity(), checked);
dialog.createNewDialog().show();
@@ -412,7 +413,7 @@ public class FeedSettingsFragment extends Fragment {
feedPreferences.getTags().clear();
feedPreferences.getTags().addAll(new HashSet<>(Arrays.asList(
foldersString.split(FeedPreferences.TAG_SEPARATOR))));
- feed.savePreferences();
+ DBWriter.setFeedPreferences(feedPreferences);
})
.setNegativeButton(R.string.cancel_label, null)
.show();
@@ -420,6 +421,19 @@ public class FeedSettingsFragment extends Fragment {
});
}
+ private void setupEpisodeNotificationPreference() {
+ SwitchPreferenceCompat pref = findPreference("episodeNotification");
+
+ pref.setChecked(feedPreferences.getShowEpisodeNotification());
+ pref.setOnPreferenceChangeListener((preference, newValue) -> {
+ boolean checked = newValue == Boolean.TRUE;
+ feedPreferences.setShowEpisodeNotification(checked);
+ DBWriter.setFeedPreferences(feedPreferences);
+ pref.setChecked(checked);
+ return false;
+ });
+ }
+
private class ApplyToEpisodesDialog extends ConfirmationDialog {
private final boolean autoDownload;
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 18a61f1e6..2e13bbd79 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java
@@ -10,6 +10,9 @@ import android.view.View;
import android.view.ViewGroup;
import androidx.fragment.app.Fragment;
import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.core.feed.FeedMedia;
+import de.danoeh.antennapod.core.storage.DBReader;
+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 de.danoeh.antennapod.view.ShownotesWebView;
@@ -82,7 +85,15 @@ public class ItemDescriptionFragment extends Fragment {
webViewLoader.dispose();
}
webViewLoader = Maybe.<String>create(emitter -> {
- Timeline timeline = new Timeline(getActivity(), controller.getMedia());
+ Playable media = controller.getMedia();
+ if (media instanceof FeedMedia) {
+ FeedMedia feedMedia = ((FeedMedia) media);
+ if (feedMedia.getItem() == null) {
+ feedMedia.setItem(DBReader.getFeedItem(feedMedia.getItemId()));
+ }
+ DBReader.loadDescriptionOfFeedItem(feedMedia.getItem());
+ }
+ Timeline timeline = new Timeline(getActivity(), media.getDescription(), media.getDuration());
emitter.onSuccess(timeline.processShownotes());
}).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
@@ -140,14 +151,8 @@ public class ItemDescriptionFragment extends Fragment {
super.onStart();
controller = new PlaybackController(getActivity()) {
@Override
- public boolean loadMediaInfo() {
+ public void loadMediaInfo() {
load();
- return true;
- }
-
- @Override
- public void setupGUI() {
- ItemDescriptionFragment.this.load();
}
};
controller.init();
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 07f59bb42..224210d63 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java
@@ -57,7 +57,7 @@ import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DownloadRequester;
import de.danoeh.antennapod.core.util.Converter;
import de.danoeh.antennapod.core.util.DateUtils;
-import de.danoeh.antennapod.core.util.ThemeUtils;
+import de.danoeh.antennapod.ui.common.ThemeUtils;
import de.danoeh.antennapod.core.util.playback.PlaybackController;
import de.danoeh.antennapod.core.util.playback.Timeline;
import de.danoeh.antennapod.view.ShownotesWebView;
@@ -238,7 +238,12 @@ public class ItemFragment extends Fragment {
public void onStart() {
super.onStart();
EventBus.getDefault().register(this);
- controller = new PlaybackController(getActivity());
+ controller = new PlaybackController(getActivity()) {
+ @Override
+ public void loadMediaInfo() {
+ // Do nothing
+ }
+ };
controller.init();
}
@@ -291,14 +296,19 @@ public class ItemFragment extends Fragment {
txtvPublished.setContentDescription(DateUtils.formatForAccessibility(getContext(), item.getPubDate()));
}
+ RequestOptions options = new RequestOptions()
+ .error(R.color.light_gray)
+ .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
+ .transforms(new FitCenter(),
+ new RoundedCorners((int) (4 * getResources().getDisplayMetrics().density)))
+ .dontAnimate();
+
Glide.with(getActivity())
- .load(ImageResourceUtils.getImageLocation(item))
- .apply(new RequestOptions()
- .error(R.color.light_gray)
- .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
- .transforms(new FitCenter(),
- new RoundedCorners((int) (4 * getResources().getDisplayMetrics().density)))
- .dontAnimate())
+ .load(item.getImageLocation())
+ .error(Glide.with(getActivity())
+ .load(ImageResourceUtils.getFallbackImageLocation(item))
+ .apply(options))
+ .apply(options)
.into(imgvCover);
updateButtons();
}
@@ -429,7 +439,9 @@ public class ItemFragment extends Fragment {
FeedItem feedItem = DBReader.getFeedItem(itemId);
Context context = getContext();
if (feedItem != null && context != null) {
- Timeline t = new Timeline(context, feedItem);
+ int duration = feedItem.getMedia() != null ? feedItem.getMedia().getDuration() : Integer.MAX_VALUE;
+ DBReader.loadDescriptionOfFeedItem(feedItem);
+ Timeline t = new Timeline(context, feedItem.getDescription(), duration);
webviewData = t.processShownotes();
}
return feedItem;
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/NavDrawerFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/NavDrawerFragment.java
index 3d82bf7a1..e8c04336f 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/NavDrawerFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/NavDrawerFragment.java
@@ -424,7 +424,7 @@ public class NavDrawerFragment extends Fragment implements SharedPreferences.OnS
flatItemList = result.second;
updateSelection(); // Selected item might be a feed
navAdapter.notifyDataSetChanged();
- progressBar.setVisibility(View.GONE);
+ progressBar.setVisibility(View.GONE); // Stays hidden once there is something in the list
}, error -> {
Log.e(TAG, Log.getStackTraceString(error));
progressBar.setVisibility(View.GONE);
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 973fcb978..e97b7cd7f 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java
@@ -41,6 +41,7 @@ import java.util.List;
public class PlaybackHistoryFragment extends Fragment implements Toolbar.OnMenuItemClickListener {
public static final String TAG = "PlaybackHistoryFragment";
+ private static final String KEY_UP_ARROW = "up_arrow";
private List<FeedItem> playbackHistory;
private PlaybackHistoryListAdapter adapter;
@@ -49,6 +50,7 @@ public class PlaybackHistoryFragment extends Fragment implements Toolbar.OnMenuI
private EmptyViewHandler emptyView;
private ProgressBar progressBar;
private Toolbar toolbar;
+ private boolean displayUpArrow;
@Override
public void onCreate(Bundle savedInstanceState) {
@@ -63,7 +65,11 @@ public class PlaybackHistoryFragment extends Fragment implements Toolbar.OnMenuI
toolbar = root.findViewById(R.id.toolbar);
toolbar.setTitle(R.string.playback_history_label);
toolbar.setOnMenuItemClickListener(this);
- ((MainActivity) getActivity()).setupToolbarToggle(toolbar);
+ displayUpArrow = getParentFragmentManager().getBackStackEntryCount() != 0;
+ if (savedInstanceState != null) {
+ displayUpArrow = savedInstanceState.getBoolean(KEY_UP_ARROW);
+ }
+ ((MainActivity) getActivity()).setupToolbarToggle(toolbar, displayUpArrow);
toolbar.inflateMenu(R.menu.playback_history);
refreshToolbarState();
@@ -98,6 +104,12 @@ public class PlaybackHistoryFragment extends Fragment implements Toolbar.OnMenuI
}
}
+ @Override
+ public void onSaveInstanceState(@NonNull Bundle outState) {
+ outState.putBoolean(KEY_UP_ARROW, displayUpArrow);
+ super.onSaveInstanceState(outState);
+ }
+
@Subscribe(threadMode = ThreadMode.MAIN)
public void onEventMainThread(FeedItemEvent event) {
Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
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 983bf4de1..2850acc15 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java
@@ -12,6 +12,7 @@ import android.view.ViewGroup;
import android.widget.CheckBox;
import android.widget.ProgressBar;
import android.widget.TextView;
+import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.widget.Toolbar;
import androidx.fragment.app.Fragment;
@@ -67,6 +68,7 @@ import static de.danoeh.antennapod.dialog.EpisodesApplyActionFragment.ACTION_REM
*/
public class QueueFragment extends Fragment implements Toolbar.OnMenuItemClickListener {
public static final String TAG = "QueueFragment";
+ private static final String KEY_UP_ARROW = "up_arrow";
private TextView infoBar;
private EpisodeItemListRecyclerView recyclerView;
@@ -74,6 +76,7 @@ public class QueueFragment extends Fragment implements Toolbar.OnMenuItemClickLi
private EmptyViewHandler emptyView;
private ProgressBar progLoading;
private Toolbar toolbar;
+ private boolean displayUpArrow;
private List<FeedItem> queue;
@@ -420,7 +423,11 @@ public class QueueFragment extends Fragment implements Toolbar.OnMenuItemClickLi
View root = inflater.inflate(R.layout.queue_fragment, container, false);
toolbar = root.findViewById(R.id.toolbar);
toolbar.setOnMenuItemClickListener(this);
- ((MainActivity) getActivity()).setupToolbarToggle(toolbar);
+ displayUpArrow = getParentFragmentManager().getBackStackEntryCount() != 0;
+ if (savedInstanceState != null) {
+ displayUpArrow = savedInstanceState.getBoolean(KEY_UP_ARROW);
+ }
+ ((MainActivity) getActivity()).setupToolbarToggle(toolbar, displayUpArrow);
toolbar.inflateMenu(R.menu.queue);
MenuItemUtils.setupSearchItem(toolbar.getMenu(), (MainActivity) getActivity(), 0, "");
refreshToolbarState();
@@ -530,6 +537,12 @@ public class QueueFragment extends Fragment implements Toolbar.OnMenuItemClickLi
return root;
}
+ @Override
+ public void onSaveInstanceState(@NonNull Bundle outState) {
+ outState.putBoolean(KEY_UP_ARROW, displayUpArrow);
+ super.onSaveInstanceState(outState);
+ }
+
private void onFragmentLoaded(final boolean restoreScrollPosition) {
if (queue != null && queue.size() > 0) {
if (recyclerAdapter == null) {
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 bb00d88e1..3c529d941 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/SubscriptionFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/SubscriptionFragment.java
@@ -8,6 +8,7 @@ import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.widget.ProgressBar;
+import androidx.annotation.NonNull;
import androidx.annotation.StringRes;
import androidx.appcompat.widget.Toolbar;
import androidx.fragment.app.Fragment;
@@ -67,6 +68,8 @@ public class SubscriptionFragment extends Fragment implements Toolbar.OnMenuItem
public static final String TAG = "SubscriptionFragment";
private static final String PREFS = "SubscriptionFragment";
private static final String PREF_NUM_COLUMNS = "columns";
+ private static final String KEY_UP_ARROW = "up_arrow";
+
private static final int MIN_NUM_COLUMNS = 2;
private static final int[] COLUMN_CHECKBOX_IDS = {
R.id.subscription_num_columns_2,
@@ -85,6 +88,7 @@ public class SubscriptionFragment extends Fragment implements Toolbar.OnMenuItem
private int mPosition = -1;
private boolean isUpdatingFeeds = false;
+ private boolean displayUpArrow;
private Disposable disposable;
private SharedPreferences prefs;
@@ -103,7 +107,11 @@ public class SubscriptionFragment extends Fragment implements Toolbar.OnMenuItem
View root = inflater.inflate(R.layout.fragment_subscriptions, container, false);
toolbar = root.findViewById(R.id.toolbar);
toolbar.setOnMenuItemClickListener(this);
- ((MainActivity) getActivity()).setupToolbarToggle(toolbar);
+ displayUpArrow = getParentFragmentManager().getBackStackEntryCount() != 0;
+ if (savedInstanceState != null) {
+ displayUpArrow = savedInstanceState.getBoolean(KEY_UP_ARROW);
+ }
+ ((MainActivity) getActivity()).setupToolbarToggle(toolbar, displayUpArrow);
toolbar.inflateMenu(R.menu.subscriptions);
for (int i = 0; i < COLUMN_CHECKBOX_IDS.length; i++) {
// Do this in Java to localize numbers
@@ -130,6 +138,12 @@ public class SubscriptionFragment extends Fragment implements Toolbar.OnMenuItem
return root;
}
+ @Override
+ public void onSaveInstanceState(@NonNull Bundle outState) {
+ outState.putBoolean(KEY_UP_ARROW, displayUpArrow);
+ super.onSaveInstanceState(outState);
+ }
+
private void refreshToolbarState() {
int columns = prefs.getInt(PREF_NUM_COLUMNS, getDefaultNumOfColumns());
toolbar.getMenu().findItem(COLUMN_CHECKBOX_IDS[columns - MIN_NUM_COLUMNS]).setChecked(true);
@@ -218,16 +232,19 @@ public class SubscriptionFragment extends Fragment implements Toolbar.OnMenuItem
disposable.dispose();
}
emptyView.hide();
- progressBar.setVisibility(View.VISIBLE);
disposable = Observable.fromCallable(DBReader::getNavDrawerData)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
- .subscribe(result -> {
- navDrawerData = result;
- subscriptionAdapter.notifyDataSetChanged();
- emptyView.updateVisibility();
- progressBar.setVisibility(View.GONE);
- }, error -> Log.e(TAG, Log.getStackTraceString(error)));
+ .subscribe(
+ result -> {
+ navDrawerData = result;
+ subscriptionAdapter.notifyDataSetChanged();
+ emptyView.updateVisibility();
+ progressBar.setVisibility(View.GONE); // Keep hidden to avoid flickering while refreshing
+ }, error -> {
+ Log.e(TAG, Log.getStackTraceString(error));
+ progressBar.setVisibility(View.GONE);
+ });
if (UserPreferences.getSubscriptionsFilter().isEnabled()) {
feedsFilteredMsg.setText("{md-info-outline} " + getString(R.string.subscriptions_are_filtered));
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/PodcastListFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/PodcastListFragment.java
index 1f5434688..7ee0936d0 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/PodcastListFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/PodcastListFragment.java
@@ -1,10 +1,7 @@
package de.danoeh.antennapod.fragment.gpodnet;
-import android.content.Context;
import android.content.Intent;
-import android.os.AsyncTask;
import android.os.Bundle;
-import androidx.fragment.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
@@ -13,9 +10,7 @@ import android.widget.Button;
import android.widget.GridView;
import android.widget.ProgressBar;
import android.widget.TextView;
-
-import java.util.List;
-
+import androidx.fragment.app.Fragment;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.activity.OnlineFeedViewActivity;
@@ -25,6 +20,12 @@ import de.danoeh.antennapod.core.service.download.AntennapodHttpClient;
import de.danoeh.antennapod.core.sync.gpoddernet.GpodnetService;
import de.danoeh.antennapod.core.sync.gpoddernet.GpodnetServiceException;
import de.danoeh.antennapod.core.sync.gpoddernet.model.GpodnetPodcast;
+import io.reactivex.Observable;
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.disposables.Disposable;
+import io.reactivex.schedulers.Schedulers;
+
+import java.util.List;
/**
* Displays a list of GPodnetPodcast-Objects in a GridView
@@ -36,6 +37,7 @@ public abstract class PodcastListFragment extends Fragment {
private ProgressBar progressBar;
private TextView txtvError;
private Button butRetry;
+ private Disposable disposable;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
@@ -64,60 +66,44 @@ public abstract class PodcastListFragment extends Fragment {
protected abstract List<GpodnetPodcast> loadPodcastData(GpodnetService service) throws GpodnetServiceException;
final void loadData() {
- AsyncTask<Void, Void, List<GpodnetPodcast>> loaderTask = new AsyncTask<Void, Void, List<GpodnetPodcast>>() {
- volatile Exception exception = null;
-
- @Override
- protected List<GpodnetPodcast> doInBackground(Void... params) {
- try {
+ if (disposable != null) {
+ disposable.dispose();
+ }
+ gridView.setVisibility(View.GONE);
+ progressBar.setVisibility(View.VISIBLE);
+ txtvError.setVisibility(View.GONE);
+ butRetry.setVisibility(View.GONE);
+ disposable = Observable.fromCallable(
+ () -> {
GpodnetService service = new GpodnetService(AntennapodHttpClient.getHttpClient(),
- GpodnetPreferences.getHostname());
+ GpodnetPreferences.getHosturl());
return loadPodcastData(service);
- } catch (GpodnetServiceException e) {
- exception = e;
- e.printStackTrace();
- return null;
- }
- }
-
- @Override
- protected void onPostExecute(List<GpodnetPodcast> gpodnetPodcasts) {
- super.onPostExecute(gpodnetPodcasts);
- final Context context = getActivity();
- if (context != null && gpodnetPodcasts != null && gpodnetPodcasts.size() > 0) {
- PodcastListAdapter listAdapter = new PodcastListAdapter(context, 0, gpodnetPodcasts);
- gridView.setAdapter(listAdapter);
- listAdapter.notifyDataSetChanged();
-
- progressBar.setVisibility(View.GONE);
- gridView.setVisibility(View.VISIBLE);
- txtvError.setVisibility(View.GONE);
- butRetry.setVisibility(View.GONE);
- } else if (context != null && gpodnetPodcasts != null) {
- gridView.setVisibility(View.GONE);
- progressBar.setVisibility(View.GONE);
- txtvError.setText(getString(R.string.search_status_no_results));
- txtvError.setVisibility(View.VISIBLE);
- butRetry.setVisibility(View.GONE);
- } else if (context != null) {
- gridView.setVisibility(View.GONE);
- progressBar.setVisibility(View.GONE);
- txtvError.setText(getString(R.string.error_msg_prefix) + exception.getMessage());
- txtvError.setVisibility(View.VISIBLE);
- butRetry.setVisibility(View.VISIBLE);
- }
- }
-
- @Override
- protected void onPreExecute() {
- super.onPreExecute();
- gridView.setVisibility(View.GONE);
- progressBar.setVisibility(View.VISIBLE);
- txtvError.setVisibility(View.GONE);
- butRetry.setVisibility(View.GONE);
- }
- };
-
- loaderTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+ })
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(
+ podcasts -> {
+ progressBar.setVisibility(View.GONE);
+ butRetry.setVisibility(View.GONE);
+
+ if (podcasts.size() > 0) {
+ PodcastListAdapter listAdapter = new PodcastListAdapter(getContext(), 0, podcasts);
+ gridView.setAdapter(listAdapter);
+ listAdapter.notifyDataSetChanged();
+ gridView.setVisibility(View.VISIBLE);
+ txtvError.setVisibility(View.GONE);
+ } else {
+ gridView.setVisibility(View.GONE);
+ txtvError.setText(getString(R.string.search_status_no_results));
+ txtvError.setVisibility(View.VISIBLE);
+ }
+ }, error -> {
+ gridView.setVisibility(View.GONE);
+ progressBar.setVisibility(View.GONE);
+ txtvError.setText(getString(R.string.error_msg_prefix) + error.getMessage());
+ txtvError.setVisibility(View.VISIBLE);
+ butRetry.setVisibility(View.VISIBLE);
+ Log.e(TAG, Log.getStackTraceString(error));
+ });
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/TagListFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/TagListFragment.java
index 2c41ee070..9d0f99aa9 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/TagListFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/TagListFragment.java
@@ -1,32 +1,34 @@
package de.danoeh.antennapod.fragment.gpodnet;
-import android.content.Context;
-import android.os.AsyncTask;
import android.os.Bundle;
+import android.util.Log;
import android.view.View;
import android.widget.TextView;
+import androidx.annotation.NonNull;
import androidx.fragment.app.ListFragment;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.adapter.gpodnet.TagListAdapter;
import de.danoeh.antennapod.core.preferences.GpodnetPreferences;
import de.danoeh.antennapod.core.service.download.AntennapodHttpClient;
import de.danoeh.antennapod.core.sync.gpoddernet.GpodnetService;
-import de.danoeh.antennapod.core.sync.gpoddernet.GpodnetServiceException;
import de.danoeh.antennapod.core.sync.gpoddernet.model.GpodnetTag;
-
-import java.util.List;
+import io.reactivex.Observable;
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.disposables.Disposable;
+import io.reactivex.schedulers.Schedulers;
public class TagListFragment extends ListFragment {
private static final int COUNT = 50;
+ private static final String TAG = "TagListFragment";
+ private Disposable disposable;
@Override
- public void onViewCreated(View view, Bundle savedInstanceState) {
+ public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
getListView().setOnItemClickListener((parent, view1, position, id) -> {
GpodnetTag tag = (GpodnetTag) getListAdapter().getItem(position);
- MainActivity activity = (MainActivity) getActivity();
- activity.loadChildFragment(TagFragment.newInstance(tag));
+ ((MainActivity) getActivity()).loadChildFragment(TagFragment.newInstance(tag));
});
startLoadTask();
@@ -35,59 +37,36 @@ public class TagListFragment extends ListFragment {
@Override
public void onDestroyView() {
super.onDestroyView();
- cancelLoadTask();
- }
- private AsyncTask<Void, Void, List<GpodnetTag>> loadTask;
-
- private void cancelLoadTask() {
- if (loadTask != null && !loadTask.isCancelled()) {
- loadTask.cancel(true);
+ if (disposable != null) {
+ disposable.dispose();
}
}
private void startLoadTask() {
- cancelLoadTask();
- loadTask = new AsyncTask<Void, Void, List<GpodnetTag>>() {
- private Exception exception;
-
- @Override
- protected List<GpodnetTag> doInBackground(Void... params) {
+ if (disposable != null) {
+ disposable.dispose();
+ }
+ setListShown(false);
+ disposable = Observable.fromCallable(
+ () -> {
GpodnetService service = new GpodnetService(AntennapodHttpClient.getHttpClient(),
- GpodnetPreferences.getHostname());
- try {
- return service.getTopTags(COUNT);
- } catch (GpodnetServiceException e) {
- e.printStackTrace();
- exception = e;
- return null;
- }
- }
-
- @Override
- protected void onPreExecute() {
- super.onPreExecute();
- setListShown(false);
- }
-
- @Override
- protected void onPostExecute(List<GpodnetTag> gpodnetTags) {
- super.onPostExecute(gpodnetTags);
- final Context context = getActivity();
- if (context != null) {
- if (gpodnetTags != null) {
- setListAdapter(new TagListAdapter(context, android.R.layout.simple_list_item_1, gpodnetTags));
- } else if (exception != null) {
+ GpodnetPreferences.getHosturl());
+ return service.getTopTags(COUNT);
+ })
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(
+ tags -> {
+ setListAdapter(new TagListAdapter(getContext(), android.R.layout.simple_list_item_1, tags));
+ setListShown(true);
+ }, error -> {
TextView txtvError = new TextView(getActivity());
- txtvError.setText(exception.getMessage());
+ txtvError.setText(error.getMessage());
getListView().setEmptyView(txtvError);
- }
- setListShown(true);
-
- }
- }
- };
- loadTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+ setListShown(true);
+ Log.e(TAG, Log.getStackTraceString(error));
+ });
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AutoDownloadPreferencesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AutoDownloadPreferencesFragment.java
index 0d6e79e84..ec61c82f2 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AutoDownloadPreferencesFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AutoDownloadPreferencesFragment.java
@@ -174,7 +174,9 @@ public class AutoDownloadPreferencesFragment extends PreferenceFragmentCompat {
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) {
+ if (v == UserPreferences.EPISODE_CLEANUP_EXCEPT_FAVORITE) {
+ entries[x] = res.getString(R.string.episode_cleanup_except_favorite_removal);
+ } else 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);
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/GpodderAuthenticationFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/GpodderAuthenticationFragment.java
new file mode 100644
index 000000000..6eb19aff2
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/GpodderAuthenticationFragment.java
@@ -0,0 +1,307 @@
+package de.danoeh.antennapod.fragment.preferences;
+
+import android.app.Dialog;
+import android.content.Context;
+import android.graphics.Paint;
+import android.os.Build;
+import android.os.Bundle;
+import android.view.View;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.LinearLayout;
+import android.widget.ProgressBar;
+import android.widget.RadioGroup;
+import android.widget.TextView;
+import android.widget.ViewFlipper;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AlertDialog;
+import androidx.fragment.app.DialogFragment;
+import com.google.android.material.button.MaterialButton;
+import com.google.android.material.textfield.TextInputLayout;
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.core.preferences.GpodnetPreferences;
+import de.danoeh.antennapod.core.service.download.AntennapodHttpClient;
+import de.danoeh.antennapod.core.sync.SyncService;
+import de.danoeh.antennapod.core.sync.gpoddernet.GpodnetService;
+import de.danoeh.antennapod.core.sync.gpoddernet.model.GpodnetDevice;
+import de.danoeh.antennapod.core.util.FileNameGenerator;
+import de.danoeh.antennapod.core.util.IntentUtils;
+import io.reactivex.Completable;
+import io.reactivex.Observable;
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.schedulers.Schedulers;
+
+import java.util.List;
+import java.util.Locale;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Guides the user through the authentication process.
+ */
+public class GpodderAuthenticationFragment extends DialogFragment {
+ public static final String TAG = "GpodnetAuthActivity";
+
+ private ViewFlipper viewFlipper;
+
+ private static final int STEP_DEFAULT = -1;
+ private static final int STEP_HOSTNAME = 0;
+ private static final int STEP_LOGIN = 1;
+ private static final int STEP_DEVICE = 2;
+ private static final int STEP_FINISH = 3;
+
+ private int currentStep = -1;
+
+ private GpodnetService service;
+ private volatile String username;
+ private volatile String password;
+ private volatile GpodnetDevice selectedDevice;
+ private List<GpodnetDevice> devices;
+
+ @NonNull
+ @Override
+ public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
+ AlertDialog.Builder dialog = new AlertDialog.Builder(getContext());
+ dialog.setTitle(GpodnetService.DEFAULT_BASE_HOST);
+ dialog.setNegativeButton(R.string.cancel_label, null);
+ dialog.setCancelable(false);
+ this.setCancelable(false);
+
+ View root = View.inflate(getContext(), R.layout.gpodnetauth_dialog, null);
+ viewFlipper = root.findViewById(R.id.viewflipper);
+ advance();
+ dialog.setView(root);
+
+ return dialog.create();
+ }
+
+ private void setupHostView(View view) {
+ final Button selectHost = view.findViewById(R.id.chooseHostButton);
+ final RadioGroup serverRadioGroup = view.findViewById(R.id.serverRadioGroup);
+ final EditText serverUrlText = view.findViewById(R.id.serverUrlText);
+
+ if (!GpodnetService.DEFAULT_BASE_HOST.equals(GpodnetPreferences.getHosturl())) {
+ serverUrlText.setText(GpodnetPreferences.getHosturl());
+ }
+ final TextInputLayout serverUrlTextInput = view.findViewById(R.id.serverUrlTextInput);
+ serverRadioGroup.setOnCheckedChangeListener((group, checkedId) -> {
+ serverUrlTextInput.setVisibility(checkedId == R.id.customServerRadio ? View.VISIBLE : View.GONE);
+ });
+ selectHost.setOnClickListener(v -> {
+ if (serverRadioGroup.getCheckedRadioButtonId() == R.id.customServerRadio) {
+ GpodnetPreferences.setHosturl(serverUrlText.getText().toString());
+ } else {
+ GpodnetPreferences.setHosturl(GpodnetService.DEFAULT_BASE_HOST);
+ }
+ service = new GpodnetService(AntennapodHttpClient.getHttpClient(), GpodnetPreferences.getHosturl());
+ getDialog().setTitle(GpodnetPreferences.getHosturl());
+ advance();
+ });
+ }
+
+ private void setupLoginView(View view) {
+ final EditText username = view.findViewById(R.id.etxtUsername);
+ final EditText password = view.findViewById(R.id.etxtPassword);
+ final Button login = view.findViewById(R.id.butLogin);
+ final TextView txtvError = view.findViewById(R.id.credentialsError);
+ final ProgressBar progressBar = view.findViewById(R.id.progBarLogin);
+ final TextView createAccount = view.findViewById(R.id.createAccountButton);
+ final TextView createAccountWarning = view.findViewById(R.id.createAccountWarning);
+
+ createAccount.setPaintFlags(createAccount.getPaintFlags() | Paint.UNDERLINE_TEXT_FLAG);
+ createAccount.setOnClickListener(v -> IntentUtils.openInBrowser(getContext(), "https://gpodder.net/register/"));
+
+ if (GpodnetPreferences.getHosturl().startsWith("http://")) {
+ createAccountWarning.setVisibility(View.VISIBLE);
+ }
+ password.setOnEditorActionListener((v, actionID, event) ->
+ actionID == EditorInfo.IME_ACTION_GO && login.performClick());
+
+ login.setOnClickListener(v -> {
+ 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;
+ }
+
+ login.setEnabled(false);
+ progressBar.setVisibility(View.VISIBLE);
+ txtvError.setVisibility(View.GONE);
+ InputMethodManager inputManager = (InputMethodManager) getContext()
+ .getSystemService(Context.INPUT_METHOD_SERVICE);
+ inputManager.hideSoftInputFromWindow(login.getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);
+
+ Completable.fromAction(() -> {
+ service.authenticate(usernameStr, passwordStr);
+ devices = service.getDevices();
+ GpodderAuthenticationFragment.this.username = usernameStr;
+ GpodderAuthenticationFragment.this.password = passwordStr;
+ })
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(() -> {
+ login.setEnabled(true);
+ progressBar.setVisibility(View.GONE);
+ advance();
+ }, error -> {
+ login.setEnabled(true);
+ progressBar.setVisibility(View.GONE);
+ txtvError.setText(error.getCause().getMessage());
+ txtvError.setVisibility(View.VISIBLE);
+ });
+
+ });
+ }
+
+ private void setupDeviceView(View view) {
+ final EditText deviceName = view.findViewById(R.id.deviceName);
+ final LinearLayout devicesContainer = view.findViewById(R.id.devicesContainer);
+ deviceName.setText(generateDeviceName());
+
+ MaterialButton createDeviceButton = view.findViewById(R.id.createDeviceButton);
+ createDeviceButton.setOnClickListener(v -> createDevice(view));
+
+ for (GpodnetDevice device : devices) {
+ View row = View.inflate(getContext(), R.layout.gpodnetauth_device_row, null);
+ Button selectDeviceButton = row.findViewById(R.id.selectDeviceButton);
+ selectDeviceButton.setOnClickListener(v -> {
+ selectedDevice = device;
+ advance();
+ });
+ selectDeviceButton.setText(device.getCaption());
+ devicesContainer.addView(row);
+ }
+ }
+
+ private void createDevice(View view) {
+ final EditText deviceName = view.findViewById(R.id.deviceName);
+ final TextView txtvError = view.findViewById(R.id.deviceSelectError);
+ final ProgressBar progBarCreateDevice = view.findViewById(R.id.progbarCreateDevice);
+
+ String deviceNameStr = deviceName.getText().toString();
+ if (isDeviceInList(deviceNameStr)) {
+ return;
+ }
+ progBarCreateDevice.setVisibility(View.VISIBLE);
+ txtvError.setVisibility(View.GONE);
+ deviceName.setEnabled(false);
+
+ Observable.fromCallable(() -> {
+ String deviceId = generateDeviceId(deviceNameStr);
+ service.configureDevice(deviceId, deviceNameStr, GpodnetDevice.DeviceType.MOBILE);
+ return new GpodnetDevice(deviceId, deviceNameStr, GpodnetDevice.DeviceType.MOBILE.toString(), 0);
+ })
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(device -> {
+ progBarCreateDevice.setVisibility(View.GONE);
+ selectedDevice = device;
+ advance();
+ }, error -> {
+ deviceName.setEnabled(true);
+ progBarCreateDevice.setVisibility(View.GONE);
+ txtvError.setText(error.getMessage());
+ txtvError.setVisibility(View.VISIBLE);
+ });
+ }
+
+ private String generateDeviceName() {
+ String baseName = getString(R.string.gpodnetauth_device_name_default, Build.MODEL);
+ String name = baseName;
+ int num = 1;
+ while (isDeviceInList(name)) {
+ name = baseName + " (" + num + ")";
+ num++;
+ }
+ return name;
+ }
+
+ private String generateDeviceId(String name) {
+ // devices names must be of a certain form:
+ // https://gpoddernet.readthedocs.org/en/latest/api/reference/general.html#devices
+ return FileNameGenerator.generateFileName(name).replaceAll("\\W", "_").toLowerCase(Locale.US);
+ }
+
+ private boolean isDeviceInList(String name) {
+ if (devices == null) {
+ return false;
+ }
+ String id = generateDeviceId(name);
+ for (GpodnetDevice device : devices) {
+ if (device.getId().equals(id) || device.getCaption().equals(name)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private GpodnetDevice findDevice(String id) {
+ if (devices == null) {
+ return null;
+ }
+ for (GpodnetDevice device : devices) {
+ if (device.getId().equals(id)) {
+ return device;
+ }
+ }
+ return null;
+ }
+
+ private void setupFinishView(View view) {
+ final Button sync = view.findViewById(R.id.butSyncNow);
+
+ sync.setOnClickListener(v -> {
+ dismiss();
+ SyncService.sync(getContext());
+ });
+ }
+
+ private void writeLoginCredentials() {
+ GpodnetPreferences.setUsername(username);
+ GpodnetPreferences.setPassword(password);
+ GpodnetPreferences.setDeviceID(selectedDevice.getId());
+ }
+
+ private void advance() {
+ if (currentStep < STEP_FINISH) {
+
+ View view = viewFlipper.getChildAt(currentStep + 1);
+ if (currentStep == STEP_DEFAULT) {
+ setupHostView(view);
+ } else if (currentStep == STEP_HOSTNAME) {
+ setupLoginView(view);
+ } else if (currentStep == STEP_LOGIN) {
+ if (username == null || password == null) {
+ throw new IllegalStateException("Username and password must not be null here");
+ } else {
+ setupDeviceView(view);
+ }
+ } else if (currentStep == STEP_DEVICE) {
+ if (selectedDevice == null) {
+ throw new IllegalStateException("Device must not be null here");
+ } else {
+ writeLoginCredentials();
+ setupFinishView(view);
+ }
+ }
+ if (currentStep != STEP_DEFAULT) {
+ viewFlipper.showNext();
+ }
+ currentStep++;
+ } else {
+ dismiss();
+ }
+ }
+
+ 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/fragment/preferences/GpodderPreferencesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/GpodderPreferencesFragment.java
index eb23a5eb1..4fb734e17 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/GpodderPreferencesFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/GpodderPreferencesFragment.java
@@ -14,19 +14,16 @@ import de.danoeh.antennapod.core.event.SyncServiceEvent;
import de.danoeh.antennapod.core.preferences.GpodnetPreferences;
import de.danoeh.antennapod.core.sync.SyncService;
import de.danoeh.antennapod.dialog.AuthenticationDialog;
-import de.danoeh.antennapod.dialog.GpodnetSetHostnameDialog;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
-
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";
@Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
@@ -51,6 +48,7 @@ public class GpodderPreferencesFragment extends PreferenceFragmentCompat {
@Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
public void syncStatusChanged(SyncServiceEvent event) {
+ updateGpodnetPreferenceScreen();
if (!GpodnetPreferences.loggedIn()) {
return;
}
@@ -66,6 +64,10 @@ public class GpodderPreferencesFragment extends PreferenceFragmentCompat {
private void setupGpodderScreen() {
final Activity activity = getActivity();
+ findPreference(PREF_GPODNET_LOGIN).setOnPreferenceClickListener(preference -> {
+ new GpodderAuthenticationFragment().show(getChildFragmentManager(), GpodderAuthenticationFragment.TAG);
+ return true;
+ });
findPreference(PREF_GPODNET_SETLOGIN_INFORMATION)
.setOnPreferenceClickListener(preference -> {
AuthenticationDialog dialog = new AuthenticationDialog(activity,
@@ -94,11 +96,6 @@ public class GpodderPreferencesFragment extends PreferenceFragmentCompat {
updateGpodnetPreferenceScreen();
return true;
});
- findPreference(PREF_GPODNET_HOSTNAME).setOnPreferenceClickListener(preference -> {
- GpodnetSetHostnameDialog.createDialog(activity).setOnDismissListener(
- dialog -> updateGpodnetPreferenceScreen());
- return true;
- });
}
private void updateGpodnetPreferenceScreen() {
@@ -119,7 +116,6 @@ public class GpodderPreferencesFragment extends PreferenceFragmentCompat {
} else {
findPreference(PREF_GPODNET_LOGOUT).setSummary(null);
}
- findPreference(PREF_GPODNET_HOSTNAME).setSummary(GpodnetPreferences.getHostname());
}
private void updateLastGpodnetSyncReport(boolean successful, long lastTime) {
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
index 77f8063f2..3889034fa 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/NetworkPreferencesFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/NetworkPreferencesFragment.java
@@ -71,13 +71,13 @@ public class NetworkPreferencesFragment extends PreferenceFragmentCompat {
Context context = getActivity().getApplicationContext();
String val;
long interval = UserPreferences.getUpdateInterval();
- if(interval > 0) {
+ 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);
+ val = context.getResources().getQuantityString(
+ R.plurals.pref_autoUpdateIntervallOrTime_every_hours, hours, hours);
} else {
int[] timeOfDay = UserPreferences.getUpdateTimeOfDay();
- if(timeOfDay.length == 2) {
+ if (timeOfDay.length == 2) {
Calendar cal = new GregorianCalendar();
cal.set(Calendar.HOUR_OF_DAY, timeOfDay[0]);
cal.set(Calendar.MINUTE, timeOfDay[1]);
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
index 689a72ba7..4d1b79965 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/UserInterfacePreferencesFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/UserInterfacePreferencesFragment.java
@@ -9,11 +9,14 @@ import androidx.preference.PreferenceFragmentCompat;
import android.widget.ListView;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.PreferenceActivity;
+import de.danoeh.antennapod.core.event.PlayerStatusEvent;
+import de.danoeh.antennapod.core.event.UnreadItemsUpdateEvent;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.dialog.SubscriptionsFilterDialog;
import de.danoeh.antennapod.dialog.FeedSortDialog;
import de.danoeh.antennapod.fragment.NavDrawerFragment;
import org.apache.commons.lang3.ArrayUtils;
+import org.greenrobot.eventbus.EventBus;
import java.util.List;
@@ -37,8 +40,17 @@ public class UserInterfacePreferencesFragment extends PreferenceFragmentCompat {
(preference, newValue) -> {
getActivity().recreate();
return true;
- }
- );
+ });
+
+ findPreference(UserPreferences.PREF_SHOW_TIME_LEFT)
+ .setOnPreferenceChangeListener(
+ (preference, newValue) -> {
+ UserPreferences.setShowRemainTimeSetting((Boolean) newValue);
+ EventBus.getDefault().post(new UnreadItemsUpdateEvent());
+ EventBus.getDefault().post(new PlayerStatusEvent());
+ return true;
+ });
+
findPreference(UserPreferences.PREF_HIDDEN_DRAWER_ITEMS)
.setOnPreferenceClickListener(preference -> {
showDrawerPreferencesDialog();
diff --git a/app/src/main/java/de/danoeh/antennapod/menuhandler/MenuItemUtils.java b/app/src/main/java/de/danoeh/antennapod/menuhandler/MenuItemUtils.java
index 9c54a529b..fbfdf537f 100644
--- a/app/src/main/java/de/danoeh/antennapod/menuhandler/MenuItemUtils.java
+++ b/app/src/main/java/de/danoeh/antennapod/menuhandler/MenuItemUtils.java
@@ -9,7 +9,7 @@ import androidx.appcompat.widget.SearchView;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.core.preferences.UserPreferences;
-import de.danoeh.antennapod.core.util.ThemeUtils;
+import de.danoeh.antennapod.ui.common.ThemeUtils;
import de.danoeh.antennapod.fragment.SearchFragment;
import java.util.HashMap;
diff --git a/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceUpgrader.java b/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceUpgrader.java
index 311f44881..03a8edbf0 100644
--- a/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceUpgrader.java
+++ b/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceUpgrader.java
@@ -2,6 +2,7 @@ package de.danoeh.antennapod.preferences;
import android.content.Context;
import android.content.SharedPreferences;
+import android.view.KeyEvent;
import androidx.preference.PreferenceManager;
import de.danoeh.antennapod.BuildConfig;
@@ -92,5 +93,16 @@ public class PreferenceUpgrader {
if (oldVersion < 1080100) {
prefs.edit().putString(UserPreferences.PREF_VIDEO_BEHAVIOR, "pip").apply();
}
+ if (oldVersion < 2010300) {
+ // Migrate hardware button preferences
+ if (prefs.getBoolean("prefHardwareForwardButtonSkips", false)) {
+ prefs.edit().putString(UserPreferences.PREF_HARDWARE_FORWARD_BUTTON,
+ String.valueOf(KeyEvent.KEYCODE_MEDIA_NEXT)).apply();
+ }
+ if (prefs.getBoolean("prefHardwarePreviousButtonRestarts", false)) {
+ prefs.edit().putString(UserPreferences.PREF_HARDWARE_PREVIOUS_BUTTON,
+ String.valueOf(KeyEvent.KEYCODE_MEDIA_PREVIOUS)).apply();
+ }
+ }
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/view/ChapterSeekBar.java b/app/src/main/java/de/danoeh/antennapod/view/ChapterSeekBar.java
new file mode 100644
index 000000000..5e80198d5
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/view/ChapterSeekBar.java
@@ -0,0 +1,129 @@
+package de.danoeh.antennapod.view;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.util.AttributeSet;
+import de.danoeh.antennapod.ui.common.ThemeUtils;
+
+public class ChapterSeekBar extends androidx.appcompat.widget.AppCompatSeekBar {
+
+ private float top;
+ private float width;
+ private float bottom;
+ private float density;
+ private float progressPrimary;
+ private float progressSecondary;
+ private float[] dividerPos;
+ private final Paint paintBackground = new Paint();
+ private final Paint paintProgressPrimary = new Paint();
+ private final Paint paintProgressSecondary = new Paint();
+
+ public ChapterSeekBar(Context context) {
+ super(context);
+ init(context);
+ }
+
+ public ChapterSeekBar(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init(context);
+ }
+
+ public ChapterSeekBar(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ init(context);
+ }
+
+ private void init(Context context) {
+ setBackground(null); // Removes the thumb shadow
+ dividerPos = null;
+ density = context.getResources().getDisplayMetrics().density;
+ paintBackground.setColor(ThemeUtils.getColorFromAttr(getContext(),
+ de.danoeh.antennapod.core.R.attr.currently_playing_background));
+ paintBackground.setAlpha(128);
+ paintProgressPrimary.setColor(ThemeUtils.getColorFromAttr(getContext(),
+ de.danoeh.antennapod.core.R.attr.colorPrimary));
+ paintProgressSecondary.setColor(ThemeUtils.getColorFromAttr(getContext(),
+ de.danoeh.antennapod.core.R.attr.seek_background));
+ }
+
+ /**
+ * Sets the relative positions of the chapter dividers.
+ * @param dividerPos of the chapter dividers relative to the duration of the media.
+ */
+ public void setDividerPos(final float[] dividerPos) {
+ if (dividerPos != null) {
+ this.dividerPos = new float[dividerPos.length + 2];
+ this.dividerPos[0] = 0;
+ System.arraycopy(dividerPos, 0, this.dividerPos, 1, dividerPos.length);
+ this.dividerPos[this.dividerPos.length - 1] = 1;
+ } else {
+ this.dividerPos = null;
+ }
+ }
+
+ @Override
+ protected synchronized void onDraw(Canvas canvas) {
+ top = getTop() + density * 7.5f;
+ bottom = getBottom() - density * 7.5f;
+ width = (float) (getRight() - getPaddingRight() - getLeft() - getPaddingLeft());
+ progressSecondary = getSecondaryProgress() / (float) getMax() * width;
+ progressPrimary = getProgress() / (float) getMax() * width;
+
+ if (dividerPos == null) {
+ drawProgress(canvas);
+ } else {
+ drawProgressChapters(canvas);
+ }
+ drawThumb(canvas);
+ }
+
+ private void drawProgress(Canvas canvas) {
+ final int saveCount = canvas.save();
+ canvas.translate(getPaddingLeft(), getPaddingTop());
+ canvas.drawRect(0, top, width, bottom, paintBackground);
+ canvas.drawRect(0, top, progressSecondary, bottom, paintProgressSecondary);
+ canvas.drawRect(0, top, progressPrimary, bottom, paintProgressPrimary);
+ canvas.restoreToCount(saveCount);
+ }
+
+ private void drawProgressChapters(Canvas canvas) {
+ final int saveCount = canvas.save();
+ int currChapter = 1;
+ float chapterMargin = density * 0.6f;
+ float topExpanded = getTop() + density * 7;
+ float bottomExpanded = getBottom() - density * 7;
+
+ canvas.translate(getPaddingLeft(), getPaddingTop());
+
+ for (int i = 1; i < dividerPos.length; i++) {
+ float right = dividerPos[i] * width - chapterMargin;
+ float left = dividerPos[i - 1] * width + chapterMargin;
+ float rightCurr = dividerPos[currChapter] * width - chapterMargin;
+ float leftCurr = dividerPos[currChapter - 1] * width + chapterMargin;
+
+ canvas.drawRect(left, top, right, bottom, paintBackground);
+
+ if (right < progressPrimary) {
+ currChapter = i + 1;
+ canvas.drawRect(left, top, right, bottom, paintProgressPrimary);
+ } else if (isPressed()) {
+ canvas.drawRect(leftCurr, topExpanded, rightCurr, bottomExpanded, paintBackground);
+ canvas.drawRect(leftCurr, topExpanded, progressPrimary, bottomExpanded, paintProgressPrimary);
+ } else {
+ if (progressSecondary > leftCurr) {
+ canvas.drawRect(leftCurr, top, progressSecondary, bottom, paintProgressSecondary);
+ }
+ canvas.drawRect(leftCurr, top, progressPrimary, bottom, paintProgressPrimary);
+ }
+ }
+ canvas.restoreToCount(saveCount);
+ }
+
+ private void drawThumb(Canvas canvas) {
+ final int saveCount = canvas.save();
+ canvas.translate(getPaddingLeft() - getThumbOffset(), getPaddingTop());
+ getThumb().draw(canvas);
+ canvas.restoreToCount(saveCount);
+ }
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/view/CircularProgressBar.java b/app/src/main/java/de/danoeh/antennapod/view/CircularProgressBar.java
deleted file mode 100644
index 2fd570ece..000000000
--- a/app/src/main/java/de/danoeh/antennapod/view/CircularProgressBar.java
+++ /dev/null
@@ -1,93 +0,0 @@
-package de.danoeh.antennapod.view;
-
-import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.graphics.RectF;
-import android.util.AttributeSet;
-import android.view.View;
-import androidx.annotation.Nullable;
-import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.core.util.ThemeUtils;
-
-public class CircularProgressBar extends View {
- public static final float MINIMUM_PERCENTAGE = 0.005f;
- public static final float MAXIMUM_PERCENTAGE = 1 - MINIMUM_PERCENTAGE;
-
- private final Paint paintBackground = new Paint();
- private final Paint paintProgress = new Paint();
- private float percentage = 0;
- private float targetPercentage = 0;
- private Object tag = null;
- private final RectF bounds = new RectF();
-
- public CircularProgressBar(Context context) {
- super(context);
- setup();
- }
-
- public CircularProgressBar(Context context, @Nullable AttributeSet attrs) {
- super(context, attrs);
- setup();
- }
-
- public CircularProgressBar(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- setup();
- }
-
- private void setup() {
- paintBackground.setAntiAlias(true);
- paintBackground.setStyle(Paint.Style.STROKE);
-
- paintProgress.setAntiAlias(true);
- paintProgress.setStyle(Paint.Style.STROKE);
- paintProgress.setStrokeCap(Paint.Cap.ROUND);
-
- int color = ThemeUtils.getColorFromAttr(getContext(), R.attr.action_icon_color);
- paintProgress.setColor(color);
- paintBackground.setColor(color);
- }
-
- /**
- * Sets the percentage to be displayed.
- * @param percentage Number from 0 to 1
- * @param tag When the tag is the same as last time calling setPercentage, the update is animated
- */
- public void setPercentage(float percentage, Object tag) {
- targetPercentage = percentage;
-
- if (tag == null || !tag.equals(this.tag)) {
- // Do not animate
- this.percentage = percentage;
- this.tag = tag;
- }
- invalidate();
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
-
- float padding = getHeight() * 0.07f;
- paintBackground.setStrokeWidth(getHeight() * 0.02f);
- paintProgress.setStrokeWidth(padding);
- bounds.set(padding, padding, getWidth() - padding, getHeight() - padding);
- canvas.drawArc(bounds, 0, 360, false, paintBackground);
-
- if (MINIMUM_PERCENTAGE <= percentage && percentage <= MAXIMUM_PERCENTAGE) {
- canvas.drawArc(bounds, -90, percentage * 360, false, paintProgress);
- }
-
- if (Math.abs(percentage - targetPercentage) > MINIMUM_PERCENTAGE) {
- float speed = 0.02f;
- if (Math.abs(targetPercentage - percentage) < 0.1 && targetPercentage > percentage) {
- speed = 0.006f;
- }
- float delta = Math.min(speed, Math.abs(targetPercentage - percentage));
- float direction = ((targetPercentage - percentage) > 0 ? 1f : -1f);
- percentage += delta * direction;
- invalidate();
- }
- }
-}
diff --git a/app/src/main/java/de/danoeh/antennapod/view/EpisodeItemListRecyclerView.java b/app/src/main/java/de/danoeh/antennapod/view/EpisodeItemListRecyclerView.java
index 83d90f98b..fb1c533c5 100644
--- a/app/src/main/java/de/danoeh/antennapod/view/EpisodeItemListRecyclerView.java
+++ b/app/src/main/java/de/danoeh/antennapod/view/EpisodeItemListRecyclerView.java
@@ -6,9 +6,9 @@ import android.content.res.Configuration;
import android.util.AttributeSet;
import android.view.View;
import androidx.appcompat.view.ContextThemeWrapper;
+import androidx.recyclerview.widget.DividerItemDecoration;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
-import com.yqritc.recyclerviewflexibledivider.HorizontalDividerItemDecoration;
import de.danoeh.antennapod.R;
import io.reactivex.annotations.Nullable;
@@ -39,7 +39,7 @@ public class EpisodeItemListRecyclerView extends RecyclerView {
layoutManager.setRecycleChildrenOnDetach(true);
setLayoutManager(layoutManager);
setHasFixedSize(true);
- addItemDecoration(new HorizontalDividerItemDecoration.Builder(getContext()).build());
+ addItemDecoration(new DividerItemDecoration(getContext(), layoutManager.getOrientation()));
setClipToPadding(false);
}
diff --git a/app/src/main/java/de/danoeh/antennapod/view/PlaybackSpeedIndicatorView.java b/app/src/main/java/de/danoeh/antennapod/view/PlaybackSpeedIndicatorView.java
deleted file mode 100644
index d7f1eac1d..000000000
--- a/app/src/main/java/de/danoeh/antennapod/view/PlaybackSpeedIndicatorView.java
+++ /dev/null
@@ -1,113 +0,0 @@
-package de.danoeh.antennapod.view;
-
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.graphics.Path;
-import android.graphics.RectF;
-import android.util.AttributeSet;
-import android.view.View;
-import androidx.annotation.Nullable;
-import de.danoeh.antennapod.R;
-
-public class PlaybackSpeedIndicatorView extends View {
- private static final float DEG_2_RAD = (float) (Math.PI / 180);
- private static final float PADDING_ANGLE = 30;
- private static final float VALUE_UNSET = -4242;
-
- private final Paint arcPaint = new Paint();
- private final Paint indicatorPaint = new Paint();
- private final Path trianglePath = new Path();
- private float angle = VALUE_UNSET;
- private float targetAngle = VALUE_UNSET;
- private float degreePerFrame = 2;
- private float paddingArc = 20;
- private float paddingIndicator = 10;
- private RectF arcBounds = new RectF();
-
- public PlaybackSpeedIndicatorView(Context context) {
- super(context);
- setup();
- }
-
- public PlaybackSpeedIndicatorView(Context context, @Nullable AttributeSet attrs) {
- super(context, attrs);
- setup();
- }
-
- public PlaybackSpeedIndicatorView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- setup();
- }
-
- private void setup() {
- setSpeed(1.0f); // Set default angle to 1.0
- targetAngle = VALUE_UNSET; // Do not move to first value that is set externally
-
- int[] colorAttrs = new int[] {R.attr.action_icon_color };
- TypedArray a = getContext().obtainStyledAttributes(colorAttrs);
- arcPaint.setColor(a.getColor(0, 0xffffffff));
- indicatorPaint.setColor(a.getColor(0, 0xffffffff));
- a.recycle();
-
- arcPaint.setAntiAlias(true);
- arcPaint.setStyle(Paint.Style.STROKE);
- arcPaint.setStrokeCap(Paint.Cap.ROUND);
-
- indicatorPaint.setAntiAlias(true);
- indicatorPaint.setStyle(Paint.Style.FILL);
-
- trianglePath.setFillType(Path.FillType.EVEN_ODD);
- }
-
- public void setSpeed(float value) {
- float maxAnglePerDirection = 90 + 45 - 2 * paddingArc;
- // Speed values above 3 are probably not too common. Cap at 3 for better differentiation
- float normalizedValue = Math.min(2.5f, value - 0.5f) / 2.5f; // Linear between 0 and 1
- float target = -maxAnglePerDirection + 2 * maxAnglePerDirection * normalizedValue;
- if (targetAngle == VALUE_UNSET) {
- angle = target;
- }
- targetAngle = target;
- degreePerFrame = Math.abs(targetAngle - angle) / 20;
- invalidate();
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-
- paddingArc = getMeasuredHeight() / 4.5f;
- paddingIndicator = getMeasuredHeight() / 6f;
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
-
- float radiusInnerCircle = getWidth() / 10f;
- canvas.drawCircle(getWidth() / 2f, getHeight() / 2f, radiusInnerCircle, indicatorPaint);
-
- trianglePath.rewind();
- float bigRadius = getHeight() / 2f - paddingIndicator;
- trianglePath.moveTo(getWidth() / 2f + (float) (bigRadius * Math.sin((-angle + 180) * DEG_2_RAD)),
- getHeight() / 2f + (float) (bigRadius * Math.cos((-angle + 180) * DEG_2_RAD)));
- trianglePath.lineTo(getWidth() / 2f + (float) (radiusInnerCircle * Math.sin((-angle + 180 - 90) * DEG_2_RAD)),
- getHeight() / 2f + (float) (radiusInnerCircle * Math.cos((-angle + 180 - 90) * DEG_2_RAD)));
- trianglePath.lineTo(getWidth() / 2f + (float) (radiusInnerCircle * Math.sin((-angle + 180 + 90) * DEG_2_RAD)),
- getHeight() / 2f + (float) (radiusInnerCircle * Math.cos((-angle + 180 + 90) * DEG_2_RAD)));
- trianglePath.close();
- canvas.drawPath(trianglePath, indicatorPaint);
-
- arcPaint.setStrokeWidth(getHeight() / 15f);
- arcBounds.set(paddingArc, paddingArc, getWidth() - paddingArc, getHeight() - paddingArc);
- canvas.drawArc(arcBounds, -180 - 45, 90 + 45 + angle - PADDING_ANGLE, false, arcPaint);
- canvas.drawArc(arcBounds, -90 + PADDING_ANGLE + angle, 90 + 45 - PADDING_ANGLE - angle, false, arcPaint);
-
- if (Math.abs(angle - targetAngle) > 0.5 && targetAngle != VALUE_UNSET) {
- angle += Math.signum(targetAngle - angle) * Math.min(degreePerFrame, Math.abs(targetAngle - angle));
- invalidate();
- }
- }
-}
diff --git a/app/src/main/java/de/danoeh/antennapod/view/RecursiveRadioGroup.java b/app/src/main/java/de/danoeh/antennapod/view/RecursiveRadioGroup.java
deleted file mode 100644
index ee5e7c51d..000000000
--- a/app/src/main/java/de/danoeh/antennapod/view/RecursiveRadioGroup.java
+++ /dev/null
@@ -1,67 +0,0 @@
-package de.danoeh.antennapod.view;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.LinearLayout;
-import android.widget.RadioButton;
-import java.util.ArrayList;
-
-/**
- * An alternative to {@link android.widget.RadioGroup} that allows to nest children.
- * Basend on https://stackoverflow.com/a/14309274.
- */
-public class RecursiveRadioGroup extends LinearLayout {
- private final ArrayList<RadioButton> radioButtons = new ArrayList<>();
- private RadioButton checkedButton = null;
-
- public RecursiveRadioGroup(Context context) {
- super(context);
- }
-
- public RecursiveRadioGroup(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public RecursiveRadioGroup(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- }
-
- @Override
- public void addView(View child, int index, ViewGroup.LayoutParams params) {
- super.addView(child, index, params);
- parseChild(child);
- }
-
- public void parseChild(final View child) {
- if (child instanceof RadioButton) {
- RadioButton button = (RadioButton) child;
- radioButtons.add(button);
- button.setOnCheckedChangeListener((buttonView, isChecked) -> {
- if (!isChecked) {
- return;
- }
- checkedButton = (RadioButton) buttonView;
-
- for (RadioButton view : radioButtons) {
- if (view != buttonView) {
- view.setChecked(false);
- }
- }
- });
- } else if (child instanceof ViewGroup) {
- parseChildren((ViewGroup) child);
- }
- }
-
- public void parseChildren(final ViewGroup child) {
- for (int i = 0; i < child.getChildCount(); i++) {
- parseChild(child.getChildAt(i));
- }
- }
-
- public RadioButton getCheckedButton() {
- return checkedButton;
- }
-}
diff --git a/app/src/main/java/de/danoeh/antennapod/view/SquareImageView.java b/app/src/main/java/de/danoeh/antennapod/view/SquareImageView.java
deleted file mode 100644
index c256ede9e..000000000
--- a/app/src/main/java/de/danoeh/antennapod/view/SquareImageView.java
+++ /dev/null
@@ -1,62 +0,0 @@
-package de.danoeh.antennapod.view;
-
-import android.content.Context;
-import android.content.res.TypedArray;
-import androidx.appcompat.widget.AppCompatImageView;
-import android.util.AttributeSet;
-import de.danoeh.antennapod.core.R;
-
-/**
- * From http://stackoverflow.com/a/19449488/6839
- */
-public class SquareImageView extends AppCompatImageView {
- public static final int DIRECTION_WIDTH = 0;
- public static final int DIRECTION_HEIGHT = 1;
- public static final int DIRECTION_MINIMUM = 2;
-
- private int direction = DIRECTION_WIDTH;
-
- public SquareImageView(Context context) {
- super(context);
- }
-
- public SquareImageView(Context context, AttributeSet attrs) {
- super(context, attrs);
- loadAttrs(context, attrs);
- }
-
- public SquareImageView(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- loadAttrs(context, attrs);
- }
-
- private void loadAttrs(Context context, AttributeSet attrs) {
- TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SquareImageView);
- direction = a.getInt(R.styleable.SquareImageView_direction, DIRECTION_WIDTH);
- a.recycle();
- }
-
- public void setDirection(int direction) {
- this.direction = direction;
- requestLayout();
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-
- switch (direction) {
- case DIRECTION_MINIMUM:
- int size = Math.min(getMeasuredWidth(), getMeasuredHeight());
- setMeasuredDimension(size, size);
- break;
- case DIRECTION_HEIGHT:
- setMeasuredDimension(getMeasuredHeight(), getMeasuredHeight());
- break;
- default:
- setMeasuredDimension(getMeasuredWidth(), getMeasuredWidth());
- break;
- }
- }
-
-} \ No newline at end of file
diff --git a/app/src/main/java/de/danoeh/antennapod/view/WrappingGridView.java b/app/src/main/java/de/danoeh/antennapod/view/WrappingGridView.java
deleted file mode 100644
index 37792b4d1..000000000
--- a/app/src/main/java/de/danoeh/antennapod/view/WrappingGridView.java
+++ /dev/null
@@ -1,35 +0,0 @@
-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/view/viewholder/DownloadItemViewHolder.java b/app/src/main/java/de/danoeh/antennapod/view/viewholder/DownloadItemViewHolder.java
index 274dd4ea8..0e446fb84 100644
--- a/app/src/main/java/de/danoeh/antennapod/view/viewholder/DownloadItemViewHolder.java
+++ b/app/src/main/java/de/danoeh/antennapod/view/viewholder/DownloadItemViewHolder.java
@@ -20,6 +20,7 @@ public class DownloadItemViewHolder extends RecyclerView.ViewHolder {
public final TextView type;
public final TextView date;
public final TextView reason;
+ public final TextView tapForDetails;
public DownloadItemViewHolder(Context context, ViewGroup parent) {
super(LayoutInflater.from(context).inflate(R.layout.downloadlog_item, parent, false));
@@ -27,6 +28,7 @@ public class DownloadItemViewHolder extends RecyclerView.ViewHolder {
type = itemView.findViewById(R.id.txtvType);
icon = itemView.findViewById(R.id.txtvIcon);
reason = itemView.findViewById(R.id.txtvReason);
+ tapForDetails = itemView.findViewById(R.id.txtvTapForDetails);
secondaryActionButton = itemView.findViewById(R.id.secondaryActionButton);
secondaryActionIcon = itemView.findViewById(R.id.secondaryActionIcon);
title = itemView.findViewById(R.id.txtvTitle);
diff --git a/app/src/main/java/de/danoeh/antennapod/view/viewholder/EpisodeItemViewHolder.java b/app/src/main/java/de/danoeh/antennapod/view/viewholder/EpisodeItemViewHolder.java
index 35744227f..8b46a781f 100644
--- a/app/src/main/java/de/danoeh/antennapod/view/viewholder/EpisodeItemViewHolder.java
+++ b/app/src/main/java/de/danoeh/antennapod/view/viewholder/EpisodeItemViewHolder.java
@@ -13,9 +13,7 @@ import android.widget.TextView;
import androidx.cardview.widget.CardView;
import androidx.recyclerview.widget.RecyclerView;
-
import com.joanzapata.iconify.Iconify;
-
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.adapter.CoverLoader;
@@ -25,13 +23,15 @@ import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.feed.MediaType;
import de.danoeh.antennapod.core.feed.util.ImageResourceUtils;
+import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.download.DownloadRequest;
+import de.danoeh.antennapod.core.service.playback.PlaybackService;
import de.danoeh.antennapod.core.storage.DownloadRequester;
import de.danoeh.antennapod.core.util.Converter;
import de.danoeh.antennapod.core.util.DateUtils;
import de.danoeh.antennapod.core.util.NetworkUtils;
-import de.danoeh.antennapod.core.util.ThemeUtils;
-import de.danoeh.antennapod.view.CircularProgressBar;
+import de.danoeh.antennapod.ui.common.ThemeUtils;
+import de.danoeh.antennapod.ui.common.CircularProgressBar;
/**
* Holds the view which shows FeedItems.
@@ -121,8 +121,8 @@ public class EpisodeItemViewHolder extends RecyclerView.ViewHolder {
if (coverHolder.getVisibility() == View.VISIBLE) {
new CoverLoader(activity)
- .withUri(ImageResourceUtils.getImageLocation(item))
- .withFallbackUri(item.getFeed().getImageLocation())
+ .withUri(ImageResourceUtils.getEpisodeListImageLocation(item))
+ .withFallbackUri(item.getFeed().getImageUrl())
.withPlaceholderView(placeholder)
.withCoverView(cover)
.load();
@@ -132,9 +132,6 @@ public class EpisodeItemViewHolder extends RecyclerView.ViewHolder {
private void bind(FeedMedia media) {
isVideo.setVisibility(media.getMediaType() == MediaType.VIDEO ? View.VISIBLE : View.GONE);
duration.setVisibility(media.getDuration() > 0 ? View.VISIBLE : View.GONE);
- duration.setText(Converter.getDurationStringLong(media.getDuration()));
- duration.setContentDescription(activity.getString(R.string.chapter_duration,
- Converter.getDurationStringLocalized(activity, media.getDuration())));
if (media.isCurrentlyPlaying()) {
itemView.setBackgroundColor(ThemeUtils.getColorFromAttr(activity, R.attr.currently_playing_background));
@@ -152,6 +149,9 @@ public class EpisodeItemViewHolder extends RecyclerView.ViewHolder {
secondaryActionProgress.setPercentage(0, item); // Animate X% -> 0%
}
+ duration.setText(Converter.getDurationStringLong(media.getDuration()));
+ duration.setContentDescription(activity.getString(R.string.chapter_duration,
+ Converter.getDurationStringLocalized(activity, media.getDuration())));
if (item.getState() == FeedItem.State.PLAYING || item.getState() == FeedItem.State.IN_PROGRESS) {
int progress = (int) (100.0 * media.getPosition() / media.getDuration());
progressBar.setProgress(progress);
@@ -160,6 +160,11 @@ public class EpisodeItemViewHolder extends RecyclerView.ViewHolder {
Converter.getDurationStringLocalized(activity, media.getPosition())));
progressBar.setVisibility(View.VISIBLE);
position.setVisibility(View.VISIBLE);
+ if (UserPreferences.shouldShowRemainingTime()) {
+ duration.setText("-" + Converter.getDurationStringLong(media.getDuration() - media.getPosition()));
+ duration.setContentDescription(activity.getString(R.string.chapter_duration,
+ Converter.getDurationStringLocalized(activity, (media.getDuration() - media.getPosition()))));
+ }
} else {
progressBar.setVisibility(View.GONE);
position.setVisibility(View.GONE);
@@ -186,6 +191,22 @@ public class EpisodeItemViewHolder extends RecyclerView.ViewHolder {
}
}
+ private void updateDuration(PlaybackPositionEvent event) {
+ int currentPosition = event.getPosition();
+ int timeDuration = event.getDuration();
+ int remainingTime = event.getDuration() - event.getPosition();
+ Log.d(TAG, "currentPosition " + Converter.getDurationStringLong(currentPosition));
+ if (currentPosition == PlaybackService.INVALID_TIME || timeDuration == PlaybackService.INVALID_TIME) {
+ Log.w(TAG, "Could not react to position observer update because of invalid time");
+ return;
+ }
+ if (UserPreferences.shouldShowRemainingTime()) {
+ duration.setText("-" + Converter.getDurationStringLong(remainingTime));
+ } else {
+ duration.setText(Converter.getDurationStringLong(timeDuration));
+ }
+ }
+
public FeedItem getFeedItem() {
return item;
}
@@ -197,7 +218,7 @@ public class EpisodeItemViewHolder extends RecyclerView.ViewHolder {
public void notifyPlaybackPositionUpdated(PlaybackPositionEvent event) {
progressBar.setProgress((int) (100.0 * event.getPosition() / event.getDuration()));
position.setText(Converter.getDurationStringLong(event.getPosition()));
- duration.setText(Converter.getDurationStringLong(event.getDuration()));
+ updateDuration(event);
duration.setVisibility(View.VISIBLE); // Even if the duration was previously unknown, it is now known
}