diff options
Diffstat (limited to 'app/src/main/java/de/danoeh/antennapod')
54 files changed, 964 insertions, 411 deletions
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/BugReportActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/BugReportActivity.java index 48264bb26..b1a0ba2a2 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/BugReportActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/BugReportActivity.java @@ -42,9 +42,8 @@ public class BugReportActivity extends AppCompatActivity { } crashDetailsTextView.setText(crashDetailsText); - findViewById(R.id.btn_open_bug_tracker).setOnClickListener(v -> { - IntentUtils.openInBrowser(BugReportActivity.this, "https://github.com/AntennaPod/AntennaPod/issues"); - }); + findViewById(R.id.btn_open_bug_tracker).setOnClickListener(v -> IntentUtils.openInBrowser( + BugReportActivity.this, "https://github.com/AntennaPod/AntennaPod/issues")); findViewById(R.id.btn_copy_log).setOnClickListener(v -> { ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE); 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 810005d2d..1d3d9bf11 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/DownloadAuthenticationActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/DownloadAuthenticationActivity.java @@ -3,6 +3,8 @@ package de.danoeh.antennapod.activity; import android.app.Activity; import android.content.Intent; import android.os.Bundle; + +import androidx.annotation.NonNull; import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.AppCompatActivity; import android.widget.Button; @@ -86,7 +88,7 @@ public class DownloadAuthenticationActivity extends AppCompatActivity { } @Override - protected void onSaveInstanceState(Bundle outState) { + protected void onSaveInstanceState(@NonNull Bundle outState) { super.onSaveInstanceState(outState); outState.putString("username", etxtUsername.getText().toString()); outState.putString("password", etxtPassword.getText().toString()); 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 eaa423708..f772cb084 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java @@ -5,10 +5,13 @@ import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.content.res.Configuration; +import android.content.res.Resources; import android.os.Build; import android.os.Bundle; import android.os.Handler; +import android.util.DisplayMetrics; import android.util.Log; +import android.util.TypedValue; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; @@ -31,6 +34,7 @@ import de.danoeh.antennapod.R; import de.danoeh.antennapod.core.event.MessageEvent; import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.core.util.StorageUtils; +import de.danoeh.antennapod.core.util.ThemeUtils; import de.danoeh.antennapod.core.util.download.AutoUpdateManager; import de.danoeh.antennapod.dialog.RatingDialog; import de.danoeh.antennapod.fragment.AddFeedFragment; @@ -68,9 +72,9 @@ public class MainActivity extends CastEnabledActivity { public static final String EXTRA_OPEN_PLAYER = "open_player"; public static final String EXTRA_REFRESH_ON_START = "refresh_on_start"; - private DrawerLayout drawerLayout; + private @Nullable DrawerLayout drawerLayout; + private @Nullable ActionBarDrawerToggle drawerToggle; private View navDrawer; - private ActionBarDrawerToggle drawerToggle; private LockableBottomSheetBehavior sheetBehavior; private long lastBackButtonPressTime = 0; private RecyclerView.RecycledViewPool recycledViewPool = new RecyclerView.RecycledViewPool(); @@ -95,10 +99,17 @@ public class MainActivity extends CastEnabledActivity { drawerLayout = findViewById(R.id.drawer_layout); navDrawer = findViewById(R.id.navDrawerFragment); + setNavDrawerSize(); final FragmentManager fm = getSupportFragmentManager(); - fm.addOnBackStackChangedListener(() -> - drawerToggle.setDrawerIndicatorEnabled(fm.getBackStackEntryCount() == 0)); + fm.addOnBackStackChangedListener(() -> { + boolean showArrow = fm.getBackStackEntryCount() != 0; + if (drawerToggle != null) { // Tablet layout does not have a drawer + drawerToggle.setDrawerIndicatorEnabled(!showArrow); + } else if (getActionBar() != null) { + getActionBar().setDisplayHomeAsUpEnabled(showArrow); + } + }); if (fm.findFragmentByTag(MAIN_FRAGMENT_TAG) == null) { String lastFragment = NavDrawerFragment.getLastNavFragment(this); @@ -151,12 +162,18 @@ public class MainActivity extends CastEnabledActivity { @Override public void setSupportActionBar(@Nullable Toolbar toolbar) { - 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); + 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); + } else if (getSupportFragmentManager().getBackStackEntryCount() == 0) { + toolbar.setNavigationIcon(null); + } else { + toolbar.setNavigationIcon(ThemeUtils.getDrawableFromAttr(this, R.attr.homeAsUpIndicator)); + } super.setSupportActionBar(toolbar); } @@ -164,7 +181,11 @@ public class MainActivity extends CastEnabledActivity { SharedPreferences prefs = getSharedPreferences(PREF_NAME, MODE_PRIVATE); if (prefs.getBoolean(PREF_IS_FIRST_LAUNCH, true)) { loadFragment(AddFeedFragment.TAG, null); - new Handler().postDelayed(() -> drawerLayout.openDrawer(navDrawer), 1500); + new Handler().postDelayed(() -> { + if (drawerLayout != null) { // Tablet layout does not have a drawer + drawerLayout.openDrawer(navDrawer); + } + }, 1500); // for backward compatibility, we only change defaults for fresh installs UserPreferences.setUpdateInterval(12); @@ -258,7 +279,10 @@ public class MainActivity extends CastEnabledActivity { // not commit anything in an AsyncTask, but that's a bigger // change than we want now. t.commitAllowingStateLoss(); - drawerLayout.closeDrawer(navDrawer); + + if (drawerLayout != null) { // Tablet layout does not have a drawer + drawerLayout.closeDrawer(navDrawer); + } } public void loadChildFragment(Fragment fragment, TransitionEffect transition) { @@ -292,13 +316,35 @@ public class MainActivity extends CastEnabledActivity { @Override protected void onPostCreate(Bundle savedInstanceState) { super.onPostCreate(savedInstanceState); - drawerToggle.syncState(); + if (drawerToggle != null) { // Tablet layout does not have a drawer + drawerToggle.syncState(); + } } @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); - drawerToggle.onConfigurationChanged(newConfig); + if (drawerToggle != null) { // Tablet layout does not have a drawer + drawerToggle.onConfigurationChanged(newConfig); + } + setNavDrawerSize(); + } + + private void setNavDrawerSize() { + if (drawerToggle == null) { // Tablet layout does not have a drawer + return; + } + float screenPercent = getResources().getInteger(R.integer.nav_drawer_screen_size_percent) * 0.01f; + int width = (int) (getScreenWidth() * screenPercent); + int maxWidth = (int) getResources().getDimension(R.dimen.nav_drawer_max_screen_size); + + navDrawer.getLayoutParams().width = Math.min(width, maxWidth); + } + + private int getScreenWidth() { + DisplayMetrics displayMetrics = new DisplayMetrics(); + getWindowManager().getDefaultDisplay().getMetrics(displayMetrics); + return displayMetrics.widthPixels; } @Override @@ -351,7 +397,7 @@ public class MainActivity extends CastEnabledActivity { @Override public boolean onOptionsItemSelected(MenuItem item) { - if (drawerToggle.onOptionsItemSelected(item)) { + if (drawerToggle != null && drawerToggle.onOptionsItemSelected(item)) { // Tablet layout does not have a drawer return true; } else if (item.getItemId() == android.R.id.home) { if (getSupportFragmentManager().getBackStackEntryCount() > 0) { @@ -374,7 +420,9 @@ public class MainActivity extends CastEnabledActivity { } else { switch (UserPreferences.getBackButtonBehavior()) { case OPEN_DRAWER: - drawerLayout.openDrawer(navDrawer); + if (drawerLayout != null) { // Tablet layout does not have drawer + drawerLayout.openDrawer(navDrawer); + } break; case SHOW_PROMPT: new AlertDialog.Builder(this) 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 34c7e3aba..b03d1e5cd 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java @@ -18,16 +18,23 @@ import android.widget.SeekBar; import android.widget.SeekBar.OnSeekBarChangeListener; import android.widget.TextView; import android.widget.Toast; + +import com.bumptech.glide.Glide; + +import org.apache.commons.lang3.StringUtils; +import org.greenrobot.eventbus.EventBus; +import org.greenrobot.eventbus.Subscribe; +import org.greenrobot.eventbus.ThreadMode; + +import java.text.NumberFormat; + import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog; -import androidx.arch.core.util.Function; +import androidx.appcompat.app.AppCompatActivity; import androidx.core.app.ActivityCompat; import androidx.core.app.ActivityOptionsCompat; import androidx.core.content.ContextCompat; -import androidx.core.util.Consumer; -import androidx.core.util.Supplier; -import com.bumptech.glide.Glide; import de.danoeh.antennapod.R; import de.danoeh.antennapod.core.event.PlaybackPositionEvent; import de.danoeh.antennapod.core.feed.FeedItem; @@ -39,7 +46,6 @@ import de.danoeh.antennapod.core.storage.DBReader; import de.danoeh.antennapod.core.storage.DBWriter; import de.danoeh.antennapod.core.util.Converter; import de.danoeh.antennapod.core.util.FeedItemUtil; -import de.danoeh.antennapod.core.util.Flavors; import de.danoeh.antennapod.core.util.IntentUtils; import de.danoeh.antennapod.core.util.ShareUtils; import de.danoeh.antennapod.core.util.StorageUtils; @@ -51,18 +57,13 @@ 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; import de.danoeh.antennapod.dialog.SleepTimerDialog; import io.reactivex.Observable; 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; - -import java.text.NumberFormat; /** @@ -304,7 +305,7 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements return false; } Playable media = controller.getMedia(); - boolean isFeedMedia = media != null && (media instanceof FeedMedia); + boolean isFeedMedia = (media instanceof FeedMedia); menu.findItem(R.id.open_feed_item).setVisible(isFeedMedia); // FeedMedia implies it belongs to a Feed @@ -313,13 +314,8 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements boolean isItemAndHasLink = isFeedMedia && ShareUtils.hasLinkToShare(((FeedMedia) media).getItem()); - menu.findItem(R.id.share_link_item).setVisible(isItemAndHasLink); - menu.findItem(R.id.share_link_with_position_item).setVisible(isItemAndHasLink); boolean isItemHasDownloadLink = isFeedMedia && ((FeedMedia) media).getDownload_url() != null; - menu.findItem(R.id.share_download_url_item).setVisible(isItemHasDownloadLink); - menu.findItem(R.id.share_download_url_with_position_item).setVisible(isItemHasDownloadLink); - menu.findItem(R.id.share_file).setVisible(isFeedMedia && ((FeedMedia) media).fileExists()); menu.findItem(R.id.share_item).setVisible(hasWebsiteLink || isItemAndHasLink || isItemHasDownloadLink); @@ -380,8 +376,7 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements new SleepTimerDialog().show(getSupportFragmentManager(), "SleepTimerDialog"); break; case R.id.audio_controls: - boolean isPlayingVideo = controller.getMedia().getMediaType() == MediaType.VIDEO; - PlaybackControlsDialog dialog = PlaybackControlsDialog.newInstance(isPlayingVideo); + PlaybackControlsDialog dialog = PlaybackControlsDialog.newInstance(); dialog.show(getSupportFragmentManager(), "playback_controls"); break; case R.id.open_feed_item: @@ -393,29 +388,10 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements case R.id.visit_website_item: IntentUtils.openInBrowser(MediaplayerActivity.this, getWebsiteLinkWithFallback(media)); break; - case R.id.share_link_item: - if (feedItem != null) { - ShareUtils.shareFeedItemLink(this, feedItem); - } - break; - case R.id.share_download_url_item: - if (feedItem != null) { - ShareUtils.shareFeedItemDownloadLink(this, feedItem); - } - break; - case R.id.share_link_with_position_item: + case R.id.share_item: if (feedItem != null) { - ShareUtils.shareFeedItemLink(this, feedItem, true); - } - break; - case R.id.share_download_url_with_position_item: - if (feedItem != null) { - ShareUtils.shareFeedItemDownloadLink(this, feedItem, true); - } - break; - case R.id.share_file: - if (media instanceof FeedMedia) { - ShareUtils.shareFeedItemFile(this, ((FeedMedia) media)); + ShareDialog shareDialog = ShareDialog.newInstance(feedItem); + shareDialog.show(getSupportFragmentManager(), "ShareEpisodeDialog"); } break; default: 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 beb6e0a9f..f6623d5dc 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java @@ -227,15 +227,14 @@ public class OnlineFeedViewActivity extends AppCompatActivity { @Override public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case android.R.id.home: - Intent destIntent = new Intent(this, MainActivity.class); - if (NavUtils.shouldUpRecreateTask(this, destIntent)) { - startActivity(destIntent); - } else { - NavUtils.navigateUpFromSameTask(this); - } - return true; + if (item.getItemId() == android.R.id.home) { + Intent destIntent = new Intent(this, MainActivity.class); + if (NavUtils.shouldUpRecreateTask(this, destIntent)) { + startActivity(destIntent); + } else { + NavUtils.navigateUpFromSameTask(this); + } + return true; } return super.onOptionsItemSelected(item); } @@ -606,9 +605,8 @@ public class OnlineFeedViewActivity extends AppCompatActivity { } final List<String> titles = new ArrayList<>(); - final List<String> urls = new ArrayList<>(); - urls.addAll(urlsMap.keySet()); + final List<String> urls = new ArrayList<>(urlsMap.keySet()); for (String url : urls) { titles.add(urlsMap.get(url)); } diff --git a/app/src/main/java/de/danoeh/antennapod/activity/gpoddernet/GpodnetAuthenticationActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/gpoddernet/GpodnetAuthenticationActivity.java index 337880317..cfd6ec702 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/gpoddernet/GpodnetAuthenticationActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/gpoddernet/GpodnetAuthenticationActivity.java @@ -88,10 +88,9 @@ public class GpodnetAuthenticationActivity extends AppCompatActivity { @Override public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case android.R.id.home: - finish(); - return true; + if (item.getItemId() == android.R.id.home) { + finish(); + return true; } return super.onOptionsItemSelected(item); } 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 690bb9e3d..6bfd34d5c 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistDescriptionAdapter.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistDescriptionAdapter.java @@ -7,6 +7,10 @@ import android.view.ViewGroup; import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + import de.danoeh.antennapod.R; import de.danoeh.antennapod.core.feed.MediaType; import de.danoeh.antennapod.core.preferences.PlaybackPreferences; @@ -32,8 +36,9 @@ public class FeedItemlistDescriptionAdapter extends ArrayAdapter<FeedItem> { super(context, resource, objects); } + @NonNull @Override - public View getView(int position, View convertView, ViewGroup parent) { + public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) { Holder holder; FeedItem item = getItem(position); 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 e66032e11..eea977c2e 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/NavListAdapter.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/NavListAdapter.java @@ -61,7 +61,7 @@ public class NavListAdapter extends BaseAdapter private final ItemAccess itemAccess; private final WeakReference<Activity> activity; - private boolean showSubscriptionList = true; + public boolean showSubscriptionList = true; public NavListAdapter(ItemAccess itemAccess, Activity context) { this.itemAccess = itemAccess; @@ -194,7 +194,7 @@ public class NavListAdapter extends BaseAdapter @Override public View getView(int position, View convertView, ViewGroup parent) { int viewType = getItemViewType(position); - View v = null; + View v; if (viewType == VIEW_TYPE_NAV) { v = getNavView((String) getItem(position), position, convertView, parent); } else if (viewType == VIEW_TYPE_SECTION_DIVIDER) { @@ -296,9 +296,17 @@ public class NavListAdapter extends BaseAdapter .getSystemService(Context.LAYOUT_INFLATER_SERVICE); convertView = inflater.inflate(R.layout.nav_section_item, parent, false); + TextView feedsFilteredMsg = convertView.findViewById(R.id.nav_feeds_filtered_message); - convertView.setEnabled(false); - convertView.setOnClickListener(null); + if (UserPreferences.getFeedFilter() != UserPreferences.FEED_FILTER_NONE && showSubscriptionList) { + convertView.setEnabled(true); + feedsFilteredMsg.setText("{md-info-outline} " + context.getString(R.string.subscriptions_are_filtered)); + Iconify.addIcons(feedsFilteredMsg); + feedsFilteredMsg.setVisibility(View.VISIBLE); + } else { + convertView.setEnabled(false); + feedsFilteredMsg.setVisibility(View.GONE); + } return convertView; } diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/QueueRecyclerAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/QueueRecyclerAdapter.java index 428a968c6..7ce086694 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/QueueRecyclerAdapter.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/QueueRecyclerAdapter.java @@ -20,16 +20,17 @@ public class QueueRecyclerAdapter extends EpisodeItemListAdapter { private static final String TAG = "QueueRecyclerAdapter"; private final ItemTouchHelper itemTouchHelper; - private boolean locked; + private boolean dragDropEnabled; + public QueueRecyclerAdapter(MainActivity mainActivity, ItemTouchHelper itemTouchHelper) { super(mainActivity); this.itemTouchHelper = itemTouchHelper; - locked = UserPreferences.isQueueLocked(); + dragDropEnabled = ! (UserPreferences.isQueueKeepSorted() || UserPreferences.isQueueLocked()); } - public void setLocked(boolean locked) { - this.locked = locked; + public void updateDragDropEnabled() { + dragDropEnabled = ! (UserPreferences.isQueueKeepSorted() || UserPreferences.isQueueLocked()); notifyDataSetChanged(); } @@ -37,14 +38,14 @@ public class QueueRecyclerAdapter extends EpisodeItemListAdapter { @SuppressLint("ClickableViewAccessibility") protected void afterBindViewHolder(EpisodeItemViewHolder holder, int pos) { View.OnTouchListener startDragTouchListener = (v1, event) -> { - if (MotionEventCompat.getActionMasked(event) == MotionEvent.ACTION_DOWN) { + if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { Log.d(TAG, "startDrag()"); itemTouchHelper.startDrag(holder); } return false; }; - if (locked) { + if (!dragDropEnabled) { holder.dragHandle.setVisibility(View.GONE); holder.dragHandle.setOnTouchListener(null); holder.coverHolder.setOnTouchListener(null); 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 ac1e94437..c3177668a 100644 --- a/app/src/main/java/de/danoeh/antennapod/dialog/EpisodesApplyActionFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/dialog/EpisodesApplyActionFragment.java @@ -220,7 +220,7 @@ public class EpisodesApplyActionFragment extends Fragment { } @Override - public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) { super.onCreateOptionsMenu(menu, inflater); inflater.inflate(R.menu.episodes_apply_action_options, menu); @@ -236,7 +236,7 @@ public class EpisodesApplyActionFragment extends Fragment { } @Override - public void onPrepareOptionsMenu(Menu menu) { + public void onPrepareOptionsMenu(@NonNull Menu menu) { // Prepare icon for select toggle button int[] icon = new int[1]; @@ -413,7 +413,7 @@ public class EpisodesApplyActionFragment extends Fragment { boolean checked = checkedIds.contains(episode.getId()); mListView.setItemChecked(i, checked); } - ActivityCompat.invalidateOptionsMenu(EpisodesApplyActionFragment.this.getActivity()); + getActivity().invalidateOptionsMenu(); toolbar.setTitle(getResources().getQuantityString(R.plurals.num_selected_label, checkedIds.size(), checkedIds.size())); } diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/FeedFilterDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/FeedFilterDialog.java new file mode 100644 index 000000000..7d1fe4026 --- /dev/null +++ b/app/src/main/java/de/danoeh/antennapod/dialog/FeedFilterDialog.java @@ -0,0 +1,40 @@ +package de.danoeh.antennapod.dialog; + +import android.content.Context; + +import androidx.appcompat.app.AlertDialog; + +import org.greenrobot.eventbus.EventBus; + +import de.danoeh.antennapod.R; +import de.danoeh.antennapod.core.event.UnreadItemsUpdateEvent; +import de.danoeh.antennapod.core.preferences.UserPreferences; + +public class FeedFilterDialog { + public static void showDialog(Context context) { + AlertDialog.Builder dialog = new AlertDialog.Builder(context); + dialog.setTitle(context.getString(R.string.pref_filter_feed_title)); + dialog.setNegativeButton(android.R.string.cancel, (d, listener) -> d.dismiss()); + + int selectedIndexTemp = 0; + int selected = UserPreferences.getFeedFilter(); + String[] entryValues = context.getResources().getStringArray(R.array.nav_drawer_feed_filter_values); + for (int i = 0; i < entryValues.length; i++) { + if (Integer.parseInt(entryValues[i]) == selected) { + selectedIndexTemp = i; + } + } + + final int selectedIndex = selectedIndexTemp; + String[] items = context.getResources().getStringArray(R.array.nav_drawer_feed_filter_options); + dialog.setSingleChoiceItems(items, selectedIndex, (d, which) -> { + if (selectedIndex != which) { + UserPreferences.setFeedFilter(entryValues[which]); + //Update subscriptions + EventBus.getDefault().post(new UnreadItemsUpdateEvent()); + } + d.dismiss(); + }); + dialog.show(); + } +} 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 d2912f90f..82010637f 100644 --- a/app/src/main/java/de/danoeh/antennapod/dialog/FilterDialog.java +++ b/app/src/main/java/de/danoeh/antennapod/dialog/FilterDialog.java @@ -1,8 +1,12 @@ package de.danoeh.antennapod.dialog; import android.content.Context; -import androidx.appcompat.app.AlertDialog; import android.text.TextUtils; +import android.view.LayoutInflater; +import android.widget.LinearLayout; +import android.widget.RadioButton; + +import androidx.appcompat.app.AlertDialog; import java.util.Arrays; import java.util.HashSet; @@ -10,6 +14,8 @@ 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; public abstract class FilterDialog { @@ -22,36 +28,46 @@ public abstract class FilterDialog { } public void openDialog() { - final String[] items = context.getResources().getStringArray(R.array.episode_filter_options); - final String[] values = context.getResources().getStringArray(R.array.episode_filter_values); - final boolean[] checkedItems = new boolean[items.length]; final Set<String> filterValues = new HashSet<>(Arrays.asList(filter.getValues())); + AlertDialog.Builder builder = new AlertDialog.Builder(context); + builder.setTitle(R.string.filter); - // make sure we have no empty strings in the filter list - for (String filterValue : filterValues) { - if (TextUtils.isEmpty(filterValue)) { - filterValues.remove(filterValue); - } + LayoutInflater inflater = LayoutInflater.from(this.context); + LinearLayout layout = (LinearLayout) inflater.inflate(R.layout.filter_dialog, null, false); + builder.setView(layout); + + for (FeedItemFilterGroup item : FeedItemFilterGroup.values()) { + RecursiveRadioGroup row = (RecursiveRadioGroup) inflater.inflate(R.layout.filter_dialog_row, null); + RadioButton filter1 = row.findViewById(R.id.filter_dialog_radioButton1); + RadioButton filter2 = row.findViewById(R.id.filter_dialog_radioButton2); + filter1.setText(item.values[0].displayName); + filter1.setTag(item.values[0].filterId); + filter2.setText(item.values[1].displayName); + filter2.setTag(item.values[1].filterId); + layout.addView(row); } - for (int i = 0; i < values.length; i++) { - String value = values[i]; - if (filterValues.contains(value)) { - checkedItems[i] = true; + for (String filterId : filterValues) { + if (!TextUtils.isEmpty(filterId)) { + ((RadioButton) layout.findViewWithTag(filterId)).setChecked(true); } } - AlertDialog.Builder builder = new AlertDialog.Builder(context); - builder.setTitle(R.string.filter); - builder.setMultiChoiceItems(items, checkedItems, (dialog, which, isChecked) -> { - if (isChecked) { - filterValues.add(values[which]); - } else { - filterValues.remove(values[which]); - } - }); builder.setPositiveButton(R.string.confirm_label, (dialog, which) -> { + filterValues.clear(); + for (int i = 0; i < layout.getChildCount(); i++) { + if (!(layout.getChildAt(i) instanceof RecursiveRadioGroup)) { + continue; + } + RecursiveRadioGroup group = (RecursiveRadioGroup) layout.getChildAt(i); + if (group.getCheckedButton() != null) { + String tag = (String) group.getCheckedButton().getTag(); + if (tag != null) { // Clear buttons use no tag + filterValues.add((String) group.getCheckedButton().getTag()); + } + } + } updateFilter(filterValues); }); builder.setNegativeButton(R.string.cancel_label, null); 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 d1ffdc148..e45533826 100644 --- a/app/src/main/java/de/danoeh/antennapod/dialog/PlaybackControlsDialog.java +++ b/app/src/main/java/de/danoeh/antennapod/dialog/PlaybackControlsDialog.java @@ -11,27 +11,21 @@ import android.widget.Button; import android.widget.CheckBox; import android.widget.SeekBar; import android.widget.TextView; -import de.danoeh.antennapod.core.preferences.PlaybackPreferences; import de.danoeh.antennapod.R; -import de.danoeh.antennapod.core.feed.util.PlaybackSpeedUtils; import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.core.util.Converter; -import de.danoeh.antennapod.core.util.playback.Playable; import de.danoeh.antennapod.core.util.playback.PlaybackController; +import de.danoeh.antennapod.view.PlaybackSpeedSeekBar; import java.util.List; import java.util.Locale; public class PlaybackControlsDialog extends DialogFragment { - private static final String ARGUMENT_IS_PLAYING_VIDEO = "isPlayingVideo"; - private PlaybackController controller; private AlertDialog dialog; - private boolean isPlayingVideo; - public static PlaybackControlsDialog newInstance(boolean isPlayingVideo) { + public static PlaybackControlsDialog newInstance() { Bundle arguments = new Bundle(); - arguments.putBoolean(ARGUMENT_IS_PLAYING_VIDEO, isPlayingVideo); PlaybackControlsDialog dialog = new PlaybackControlsDialog(); dialog.setArguments(arguments); return dialog; @@ -65,8 +59,6 @@ public class PlaybackControlsDialog extends DialogFragment { @NonNull @Override public Dialog onCreateDialog(Bundle savedInstanceState) { - isPlayingVideo = getArguments() != null && getArguments().getBoolean(ARGUMENT_IS_PLAYING_VIDEO); - dialog = new AlertDialog.Builder(getContext()) .setTitle(R.string.audio_controls) .setView(R.layout.audio_controls) @@ -79,68 +71,18 @@ public class PlaybackControlsDialog extends DialogFragment { } private void setupUi() { - final SeekBar barPlaybackSpeed = dialog.findViewById(R.id.playback_speed); - final TextView butDecSpeed = dialog.findViewById(R.id.butDecSpeed); - butDecSpeed.setOnClickListener(v -> { - if (controller != null && controller.canSetPlaybackSpeed()) { - barPlaybackSpeed.setProgress(barPlaybackSpeed.getProgress() - 2); - } else { - VariableSpeedDialog.showGetPluginDialog(getContext()); - } - }); - final TextView butIncSpeed = dialog.findViewById(R.id.butIncSpeed); - butIncSpeed.setOnClickListener(v -> { - if (controller != null && controller.canSetPlaybackSpeed()) { - barPlaybackSpeed.setProgress(barPlaybackSpeed.getProgress() + 2); - } else { - VariableSpeedDialog.showGetPluginDialog(getContext()); - } - }); - final TextView txtvPlaybackSpeed = dialog.findViewById(R.id.txtvPlaybackSpeed); - float currentSpeed = getCurrentSpeed(); - txtvPlaybackSpeed.setText(String.format(Locale.getDefault(), "%.2fx", currentSpeed)); - barPlaybackSpeed.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { - @Override - public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { - if (controller != null && controller.canSetPlaybackSpeed()) { - float playbackSpeed = (progress + 10) / 20.0f; - controller.setPlaybackSpeed(playbackSpeed); - - PlaybackPreferences.setCurrentlyPlayingTemporaryPlaybackSpeed(playbackSpeed); - if (isPlayingVideo) { - UserPreferences.setVideoPlaybackSpeed(playbackSpeed); - } else { - UserPreferences.setPlaybackSpeed(playbackSpeed); - } - - String speedStr = String.format(Locale.getDefault(), "%.2fx", playbackSpeed); - txtvPlaybackSpeed.setText(speedStr); - } else if (fromUser) { - float speed = getCurrentSpeed(); - barPlaybackSpeed.post(() -> barPlaybackSpeed.setProgress(Math.round((20 * speed) - 10))); - } - } + PlaybackSpeedSeekBar speedSeekBar = dialog.findViewById(R.id.speed_seek_bar); + speedSeekBar.setController(controller); + speedSeekBar.setProgressChangedListener(speed + -> txtvPlaybackSpeed.setText(String.format(Locale.getDefault(), "%.2fx", speed))); - @Override - public void onStartTrackingTouch(SeekBar seekBar) { - if (controller != null && !controller.canSetPlaybackSpeed()) { - VariableSpeedDialog.showGetPluginDialog(getContext()); - } - } - - @Override - public void onStopTrackingTouch(SeekBar seekBar) { - } - }); - barPlaybackSpeed.setProgress(Math.round((20 * currentSpeed) - 10)); - - final SeekBar barLeftVolume = (SeekBar) dialog.findViewById(R.id.volume_left); + final SeekBar barLeftVolume = dialog.findViewById(R.id.volume_left); barLeftVolume.setProgress(UserPreferences.getLeftVolumePercentage()); - final SeekBar barRightVolume = (SeekBar) dialog.findViewById(R.id.volume_right); + final SeekBar barRightVolume = dialog.findViewById(R.id.volume_right); barRightVolume.setProgress(UserPreferences.getRightVolumePercentage()); - final CheckBox stereoToMono = (CheckBox) dialog.findViewById(R.id.stereo_to_mono); + final CheckBox stereoToMono = dialog.findViewById(R.id.stereo_to_mono); stereoToMono.setChecked(UserPreferences.stereoToMono()); if (controller != null && !controller.canDownmix()) { stereoToMono.setEnabled(false); @@ -220,13 +162,4 @@ public class PlaybackControlsDialog extends DialogFragment { new Handler().postDelayed(this::setupAudioTracks, 500); }); } - - private float getCurrentSpeed() { - Playable media = null; - if (controller != null) { - media = controller.getMedia(); - } - - return PlaybackSpeedUtils.getCurrentPlaybackSpeed(media); - } } diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/ProxyDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/ProxyDialog.java index 0c25e3e9f..d0fb91692 100644 --- a/app/src/main/java/de/danoeh/antennapod/dialog/ProxyDialog.java +++ b/app/src/main/java/de/danoeh/antennapod/dialog/ProxyDialog.java @@ -23,6 +23,7 @@ import java.net.Proxy; import java.net.SocketAddress; import java.util.ArrayList; import java.util.List; +import java.util.Locale; import java.util.concurrent.TimeUnit; import de.danoeh.antennapod.R; @@ -63,6 +64,8 @@ public class ProxyDialog { public Dialog show() { View content = View.inflate(context, R.layout.proxy_settings, null); + spType = content.findViewById(R.id.spType); + dialog = new AlertDialog.Builder(context) .setTitle(R.string.pref_proxy_title) .setView(content) @@ -76,7 +79,7 @@ public class ProxyDialog { test(); return; } - String type = (String) ((Spinner) content.findViewById(R.id.spType)).getSelectedItem(); + String type = (String) spType.getSelectedItem(); ProxyConfig proxy; if (Proxy.Type.valueOf(type) == Proxy.Type.DIRECT) { proxy = ProxyConfig.direct(); @@ -106,7 +109,6 @@ public class ProxyDialog { dialog.dismiss(); }); - spType = content.findViewById(R.id.spType); List<String> types = new ArrayList<>(); types.add(Proxy.Type.DIRECT.name()); types.add(Proxy.Type.HTTP.name()); @@ -227,12 +229,11 @@ public class ProxyDialog { if(required) { testSuccessful = false; dialog.getButton(AlertDialog.BUTTON_POSITIVE).setText(R.string.proxy_test_label); - dialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(true); } else { testSuccessful = true; dialog.getButton(AlertDialog.BUTTON_POSITIVE).setText(android.R.string.ok); - dialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(true); } + dialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(true); } private void test() { @@ -261,7 +262,7 @@ public class ProxyDialog { portValue = Integer.parseInt(port); } SocketAddress address = InetSocketAddress.createUnresolved(host, portValue); - Proxy.Type proxyType = Proxy.Type.valueOf(type.toUpperCase()); + Proxy.Type proxyType = Proxy.Type.valueOf(type.toUpperCase(Locale.US)); Proxy proxy = new Proxy(proxyType, address); OkHttpClient.Builder builder = AntennapodHttpClient.newBuilder() .connectTimeout(10, TimeUnit.SECONDS) diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/ShareDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/ShareDialog.java new file mode 100644 index 000000000..8104d3539 --- /dev/null +++ b/app/src/main/java/de/danoeh/antennapod/dialog/ShareDialog.java @@ -0,0 +1,112 @@ +package de.danoeh.antennapod.dialog; + +import android.app.Dialog; +import android.content.Context; +import android.content.SharedPreferences; +import android.os.Bundle; +import android.view.View; +import android.widget.CheckBox; +import android.widget.RadioButton; +import android.widget.RadioGroup; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AlertDialog; +import androidx.fragment.app.DialogFragment; +import de.danoeh.antennapod.R; +import de.danoeh.antennapod.core.feed.FeedItem; +import de.danoeh.antennapod.core.util.ShareUtils; + +public class ShareDialog extends DialogFragment { + + private static final String ARGUMENT_FEED_ITEM = "feedItem"; + + private static final String TAG = "ShareDialog"; + private Context ctx; + private FeedItem item; + + private static final String PREF_SHARE_DIALOG_OPTION = "prefShareDialogOption"; + private static final String PREF_SHARE_EPISODE_START_AT = "prefShareEpisodeStartAt"; + + private RadioGroup radioGroup; + private RadioButton radioEpisodeWebsite; + private RadioButton radioMediaFile; + private CheckBox checkBoxStartAt; + private SharedPreferences prefs; + + public ShareDialog() { + // Empty constructor required for DialogFragment + } + + public static ShareDialog newInstance(FeedItem item) { + Bundle arguments = new Bundle(); + arguments.putSerializable(ARGUMENT_FEED_ITEM, item); + ShareDialog dialog = new ShareDialog(); + dialog.setArguments(arguments); + return dialog; + } + + @NonNull + @Override + public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) { + if (getArguments() != null) { + ctx = getActivity(); + item = (FeedItem) getArguments().getSerializable(ARGUMENT_FEED_ITEM); + prefs = getActivity().getSharedPreferences("ShareDialog", Context.MODE_PRIVATE); + } + + View content = View.inflate(ctx, R.layout.share_episode_dialog, null); + AlertDialog.Builder builder = new AlertDialog.Builder(ctx); + builder.setTitle(R.string.share_label); + builder.setView(content); + + radioGroup = content.findViewById(R.id.share_dialog_radio_group); + radioEpisodeWebsite = content.findViewById(R.id.share_episode_website_radio); + radioMediaFile = content.findViewById(R.id.share_media_file_radio); + checkBoxStartAt = content.findViewById(R.id.share_start_at_timer_dialog); + + setupOptions(); + + builder.setPositiveButton(R.string.share_label, (dialog, id) -> { + boolean includePlaybackPosition = checkBoxStartAt.isChecked(); + if (radioEpisodeWebsite.isChecked()) { + ShareUtils.shareFeedItemLink(ctx, item, includePlaybackPosition); + prefs.edit().putString(PREF_SHARE_DIALOG_OPTION, "website").apply(); + } else { + ShareUtils.shareFeedItemDownloadLink(ctx, item, includePlaybackPosition); + prefs.edit().putString(PREF_SHARE_DIALOG_OPTION, "media").apply(); + } + prefs.edit().putBoolean(PREF_SHARE_EPISODE_START_AT, includePlaybackPosition).apply(); + }).setNegativeButton(R.string.cancel_label, (dialog, id) -> dialog.dismiss()); + + return builder.create(); + } + + private void setupOptions() { + final boolean hasMedia = item.getMedia() != null; + + if (!ShareUtils.hasLinkToShare(item)) { + radioEpisodeWebsite.setVisibility(View.GONE); + radioMediaFile.setChecked(true); + } + + if (!hasMedia || item.getMedia().getDownload_url() == null) { + radioMediaFile.setVisibility(View.GONE); + radioEpisodeWebsite.setChecked(true); + } + + if (radioEpisodeWebsite.getVisibility() == View.VISIBLE && radioMediaFile.getVisibility() == View.VISIBLE) { + String option = prefs.getString(PREF_SHARE_DIALOG_OPTION, "website"); + if (option.equals("website")) { + radioEpisodeWebsite.setChecked(true); + radioMediaFile.setChecked(false); + } else { + radioEpisodeWebsite.setChecked(false); + radioMediaFile.setChecked(true); + } + } + + boolean switchIsChecked = prefs.getBoolean(PREF_SHARE_EPISODE_START_AT, false); + checkBoxStartAt.setChecked(switchIsChecked); + } +} 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 3a6ba183f..ef8ed335d 100644 --- a/app/src/main/java/de/danoeh/antennapod/dialog/VariableSpeedDialog.java +++ b/app/src/main/java/de/danoeh/antennapod/dialog/VariableSpeedDialog.java @@ -1,97 +1,171 @@ package de.danoeh.antennapod.dialog; +import android.app.Dialog; import android.content.Context; -import android.os.Build; +import android.os.Bundle; +import android.view.View; +import android.view.ViewGroup; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog; +import androidx.fragment.app.DialogFragment; +import androidx.recyclerview.widget.GridLayoutManager; +import androidx.recyclerview.widget.RecyclerView; +import com.google.android.material.chip.Chip; +import com.google.android.material.snackbar.Snackbar; import de.danoeh.antennapod.R; import de.danoeh.antennapod.core.preferences.UserPreferences; +import de.danoeh.antennapod.core.util.playback.PlaybackController; +import de.danoeh.antennapod.view.ItemOffsetDecoration; +import de.danoeh.antennapod.view.PlaybackSpeedSeekBar; import java.text.DecimalFormat; import java.text.DecimalFormatSymbols; import java.util.ArrayList; -import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.Locale; -public class VariableSpeedDialog { +public class VariableSpeedDialog extends DialogFragment { + private SpeedSelectionAdapter adapter; + private final DecimalFormat speedFormat; + private PlaybackController controller; + private final List<Float> selectedSpeeds; + private PlaybackSpeedSeekBar speedSeekBar; + private Chip addCurrentSpeedChip; - private VariableSpeedDialog() { - } - - public static void showDialog(final Context context) { - if (UserPreferences.useSonic() - || UserPreferences.useExoplayer() - || Build.VERSION.SDK_INT >= 23) { - showSpeedSelectorDialog(context); - } else { - showGetPluginDialog(context, true); - } + public VariableSpeedDialog() { + DecimalFormatSymbols format = new DecimalFormatSymbols(Locale.US); + format.setDecimalSeparator('.'); + speedFormat = new DecimalFormat("0.00", format); + selectedSpeeds = new ArrayList<>(UserPreferences.getPlaybackSpeedArray()); } public static void showGetPluginDialog(final Context context) { - showGetPluginDialog(context, false); - } - - private static void showGetPluginDialog(final Context context, boolean showSpeedSelector) { AlertDialog.Builder builder = new AlertDialog.Builder(context); builder.setTitle(R.string.no_playback_plugin_title); builder.setMessage(R.string.no_playback_plugin_or_sonic_msg); builder.setPositiveButton(R.string.enable_sonic, (dialog, which) -> { UserPreferences.enableSonic(); - if (showSpeedSelector) { - showSpeedSelectorDialog(context); - } }); builder.setNeutralButton(R.string.close_label, null); builder.show(); } - private static void showSpeedSelectorDialog(final Context context) { - DecimalFormatSymbols format = new DecimalFormatSymbols(Locale.US); - format.setDecimalSeparator('.'); - DecimalFormat speedFormat = new DecimalFormat("0.00", format); - - final String[] speedValues = context.getResources().getStringArray( - R.array.playback_speed_values); - // According to Java spec these get initialized to false on creation - final boolean[] speedChecked = new boolean[speedValues.length]; - - // Build the "isChecked" array so that multiChoice dialog is populated correctly - List<String> selectedSpeedList = new ArrayList<>(); - float[] selectedSpeeds = UserPreferences.getPlaybackSpeedArray(); - for (float speed : selectedSpeeds) { - selectedSpeedList.add(speedFormat.format(speed)); + @Override + public void onStart() { + super.onStart(); + controller = new PlaybackController(getActivity()) { + @Override + public void setupGUI() { + updateSpeed(); + } + + @Override + public void onPlaybackSpeedChange() { + updateSpeed(); + } + }; + controller.init(); + speedSeekBar.setController(controller); + } + + private void updateSpeed() { + speedSeekBar.updateSpeed(); + addCurrentSpeedChip.setText(speedFormat.format(controller.getCurrentPlaybackSpeedMultiplier())); + } + + @Override + public void onStop() { + super.onStop(); + controller.release(); + controller = null; + } + + @NonNull + @Override + public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) { + AlertDialog.Builder builder = new AlertDialog.Builder(getContext()); + builder.setPositiveButton(R.string.close_label, null); + + View root = View.inflate(getContext(), R.layout.speed_select_dialog, null); + speedSeekBar = root.findViewById(R.id.speed_seek_bar); + RecyclerView selectedSpeedsGrid = root.findViewById(R.id.selected_speeds_grid); + selectedSpeedsGrid.setLayoutManager(new GridLayoutManager(getContext(), 3)); + selectedSpeedsGrid.addItemDecoration(new ItemOffsetDecoration(getContext(), 4)); + adapter = new SpeedSelectionAdapter(); + adapter.setHasStableIds(true); + selectedSpeedsGrid.setAdapter(adapter); + + addCurrentSpeedChip = root.findViewById(R.id.add_current_speed_chip); + addCurrentSpeedChip.setCloseIconVisible(true); + addCurrentSpeedChip.setCloseIconResource(R.drawable.ic_add_black); + addCurrentSpeedChip.setOnCloseIconClickListener(v -> addCurrentSpeed()); + addCurrentSpeedChip.setOnClickListener(v -> addCurrentSpeed()); + + builder.setView(root); + return builder.create(); + } + + private void addCurrentSpeed() { + float newSpeed = controller.getCurrentPlaybackSpeedMultiplier(); + if (selectedSpeeds.contains(newSpeed)) { + Snackbar.make(addCurrentSpeedChip, + getString(R.string.preset_already_exists, newSpeed), Snackbar.LENGTH_LONG).show(); + } else { + selectedSpeeds.add(newSpeed); + Collections.sort(selectedSpeeds); + UserPreferences.setPlaybackSpeedArray(selectedSpeeds); + adapter.notifyDataSetChanged(); } + } - for (int i = 0; i < speedValues.length; i++) { - speedChecked[i] = selectedSpeedList.contains(speedValues[i]); + public class SpeedSelectionAdapter extends RecyclerView.Adapter<SpeedSelectionAdapter.ViewHolder> { + + @Override + @NonNull + public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + Chip chip = new Chip(getContext()); + chip.setCloseIconVisible(true); + chip.setCloseIconResource(R.drawable.ic_delete_black); + return new ViewHolder(chip); } - AlertDialog.Builder builder = new AlertDialog.Builder(context); - builder.setTitle(R.string.set_playback_speed_label); - builder.setMultiChoiceItems(R.array.playback_speed_values, - speedChecked, (dialog, which, isChecked) -> speedChecked[which] = isChecked); - builder.setNegativeButton(android.R.string.cancel, null); - builder.setPositiveButton(android.R.string.ok, - (dialog, which) -> { - int choiceCount = 0; - for (boolean checked : speedChecked) { - if (checked) { - choiceCount++; - } - } - String[] newSpeedValues = new String[choiceCount]; - int newSpeedIndex = 0; - for (int i = 0; i < speedChecked.length; i++) { - if (speedChecked[i]) { - newSpeedValues[newSpeedIndex++] = speedValues[i]; - } + @Override + public void onBindViewHolder(@NonNull ViewHolder holder, int position) { + float speed = selectedSpeeds.get(position); + + holder.chip.setText(speedFormat.format(speed)); + holder.chip.setOnCloseIconClickListener(v -> { + selectedSpeeds.remove(speed); + UserPreferences.setPlaybackSpeedArray(selectedSpeeds); + notifyDataSetChanged(); + }); + holder.chip.setOnClickListener(v -> { + if (controller != null) { + controller.setPlaybackSpeed(speed); + notifyDataSetChanged(); } + }); + } - UserPreferences.setPlaybackSpeedArray(newSpeedValues); + @Override + public int getItemCount() { + return selectedSpeeds.size(); + } - }); - builder.create().show(); - } + @Override + public long getItemId(int position) { + return selectedSpeeds.get(position).hashCode(); + } + + public class ViewHolder extends RecyclerView.ViewHolder { + Chip chip; + ViewHolder(Chip itemView) { + super(itemView); + chip = itemView; + } + } + } } 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 546684f14..167daa08b 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/AddFeedFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/AddFeedFragment.java @@ -12,9 +12,13 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.EditText; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AppCompatActivity; import androidx.fragment.app.Fragment; + import de.danoeh.antennapod.R; import de.danoeh.antennapod.activity.MainActivity; import de.danoeh.antennapod.activity.OnlineFeedViewActivity; @@ -36,7 +40,10 @@ public class AddFeedFragment extends Fragment { private MainActivity activity; @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + @Nullable + public View onCreateView(@NonNull LayoutInflater inflater, + @Nullable ViewGroup container, + @Nullable Bundle savedInstanceState) { super.onCreateView(inflater, container, savedInstanceState); View root = inflater.inflate(R.layout.addfeed, container, false); activity = (MainActivity) getActivity(); 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 05f38000c..4a1c12e0a 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java @@ -45,20 +45,18 @@ public class AllEpisodesFragment extends EpisodesListFragment { @Override public boolean onOptionsItemSelected(MenuItem item) { if (!super.onOptionsItemSelected(item)) { - switch (item.getItemId()) { - case R.id.filter_items: - showFilterDialog(); - return true; - default: - return false; + if (item.getItemId() == R.id.filter_items) { + showFilterDialog(); + return true; } + return false; } else { return true; } } @Override - public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) { super.onCreateOptionsMenu(menu, inflater); menu.findItem(R.id.filter_items).setVisible(true); menu.findItem(R.id.mark_all_read_item).setVisible(true); @@ -69,7 +67,7 @@ public class AllEpisodesFragment extends EpisodesListFragment { super.onFragmentLoaded(episodes); if (feedItemFilter.getValues().length > 0) { - txtvInformation.setText("{fa-info-circle} " + this.getString(R.string.filtered_label)); + txtvInformation.setText("{md-info-outline} " + this.getString(R.string.filtered_label)); Iconify.addIcons(txtvInformation); txtvInformation.setVisibility(View.VISIBLE); } else { 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 5ca8b666a..7eb749681 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/AudioPlayerFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/AudioPlayerFragment.java @@ -13,7 +13,6 @@ import android.widget.ImageButton; import android.widget.ProgressBar; import android.widget.SeekBar; import android.widget.TextView; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog; @@ -21,17 +20,8 @@ import androidx.appcompat.widget.Toolbar; import androidx.fragment.app.Fragment; import androidx.viewpager2.adapter.FragmentStateAdapter; import androidx.viewpager2.widget.ViewPager2; - import com.google.android.material.bottomsheet.BottomSheetBehavior; - import com.google.android.material.snackbar.Snackbar; -import org.greenrobot.eventbus.EventBus; -import org.greenrobot.eventbus.Subscribe; -import org.greenrobot.eventbus.ThreadMode; - -import java.text.DecimalFormat; -import java.text.NumberFormat; - import de.danoeh.antennapod.R; import de.danoeh.antennapod.activity.CastEnabledActivity; import de.danoeh.antennapod.activity.MainActivity; @@ -40,7 +30,6 @@ 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.util.PlaybackSpeedUtils; -import de.danoeh.antennapod.core.preferences.PlaybackPreferences; import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.core.service.playback.PlaybackService; import de.danoeh.antennapod.core.util.Converter; @@ -60,6 +49,13 @@ import io.reactivex.Maybe; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.Disposable; import io.reactivex.schedulers.Schedulers; +import org.greenrobot.eventbus.EventBus; +import org.greenrobot.eventbus.Subscribe; +import org.greenrobot.eventbus.ThreadMode; + +import java.text.DecimalFormat; +import java.text.NumberFormat; +import java.util.List; /** * Shows the audio player. @@ -96,7 +92,9 @@ public class AudioPlayerFragment extends Fragment implements private boolean showTimeLeft; @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + public View onCreateView(@NonNull LayoutInflater inflater, + @Nullable ViewGroup container, + @Nullable Bundle savedInstanceState) { super.onCreateView(inflater, container, savedInstanceState); View root = inflater.inflate(R.layout.audioplayer_fragment, container, false); toolbar = root.findViewById(R.id.toolbar); @@ -206,30 +204,29 @@ public class AudioPlayerFragment extends Fragment implements VariableSpeedDialog.showGetPluginDialog(getContext()); return; } - float[] availableSpeeds = UserPreferences.getPlaybackSpeedArray(); + List<Float> availableSpeeds = UserPreferences.getPlaybackSpeedArray(); float currentSpeed = controller.getCurrentPlaybackSpeedMultiplier(); int newSpeedIndex = 0; - while (newSpeedIndex < availableSpeeds.length && availableSpeeds[newSpeedIndex] < currentSpeed + EPSILON) { + while (newSpeedIndex < availableSpeeds.size() + && availableSpeeds.get(newSpeedIndex) < currentSpeed + EPSILON) { newSpeedIndex++; } float newSpeed; - if (availableSpeeds.length == 0) { + if (availableSpeeds.size() == 0) { newSpeed = 1.0f; - } else if (newSpeedIndex == availableSpeeds.length) { - newSpeed = availableSpeeds[0]; + } else if (newSpeedIndex == availableSpeeds.size()) { + newSpeed = availableSpeeds.get(0); } else { - newSpeed = availableSpeeds[newSpeedIndex]; + newSpeed = availableSpeeds.get(newSpeedIndex); } - PlaybackPreferences.setCurrentlyPlayingTemporaryPlaybackSpeed(newSpeed); - UserPreferences.setPlaybackSpeed(newSpeed); controller.setPlaybackSpeed(newSpeed); loadMediaInfo(); }); butPlaybackSpeed.setOnLongClickListener(v -> { - VariableSpeedDialog.showDialog(getContext()); + new VariableSpeedDialog().show(getChildFragmentManager(), null); return true; }); butPlaybackSpeed.setVisibility(View.VISIBLE); @@ -491,11 +488,11 @@ public class AudioPlayerFragment extends Fragment implements switch (item.getItemId()) { case R.id.disable_sleeptimer_item: // Fall-through case R.id.set_sleeptimer_item: - new SleepTimerDialog().show(getFragmentManager(), "SleepTimerDialog"); + new SleepTimerDialog().show(getChildFragmentManager(), "SleepTimerDialog"); return true; case R.id.audio_controls: - PlaybackControlsDialog dialog = PlaybackControlsDialog.newInstance(false); - dialog.show(getFragmentManager(), "playback_controls"); + PlaybackControlsDialog dialog = PlaybackControlsDialog.newInstance(); + dialog.show(getChildFragmentManager(), "playback_controls"); return true; case R.id.open_feed_item: if (feedItem != null) { 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 48c25552f..6911687dd 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/ChaptersFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/ChaptersFragment.java @@ -45,7 +45,6 @@ public class ChaptersFragment extends Fragment { RecyclerView recyclerView = root.findViewById(R.id.recyclerView); layoutManager = new LinearLayoutManager(getActivity()); recyclerView.setLayoutManager(layoutManager); - recyclerView.setHasFixedSize(true); recyclerView.addItemDecoration(new HorizontalDividerItemDecoration.Builder(getActivity()).build()); adapter = new ChaptersListAdapter(getActivity(), pos -> { diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java index 13941dd2c..55a5d744e 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java @@ -17,16 +17,21 @@ import de.danoeh.antennapod.R; import de.danoeh.antennapod.activity.MainActivity; import de.danoeh.antennapod.adapter.EpisodeItemListAdapter; import de.danoeh.antennapod.adapter.actionbutton.DeleteActionButton; +import de.danoeh.antennapod.core.event.DownloadEvent; import de.danoeh.antennapod.core.event.DownloadLogEvent; import de.danoeh.antennapod.core.event.FeedItemEvent; import de.danoeh.antennapod.core.event.PlaybackPositionEvent; import de.danoeh.antennapod.core.event.PlayerStatusEvent; import de.danoeh.antennapod.core.event.UnreadItemsUpdateEvent; import de.danoeh.antennapod.core.feed.FeedItem; +import de.danoeh.antennapod.core.service.download.DownloadService; import de.danoeh.antennapod.core.storage.DBReader; +import de.danoeh.antennapod.core.storage.DownloadRequester; import de.danoeh.antennapod.core.util.FeedItemUtil; +import de.danoeh.antennapod.core.util.download.AutoUpdateManager; import de.danoeh.antennapod.dialog.EpisodesApplyActionFragment; import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler; +import de.danoeh.antennapod.menuhandler.MenuItemUtils; import de.danoeh.antennapod.view.EmptyViewHandler; import de.danoeh.antennapod.view.EpisodeItemListRecyclerView; import de.danoeh.antennapod.view.viewholder.EpisodeItemViewHolder; @@ -58,6 +63,8 @@ public class CompletedDownloadsFragment extends Fragment { private Disposable disposable; private EmptyViewHandler emptyView; + private boolean isUpdatingFeeds = false; + @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { @@ -104,10 +111,11 @@ public class CompletedDownloadsFragment extends Fragment { } @Override - public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) { super.onCreateOptionsMenu(menu, inflater); inflater.inflate(R.menu.downloads_completed, menu); menu.findItem(R.id.episode_actions).setVisible(items.size() > 0); + isUpdatingFeeds = MenuItemUtils.updateRefreshMenuItem(menu, R.id.refresh_item, updateRefreshMenuItemChecker); } @Override @@ -116,10 +124,24 @@ public class CompletedDownloadsFragment extends Fragment { ((MainActivity) requireActivity()) .loadChildFragment(EpisodesApplyActionFragment.newInstance(items, ACTION_DELETE | ACTION_ADD_TO_QUEUE)); return true; + } else if (item.getItemId() == R.id.refresh_item) { + AutoUpdateManager.runImmediate(requireContext()); + return true; } return false; } + @Subscribe(sticky = true, threadMode = ThreadMode.MAIN) + public void onEventMainThread(DownloadEvent event) { + Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]"); + if (event.hasChangedFeedUpdateStatus(isUpdatingFeeds)) { + getActivity().invalidateOptionsMenu(); + } + } + + private final MenuItemUtils.UpdateRefreshMenuItemChecker updateRefreshMenuItemChecker = + () -> DownloadService.isRunning && DownloadRequester.getInstance().isDownloadingFeeds(); + @Override public boolean onContextItemSelected(@NonNull MenuItem item) { FeedItem selectedItem = adapter.getSelectedItem(); diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/DownloadLogFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/DownloadLogFragment.java index 312e3cb62..055d88285 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/DownloadLogFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/DownloadLogFragment.java @@ -1,11 +1,16 @@ package de.danoeh.antennapod.fragment; import android.app.Dialog; +import android.content.ClipData; +import android.content.ClipboardManager; +import android.content.Context; import android.content.res.TypedArray; import android.os.Bundle; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog; import androidx.fragment.app.ListFragment; -import androidx.core.view.MenuItemCompat; import android.util.Log; import android.view.Menu; import android.view.MenuInflater; @@ -17,14 +22,21 @@ import android.widget.TextView; import java.util.ArrayList; import java.util.List; +import com.google.android.material.snackbar.Snackbar; import de.danoeh.antennapod.R; +import de.danoeh.antennapod.activity.MainActivity; import de.danoeh.antennapod.adapter.DownloadLogAdapter; +import de.danoeh.antennapod.core.event.DownloadEvent; import de.danoeh.antennapod.core.event.DownloadLogEvent; import de.danoeh.antennapod.core.feed.Feed; import de.danoeh.antennapod.core.feed.FeedMedia; +import de.danoeh.antennapod.core.service.download.DownloadService; import de.danoeh.antennapod.core.service.download.DownloadStatus; import de.danoeh.antennapod.core.storage.DBReader; import de.danoeh.antennapod.core.storage.DBWriter; +import de.danoeh.antennapod.core.storage.DownloadRequester; +import de.danoeh.antennapod.core.util.download.AutoUpdateManager; +import de.danoeh.antennapod.menuhandler.MenuItemUtils; import de.danoeh.antennapod.view.EmptyViewHandler; import io.reactivex.Observable; import io.reactivex.android.schedulers.AndroidSchedulers; @@ -32,6 +44,7 @@ import io.reactivex.disposables.Disposable; import io.reactivex.schedulers.Schedulers; import org.greenrobot.eventbus.EventBus; import org.greenrobot.eventbus.Subscribe; +import org.greenrobot.eventbus.ThreadMode; /** * Shows the download log @@ -44,6 +57,8 @@ public class DownloadLogFragment extends ListFragment { private DownloadLogAdapter adapter; private Disposable disposable; + private boolean isUpdatingFeeds = false; + @Override public void onStart() { super.onStart(); @@ -65,7 +80,7 @@ public class DownloadLogFragment extends ListFragment { } @Override - public void onViewCreated(View view, Bundle savedInstanceState) { + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); // add padding final ListView lv = getListView(); @@ -93,11 +108,11 @@ public class DownloadLogFragment extends ListFragment { private void onFragmentLoaded() { setListShown(true); adapter.notifyDataSetChanged(); - getActivity().supportInvalidateOptionsMenu(); + getActivity().invalidateOptionsMenu(); } @Override - public void onListItemClick(ListView l, View v, int position, long id) { + public void onListItemClick(@NonNull ListView l, @NonNull View v, int position, long id) { super.onListItemClick(l, v, position, id); DownloadStatus status = adapter.getItem(position); @@ -119,10 +134,17 @@ public class DownloadLogFragment extends ListFragment { message = status.getReasonDetailed(); } + String messageFull = getString(R.string.download_error_details_message, message, url); AlertDialog.Builder builder = new AlertDialog.Builder(getContext()); builder.setTitle(R.string.download_error_details); - builder.setMessage(getString(R.string.download_error_details_message, message, url)); + builder.setMessage(messageFull); builder.setPositiveButton(android.R.string.ok, null); + builder.setNeutralButton(R.string.copy_to_clipboard, (dialog, which) -> { + ClipboardManager clipboard = (ClipboardManager) getContext().getSystemService(Context.CLIPBOARD_SERVICE); + ClipData clip = ClipData.newPlainText(getString(R.string.download_error_details), messageFull); + clipboard.setPrimaryClip(clip); + ((MainActivity) getActivity()).showSnackbarAbovePlayer(R.string.copied_to_clipboard, Snackbar.LENGTH_SHORT); + }); Dialog dialog = builder.show(); ((TextView) dialog.findViewById(android.R.id.message)).setTextIsSelectable(true); } @@ -150,20 +172,14 @@ public class DownloadLogFragment extends ListFragment { } @Override - public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { - if (!isAdded()) { - return; - } + public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) { super.onCreateOptionsMenu(menu, inflater); - MenuItem clearHistory = menu.add(Menu.NONE, R.id.clear_history_item, Menu.CATEGORY_CONTAINER, R.string.clear_history_label); - MenuItemCompat.setShowAsAction(clearHistory, MenuItemCompat.SHOW_AS_ACTION_IF_ROOM); - TypedArray drawables = getActivity().obtainStyledAttributes(new int[]{R.attr.ic_delete}); - clearHistory.setIcon(drawables.getDrawable(0)); - drawables.recycle(); + inflater.inflate(R.menu.downloads_log, menu); + isUpdatingFeeds = MenuItemUtils.updateRefreshMenuItem(menu, R.id.refresh_item, updateRefreshMenuItemChecker); } @Override - public void onPrepareOptionsMenu(Menu menu) { + public void onPrepareOptionsMenu(@NonNull Menu menu) { super.onPrepareOptionsMenu(menu); MenuItem menuItem = menu.findItem(R.id.clear_history_item); if (menuItem != null) { @@ -172,12 +188,15 @@ public class DownloadLogFragment extends ListFragment { } @Override - public boolean onOptionsItemSelected(MenuItem item) { + public boolean onOptionsItemSelected(@NonNull MenuItem item) { if (!super.onOptionsItemSelected(item)) { switch (item.getItemId()) { case R.id.clear_history_item: DBWriter.clearDownloadLog(); return true; + case R.id.refresh_item: + AutoUpdateManager.runImmediate(requireContext()); + return true; default: return false; } @@ -186,6 +205,17 @@ public class DownloadLogFragment extends ListFragment { } } + @Subscribe(sticky = true, threadMode = ThreadMode.MAIN) + public void onEventMainThread(DownloadEvent event) { + Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]"); + if (event.hasChangedFeedUpdateStatus(isUpdatingFeeds)) { + getActivity().invalidateOptionsMenu(); + } + } + + private final MenuItemUtils.UpdateRefreshMenuItemChecker updateRefreshMenuItemChecker = + () -> DownloadService.isRunning && DownloadRequester.getInstance().isDownloadingFeeds(); + private void loadItems() { if (disposable != null) { disposable.dispose(); 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 cf0c6db90..c173bf8ee 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/DownloadsFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/DownloadsFragment.java @@ -8,6 +8,7 @@ import android.view.View; import android.view.ViewGroup; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.widget.Toolbar; import androidx.fragment.app.Fragment; @@ -39,7 +40,9 @@ public class DownloadsFragment extends Fragment { private TabLayout tabLayout; @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + public View onCreateView(@NonNull LayoutInflater inflater, + @Nullable ViewGroup container, + @Nullable Bundle savedInstanceState) { super.onCreateView(inflater, container, savedInstanceState); View root = inflater.inflate(R.layout.pager_fragment, container, false); Toolbar toolbar = root.findViewById(R.id.toolbar); @@ -76,7 +79,7 @@ public class DownloadsFragment extends Fragment { } @Override - public void onViewCreated(View view, Bundle savedInstanceState) { + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); if (getArguments() != null) { int tab = getArguments().getInt(ARG_SELECTED_TAB); 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 29b6a1b16..44f6c8827 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java @@ -120,7 +120,7 @@ public abstract class EpisodesListFragment extends Fragment { } @Override - public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) { if (!isAdded()) { return; } @@ -131,7 +131,7 @@ public abstract class EpisodesListFragment extends Fragment { } @Override - public boolean onOptionsItemSelected(MenuItem item) { + public boolean onOptionsItemSelected(@NonNull MenuItem item) { if (!super.onOptionsItemSelected(item)) { switch (item.getItemId()) { case R.id.refresh_item: @@ -176,7 +176,7 @@ public abstract class EpisodesListFragment extends Fragment { } @Override - public boolean onContextItemSelected(MenuItem item) { + public boolean onContextItemSelected(@NonNull MenuItem item) { Log.d(TAG, "onContextItemSelected() called with: " + "item = [" + item + "]"); if (!getUserVisibleHint()) { return false; diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/FavoriteEpisodesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/FavoriteEpisodesFragment.java index 4f8b4f00c..d50be88c5 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/FavoriteEpisodesFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/FavoriteEpisodesFragment.java @@ -53,12 +53,14 @@ public class FavoriteEpisodesFragment extends EpisodesListFragment { ItemTouchHelper.SimpleCallback simpleItemTouchCallback = new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT) { @Override - public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) { + public boolean onMove(@NonNull RecyclerView recyclerView, + @NonNull RecyclerView.ViewHolder viewHolder, + @NonNull RecyclerView.ViewHolder target) { return false; } @Override - public void onSwiped(RecyclerView.ViewHolder viewHolder, int swipeDir) { + public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int swipeDir) { EpisodeItemViewHolder holder = (EpisodeItemViewHolder) viewHolder; Log.d(TAG, String.format("remove(%s)", holder.getFeedItem().getId())); 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 c58e6c15f..ae03b5032 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/FeedInfoFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/FeedInfoFragment.java @@ -3,6 +3,7 @@ package de.danoeh.antennapod.fragment; import android.content.ClipData; import android.content.Context; import android.content.Intent; +import android.content.res.Configuration; import android.graphics.LightingColorFilter; import android.net.Uri; import android.os.Bundle; @@ -47,9 +48,6 @@ import io.reactivex.MaybeOnSubscribe; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.Disposable; import io.reactivex.schedulers.Schedulers; -import org.apache.commons.lang3.StringUtils; -import org.jsoup.Jsoup; -import org.jsoup.nodes.Document; /** * Displays information about a feed. @@ -71,6 +69,8 @@ public class FeedInfoFragment extends Fragment { private TextView txtvUrl; private TextView txtvAuthorHeader; private ImageView imgvBackground; + private View infoContainer; + private View header; private Menu optionsMenu; private ToolbarIconTintManager iconTintManager; @@ -124,6 +124,8 @@ public class FeedInfoFragment extends Fragment { txtvTitle = root.findViewById(R.id.txtvTitle); txtvAuthorHeader = root.findViewById(R.id.txtvAuthor); imgvBackground = root.findViewById(R.id.imgvBackground); + header = root.findViewById(R.id.headerContainer); + infoContainer = root.findViewById(R.id.infoContainer); root.findViewById(R.id.butShowInfo).setVisibility(View.INVISIBLE); root.findViewById(R.id.butShowSettings).setVisibility(View.INVISIBLE); // https://github.com/bumptech/glide/issues/529 @@ -159,6 +161,15 @@ public class FeedInfoFragment extends Fragment { }, error -> Log.d(TAG, Log.getStackTraceString(error)), () -> { }); } + @Override + public void onConfigurationChanged(@NonNull Configuration newConfig) { + super.onConfigurationChanged(newConfig); + int horizontalSpacing = (int) getResources().getDimension(R.dimen.additional_horizontal_spacing); + header.setPadding(horizontalSpacing, header.getPaddingTop(), horizontalSpacing, header.getPaddingBottom()); + infoContainer.setPadding(horizontalSpacing, infoContainer.getPaddingTop(), + horizontalSpacing, infoContainer.getPaddingBottom()); + } + private void showFeed() { Log.d(TAG, "Language is " + feed.getLanguage()); Log.d(TAG, "Author is " + feed.getAuthor()); @@ -216,7 +227,7 @@ public class FeedInfoFragment extends Fragment { } @Override - public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) { super.onCreateOptionsMenu(menu, inflater); inflater.inflate(R.menu.feedinfo, menu); optionsMenu = menu; @@ -224,7 +235,7 @@ public class FeedInfoFragment extends Fragment { } @Override - public void onPrepareOptionsMenu(Menu menu) { + public void onPrepareOptionsMenu(@NonNull Menu menu) { super.onPrepareOptionsMenu(menu); menu.findItem(R.id.share_link_item).setVisible(feed != null && feed.getLink() != null); menu.findItem(R.id.visit_website_item).setVisible(feed != null && feed.getLink() != null @@ -232,7 +243,7 @@ public class FeedInfoFragment extends Fragment { } @Override - public boolean onOptionsItemSelected(MenuItem item) { + public boolean onOptionsItemSelected(@NonNull MenuItem item) { if (feed == null) { ((MainActivity) getActivity()).showSnackbarAbovePlayer( R.string.please_wait_for_data, Toast.LENGTH_LONG); 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 67433166c..965cfdc86 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/FeedItemlistFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/FeedItemlistFragment.java @@ -2,6 +2,8 @@ package de.danoeh.antennapod.fragment; import android.content.Context; import android.content.DialogInterface; +import android.content.res.Configuration; +import android.content.Intent; import android.graphics.LightingColorFilter; import android.os.Bundle; import android.util.Log; @@ -52,6 +54,7 @@ import de.danoeh.antennapod.core.glide.FastBlurTransformation; import de.danoeh.antennapod.core.service.download.DownloadService; import de.danoeh.antennapod.core.storage.DBReader; import de.danoeh.antennapod.core.storage.DBTasks; +import de.danoeh.antennapod.core.storage.DBWriter; import de.danoeh.antennapod.core.storage.DownloadRequestException; import de.danoeh.antennapod.core.storage.DownloadRequester; import de.danoeh.antennapod.core.util.FeedItemPermutors; @@ -60,6 +63,7 @@ import de.danoeh.antennapod.core.util.Optional; import de.danoeh.antennapod.core.util.ThemeUtils; import de.danoeh.antennapod.core.util.gui.MoreContentListFooterUtil; import de.danoeh.antennapod.dialog.EpisodesApplyActionFragment; +import de.danoeh.antennapod.dialog.FilterDialog; import de.danoeh.antennapod.dialog.RenameFeedDialog; import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler; import de.danoeh.antennapod.menuhandler.FeedMenuHandler; @@ -77,6 +81,7 @@ import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.ThreadMode; import java.util.List; +import java.util.Set; /** * Displays a list of FeedItems. @@ -98,6 +103,7 @@ public class FeedItemlistFragment extends Fragment implements AdapterView.OnItem private TextView txtvAuthor; private ImageButton butShowInfo; private ImageButton butShowSettings; + private View header; private Menu optionsMenu; private ToolbarIconTintManager iconTintManager; @@ -155,6 +161,7 @@ public class FeedItemlistFragment extends Fragment implements AdapterView.OnItem butShowSettings = root.findViewById(R.id.butShowSettings); txtvInformation = root.findViewById(R.id.txtvInformation); txtvFailure = root.findViewById(R.id.txtvFailure); + header = root.findViewById(R.id.headerContainer); AppBarLayout appBar = root.findViewById(R.id.appBar); CollapsingToolbarLayout collapsingToolbar = root.findViewById(R.id.collapsing_toolbar); @@ -221,7 +228,7 @@ public class FeedItemlistFragment extends Fragment implements AdapterView.OnItem }; @Override - public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) { if (!isAdded()) { return; } @@ -239,14 +246,21 @@ public class FeedItemlistFragment extends Fragment implements AdapterView.OnItem } @Override - public void onPrepareOptionsMenu(Menu menu) { + public void onPrepareOptionsMenu(@NonNull Menu menu) { if (feed != null) { FeedMenuHandler.onPrepareOptionsMenu(menu, feed); } } @Override - public boolean onOptionsItemSelected(MenuItem item) { + public void onConfigurationChanged(@NonNull Configuration newConfig) { + super.onConfigurationChanged(newConfig); + int horizontalSpacing = (int) getResources().getDimension(R.dimen.additional_horizontal_spacing); + header.setPadding(horizontalSpacing, header.getPaddingTop(), horizontalSpacing, header.getPaddingBottom()); + } + + @Override + public boolean onOptionsItemSelected(@NonNull MenuItem item) { if (!super.onOptionsItemSelected(item)) { if (feed == null) { ((MainActivity) getActivity()).showSnackbarAbovePlayer( @@ -405,7 +419,7 @@ public class FeedItemlistFragment extends Fragment implements AdapterView.OnItem private void updateSyncProgressBarVisibility() { if (isUpdatingFeed != updateRefreshMenuItemChecker.isRefreshing()) { - getActivity().supportInvalidateOptionsMenu(); + getActivity().invalidateOptionsMenu(); } if (!DownloadRequester.getInstance().isDownloadingFeeds()) { nextPageLoader.getRoot().setVisibility(View.GONE); @@ -429,7 +443,7 @@ public class FeedItemlistFragment extends Fragment implements AdapterView.OnItem adapter.updateItems(feed.getItems()); } - getActivity().supportInvalidateOptionsMenu(); + getActivity().invalidateOptionsMenu(); updateSyncProgressBarVisibility(); } @@ -454,8 +468,19 @@ public class FeedItemlistFragment extends Fragment implements AdapterView.OnItem RelativeLayout.LayoutParams p = (RelativeLayout.LayoutParams) txtvInformation.getLayoutParams(); p.addRule(RelativeLayout.BELOW, R.id.txtvFailure); } - txtvInformation.setText("{fa-info-circle} " + this.getString(R.string.filtered_label)); + txtvInformation.setText("{md-info-outline} " + this.getString(R.string.filtered_label)); Iconify.addIcons(txtvInformation); + txtvInformation.setOnClickListener((l) -> { + FilterDialog filterDialog = new FilterDialog(requireContext(), feed.getItemFilter()) { + @Override + protected void updateFilter(Set<String> filterValues) { + feed.setItemFilter(filterValues.toArray(new String[0])); + DBWriter.setFeedItemsFilter(feed.getId(), filterValues); + } + }; + + filterDialog.openDialog(); + }); txtvInformation.setVisibility(View.VISIBLE); } else { txtvInformation.setVisibility(View.GONE); @@ -482,6 +507,14 @@ public class FeedItemlistFragment extends Fragment implements AdapterView.OnItem ((MainActivity) getActivity()).loadChildFragment(fragment, TransitionEffect.SLIDE); } }); + txtvFailure.setOnClickListener(v -> { + Intent intent = new Intent(getContext(), MainActivity.class); + intent.putExtra(MainActivity.EXTRA_FRAGMENT_TAG, DownloadsFragment.TAG); + Bundle args = new Bundle(); + args.putInt(DownloadsFragment.ARG_SELECTED_TAB, DownloadsFragment.POS_LOG); + intent.putExtra(MainActivity.EXTRA_FRAGMENT_ARGS, args); + startActivity(intent); + }); headerCreated = true; } 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 8251e8716..209e5d0e1 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/FeedSettingsFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/FeedSettingsFragment.java @@ -66,7 +66,7 @@ public class FeedSettingsFragment extends Fragment { Toolbar toolbar = root.findViewById(R.id.toolbar); ((AppCompatActivity) getActivity()).setSupportActionBar(toolbar); - getFragmentManager().beginTransaction() + getParentFragmentManager().beginTransaction() .replace(R.id.settings_fragment_container, FeedSettingsPreferenceFragment.newInstance(feedId), "settings_fragment") .commitAllowingStateLoss(); 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 aaf0fc7d4..337c789fe 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java @@ -145,9 +145,7 @@ public class ItemFragment extends Fragment { } txtvDuration = layout.findViewById(R.id.txtvDuration); txtvPublished = layout.findViewById(R.id.txtvPublished); - if (Build.VERSION.SDK_INT >= 14) { // ellipsize is causing problems on old versions, see #448 - txtvTitle.setEllipsize(TextUtils.TruncateAt.END); - } + txtvTitle.setEllipsize(TextUtils.TruncateAt.END); webvDescription = layout.findViewById(R.id.webvDescription); webvDescription.setTimecodeSelectedListener(time -> { if (controller != null && item.getMedia() != null && controller.getMedia() != null diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ItemPagerFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ItemPagerFragment.java index c198ce258..3b1171df4 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/ItemPagerFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItemPagerFragment.java @@ -140,13 +140,11 @@ public class ItemPagerFragment extends Fragment { @Override public boolean onOptionsItemSelected(MenuItem menuItem) { - switch (menuItem.getItemId()) { - case R.id.open_podcast: - openPodcast(); - return true; - default: - return FeedItemMenuHandler.onMenuItemClicked(this, menuItem.getItemId(), item); + if (menuItem.getItemId() == R.id.open_podcast) { + openPodcast(); + return true; } + return FeedItemMenuHandler.onMenuItemClicked(this, menuItem.getItemId(), item); } @Subscribe(threadMode = ThreadMode.MAIN) 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 da5710936..8b7d2b886 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/NavDrawerFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/NavDrawerFragment.java @@ -37,6 +37,7 @@ import de.danoeh.antennapod.core.storage.DBReader; import de.danoeh.antennapod.core.storage.DBWriter; import de.danoeh.antennapod.core.util.FeedItemUtil; import de.danoeh.antennapod.core.util.IntentUtils; +import de.danoeh.antennapod.dialog.FeedFilterDialog; import de.danoeh.antennapod.dialog.RenameFeedDialog; import io.reactivex.Observable; import io.reactivex.android.schedulers.AndroidSchedulers; @@ -86,9 +87,8 @@ public class NavDrawerFragment extends Fragment implements AdapterView.OnItemCli registerForContextMenu(navList); updateSelection(); - root.findViewById(R.id.nav_settings).setOnClickListener(v -> { - startActivity(new Intent(getActivity(), PreferenceActivity.class)); - }); + root.findViewById(R.id.nav_settings).setOnClickListener(v -> + startActivity(new Intent(getActivity(), PreferenceActivity.class))); getContext().getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE) .registerOnSharedPreferenceChangeListener(this); return root; @@ -388,6 +388,9 @@ public class NavDrawerFragment extends Fragment implements AdapterView.OnItemCli startActivity(intent); } } + } else if (UserPreferences.getFeedFilter() != UserPreferences.FEED_FILTER_NONE + && navAdapter.showSubscriptionList) { + FeedFilterDialog.showDialog(requireContext()); } } diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/OnlineSearchFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/OnlineSearchFragment.java index d9c31f993..8ecb692a5 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/OnlineSearchFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/OnlineSearchFragment.java @@ -126,7 +126,7 @@ public class OnlineSearchFragment extends Fragment { super.onCreateOptionsMenu(menu, inflater); inflater.inflate(R.menu.online_search, menu); MenuItem searchItem = menu.findItem(R.id.action_search); - final SearchView sv = (SearchView) MenuItemCompat.getActionView(searchItem); + final SearchView sv = (SearchView) searchItem.getActionView(); sv.setQueryHint(getString(R.string.search_podcast_hint)); sv.setOnQueryTextListener(new SearchView.OnQueryTextListener() { @Override 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 dabff7269..db4bda1f5 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java @@ -153,7 +153,7 @@ public class PlaybackHistoryFragment extends Fragment { } super.onCreateOptionsMenu(menu, inflater); MenuItem clearHistory = menu.add(Menu.NONE, R.id.clear_history_item, Menu.CATEGORY_CONTAINER, R.string.clear_history_label); - MenuItemCompat.setShowAsAction(clearHistory, MenuItemCompat.SHOW_AS_ACTION_IF_ROOM); + clearHistory.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); TypedArray drawables = getActivity().obtainStyledAttributes(new int[]{R.attr.ic_delete}); clearHistory.setIcon(drawables.getDrawable(0)); drawables.recycle(); @@ -171,13 +171,11 @@ public class PlaybackHistoryFragment extends Fragment { @Override public boolean onOptionsItemSelected(MenuItem item) { if (!super.onOptionsItemSelected(item)) { - switch (item.getItemId()) { - case R.id.clear_history_item: - DBWriter.clearPlaybackHistory(); - return true; - default: - return false; + if (item.getItemId() == R.id.clear_history_item) { + DBWriter.clearPlaybackHistory(); + return true; } + return false; } else { return true; } @@ -196,18 +194,18 @@ public class PlaybackHistoryFragment extends Fragment { @Subscribe(threadMode = ThreadMode.MAIN) public void onHistoryUpdated(PlaybackHistoryEvent event) { loadItems(); - getActivity().supportInvalidateOptionsMenu(); + getActivity().invalidateOptionsMenu(); } @Subscribe(threadMode = ThreadMode.MAIN) public void onPlayerStatusChanged(PlayerStatusEvent event) { loadItems(); - getActivity().supportInvalidateOptionsMenu(); + getActivity().invalidateOptionsMenu(); } private void onFragmentLoaded() { adapter.notifyDataSetChanged(); - getActivity().supportInvalidateOptionsMenu(); + getActivity().invalidateOptionsMenu(); } private void loadItems() { 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 49c53627f..da156f904 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java @@ -16,7 +16,6 @@ import android.widget.ProgressBar; import android.widget.TextView; import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AppCompatActivity; -import androidx.core.view.ViewCompat; import androidx.fragment.app.Fragment; import androidx.recyclerview.widget.ItemTouchHelper; import androidx.recyclerview.widget.RecyclerView; @@ -136,6 +135,7 @@ public class QueueFragment extends Fragment { recyclerAdapter.notifyItemInserted(event.position); break; case SET_QUEUE: + case SORTED: //Deliberate fall-through queue = event.items; recyclerAdapter.notifyDataSetChanged(); break; @@ -149,10 +149,6 @@ public class QueueFragment extends Fragment { queue.clear(); recyclerAdapter.notifyDataSetChanged(); break; - case SORTED: - queue = event.items; - recyclerAdapter.notifyDataSetChanged(); - break; case MOVED: return; } @@ -216,7 +212,7 @@ public class QueueFragment extends Fragment { public void onPlayerStatusChanged(PlayerStatusEvent event) { loadItems(false); if (isUpdatingFeeds != updateRefreshMenuItemChecker.isRefreshing()) { - getActivity().supportInvalidateOptionsMenu(); + getActivity().invalidateOptionsMenu(); } } @@ -225,7 +221,7 @@ public class QueueFragment extends Fragment { // Sent when playback position is reset loadItems(false); if (isUpdatingFeeds != updateRefreshMenuItemChecker.isRefreshing()) { - getActivity().supportInvalidateOptionsMenu(); + getActivity().invalidateOptionsMenu(); } } @@ -340,10 +336,10 @@ public class QueueFragment extends Fragment { SortOrder sortOrder = UserPreferences.getQueueKeepSortedOrder(); DBWriter.reorderQueue(sortOrder, true); if (recyclerAdapter != null) { - recyclerAdapter.setLocked(true); + recyclerAdapter.updateDragDropEnabled(); } } else if (recyclerAdapter != null) { - recyclerAdapter.setLocked(UserPreferences.isQueueLocked()); + recyclerAdapter.updateDragDropEnabled(); } getActivity().invalidateOptionsMenu(); return true; @@ -384,9 +380,9 @@ public class QueueFragment extends Fragment { private void setQueueLocked(boolean locked) { UserPreferences.setQueueLocked(locked); - getActivity().supportInvalidateOptionsMenu(); + getActivity().invalidateOptionsMenu(); if (recyclerAdapter != null) { - recyclerAdapter.setLocked(locked); + recyclerAdapter.updateDragDropEnabled(); } if (locked) { ((MainActivity) getActivity()).showSnackbarAbovePlayer(R.string.queue_locked, Snackbar.LENGTH_SHORT); @@ -572,7 +568,7 @@ public class QueueFragment extends Fragment { // we need to refresh the options menu because it sometimes // needs data that may have just been loaded. - getActivity().supportInvalidateOptionsMenu(); + getActivity().invalidateOptionsMenu(); refreshInfoBar(); } diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/RunningDownloadsFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/RunningDownloadsFragment.java index ddcf09992..ca9fba694 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/RunningDownloadsFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/RunningDownloadsFragment.java @@ -1,8 +1,13 @@ package de.danoeh.antennapod.fragment; import android.os.Bundle; + +import androidx.annotation.NonNull; import androidx.fragment.app.ListFragment; import android.util.Log; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; import android.view.View; import android.widget.ListView; import android.widget.Toast; @@ -21,10 +26,13 @@ import de.danoeh.antennapod.core.event.DownloaderUpdate; import de.danoeh.antennapod.core.feed.FeedMedia; import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.core.service.download.DownloadRequest; +import de.danoeh.antennapod.core.service.download.DownloadService; import de.danoeh.antennapod.core.service.download.Downloader; import de.danoeh.antennapod.core.storage.DBReader; import de.danoeh.antennapod.core.storage.DBWriter; import de.danoeh.antennapod.core.storage.DownloadRequester; +import de.danoeh.antennapod.core.util.download.AutoUpdateManager; +import de.danoeh.antennapod.menuhandler.MenuItemUtils; import de.danoeh.antennapod.view.EmptyViewHandler; import org.greenrobot.eventbus.ThreadMode; @@ -38,6 +46,8 @@ public class RunningDownloadsFragment extends ListFragment { private DownloadlistAdapter adapter; private List<Downloader> downloaderList = new ArrayList<>(); + private boolean isUpdatingFeeds = false; + @Override public void onViewCreated(View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); @@ -66,6 +76,12 @@ public class RunningDownloadsFragment extends ListFragment { } @Override + public void onResume() { + super.onResume(); + setHasOptionsMenu(true); + } + + @Override public void onStop() { super.onStop(); EventBus.getDefault().unregister(this); @@ -77,6 +93,33 @@ public class RunningDownloadsFragment extends ListFragment { setListAdapter(null); } + @Override + public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) { + super.onCreateOptionsMenu(menu, inflater); + inflater.inflate(R.menu.downloads_running, menu); + isUpdatingFeeds = MenuItemUtils.updateRefreshMenuItem(menu, R.id.refresh_item, updateRefreshMenuItemChecker); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + if (item.getItemId() == R.id.refresh_item) { + AutoUpdateManager.runImmediate(requireContext()); + return true; + } + return false; + } + + @Subscribe(sticky = true, threadMode = ThreadMode.MAIN) + public void onEventMainThread(DownloadEvent event) { + Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]"); + if (event.hasChangedFeedUpdateStatus(isUpdatingFeeds)) { + getActivity().invalidateOptionsMenu(); + } + } + + private final MenuItemUtils.UpdateRefreshMenuItemChecker updateRefreshMenuItemChecker = + () -> DownloadService.isRunning && DownloadRequester.getInstance().isDownloadingFeeds(); + @Subscribe(sticky = true, threadMode = ThreadMode.MAIN) public void onEvent(DownloadEvent event) { Log.d(TAG, "onEvent() called with: " + "event = [" + event + "]"); diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java index 0d33c1282..0c03d407e 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java @@ -124,7 +124,6 @@ public class SearchFragment extends Fragment { LinearLayoutManager layoutManagerFeeds = new LinearLayoutManager(getActivity()); layoutManagerFeeds.setOrientation(RecyclerView.HORIZONTAL); recyclerViewFeeds.setLayoutManager(layoutManagerFeeds); - recyclerViewFeeds.setHasFixedSize(true); adapterFeeds = new FeedSearchResultAdapter((MainActivity) getActivity()); recyclerViewFeeds.setAdapter(adapterFeeds); @@ -174,7 +173,7 @@ public class SearchFragment extends Fragment { @Override public boolean onMenuItemActionCollapse(MenuItem item) { - getFragmentManager().popBackStack(); + getParentFragmentManager().popBackStack(); return true; } }); 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 ba5d44b4d..33e4cb764 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/SubscriptionFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/SubscriptionFragment.java @@ -19,8 +19,10 @@ import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.GridView; +import android.widget.TextView; import com.google.android.material.floatingactionbutton.FloatingActionButton; +import com.joanzapata.iconify.Iconify; import java.util.concurrent.Callable; @@ -34,6 +36,7 @@ import de.danoeh.antennapod.core.event.FeedListUpdateEvent; import de.danoeh.antennapod.core.event.UnreadItemsUpdateEvent; import de.danoeh.antennapod.core.feed.Feed; import de.danoeh.antennapod.core.preferences.PlaybackPreferences; +import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.core.service.download.DownloadService; import de.danoeh.antennapod.core.service.playback.PlaybackService; import de.danoeh.antennapod.core.storage.DBReader; @@ -42,6 +45,7 @@ import de.danoeh.antennapod.core.storage.DownloadRequester; import de.danoeh.antennapod.core.util.FeedItemUtil; import de.danoeh.antennapod.core.util.IntentUtils; import de.danoeh.antennapod.core.util.download.AutoUpdateManager; +import de.danoeh.antennapod.dialog.FeedFilterDialog; import de.danoeh.antennapod.dialog.RenameFeedDialog; import de.danoeh.antennapod.menuhandler.MenuItemUtils; import de.danoeh.antennapod.view.EmptyViewHandler; @@ -68,6 +72,7 @@ public class SubscriptionFragment extends Fragment { private FloatingActionButton subscriptionAddButton; private ProgressBar progressBar; private EmptyViewHandler emptyView; + private TextView feedsFilteredMsg; private int mPosition = -1; private boolean isUpdatingFeeds = false; @@ -90,10 +95,13 @@ public class SubscriptionFragment extends Fragment { View root = inflater.inflate(R.layout.fragment_subscriptions, container, false); ((AppCompatActivity) getActivity()).setSupportActionBar(root.findViewById(R.id.toolbar)); subscriptionGridLayout = root.findViewById(R.id.subscriptions_grid); - subscriptionGridLayout.setNumColumns(prefs.getInt(PREF_NUM_COLUMNS, 3)); + subscriptionGridLayout.setNumColumns(prefs.getInt(PREF_NUM_COLUMNS, getDefaultNumOfColumns())); registerForContextMenu(subscriptionGridLayout); subscriptionAddButton = root.findViewById(R.id.subscriptions_add); progressBar = root.findViewById(R.id.progLoading); + + feedsFilteredMsg = root.findViewById(R.id.feeds_filtered_message); + feedsFilteredMsg.setOnClickListener((l) -> FeedFilterDialog.showDialog(requireContext())); return root; } @@ -102,7 +110,7 @@ public class SubscriptionFragment extends Fragment { super.onCreateOptionsMenu(menu, inflater); inflater.inflate(R.menu.subscriptions, menu); - int columns = prefs.getInt(PREF_NUM_COLUMNS, 3); + int columns = prefs.getInt(PREF_NUM_COLUMNS, getDefaultNumOfColumns()); menu.findItem(R.id.subscription_num_columns_2).setChecked(columns == 2); menu.findItem(R.id.subscription_num_columns_3).setChecked(columns == 3); menu.findItem(R.id.subscription_num_columns_4).setChecked(columns == 4); @@ -120,6 +128,9 @@ public class SubscriptionFragment extends Fragment { case R.id.refresh_item: AutoUpdateManager.runImmediate(requireContext()); return true; + case R.id.subscriptions_filter: + FeedFilterDialog.showDialog(requireContext()); + return true; case R.id.subscription_num_columns_2: setColumnNumber(2); return true; @@ -198,6 +209,18 @@ public class SubscriptionFragment extends Fragment { emptyView.updateVisibility(); progressBar.setVisibility(View.GONE); }, error -> Log.e(TAG, Log.getStackTraceString(error))); + + if (UserPreferences.getFeedFilter() != UserPreferences.FEED_FILTER_NONE) { + feedsFilteredMsg.setText("{md-info-outline} " + getString(R.string.subscriptions_are_filtered)); + Iconify.addIcons(feedsFilteredMsg); + feedsFilteredMsg.setVisibility(View.VISIBLE); + } else { + feedsFilteredMsg.setVisibility(View.GONE); + } + } + + private int getDefaultNumOfColumns() { + return getResources().getInteger(R.integer.subscriptions_default_num_of_columns); } @Override 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 e1c85a2d3..d5cac07f1 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 @@ -56,9 +56,9 @@ public abstract class PodcastListFragment extends Fragment { super.onCreateOptionsMenu(menu, inflater); inflater.inflate(R.menu.gpodder_podcasts, menu); MenuItem searchItem = menu.findItem(R.id.action_search); - final SearchView sv = (SearchView) MenuItemCompat.getActionView(searchItem); + final SearchView sv = (SearchView) searchItem.getActionView(); sv.setQueryHint(getString(R.string.gpodnet_search_hint)); - sv.setOnQueryTextListener(new androidx.appcompat.widget.SearchView.OnQueryTextListener() { + sv.setOnQueryTextListener(new SearchView.OnQueryTextListener() { @Override public boolean onQueryTextSubmit(String s) { sv.clearFocus(); diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/SearchListFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/SearchListFragment.java index 80f1a6ae0..72a752bf1 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/SearchListFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/SearchListFragment.java @@ -48,7 +48,7 @@ public class SearchListFragment extends PodcastListFragment { super.onCreateOptionsMenu(menu, inflater); // parent already inflated menu MenuItem searchItem = menu.findItem(R.id.action_search); - final SearchView sv = (SearchView) MenuItemCompat.getActionView(searchItem); + final SearchView sv = (SearchView) searchItem.getActionView(); sv.setQueryHint(getString(R.string.gpodnet_search_hint)); sv.setQuery(query, false); sv.setOnQueryTextListener(new SearchView.OnQueryTextListener() { 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 a7a0781ce..53a31b68d 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 @@ -38,7 +38,7 @@ public class TagListFragment extends ListFragment { super.onCreateOptionsMenu(menu, inflater); inflater.inflate(R.menu.gpodder_podcasts, menu); MenuItem searchItem = menu.findItem(R.id.action_search); - final SearchView sv = (SearchView) MenuItemCompat.getActionView(searchItem); + final SearchView sv = (SearchView) searchItem.getActionView(); sv.setQueryHint(getString(R.string.gpodnet_search_hint)); sv.setOnQueryTextListener(new SearchView.OnQueryTextListener() { @Override diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AboutFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AboutFragment.java index 0fa7bd4bb..eb57972a1 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AboutFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AboutFragment.java @@ -28,12 +28,12 @@ public class AboutFragment extends PreferenceFragmentCompat { return true; }); findPreference("about_developers").setOnPreferenceClickListener((preference) -> { - getFragmentManager().beginTransaction().replace(R.id.content, new AboutDevelopersFragment()) + getParentFragmentManager().beginTransaction().replace(R.id.content, new AboutDevelopersFragment()) .addToBackStack(getString(R.string.developers)).commit(); return true; }); findPreference("about_translators").setOnPreferenceClickListener((preference) -> { - getFragmentManager().beginTransaction().replace(R.id.content, new AboutTranslatorsFragment()) + getParentFragmentManager().beginTransaction().replace(R.id.content, new AboutTranslatorsFragment()) .addToBackStack(getString(R.string.translators)).commit(); return true; }); @@ -42,7 +42,7 @@ public class AboutFragment extends PreferenceFragmentCompat { return true; }); findPreference("about_licenses").setOnPreferenceClickListener((preference) -> { - getFragmentManager().beginTransaction().replace(R.id.content, new AboutLicensesFragment()) + getParentFragmentManager().beginTransaction().replace(R.id.content, new AboutLicensesFragment()) .addToBackStack(getString(R.string.translators)).commit(); return true; }); 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 6b15e4301..064c4b3bc 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AutoDownloadPreferencesFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AutoDownloadPreferencesFragment.java @@ -1,5 +1,6 @@ package de.danoeh.antennapod.fragment.preferences; +import android.annotation.SuppressLint; import android.app.Activity; import android.content.Context; import android.content.res.Resources; @@ -85,6 +86,7 @@ public class AutoDownloadPreferencesFragment extends PreferenceFragmentCompat { return val == null ? "" : val; } + @SuppressLint("MissingPermission") // getConfiguredNetworks needs location permission starting with API 29 private void buildAutodownloadSelectedNetworksPreference() { if (Build.VERSION.SDK_INT >= 29) { return; 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 8f8b4675d..546e12e65 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 @@ -3,9 +3,11 @@ package de.danoeh.antennapod.fragment.preferences; import android.app.Activity; import android.content.SharedPreferences; import android.os.Bundle; +import androidx.core.text.HtmlCompat; import androidx.preference.Preference; import androidx.preference.PreferenceFragmentCompat; -import android.text.Html; + +import android.text.Spanned; import android.text.format.DateUtils; import android.widget.Toast; import com.google.android.material.snackbar.Snackbar; @@ -115,7 +117,8 @@ public class GpodderPreferencesFragment extends PreferenceFragmentCompat { String format = getActivity().getString(R.string.pref_gpodnet_login_status); String summary = String.format(format, GpodnetPreferences.getUsername(), GpodnetPreferences.getDeviceID()); - findPreference(PREF_GPODNET_LOGOUT).setSummary(Html.fromHtml(summary)); + Spanned formattedSummary = HtmlCompat.fromHtml(summary, HtmlCompat.FROM_HTML_MODE_LEGACY); + findPreference(PREF_GPODNET_LOGOUT).setSummary(formattedSummary); updateLastGpodnetSyncReport(SyncService.isLastSyncSuccessful(getContext()), SyncService.getLastSyncAttempt(getContext())); } else { diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/ImportExportPreferencesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/ImportExportPreferencesFragment.java index f3b4d3003..5eb4a3aea 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/ImportExportPreferencesFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/ImportExportPreferencesFragment.java @@ -38,6 +38,7 @@ import java.io.FileOutputStream; import java.text.SimpleDateFormat; import java.util.Date; import java.util.List; +import java.util.Locale; public class ImportExportPreferencesFragment extends PreferenceFragmentCompat { private static final String TAG = "ImportExPrefFragment"; @@ -86,9 +87,7 @@ public class ImportExportPreferencesFragment extends PreferenceFragmentCompat { } private String dateStampFilename(String fname) { - return String.format(fname, - new SimpleDateFormat("yyyy-MM-dd") - .format(new Date())); + return String.format(fname, new SimpleDateFormat("yyyy-MM-dd", Locale.US).format(new Date())); } private void setupStorageScreen() { diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/MainPreferencesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/MainPreferencesFragment.java index 406585808..0409ce11b 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/MainPreferencesFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/MainPreferencesFragment.java @@ -62,14 +62,14 @@ public class MainPreferencesFragment extends PreferenceFragmentCompat { findPreference(PREF_ABOUT).setOnPreferenceClickListener( preference -> { - getFragmentManager().beginTransaction().replace(R.id.content, new AboutFragment()) + getParentFragmentManager().beginTransaction().replace(R.id.content, new AboutFragment()) .addToBackStack(getString(R.string.about_pref)).commit(); return true; } ); findPreference(STATISTICS).setOnPreferenceClickListener( preference -> { - getFragmentManager().beginTransaction().replace(R.id.content, new StatisticsFragment()) + getParentFragmentManager().beginTransaction().replace(R.id.content, new StatisticsFragment()) .addToBackStack(getString(R.string.statistics_label)).commit(); return true; } diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/PlaybackPreferencesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/PlaybackPreferencesFragment.java index 741080cf1..f07e59afe 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/PlaybackPreferencesFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/PlaybackPreferencesFragment.java @@ -45,7 +45,7 @@ public class PlaybackPreferencesFragment extends PreferenceFragmentCompat { final Activity activity = getActivity(); findPreference(PREF_PLAYBACK_SPEED_LAUNCHER).setOnPreferenceClickListener(preference -> { - VariableSpeedDialog.showDialog(activity); + new VariableSpeedDialog().show(getChildFragmentManager(), null); return true; }); findPreference(PREF_PLAYBACK_REWIND_DELTA_LAUNCHER).setOnPreferenceClickListener(preference -> { @@ -116,7 +116,7 @@ public class PlaybackPreferencesFragment extends PreferenceFragmentCompat { if(x == 0) { entries[x] = res.getString(R.string.pref_smart_mark_as_played_disabled); } else { - Integer v = Integer.parseInt(values[x]); + int v = Integer.parseInt(values[x]); if(v < 60) { entries[x] = res.getQuantityString(R.plurals.time_seconds_quantified, v, v); } else { 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 a44623f48..4596fc90e 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 @@ -12,6 +12,7 @@ import de.danoeh.antennapod.R; import de.danoeh.antennapod.activity.MainActivity; import de.danoeh.antennapod.activity.PreferenceActivity; import de.danoeh.antennapod.core.preferences.UserPreferences; +import de.danoeh.antennapod.dialog.FeedFilterDialog; import de.danoeh.antennapod.fragment.NavDrawerFragment; import org.apache.commons.lang3.ArrayUtils; @@ -75,6 +76,12 @@ public class UserInterfacePreferencesFragment extends PreferenceFragmentCompat { return true; }); + findPreference(UserPreferences.PREF_FILTER_FEED) + .setOnPreferenceClickListener((preference -> { + FeedFilterDialog.showDialog(requireContext()); + return true; + })); + if (Build.VERSION.SDK_INT >= 26) { findPreference(UserPreferences.PREF_EXPANDED_NOTIFICATION).setVisible(false); } diff --git a/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedItemMenuHandler.java b/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedItemMenuHandler.java index 1f15f66ec..bddafb75e 100644 --- a/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedItemMenuHandler.java +++ b/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedItemMenuHandler.java @@ -2,13 +2,14 @@ package de.danoeh.antennapod.menuhandler; import android.content.Context; import android.os.Handler; -import androidx.annotation.NonNull; -import com.google.android.material.snackbar.Snackbar; -import androidx.fragment.app.Fragment; import android.util.Log; import android.view.Menu; import android.view.MenuItem; +import com.google.android.material.snackbar.Snackbar; + +import androidx.annotation.NonNull; +import androidx.fragment.app.Fragment; import de.danoeh.antennapod.R; import de.danoeh.antennapod.activity.MainActivity; import de.danoeh.antennapod.core.feed.FeedItem; @@ -23,6 +24,7 @@ import de.danoeh.antennapod.core.sync.model.EpisodeAction; import de.danoeh.antennapod.core.util.FeedItemUtil; import de.danoeh.antennapod.core.util.IntentUtils; import de.danoeh.antennapod.core.util.ShareUtils; +import de.danoeh.antennapod.dialog.ShareDialog; /** * Handles interactions with the FeedItemMenu. @@ -61,20 +63,9 @@ public class FeedItemMenuHandler { } if (!ShareUtils.hasLinkToShare(selectedItem)) { setItemVisibility(menu, R.id.visit_website_item, false); - setItemVisibility(menu, R.id.share_link_item, false); - setItemVisibility(menu, R.id.share_link_with_position_item, false); - } - if (!hasMedia || selectedItem.getMedia().getDownload_url() == null) { - setItemVisibility(menu, R.id.share_download_url_item, false); - setItemVisibility(menu, R.id.share_download_url_with_position_item, false); - } - if(!hasMedia || selectedItem.getMedia().getPosition() <= 0) { - setItemVisibility(menu, R.id.share_download_url_with_position_item, false); - setItemVisibility(menu, R.id.share_link_with_position_item, false); } boolean fileDownloaded = hasMedia && selectedItem.getMedia().fileExists(); - setItemVisibility(menu, R.id.share_file, fileDownloaded); setItemVisibility(menu, R.id.remove_new_flag_item, selectedItem.isNew()); if (selectedItem.isPlayed()) { @@ -243,20 +234,9 @@ public class FeedItemMenuHandler { case R.id.visit_website_item: IntentUtils.openInBrowser(context, FeedItemUtil.getLinkWithFallback(selectedItem)); break; - case R.id.share_link_item: - ShareUtils.shareFeedItemLink(context, selectedItem); - break; - case R.id.share_download_url_item: - ShareUtils.shareFeedItemDownloadLink(context, selectedItem); - break; - case R.id.share_link_with_position_item: - ShareUtils.shareFeedItemLink(context, selectedItem, true); - break; - case R.id.share_download_url_with_position_item: - ShareUtils.shareFeedItemDownloadLink(context, selectedItem, true); - break; - case R.id.share_file: - ShareUtils.shareFeedItemFile(context, selectedItem.getMedia()); + case R.id.share_item: + ShareDialog shareDialog = ShareDialog.newInstance(selectedItem); + shareDialog.show((fragment.getActivity().getSupportFragmentManager()), "ShareEpisodeDialog"); break; default: Log.d(TAG, "Unknown menuItemId: " + menuItemId); 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 58d562616..83d90f98b 100644 --- a/app/src/main/java/de/danoeh/antennapod/view/EpisodeItemListRecyclerView.java +++ b/app/src/main/java/de/danoeh/antennapod/view/EpisodeItemListRecyclerView.java @@ -2,6 +2,7 @@ package de.danoeh.antennapod.view; import android.content.Context; import android.content.SharedPreferences; +import android.content.res.Configuration; import android.util.AttributeSet; import android.view.View; import androidx.appcompat.view.ContextThemeWrapper; @@ -39,6 +40,14 @@ public class EpisodeItemListRecyclerView extends RecyclerView { setLayoutManager(layoutManager); setHasFixedSize(true); addItemDecoration(new HorizontalDividerItemDecoration.Builder(getContext()).build()); + setClipToPadding(false); + } + + @Override + protected void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + int horizontalSpacing = (int) getResources().getDimension(R.dimen.additional_horizontal_spacing); + setPadding(horizontalSpacing, getPaddingTop(), horizontalSpacing, getPaddingBottom()); } public void saveScrollPosition(String tag) { diff --git a/app/src/main/java/de/danoeh/antennapod/view/ItemOffsetDecoration.java b/app/src/main/java/de/danoeh/antennapod/view/ItemOffsetDecoration.java new file mode 100644 index 000000000..4a1267d81 --- /dev/null +++ b/app/src/main/java/de/danoeh/antennapod/view/ItemOffsetDecoration.java @@ -0,0 +1,24 @@ +package de.danoeh.antennapod.view; + +import android.content.Context; +import android.graphics.Rect; +import android.view.View; +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; + +/** + * Source: https://stackoverflow.com/a/30794046 + */ +public class ItemOffsetDecoration extends RecyclerView.ItemDecoration { + private final int itemOffset; + + public ItemOffsetDecoration(@NonNull Context context, int itemOffsetDp) { + itemOffset = (int) (itemOffsetDp * context.getResources().getDisplayMetrics().density); + } + + @Override + public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { + super.getItemOffsets(outRect, view, parent, state); + outRect.set(itemOffset, itemOffset, itemOffset, itemOffset); + } +} diff --git a/app/src/main/java/de/danoeh/antennapod/view/NestedScrollableHost.java b/app/src/main/java/de/danoeh/antennapod/view/NestedScrollableHost.java index a4daa9109..0e1846f1c 100644 --- a/app/src/main/java/de/danoeh/antennapod/view/NestedScrollableHost.java +++ b/app/src/main/java/de/danoeh/antennapod/view/NestedScrollableHost.java @@ -54,7 +54,7 @@ class NestedScrollableHost extends FrameLayout { touchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); } - private int touchSlop = 0; + private int touchSlop; private float initialX = 0f; private float initialY = 0f; diff --git a/app/src/main/java/de/danoeh/antennapod/view/PlaybackSpeedSeekBar.java b/app/src/main/java/de/danoeh/antennapod/view/PlaybackSpeedSeekBar.java new file mode 100644 index 000000000..47797e4a4 --- /dev/null +++ b/app/src/main/java/de/danoeh/antennapod/view/PlaybackSpeedSeekBar.java @@ -0,0 +1,86 @@ +package de.danoeh.antennapod.view; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.View; +import android.widget.FrameLayout; +import android.widget.SeekBar; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.core.util.Consumer; +import de.danoeh.antennapod.R; +import de.danoeh.antennapod.core.util.playback.PlaybackController; +import de.danoeh.antennapod.dialog.VariableSpeedDialog; + +public class PlaybackSpeedSeekBar extends FrameLayout { + private SeekBar seekBar; + private PlaybackController controller; + private Consumer<Float> progressChangedListener; + + public PlaybackSpeedSeekBar(@NonNull Context context) { + super(context); + setup(); + } + + public PlaybackSpeedSeekBar(@NonNull Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + setup(); + } + + public PlaybackSpeedSeekBar(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + setup(); + } + + private void setup() { + View.inflate(getContext(), R.layout.playback_speed_seek_bar, this); + seekBar = findViewById(R.id.playback_speed); + findViewById(R.id.butDecSpeed).setOnClickListener(v -> seekBar.setProgress(seekBar.getProgress() - 2)); + findViewById(R.id.butIncSpeed).setOnClickListener(v -> seekBar.setProgress(seekBar.getProgress() + 2)); + + seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { + @Override + public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { + if (controller != null && controller.canSetPlaybackSpeed()) { + float playbackSpeed = (progress + 10) / 20.0f; + controller.setPlaybackSpeed(playbackSpeed); + + if (progressChangedListener != null) { + progressChangedListener.accept(playbackSpeed); + } + } else if (fromUser) { + seekBar.post(() -> updateSpeed()); + } + } + + @Override + public void onStartTrackingTouch(SeekBar seekBar) { + if (controller != null && !controller.canSetPlaybackSpeed()) { + VariableSpeedDialog.showGetPluginDialog(getContext()); + } + } + + @Override + public void onStopTrackingTouch(SeekBar seekBar) { + } + }); + } + + public void updateSpeed() { + if (controller != null) { + seekBar.setProgress(Math.round((20 * controller.getCurrentPlaybackSpeedMultiplier()) - 10)); + } + } + + public void setController(PlaybackController controller) { + this.controller = controller; + updateSpeed(); + if (progressChangedListener != null && controller != null) { + progressChangedListener.accept(controller.getCurrentPlaybackSpeedMultiplier()); + } + } + + public void setProgressChangedListener(Consumer<Float> progressChangedListener) { + this.progressChangedListener = progressChangedListener; + } +} diff --git a/app/src/main/java/de/danoeh/antennapod/view/RecursiveRadioGroup.java b/app/src/main/java/de/danoeh/antennapod/view/RecursiveRadioGroup.java new file mode 100644 index 000000000..ee5e7c51d --- /dev/null +++ b/app/src/main/java/de/danoeh/antennapod/view/RecursiveRadioGroup.java @@ -0,0 +1,67 @@ +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/ToolbarIconTintManager.java b/app/src/main/java/de/danoeh/antennapod/view/ToolbarIconTintManager.java index dcf8ff20d..163100fff 100644 --- a/app/src/main/java/de/danoeh/antennapod/view/ToolbarIconTintManager.java +++ b/app/src/main/java/de/danoeh/antennapod/view/ToolbarIconTintManager.java @@ -33,11 +33,15 @@ public abstract class ToolbarIconTintManager implements AppBarLayout.OnOffsetCha public void updateTint() { if (isTinted) { doTint(new ContextThemeWrapper(context, R.style.Theme_AntennaPod_Dark)); - toolbar.getNavigationIcon().setColorFilter(0xffffffff, PorterDuff.Mode.SRC_ATOP); + if (toolbar.getNavigationIcon() != null) { // Tablets do not show a navigation icon + toolbar.getNavigationIcon().setColorFilter(0xffffffff, PorterDuff.Mode.SRC_ATOP); + } toolbar.getOverflowIcon().setColorFilter(0xffffffff, PorterDuff.Mode.SRC_ATOP); } else { doTint(context); - toolbar.getNavigationIcon().clearColorFilter(); + if (toolbar.getNavigationIcon() != null) { // Tablets do not show a navigation icon + toolbar.getNavigationIcon().clearColorFilter(); + } toolbar.getOverflowIcon().clearColorFilter(); } } |