From cedfb27ccaef74d954bb3a8f0ea471be63a1375c Mon Sep 17 00:00:00 2001 From: Ebrahim Byagowi Date: Wed, 8 Apr 2020 15:32:51 +0430 Subject: [PATCH 01/21] Port AudioPlayer's viewpager to ViewPager2, improve RTL support --- app/build.gradle | 1 + .../fragment/AudioPlayerFragment.java | 45 ++++++++++--------- .../antennapod/view/PagerIndicatorView.java | 23 ++++++---- .../main/res/layout/audioplayer_fragment.xml | 2 +- 4 files changed, 39 insertions(+), 32 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 6fb84c29a..b400b8700 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -142,6 +142,7 @@ dependencies { implementation "androidx.preference:preference:1.1.0" implementation "androidx.gridlayout:gridlayout:1.0.0" implementation "androidx.recyclerview:recyclerview:1.1.0" + implementation "androidx.viewpager2:viewpager2:1.0.0" implementation "androidx.coordinatorlayout:coordinatorlayout:1.1.0" implementation "androidx.media:media:1.1.0" implementation "androidx.work:work-runtime:$workManagerVersion" 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 7f0326019..e57ea63dc 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/AudioPlayerFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/AudioPlayerFragment.java @@ -13,20 +13,28 @@ 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; import androidx.appcompat.widget.Toolbar; import androidx.fragment.app.Fragment; -import androidx.fragment.app.FragmentManager; -import androidx.fragment.app.FragmentPagerAdapter; -import androidx.viewpager.widget.ViewPager; +import androidx.viewpager2.adapter.FragmentStateAdapter; +import androidx.viewpager2.widget.ViewPager2; + import com.google.android.material.bottomsheet.BottomSheetBehavior; + +import org.greenrobot.eventbus.EventBus; +import org.greenrobot.eventbus.Subscribe; +import org.greenrobot.eventbus.ThreadMode; + +import java.text.DecimalFormat; + import de.danoeh.antennapod.R; import de.danoeh.antennapod.activity.CastEnabledActivity; import de.danoeh.antennapod.activity.MainActivity; import de.danoeh.antennapod.core.event.FavoritesEvent; import de.danoeh.antennapod.core.event.PlaybackPositionEvent; -import de.danoeh.antennapod.core.feed.Chapter; import de.danoeh.antennapod.core.feed.FeedItem; import de.danoeh.antennapod.core.feed.FeedMedia; import de.danoeh.antennapod.core.feed.util.PlaybackSpeedUtils; @@ -50,12 +58,6 @@ 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.util.List; /** * Shows the audio player. @@ -73,7 +75,7 @@ public class AudioPlayerFragment extends Fragment implements PlaybackSpeedIndicatorView butPlaybackSpeed; TextView txtvPlaybackSpeed; - private ViewPager pager; + private ViewPager2 pager; private PagerIndicatorView pageIndicator; private TextView txtvPosition; private TextView txtvLength; @@ -127,11 +129,10 @@ public class AudioPlayerFragment extends Fragment implements sbPosition.setOnSeekBarChangeListener(this); pager = root.findViewById(R.id.pager); - AudioPlayerPagerAdapter pagerAdapter = new AudioPlayerPagerAdapter(getChildFragmentManager()); - pager.setAdapter(pagerAdapter); + pager.setAdapter(new AudioPlayerPagerAdapter(this)); // Required for getChildAt(int) in ViewPagerBottomSheetBehavior to return the correct page - pager.setOffscreenPageLimit(NUM_CONTENT_FRAGMENTS); - pager.addOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() { + // pager.setOffscreenPageLimit(NUM_CONTENT_FRAGMENTS); + pager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() { @Override public void onPageSelected(int position) { pager.post(() -> ((MainActivity) getActivity()).getBottomSheet().updateScrollingChild()); @@ -502,30 +503,30 @@ public class AudioPlayerFragment extends Fragment implements return false; } - private static class AudioPlayerPagerAdapter extends FragmentPagerAdapter { + private static class AudioPlayerPagerAdapter extends FragmentStateAdapter { private static final String TAG = "AudioPlayerPagerAdapter"; - public AudioPlayerPagerAdapter(FragmentManager fm) { - super(fm); + public AudioPlayerPagerAdapter(@NonNull Fragment fragment) { + super(fragment); } + @NonNull @Override - public Fragment getItem(int position) { + public Fragment createFragment(int position) { Log.d(TAG, "getItem(" + position + ")"); switch (position) { case POS_COVER: return new CoverFragment(); case POS_DESCR: return new ItemDescriptionFragment(); + default: case POS_CHAPTERS: return new ChaptersFragment(); - default: - return null; } } @Override - public int getCount() { + public int getItemCount() { return NUM_CONTENT_FRAGMENTS; } } diff --git a/app/src/main/java/de/danoeh/antennapod/view/PagerIndicatorView.java b/app/src/main/java/de/danoeh/antennapod/view/PagerIndicatorView.java index 53a95eede..780ee0d88 100644 --- a/app/src/main/java/de/danoeh/antennapod/view/PagerIndicatorView.java +++ b/app/src/main/java/de/danoeh/antennapod/view/PagerIndicatorView.java @@ -1,15 +1,17 @@ package de.danoeh.antennapod.view; +import android.animation.ArgbEvaluator; import android.content.Context; import android.content.res.TypedArray; -import android.database.DataSetObserver; import android.graphics.Canvas; import android.graphics.Paint; import android.util.AttributeSet; import android.view.View; + import androidx.annotation.Nullable; -import androidx.vectordrawable.graphics.drawable.ArgbEvaluator; -import androidx.viewpager.widget.ViewPager; +import androidx.core.view.ViewCompat; +import androidx.recyclerview.widget.RecyclerView; +import androidx.viewpager2.widget.ViewPager2; public class PagerIndicatorView extends View { private final Paint paint = new Paint(); @@ -38,26 +40,29 @@ public class PagerIndicatorView extends View { paint.setAntiAlias(true); paint.setStyle(Paint.Style.FILL); - int[] colorAttrs = new int[] { android.R.attr.textColorSecondary }; + int[] colorAttrs = new int[]{android.R.attr.textColorSecondary}; TypedArray a = getContext().obtainStyledAttributes(colorAttrs); circleColorHighlight = a.getColor(0, 0xffffffff); circleColor = (Integer) new ArgbEvaluator().evaluate(0.8f, 0x00ffffff, circleColorHighlight); a.recycle(); } - public void setViewPager(ViewPager pager) { - numPages = pager.getAdapter().getCount(); - pager.getAdapter().registerDataSetObserver(new DataSetObserver() { + public void setViewPager(ViewPager2 pager) { + numPages = pager.getAdapter().getItemCount(); + pager.getAdapter().registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() { @Override public void onChanged() { - numPages = pager.getAdapter().getCount(); + numPages = pager.getAdapter().getItemCount(); invalidate(); } }); - pager.addOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() { + pager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() { @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { PagerIndicatorView.this.position = position + positionOffset; + if (ViewCompat.getLayoutDirection(pager) == ViewCompat.LAYOUT_DIRECTION_RTL) { + PagerIndicatorView.this.position = numPages - 1 - PagerIndicatorView.this.position; + } invalidate(); } }); diff --git a/app/src/main/res/layout/audioplayer_fragment.xml b/app/src/main/res/layout/audioplayer_fragment.xml index d01228d27..a9e9137f2 100644 --- a/app/src/main/res/layout/audioplayer_fragment.xml +++ b/app/src/main/res/layout/audioplayer_fragment.xml @@ -34,7 +34,7 @@ tools:background="@android:color/holo_green_light" android:elevation="8dp"/> - Date: Wed, 8 Apr 2020 15:50:08 +0430 Subject: [PATCH 02/21] Adopt StatisticsFragment with ViewPager2 --- .../preferences/StatisticsFragment.java | 67 +++++++++---------- .../main/res/layout/statistics_fragment.xml | 2 +- 2 files changed, 33 insertions(+), 36 deletions(-) diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/StatisticsFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/StatisticsFragment.java index 4b8cf1e1c..1f93bd0e5 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/StatisticsFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/StatisticsFragment.java @@ -1,17 +1,17 @@ package de.danoeh.antennapod.fragment.preferences; -import android.content.res.Resources; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import androidx.annotation.NonNull; import androidx.fragment.app.Fragment; -import androidx.fragment.app.FragmentManager; -import androidx.fragment.app.FragmentPagerAdapter; -import androidx.viewpager.widget.ViewPager; +import androidx.viewpager2.adapter.FragmentStateAdapter; +import androidx.viewpager2.widget.ViewPager2; import com.google.android.material.tabs.TabLayout; +import com.google.android.material.tabs.TabLayoutMediator; import de.danoeh.antennapod.R; import de.danoeh.antennapod.activity.PreferenceActivity; @@ -29,25 +29,34 @@ public class StatisticsFragment extends Fragment { private TabLayout tabLayout; - private ViewPager viewPager; + private ViewPager2 viewPager; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); } - public View onCreateView(LayoutInflater inflater, ViewGroup container, + public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { super.onCreateView(inflater, container, savedInstanceState); setHasOptionsMenu(true); View rootView = inflater.inflate(R.layout.pager_fragment, container, false); viewPager = rootView.findViewById(R.id.viewpager); - viewPager.setAdapter(new StatisticsPagerAdapter(getChildFragmentManager(), getResources())); - + viewPager.setAdapter(new StatisticsPagerAdapter(this)); // Give the TabLayout the ViewPager tabLayout = rootView.findViewById(R.id.sliding_tabs); - tabLayout.setupWithViewPager(viewPager); + new TabLayoutMediator(tabLayout, viewPager, (tab, position) -> { + switch (position) { + case POS_LISTENED_HOURS: + tab.setText(getString(R.string.playback_statistics_label)); + break; + default: + case POS_SPACE_TAKEN: + tab.setText(getString(R.string.download_statistics_label)); + break; + } + }).attach(); rootView.findViewById(R.id.toolbar).setVisibility(View.GONE); @@ -60,39 +69,27 @@ public class StatisticsFragment extends Fragment { ((PreferenceActivity) getActivity()).getSupportActionBar().setTitle(R.string.statistics_label); } - public static class StatisticsPagerAdapter extends FragmentPagerAdapter { + public static class StatisticsPagerAdapter extends FragmentStateAdapter { - private final Resources resources; - - public StatisticsPagerAdapter(FragmentManager fm, Resources resources) { - super(fm); - this.resources = resources; + StatisticsPagerAdapter(@NonNull Fragment fragment) { + super(fragment); } + @NonNull @Override - public Fragment getItem(int position) { - if (position == 0) { - return new PlaybackStatisticsFragment(); - } else { - return new DownloadStatisticsFragment(); - } - } - - @Override - public int getCount() { - return TOTAL_COUNT; - } - - @Override - public CharSequence getPageTitle(int position) { + public Fragment createFragment(int position) { switch (position) { - case POS_LISTENED_HOURS: - return resources.getString(R.string.playback_statistics_label); - case POS_SPACE_TAKEN: - return resources.getString(R.string.download_statistics_label); + case 0: + return new PlaybackStatisticsFragment(); default: - return super.getPageTitle(position); + case 1: + return new DownloadStatisticsFragment(); } } + + @Override + public int getItemCount() { + return TOTAL_COUNT; + } } } diff --git a/app/src/main/res/layout/statistics_fragment.xml b/app/src/main/res/layout/statistics_fragment.xml index e69bc1a70..f931adda8 100644 --- a/app/src/main/res/layout/statistics_fragment.xml +++ b/app/src/main/res/layout/statistics_fragment.xml @@ -14,7 +14,7 @@ app:tabGravity="fill" app:tabMode="fixed" /> - Date: Wed, 8 Apr 2020 16:20:31 +0430 Subject: [PATCH 03/21] Port pager_fragment users to ViewPager2 --- .../fragment/DownloadsFragment.java | 71 +++++++++---------- .../antennapod/fragment/EpisodesFragment.java | 54 +++++++------- .../fragment/gpodnet/GpodnetMainFragment.java | 66 +++++++++-------- .../preferences/StatisticsFragment.java | 5 +- app/src/main/res/layout/pager_fragment.xml | 2 +- 5 files changed, 103 insertions(+), 95 deletions(-) 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 6f537be96..90ccd91ce 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/DownloadsFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/DownloadsFragment.java @@ -2,19 +2,21 @@ package de.danoeh.antennapod.fragment; import android.content.Context; import android.content.SharedPreferences; -import android.content.res.Resources; import android.os.Bundle; -import androidx.appcompat.app.AppCompatActivity; -import androidx.appcompat.widget.Toolbar; -import com.google.android.material.tabs.TabLayout; -import androidx.fragment.app.Fragment; -import androidx.fragment.app.FragmentManager; -import androidx.fragment.app.FragmentPagerAdapter; -import androidx.viewpager.widget.ViewPager; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import androidx.annotation.NonNull; +import androidx.appcompat.app.AppCompatActivity; +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.tabs.TabLayout; +import com.google.android.material.tabs.TabLayoutMediator; + import de.danoeh.antennapod.R; /** @@ -29,10 +31,11 @@ public class DownloadsFragment extends Fragment { public static final int POS_RUNNING = 0; private static final int POS_COMPLETED = 1; public static final int POS_LOG = 2; + private static final int TOTAL_COUNT = 3; private static final String PREF_LAST_TAB_POSITION = "tab_position"; - private ViewPager viewPager; + private ViewPager2 viewPager; private TabLayout tabLayout; @Override @@ -44,12 +47,23 @@ public class DownloadsFragment extends Fragment { ((AppCompatActivity) getActivity()).setSupportActionBar(toolbar); viewPager = root.findViewById(R.id.viewpager); - DownloadsPagerAdapter pagerAdapter = new DownloadsPagerAdapter(getChildFragmentManager(), getResources()); - viewPager.setAdapter(pagerAdapter); + viewPager.setAdapter(new DownloadsPagerAdapter(this)); // Give the TabLayout the ViewPager tabLayout = root.findViewById(R.id.sliding_tabs); - tabLayout.setupWithViewPager(viewPager); + new TabLayoutMediator(tabLayout, viewPager, (tab, position) -> { + switch (position) { + case POS_RUNNING: + tab.setText(R.string.downloads_running_label); + break; + case POS_COMPLETED: + tab.setText(R.string.downloads_completed_label); + break; + case POS_LOG: + tab.setText(R.string.downloads_log_label); + break; + } + }).attach(); return root; } @@ -83,46 +97,29 @@ public class DownloadsFragment extends Fragment { viewPager.setCurrentItem(lastPosition); } - public static class DownloadsPagerAdapter extends FragmentPagerAdapter { + public static class DownloadsPagerAdapter extends FragmentStateAdapter { - final Resources resources; - - public DownloadsPagerAdapter(FragmentManager fm, Resources resources) { - super(fm); - this.resources = resources; + DownloadsPagerAdapter(@NonNull Fragment fragment) { + super(fragment); } + @NonNull @Override - public Fragment getItem(int position) { + public Fragment createFragment(int position) { switch (position) { case POS_RUNNING: return new RunningDownloadsFragment(); case POS_COMPLETED: return new CompletedDownloadsFragment(); + default: case POS_LOG: return new DownloadLogFragment(); - default: - return null; } } @Override - public int getCount() { - return 3; - } - - @Override - public CharSequence getPageTitle(int position) { - switch (position) { - case POS_RUNNING: - return resources.getString(R.string.downloads_running_label); - case POS_COMPLETED: - return resources.getString(R.string.downloads_completed_label); - case POS_LOG: - return resources.getString(R.string.downloads_log_label); - default: - return super.getPageTitle(position); - } + public int getItemCount() { + return TOTAL_COUNT; } } } diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesFragment.java index 275496f24..04f7bd964 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesFragment.java @@ -6,13 +6,17 @@ import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; + import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.widget.Toolbar; import androidx.fragment.app.Fragment; -import androidx.fragment.app.FragmentPagerAdapter; -import androidx.viewpager.widget.ViewPager; +import androidx.viewpager2.adapter.FragmentStateAdapter; +import androidx.viewpager2.widget.ViewPager2; + import com.google.android.material.tabs.TabLayout; +import com.google.android.material.tabs.TabLayoutMediator; + import de.danoeh.antennapod.R; public class EpisodesFragment extends Fragment { @@ -27,7 +31,7 @@ public class EpisodesFragment extends Fragment { private TabLayout tabLayout; - private ViewPager viewPager; + private ViewPager2 viewPager; //Mandatory Constructor public EpisodesFragment() { @@ -46,11 +50,25 @@ public class EpisodesFragment extends Fragment { toolbar.setTitle(R.string.episodes_label); ((AppCompatActivity) getActivity()).setSupportActionBar(toolbar); viewPager = rootView.findViewById(R.id.viewpager); - viewPager.setAdapter(new EpisodesPagerAdapter()); + viewPager.setAdapter(new EpisodesPagerAdapter(this)); // Give the TabLayout the ViewPager tabLayout = rootView.findViewById(R.id.sliding_tabs); - tabLayout.setupWithViewPager(viewPager); + + new TabLayoutMediator(tabLayout, viewPager, (tab, position) -> { + switch (position) { + case POS_ALL_EPISODES: + tab.setText(R.string.all_episodes_short_label); + break; + case POS_NEW_EPISODES: + tab.setText(R.string.new_episodes_label); + break; + case POS_FAV_EPISODES: + tab.setText(R.string.favorite_episodes_label); + break; + } + }).attach(); + return rootView; } @@ -74,15 +92,15 @@ public class EpisodesFragment extends Fragment { viewPager.setCurrentItem(lastPosition); } - public class EpisodesPagerAdapter extends FragmentPagerAdapter { + public class EpisodesPagerAdapter extends FragmentStateAdapter { - public EpisodesPagerAdapter() { - super(getChildFragmentManager()); + EpisodesPagerAdapter(@NonNull Fragment fragment) { + super(fragment); } - @Override @NonNull - public Fragment getItem(int position) { + @Override + public Fragment createFragment(int position) { switch (position) { case 0: return new NewEpisodesFragment(); @@ -94,22 +112,8 @@ public class EpisodesFragment extends Fragment { } @Override - public int getCount() { + public int getItemCount() { return TOTAL_COUNT; } - - @Override - public CharSequence getPageTitle(int position) { - switch (position) { - case POS_ALL_EPISODES: - return getString(R.string.all_episodes_short_label); - case POS_NEW_EPISODES: - return getString(R.string.new_episodes_label); - case POS_FAV_EPISODES: - return getString(R.string.favorite_episodes_label); - default: - return super.getPageTitle(position); - } - } } } diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/GpodnetMainFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/GpodnetMainFragment.java index 3ebdd80d8..ee63fbcf4 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/GpodnetMainFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/GpodnetMainFragment.java @@ -4,13 +4,20 @@ import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; + +import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.widget.Toolbar; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentPagerAdapter; import androidx.viewpager.widget.ViewPager; +import androidx.viewpager2.adapter.FragmentStateAdapter; +import androidx.viewpager2.widget.ViewPager2; + import com.google.android.material.tabs.TabLayout; +import com.google.android.material.tabs.TabLayoutMediator; + import de.danoeh.antennapod.R; /** @@ -18,6 +25,11 @@ import de.danoeh.antennapod.R; */ public class GpodnetMainFragment extends Fragment { + private static final int NUM_PAGES = 2; + private static final int POS_TOPLIST = 0; + private static final int POS_TAGS = 1; + private static final int POS_SUGGESTIONS = 2; + @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { super.onCreateView(inflater, container, savedInstanceState); @@ -26,63 +38,59 @@ public class GpodnetMainFragment extends Fragment { toolbar.setTitle(R.string.gpodnet_main_label); ((AppCompatActivity) getActivity()).setSupportActionBar(toolbar); - ViewPager viewPager = root.findViewById(R.id.viewpager); - GpodnetPagerAdapter pagerAdapter = new GpodnetPagerAdapter(getChildFragmentManager()); + ViewPager2 viewPager = root.findViewById(R.id.viewpager); + GpodnetPagerAdapter pagerAdapter = new GpodnetPagerAdapter(this); viewPager.setAdapter(pagerAdapter); // Give the TabLayout the ViewPager TabLayout tabLayout = root.findViewById(R.id.sliding_tabs); - tabLayout.setupWithViewPager(viewPager); + + new TabLayoutMediator(tabLayout, viewPager, (tab, position) -> { + switch (position) { + case POS_TAGS: + tab.setText(R.string.gpodnet_taglist_header); + break; + case POS_TOPLIST: + tab.setText(R.string.gpodnet_toplist_header); + break; + default: + case POS_SUGGESTIONS: + tab.setText(R.string.gpodnet_suggestions_header); + break; + } + }).attach(); return root; } - public class GpodnetPagerAdapter extends FragmentPagerAdapter { - private static final int NUM_PAGES = 2; - private static final int POS_TOPLIST = 0; - private static final int POS_TAGS = 1; - private static final int POS_SUGGESTIONS = 2; + public static class GpodnetPagerAdapter extends FragmentStateAdapter { - public GpodnetPagerAdapter(FragmentManager fm) { - super(fm); + GpodnetPagerAdapter(@NonNull Fragment fragment) { + super(fragment); } + @NonNull @Override - public Fragment getItem(int i) { + public Fragment createFragment(int position) { Bundle arguments = new Bundle(); arguments.putBoolean(PodcastListFragment.ARGUMENT_HIDE_TOOLBAR, true); - switch (i) { + switch (position) { case POS_TAGS: return new TagListFragment(); case POS_TOPLIST: PodcastListFragment topListFragment = new PodcastTopListFragment(); topListFragment.setArguments(arguments); return topListFragment; + default: case POS_SUGGESTIONS: PodcastListFragment suggestionsFragment = new SuggestionListFragment(); suggestionsFragment.setArguments(arguments); return suggestionsFragment; - default: - return null; } } @Override - public CharSequence getPageTitle(int position) { - switch (position) { - case POS_TAGS: - return getString(R.string.gpodnet_taglist_header); - case POS_TOPLIST: - return getString(R.string.gpodnet_toplist_header); - case POS_SUGGESTIONS: - return getString(R.string.gpodnet_suggestions_header); - default: - return super.getPageTitle(position); - } - } - - @Override - public int getCount() { + public int getItemCount() { return NUM_PAGES; } } diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/StatisticsFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/StatisticsFragment.java index 1f93bd0e5..852946ec4 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/StatisticsFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/StatisticsFragment.java @@ -49,11 +49,10 @@ public class StatisticsFragment extends Fragment { new TabLayoutMediator(tabLayout, viewPager, (tab, position) -> { switch (position) { case POS_LISTENED_HOURS: - tab.setText(getString(R.string.playback_statistics_label)); + tab.setText(R.string.playback_statistics_label); break; - default: case POS_SPACE_TAKEN: - tab.setText(getString(R.string.download_statistics_label)); + tab.setText(R.string.download_statistics_label); break; } }).attach(); diff --git a/app/src/main/res/layout/pager_fragment.xml b/app/src/main/res/layout/pager_fragment.xml index 6a642caae..5a1e26c73 100644 --- a/app/src/main/res/layout/pager_fragment.xml +++ b/app/src/main/res/layout/pager_fragment.xml @@ -21,7 +21,7 @@ app:tabMode="fixed" app:tabGravity="fill"/> - From 74ec3b1e4a9f81cc23ffd17ea4867a25dd8d7f04 Mon Sep 17 00:00:00 2001 From: Ebrahim Byagowi Date: Wed, 8 Apr 2020 16:24:21 +0430 Subject: [PATCH 04/21] Port ItemPagerFragment to ViewPager2 --- .../fragment/ItemPagerFragment.java | 43 ++++++++----------- .../res/layout/feeditem_pager_fragment.xml | 2 +- .../main/res/layout/statistics_fragment.xml | 2 +- 3 files changed, 19 insertions(+), 28 deletions(-) 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 aac89a65f..6f9ccdd1e 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/ItemPagerFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItemPagerFragment.java @@ -7,29 +7,30 @@ import android.view.MenuInflater; import android.view.MenuItem; 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.core.view.ViewCompat; import androidx.fragment.app.Fragment; -import androidx.fragment.app.FragmentStatePagerAdapter; -import androidx.viewpager.widget.ViewPager; +import androidx.viewpager2.adapter.FragmentStateAdapter; +import androidx.viewpager2.widget.ViewPager2; + +import org.greenrobot.eventbus.EventBus; +import org.greenrobot.eventbus.Subscribe; +import org.greenrobot.eventbus.ThreadMode; + import de.danoeh.antennapod.R; -import de.danoeh.antennapod.activity.CastEnabledActivity; import de.danoeh.antennapod.activity.MainActivity; import de.danoeh.antennapod.core.event.FeedItemEvent; import de.danoeh.antennapod.core.feed.FeedItem; import de.danoeh.antennapod.core.storage.DBReader; -import de.danoeh.antennapod.core.util.Flavors; import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler; import io.reactivex.Observable; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.Disposable; import io.reactivex.schedulers.Schedulers; -import org.greenrobot.eventbus.EventBus; -import org.greenrobot.eventbus.Subscribe; -import org.greenrobot.eventbus.ThreadMode; /** * Displays information about a list of FeedItems. @@ -41,7 +42,7 @@ public class ItemPagerFragment extends Fragment { /** * Creates a new instance of an ItemPagerFragment. * - * @param feeditems The IDs of the FeedItems that belong to the same list + * @param feeditems The IDs of the FeedItems that belong to the same list * @param feedItemPos The position of the FeedItem that is currently shown * @return The ItemFragment instance */ @@ -76,31 +77,21 @@ public class ItemPagerFragment extends Fragment { feedItems = getArguments().getLongArray(ARG_FEEDITEMS); int feedItemPos = getArguments().getInt(ARG_FEEDITEM_POS); - ViewPager pager = layout.findViewById(R.id.pager); + ViewPager2 pager = layout.findViewById(R.id.pager); // FragmentStatePagerAdapter documentation: // > When using FragmentStatePagerAdapter the host ViewPager must have a valid ID set. // When opening multiple ItemPagerFragments by clicking "item" -> "visit podcast" -> "item" -> etc, // the ID is no longer unique and FragmentStatePagerAdapter does not display any pages. int newId = ViewCompat.generateViewId(); pager.setId(newId); - pager.setAdapter(new ItemPagerAdapter()); + pager.setAdapter(new ItemPagerAdapter(this)); pager.setCurrentItem(feedItemPos); loadItem(feedItems[feedItemPos]); - pager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { - @Override - public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { - - } - + pager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() { @Override public void onPageSelected(int position) { loadItem(feedItems[position]); } - - @Override - public void onPageScrollStateChanged(int state) { - - } }); EventBus.getDefault().register(this); @@ -173,20 +164,20 @@ public class ItemPagerFragment extends Fragment { ((MainActivity) getActivity()).loadChildFragment(fragment); } - private class ItemPagerAdapter extends FragmentStatePagerAdapter { + private class ItemPagerAdapter extends FragmentStateAdapter { - ItemPagerAdapter() { - super(getFragmentManager(), BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT); + ItemPagerAdapter(@NonNull Fragment fragment) { + super(fragment); } @NonNull @Override - public Fragment getItem(int position) { + public Fragment createFragment(int position) { return ItemFragment.newInstance(feedItems[position]); } @Override - public int getCount() { + public int getItemCount() { return feedItems.length; } } diff --git a/app/src/main/res/layout/feeditem_pager_fragment.xml b/app/src/main/res/layout/feeditem_pager_fragment.xml index 50c490611..ac7316dd8 100644 --- a/app/src/main/res/layout/feeditem_pager_fragment.xml +++ b/app/src/main/res/layout/feeditem_pager_fragment.xml @@ -14,7 +14,7 @@ app:navigationIcon="?homeAsUpIndicator" android:id="@+id/toolbar"/> - diff --git a/app/src/main/res/layout/statistics_fragment.xml b/app/src/main/res/layout/statistics_fragment.xml index f931adda8..7b2550e93 100644 --- a/app/src/main/res/layout/statistics_fragment.xml +++ b/app/src/main/res/layout/statistics_fragment.xml @@ -14,7 +14,7 @@ app:tabGravity="fill" app:tabMode="fixed" /> - Date: Wed, 8 Apr 2020 16:42:23 +0430 Subject: [PATCH 05/21] Match pager_fragment's pager height with its parent --- app/src/main/res/layout/pager_fragment.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/layout/pager_fragment.xml b/app/src/main/res/layout/pager_fragment.xml index 5a1e26c73..f7bf058c4 100644 --- a/app/src/main/res/layout/pager_fragment.xml +++ b/app/src/main/res/layout/pager_fragment.xml @@ -24,6 +24,6 @@ + android:layout_height="match_parent"/> From 3a11f2842bb991bdf9f86345f5005cd7194a55b2 Mon Sep 17 00:00:00 2001 From: Ebrahim Byagowi Date: Wed, 8 Apr 2020 22:39:39 +0430 Subject: [PATCH 06/21] Remove remained references of ViewPager --- .../material/bottomsheet/ViewPagerBottomSheetBehavior.java | 7 ++++--- .../antennapod/fragment/gpodnet/GpodnetMainFragment.java | 3 --- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/com/google/android/material/bottomsheet/ViewPagerBottomSheetBehavior.java b/app/src/main/java/com/google/android/material/bottomsheet/ViewPagerBottomSheetBehavior.java index 1667006a5..5e74a5834 100644 --- a/app/src/main/java/com/google/android/material/bottomsheet/ViewPagerBottomSheetBehavior.java +++ b/app/src/main/java/com/google/android/material/bottomsheet/ViewPagerBottomSheetBehavior.java @@ -4,8 +4,9 @@ import android.content.Context; import android.util.AttributeSet; import android.view.View; import android.view.ViewGroup; + import androidx.core.view.ViewCompat; -import androidx.viewpager.widget.ViewPager; +import androidx.viewpager2.widget.ViewPager2; import java.lang.ref.WeakReference; @@ -31,8 +32,8 @@ public class ViewPagerBottomSheetBehavior extends BottomSheetBeh return view; } - if (view instanceof ViewPager) { - ViewPager viewPager = (ViewPager) view; + if (view instanceof ViewPager2) { + ViewPager2 viewPager = (ViewPager2) view; View currentViewPagerChild = viewPager.getChildAt(viewPager.getCurrentItem()); if (currentViewPagerChild != null) { return findScrollingChild(currentViewPagerChild); diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/GpodnetMainFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/GpodnetMainFragment.java index ee63fbcf4..4ccc53118 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/GpodnetMainFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/GpodnetMainFragment.java @@ -9,9 +9,6 @@ import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.widget.Toolbar; import androidx.fragment.app.Fragment; -import androidx.fragment.app.FragmentManager; -import androidx.fragment.app.FragmentPagerAdapter; -import androidx.viewpager.widget.ViewPager; import androidx.viewpager2.adapter.FragmentStateAdapter; import androidx.viewpager2.widget.ViewPager2; From 965fc5c27dff8d9478a4069886896aaeb93a80d0 Mon Sep 17 00:00:00 2001 From: Ebrahim Byagowi Date: Thu, 9 Apr 2020 18:37:31 +0430 Subject: [PATCH 07/21] Add default cases for switches lack them --- .../java/de/danoeh/antennapod/fragment/DownloadsFragment.java | 2 ++ .../java/de/danoeh/antennapod/fragment/EpisodesFragment.java | 2 ++ .../antennapod/fragment/preferences/StatisticsFragment.java | 2 ++ 3 files changed, 6 insertions(+) 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 90ccd91ce..bb17e3982 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/DownloadsFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/DownloadsFragment.java @@ -62,6 +62,8 @@ public class DownloadsFragment extends Fragment { case POS_LOG: tab.setText(R.string.downloads_log_label); break; + default: + break; } }).attach(); diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesFragment.java index 04f7bd964..795c42280 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesFragment.java @@ -66,6 +66,8 @@ public class EpisodesFragment extends Fragment { case POS_FAV_EPISODES: tab.setText(R.string.favorite_episodes_label); break; + default: + break; } }).attach(); diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/StatisticsFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/StatisticsFragment.java index 852946ec4..e804623c2 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/StatisticsFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/StatisticsFragment.java @@ -54,6 +54,8 @@ public class StatisticsFragment extends Fragment { case POS_SPACE_TAKEN: tab.setText(R.string.download_statistics_label); break; + default: + break; } }).attach(); From 75e9ee9e0da4cf930a424f0edf61f2ca37a7ca54 Mon Sep 17 00:00:00 2001 From: Ebrahim Byagowi Date: Fri, 10 Apr 2020 14:18:40 +0430 Subject: [PATCH 08/21] Use constants instead hardcoded numbers in view pagers adapters --- .../antennapod/fragment/EpisodesFragment.java | 13 +++++++------ .../fragment/preferences/StatisticsFragment.java | 4 ++-- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesFragment.java index 795c42280..d1c553666 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesFragment.java @@ -57,12 +57,12 @@ public class EpisodesFragment extends Fragment { new TabLayoutMediator(tabLayout, viewPager, (tab, position) -> { switch (position) { - case POS_ALL_EPISODES: - tab.setText(R.string.all_episodes_short_label); - break; case POS_NEW_EPISODES: tab.setText(R.string.new_episodes_label); break; + case POS_ALL_EPISODES: + tab.setText(R.string.all_episodes_short_label); + break; case POS_FAV_EPISODES: tab.setText(R.string.favorite_episodes_label); break; @@ -94,7 +94,7 @@ public class EpisodesFragment extends Fragment { viewPager.setCurrentItem(lastPosition); } - public class EpisodesPagerAdapter extends FragmentStateAdapter { + static class EpisodesPagerAdapter extends FragmentStateAdapter { EpisodesPagerAdapter(@NonNull Fragment fragment) { super(fragment); @@ -104,11 +104,12 @@ public class EpisodesFragment extends Fragment { @Override public Fragment createFragment(int position) { switch (position) { - case 0: + case POS_NEW_EPISODES: return new NewEpisodesFragment(); - case 1: + case POS_ALL_EPISODES: return new AllEpisodesFragment(); default: + case POS_FAV_EPISODES: return new FavoriteEpisodesFragment(); } } diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/StatisticsFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/StatisticsFragment.java index e804623c2..b455a10ee 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/StatisticsFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/StatisticsFragment.java @@ -80,10 +80,10 @@ public class StatisticsFragment extends Fragment { @Override public Fragment createFragment(int position) { switch (position) { - case 0: + case POS_LISTENED_HOURS: return new PlaybackStatisticsFragment(); default: - case 1: + case POS_SPACE_TAKEN: return new DownloadStatisticsFragment(); } } From 260d7ee9446258508a2b9145267bfe858a0e9540 Mon Sep 17 00:00:00 2001 From: Ebrahim Byagowi Date: Fri, 10 Apr 2020 16:34:47 +0430 Subject: [PATCH 09/21] Bring back AudioPlayerFragment pager's setOffscreenPageLimit The reason needs cast: https://issuetracker.google.com/issues/144669596 --- .../java/de/danoeh/antennapod/fragment/AudioPlayerFragment.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 e57ea63dc..e905ccd95 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/AudioPlayerFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/AudioPlayerFragment.java @@ -131,7 +131,7 @@ public class AudioPlayerFragment extends Fragment implements pager = root.findViewById(R.id.pager); pager.setAdapter(new AudioPlayerPagerAdapter(this)); // Required for getChildAt(int) in ViewPagerBottomSheetBehavior to return the correct page - // pager.setOffscreenPageLimit(NUM_CONTENT_FRAGMENTS); + pager.setOffscreenPageLimit((int) NUM_CONTENT_FRAGMENTS); pager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() { @Override public void onPageSelected(int position) { From 292afd7d309c02cfb760a5722affcec274ed935c Mon Sep 17 00:00:00 2001 From: Ebrahim Byagowi Date: Fri, 10 Apr 2020 21:08:49 +0430 Subject: [PATCH 10/21] Disable bringing episode animation As was requested in https://github.com/AntennaPod/AntennaPod/pull/4020#discussion_r406797233 --- .../java/de/danoeh/antennapod/fragment/ItemPagerFragment.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 6f9ccdd1e..0c7eb23d2 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/ItemPagerFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItemPagerFragment.java @@ -85,7 +85,7 @@ public class ItemPagerFragment extends Fragment { int newId = ViewCompat.generateViewId(); pager.setId(newId); pager.setAdapter(new ItemPagerAdapter(this)); - pager.setCurrentItem(feedItemPos); + pager.setCurrentItem(feedItemPos, false); loadItem(feedItems[feedItemPos]); pager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() { @Override From 77dc78dfe8acf5ce72eafbf15a657338769edc42 Mon Sep 17 00:00:00 2001 From: Ebrahim Byagowi Date: Sat, 11 Apr 2020 00:10:37 +0430 Subject: [PATCH 11/21] Fix view pager scrolling of bottom sheet As was suggested in https://github.com/AntennaPod/AntennaPod/pull/4020#discussion_r406795883 --- .../material/bottomsheet/ViewPagerBottomSheetBehavior.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/google/android/material/bottomsheet/ViewPagerBottomSheetBehavior.java b/app/src/main/java/com/google/android/material/bottomsheet/ViewPagerBottomSheetBehavior.java index 5e74a5834..9ed4897d2 100644 --- a/app/src/main/java/com/google/android/material/bottomsheet/ViewPagerBottomSheetBehavior.java +++ b/app/src/main/java/com/google/android/material/bottomsheet/ViewPagerBottomSheetBehavior.java @@ -6,6 +6,7 @@ import android.view.View; import android.view.ViewGroup; import androidx.core.view.ViewCompat; +import androidx.recyclerview.widget.RecyclerView; import androidx.viewpager2.widget.ViewPager2; import java.lang.ref.WeakReference; @@ -34,7 +35,8 @@ public class ViewPagerBottomSheetBehavior extends BottomSheetBeh if (view instanceof ViewPager2) { ViewPager2 viewPager = (ViewPager2) view; - View currentViewPagerChild = viewPager.getChildAt(viewPager.getCurrentItem()); + RecyclerView recycler = (RecyclerView) viewPager.getChildAt(0); + View currentViewPagerChild = recycler.getChildAt(viewPager.getCurrentItem()); if (currentViewPagerChild != null) { return findScrollingChild(currentViewPagerChild); } From 1b9937c2bee33d23e8a7f93ac0e7bfae0a47ddbb Mon Sep 17 00:00:00 2001 From: Ebrahim Byagowi Date: Sat, 11 Apr 2020 02:16:13 +0430 Subject: [PATCH 12/21] Restore current tab right after downloads and episodes views creation --- .../antennapod/fragment/DownloadsFragment.java | 15 +++++---------- .../antennapod/fragment/EpisodesFragment.java | 15 +++++---------- 2 files changed, 10 insertions(+), 20 deletions(-) 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 bb17e3982..1c13161b7 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/DownloadsFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/DownloadsFragment.java @@ -67,6 +67,11 @@ public class DownloadsFragment extends Fragment { } }).attach(); + // restore our last position + SharedPreferences prefs = getActivity().getSharedPreferences(TAG, Context.MODE_PRIVATE); + int lastPosition = prefs.getInt(PREF_LAST_TAB_POSITION, 0); + viewPager.setCurrentItem(lastPosition); + return root; } @@ -89,16 +94,6 @@ public class DownloadsFragment extends Fragment { editor.apply(); } - @Override - public void onStart() { - super.onStart(); - - // restore our last position - SharedPreferences prefs = getActivity().getSharedPreferences(TAG, Context.MODE_PRIVATE); - int lastPosition = prefs.getInt(PREF_LAST_TAB_POSITION, 0); - viewPager.setCurrentItem(lastPosition); - } - public static class DownloadsPagerAdapter extends FragmentStateAdapter { DownloadsPagerAdapter(@NonNull Fragment fragment) { diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesFragment.java index d1c553666..6126bfb83 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesFragment.java @@ -71,6 +71,11 @@ public class EpisodesFragment extends Fragment { } }).attach(); + // restore our last position + SharedPreferences prefs = getActivity().getSharedPreferences(TAG, Context.MODE_PRIVATE); + int lastPosition = prefs.getInt(PREF_LAST_TAB_POSITION, 0); + viewPager.setCurrentItem(lastPosition); + return rootView; } @@ -84,16 +89,6 @@ public class EpisodesFragment extends Fragment { editor.apply(); } - @Override - public void onStart() { - super.onStart(); - - // restore our last position - SharedPreferences prefs = getActivity().getSharedPreferences(TAG, Context.MODE_PRIVATE); - int lastPosition = prefs.getInt(PREF_LAST_TAB_POSITION, 0); - viewPager.setCurrentItem(lastPosition); - } - static class EpisodesPagerAdapter extends FragmentStateAdapter { EpisodesPagerAdapter(@NonNull Fragment fragment) { From c88336f17fc3e234319405bb70841ce5237d0626 Mon Sep 17 00:00:00 2001 From: Ebrahim Byagowi Date: Sat, 11 Apr 2020 02:20:12 +0430 Subject: [PATCH 13/21] Disable downloads and episodes view tab position restore animation --- .../java/de/danoeh/antennapod/fragment/DownloadsFragment.java | 2 +- .../java/de/danoeh/antennapod/fragment/EpisodesFragment.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) 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 1c13161b7..cf0c6db90 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/DownloadsFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/DownloadsFragment.java @@ -70,7 +70,7 @@ public class DownloadsFragment extends Fragment { // restore our last position SharedPreferences prefs = getActivity().getSharedPreferences(TAG, Context.MODE_PRIVATE); int lastPosition = prefs.getInt(PREF_LAST_TAB_POSITION, 0); - viewPager.setCurrentItem(lastPosition); + viewPager.setCurrentItem(lastPosition, false); return root; } diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesFragment.java index 6126bfb83..e98890627 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesFragment.java @@ -74,7 +74,7 @@ public class EpisodesFragment extends Fragment { // restore our last position SharedPreferences prefs = getActivity().getSharedPreferences(TAG, Context.MODE_PRIVATE); int lastPosition = prefs.getInt(PREF_LAST_TAB_POSITION, 0); - viewPager.setCurrentItem(lastPosition); + viewPager.setCurrentItem(lastPosition, false); return rootView; } From 430e0d403e5f9c88dc4fc045077cad546220def8 Mon Sep 17 00:00:00 2001 From: Ebrahim Byagowi Date: Sun, 12 Apr 2020 12:04:16 +0430 Subject: [PATCH 14/21] Fix menu items duplication in episodes and downloads fragment As ViewPager2 different lifecycle apparently setHasOptionsMenu needs to be called from onResume() from now on. --- .../antennapod/fragment/CompletedDownloadsFragment.java | 7 ++++++- .../danoeh/antennapod/fragment/DownloadLogFragment.java | 8 ++++++-- .../danoeh/antennapod/fragment/EpisodesListFragment.java | 2 +- 3 files changed, 13 insertions(+), 4 deletions(-) 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 a3c07721a..6629d74e2 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java @@ -86,10 +86,15 @@ public class CompletedDownloadsFragment extends Fragment { @Override public void onStart() { super.onStart(); - setHasOptionsMenu(true); loadItems(); } + @Override + public void onResume() { + super.onResume(); + setHasOptionsMenu(true); + } + @Override public void onStop() { super.onStop(); 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 0305a7d48..a2fea9f24 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/DownloadLogFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/DownloadLogFragment.java @@ -50,6 +50,12 @@ public class DownloadLogFragment extends ListFragment { loadItems(); } + @Override + public void onResume() { + super.onResume(); + setHasOptionsMenu(true); + } + @Override public void onStop() { super.onStop(); @@ -61,8 +67,6 @@ public class DownloadLogFragment extends ListFragment { @Override public void onViewCreated(View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); - setHasOptionsMenu(true); - // add padding final ListView lv = getListView(); lv.setClipToPadding(false); 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 189e026a1..4f283a9ab 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java @@ -83,7 +83,6 @@ public abstract class EpisodesListFragment extends Fragment { @Override public void onStart() { super.onStart(); - setHasOptionsMenu(true); EventBus.getDefault().register(this); loadItems(); } @@ -91,6 +90,7 @@ public abstract class EpisodesListFragment extends Fragment { @Override public void onResume() { super.onResume(); + setHasOptionsMenu(true); registerForContextMenu(recyclerView); } From 17962b57a052593827628d2db9932318ce2cb514 Mon Sep 17 00:00:00 2001 From: Ebrahim Byagowi Date: Sun, 12 Apr 2020 14:10:51 +0430 Subject: [PATCH 15/21] Use more optimized way to detect direction in PagerIndicatorView --- .../java/de/danoeh/antennapod/view/PagerIndicatorView.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/de/danoeh/antennapod/view/PagerIndicatorView.java b/app/src/main/java/de/danoeh/antennapod/view/PagerIndicatorView.java index 780ee0d88..240a565c8 100644 --- a/app/src/main/java/de/danoeh/antennapod/view/PagerIndicatorView.java +++ b/app/src/main/java/de/danoeh/antennapod/view/PagerIndicatorView.java @@ -9,10 +9,13 @@ import android.util.AttributeSet; import android.view.View; import androidx.annotation.Nullable; +import androidx.core.text.TextUtilsCompat; import androidx.core.view.ViewCompat; import androidx.recyclerview.widget.RecyclerView; import androidx.viewpager2.widget.ViewPager2; +import java.util.Locale; + public class PagerIndicatorView extends View { private final Paint paint = new Paint(); private float position = 0; @@ -56,11 +59,13 @@ public class PagerIndicatorView extends View { invalidate(); } }); + boolean isLocaleRtl = TextUtilsCompat.getLayoutDirectionFromLocale(Locale.getDefault()) + == ViewCompat.LAYOUT_DIRECTION_RTL; pager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() { @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { PagerIndicatorView.this.position = position + positionOffset; - if (ViewCompat.getLayoutDirection(pager) == ViewCompat.LAYOUT_DIRECTION_RTL) { + if (isLocaleRtl) { PagerIndicatorView.this.position = numPages - 1 - PagerIndicatorView.this.position; } invalidate(); From 6f74af75920cabf33c16b08bce1d4297687e8d08 Mon Sep 17 00:00:00 2001 From: Ebrahim Byagowi Date: Sun, 12 Apr 2020 18:39:45 +0430 Subject: [PATCH 16/21] Wrap scrollable childs of viewpager in NestedScrollableHost --- .../antennapod/view/NestedScrollableHost.java | 133 ++++++++++++++++++ app/src/main/res/layout/feeditem_fragment.xml | 16 ++- .../res/layout/item_description_fragment.xml | 21 ++- 3 files changed, 158 insertions(+), 12 deletions(-) create mode 100644 app/src/main/java/de/danoeh/antennapod/view/NestedScrollableHost.java diff --git a/app/src/main/java/de/danoeh/antennapod/view/NestedScrollableHost.java b/app/src/main/java/de/danoeh/antennapod/view/NestedScrollableHost.java new file mode 100644 index 000000000..2be49ada4 --- /dev/null +++ b/app/src/main/java/de/danoeh/antennapod/view/NestedScrollableHost.java @@ -0,0 +1,133 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Source: https://github.com/android/views-widgets-samples/blob/87e58d1/ViewPager2/app/src/main/java/androidx/viewpager2/integration/testapp/NestedScrollableHost.kt + */ + +package de.danoeh.antennapod.view; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewConfiguration; +import android.widget.FrameLayout; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.viewpager2.widget.ViewPager2; + +import static androidx.viewpager2.widget.ViewPager2.ORIENTATION_HORIZONTAL; + +/** + * Layout to wrap a scrollable component inside a ViewPager2. Provided as a solution to the problem + * where pages of ViewPager2 have nested scrollable elements that scroll in the same direction as + * ViewPager2. The scrollable element needs to be the immediate and only child of this host layout. + * + *

This solution has limitations when using multiple levels of nested scrollable elements + * (e.g. a horizontal RecyclerView in a vertical RecyclerView in a horizontal ViewPager2).

+ */ +class NestedScrollableHost extends FrameLayout { + + public NestedScrollableHost(@NonNull Context context) { + super(context); + touchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); + } + + public NestedScrollableHost(@NonNull Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + touchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); + } + + private int touchSlop = 0; + private float initialX = 0f; + private float initialY = 0f; + + private ViewPager2 getParentViewPager() { + View v = (View) getParent(); + while (v != null && !(v instanceof ViewPager2)) { + v = (View) v.getParent(); + } + return v == null ? null : (ViewPager2) v; + } + + private View getChild() { + return getChildCount() > 0 ? getChildAt(0) : null; + } + + private boolean canChildScroll(int orientation, float delta) { + int direction = (int) -Math.copySign(1, delta); + View child = getChild(); + if (child == null) { + return false; + } + switch (orientation) { + case 0: + return child.canScrollHorizontally(direction); + case 1: + return child.canScrollVertically(direction); + default: + return false; + } + } + + public boolean onInterceptTouchEvent(MotionEvent e) { + handleInterceptTouchEvent(e); + return super.onInterceptTouchEvent(e); + } + + private void handleInterceptTouchEvent(MotionEvent e) { + ViewPager2 parentViewPager = getParentViewPager(); + if (parentViewPager == null) { + return; + } + int orientation = parentViewPager.getOrientation(); + + // Early return if child can't scroll in same direction as parent + if (!canChildScroll(orientation, -1f) && !canChildScroll(orientation, 1f)) { + return; + } + + if (e.getAction() == MotionEvent.ACTION_DOWN) { + initialX = e.getX(); + initialY = e.getY(); + getParent().requestDisallowInterceptTouchEvent(true); + } else if (e.getAction() == MotionEvent.ACTION_MOVE) { + int dx = (int) (e.getX() - initialX); + int dy = (int) (e.getY() - initialY); + boolean isVpHorizontal = orientation == ORIENTATION_HORIZONTAL; + + // assuming ViewPager2 touch-slop is 2x touch-slop of child + float scaledDx = Math.abs(dx) * (isVpHorizontal ? .5f : 1f); + float scaledDy = Math.abs(dy) * (isVpHorizontal ? 1f : .5f); + + if (scaledDx > touchSlop || scaledDy > touchSlop) { + if (isVpHorizontal == (scaledDy > scaledDx)) { + // Gesture is perpendicular, allow all parents to intercept + getParent().requestDisallowInterceptTouchEvent(false); + } else { + // Gesture is parallel, query child if movement in that direction is possible + if (canChildScroll(orientation, isVpHorizontal ? dx : dy)) { + // Child can scroll, disallow all parents to intercept + getParent().requestDisallowInterceptTouchEvent(true); + } else { + // Child cannot scroll, allow all parents to intercept + getParent().requestDisallowInterceptTouchEvent(false); + } + } + } + } + } +} diff --git a/app/src/main/res/layout/feeditem_fragment.xml b/app/src/main/res/layout/feeditem_fragment.xml index fe1063d85..8dfbbe562 100644 --- a/app/src/main/res/layout/feeditem_fragment.xml +++ b/app/src/main/res/layout/feeditem_fragment.xml @@ -171,12 +171,18 @@ - + android:layout_width="match_parent" + android:layout_height="match_parent"> + + + + - - + + + + + android:layout_height="wrap_content" /> + + + \ No newline at end of file From 7bdacf8fdebbf38ecdc797d2b67506e3eeffb5a6 Mon Sep 17 00:00:00 2001 From: Ebrahim Byagowi Date: Wed, 15 Apr 2020 17:23:24 +0430 Subject: [PATCH 17/21] Modify NestedScrollableHost to suit our need Now manages nested child scrolling when the orientation isn't perpendicular --- .../antennapod/view/NestedScrollableHost.java | 66 +++++++------------ 1 file changed, 22 insertions(+), 44 deletions(-) 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 2be49ada4..a4daa9109 100644 --- a/app/src/main/java/de/danoeh/antennapod/view/NestedScrollableHost.java +++ b/app/src/main/java/de/danoeh/antennapod/view/NestedScrollableHost.java @@ -14,6 +14,7 @@ * limitations under the License. * * Source: https://github.com/android/views-widgets-samples/blob/87e58d1/ViewPager2/app/src/main/java/androidx/viewpager2/integration/testapp/NestedScrollableHost.kt + * And modified for our need */ package de.danoeh.antennapod.view; @@ -23,6 +24,7 @@ import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; import android.view.ViewConfiguration; +import android.view.ViewParent; import android.widget.FrameLayout; import androidx.annotation.NonNull; @@ -30,6 +32,7 @@ import androidx.annotation.Nullable; import androidx.viewpager2.widget.ViewPager2; import static androidx.viewpager2.widget.ViewPager2.ORIENTATION_HORIZONTAL; +import static androidx.viewpager2.widget.ViewPager2.ORIENTATION_VERTICAL; /** * Layout to wrap a scrollable component inside a ViewPager2. Provided as a solution to the problem @@ -37,7 +40,7 @@ import static androidx.viewpager2.widget.ViewPager2.ORIENTATION_HORIZONTAL; * ViewPager2. The scrollable element needs to be the immediate and only child of this host layout. * *

This solution has limitations when using multiple levels of nested scrollable elements - * (e.g. a horizontal RecyclerView in a vertical RecyclerView in a horizontal ViewPager2).

+ * (e.g. a horizontal RecyclerView in a vertical RecyclerView in a horizontal ViewPager2). */ class NestedScrollableHost extends FrameLayout { @@ -63,47 +66,19 @@ class NestedScrollableHost extends FrameLayout { return v == null ? null : (ViewPager2) v; } - private View getChild() { - return getChildCount() > 0 ? getChildAt(0) : null; - } - - private boolean canChildScroll(int orientation, float delta) { - int direction = (int) -Math.copySign(1, delta); - View child = getChild(); - if (child == null) { - return false; - } - switch (orientation) { - case 0: - return child.canScrollHorizontally(direction); - case 1: - return child.canScrollVertically(direction); - default: - return false; - } - } - public boolean onInterceptTouchEvent(MotionEvent e) { - handleInterceptTouchEvent(e); - return super.onInterceptTouchEvent(e); - } - - private void handleInterceptTouchEvent(MotionEvent e) { ViewPager2 parentViewPager = getParentViewPager(); if (parentViewPager == null) { - return; + return super.onInterceptTouchEvent(e); } - int orientation = parentViewPager.getOrientation(); - // Early return if child can't scroll in same direction as parent - if (!canChildScroll(orientation, -1f) && !canChildScroll(orientation, 1f)) { - return; - } + ViewParent parent = getParent(); + int orientation = parentViewPager.getOrientation(); if (e.getAction() == MotionEvent.ACTION_DOWN) { initialX = e.getX(); initialY = e.getY(); - getParent().requestDisallowInterceptTouchEvent(true); + parent.requestDisallowInterceptTouchEvent(true); } else if (e.getAction() == MotionEvent.ACTION_MOVE) { int dx = (int) (e.getX() - initialX); int dy = (int) (e.getY() - initialY); @@ -112,22 +87,25 @@ class NestedScrollableHost extends FrameLayout { // assuming ViewPager2 touch-slop is 2x touch-slop of child float scaledDx = Math.abs(dx) * (isVpHorizontal ? .5f : 1f); float scaledDy = Math.abs(dy) * (isVpHorizontal ? 1f : .5f); - if (scaledDx > touchSlop || scaledDy > touchSlop) { + int value = isVpHorizontal ? dy : dx; if (isVpHorizontal == (scaledDy > scaledDx)) { - // Gesture is perpendicular, allow all parents to intercept - getParent().requestDisallowInterceptTouchEvent(false); + // Gesture is perpendicular + orientation = orientation == ORIENTATION_VERTICAL + ? ORIENTATION_HORIZONTAL : ORIENTATION_VERTICAL; + value = isVpHorizontal ? dy : dx; + } + + int direction = (int) -Math.copySign(1, value); + View child = getChildAt(0); + if (orientation == ORIENTATION_HORIZONTAL) { + parent.requestDisallowInterceptTouchEvent(child.canScrollHorizontally(direction)); } else { - // Gesture is parallel, query child if movement in that direction is possible - if (canChildScroll(orientation, isVpHorizontal ? dx : dy)) { - // Child can scroll, disallow all parents to intercept - getParent().requestDisallowInterceptTouchEvent(true); - } else { - // Child cannot scroll, allow all parents to intercept - getParent().requestDisallowInterceptTouchEvent(false); - } + parent.requestDisallowInterceptTouchEvent(child.canScrollVertically(direction)); } } } + + return super.onInterceptTouchEvent(e); } } From 874eecd3cdcfecd2cea98907fc4daac6e0161939 Mon Sep 17 00:00:00 2001 From: Hannes Achleitner Date: Sun, 26 Apr 2020 17:07:03 +0200 Subject: [PATCH 18/21] Gradle verifiction --- .github/workflows/gradle-wrapper-validation.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 .github/workflows/gradle-wrapper-validation.yml diff --git a/.github/workflows/gradle-wrapper-validation.yml b/.github/workflows/gradle-wrapper-validation.yml new file mode 100644 index 000000000..f365456a1 --- /dev/null +++ b/.github/workflows/gradle-wrapper-validation.yml @@ -0,0 +1,10 @@ +name: "Validate Gradle Wrapper" +on: [pull_request] + +jobs: + validation: + name: "Validation" + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: gradle/wrapper-validation-action@v1 From aeb7151b3ed215e662db7895dbe171d99590f585 Mon Sep 17 00:00:00 2001 From: ByteHamster Date: Wed, 29 Apr 2020 20:34:53 +0200 Subject: [PATCH 19/21] Removed mention of Prestissimo from issue template --- .github/ISSUE_TEMPLATE/bug_report.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 2c2e0cfa2..9f0531c01 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -32,7 +32,7 @@ If you are experiencing a crash, including the stacktrace will likely get it fix 1. Than that 1. Then -**Environment**: [Settings you have changed, e.g. Auto Download. "Unusual" devices you use, e.g. Bluetooth headphones. Do you still use Prestissimo? Did you select another media player?] +**Environment**: [Settings you have changed, e.g. Auto Download. "Unusual" devices you use, e.g. Bluetooth headphones. Did you select another media player?] **Stacktrace/Logcat**: ``` From 46ddcf3c49f6fc278530c4d73b076e79d5a9c993 Mon Sep 17 00:00:00 2001 From: ByteHamster Date: Wed, 29 Apr 2020 21:03:02 +0200 Subject: [PATCH 20/21] Allow plural of 'x selected' string --- .../danoeh/antennapod/dialog/EpisodesApplyActionFragment.java | 3 ++- core/src/main/res/values/strings.xml | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) 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 7e052b445..9198c7a3e 100644 --- a/app/src/main/java/de/danoeh/antennapod/dialog/EpisodesApplyActionFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/dialog/EpisodesApplyActionFragment.java @@ -410,7 +410,8 @@ public class EpisodesApplyActionFragment extends Fragment { } ActivityCompat.invalidateOptionsMenu(EpisodesApplyActionFragment.this.getActivity()); showSpeedDialIfAnyChecked(); - toolbar.setTitle(getString(R.string.num_selected_label, checkedIds.size())); + toolbar.setTitle(getResources().getQuantityString(R.plurals.num_selected_label, + checkedIds.size(), checkedIds.size())); } private void queueChecked() { diff --git a/core/src/main/res/values/strings.xml b/core/src/main/res/values/strings.xml index efd7d698a..2a07fd6f2 100644 --- a/core/src/main/res/values/strings.xml +++ b/core/src/main/res/values/strings.xml @@ -116,7 +116,9 @@ 1 day after finishing %d days after finishing - %d selected + + %d selected + Loading moreā€¦ From ca9ff0094689caa73402cd0ea756ab3e3fa7b5a2 Mon Sep 17 00:00:00 2001 From: Michael White Date: Fri, 1 May 2020 06:08:39 -0400 Subject: [PATCH 21/21] set max width on external player image (#4071) --- app/src/main/res/layout/external_player_fragment.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/main/res/layout/external_player_fragment.xml b/app/src/main/res/layout/external_player_fragment.xml index 7fb1296a9..dd8f7fe40 100644 --- a/app/src/main/res/layout/external_player_fragment.xml +++ b/app/src/main/res/layout/external_player_fragment.xml @@ -27,7 +27,8 @@ android:layout_height="match_parent" android:adjustViewBounds="true" android:cropToPadding="true" - android:scaleType="centerCrop" + android:maxWidth="96dp" + android:scaleType="fitCenter" tools:src="@tools:sample/avatars"/>