diff options
author | H. Lehmann <ByteHamster@users.noreply.github.com> | 2020-05-01 12:40:03 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-05-01 12:40:03 +0200 |
commit | b870c233eed3e2ea085d41daf4c099f20b9794d4 (patch) | |
tree | 6703dbb336be1e69c034b7236850924c603f88ed | |
parent | 95f269ae3025283daeb838a4197c183a92d1164d (diff) | |
parent | 7bdacf8fdebbf38ecdc797d2b67506e3eeffb5a6 (diff) | |
download | AntennaPod-b870c233eed3e2ea085d41daf4c099f20b9794d4.zip |
Merge pull request #4020 from ebraminio/rtl
Adopt the project with ViewPager2
19 files changed, 370 insertions, 232 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/com/google/android/material/bottomsheet/ViewPagerBottomSheetBehavior.java b/app/src/main/java/com/google/android/material/bottomsheet/ViewPagerBottomSheetBehavior.java index 1667006a5..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 @@ -4,8 +4,10 @@ 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.recyclerview.widget.RecyclerView; +import androidx.viewpager2.widget.ViewPager2; import java.lang.ref.WeakReference; @@ -31,9 +33,10 @@ public class ViewPagerBottomSheetBehavior<V extends View> extends BottomSheetBeh return view; } - if (view instanceof ViewPager) { - ViewPager viewPager = (ViewPager) view; - View currentViewPagerChild = viewPager.getChildAt(viewPager.getCurrentItem()); + if (view instanceof ViewPager2) { + ViewPager2 viewPager = (ViewPager2) view; + RecyclerView recycler = (RecyclerView) viewPager.getChildAt(0); + View currentViewPagerChild = recycler.getChildAt(viewPager.getCurrentItem()); if (currentViewPagerChild != null) { return findScrollingChild(currentViewPagerChild); } 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..e905ccd95 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((int) 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/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,11 +86,16 @@ 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(); if (disposable != null) { 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 @@ -51,6 +51,12 @@ public class DownloadLogFragment extends ListFragment { } @Override + public void onResume() { + super.onResume(); + setHasOptionsMenu(true); + } + + @Override public void onStop() { super.onStop(); if (disposable != null) { @@ -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/DownloadsFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/DownloadsFragment.java index 6f537be96..cf0c6db90 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,30 @@ 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; + default: + break; + } + }).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, false); return root; } @@ -73,56 +94,29 @@ 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 FragmentPagerAdapter { - - final Resources resources; + public static class DownloadsPagerAdapter extends FragmentStateAdapter { - 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..e98890627 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,32 @@ 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_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; + default: + break; + } + }).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, false); + return rootView; } @@ -64,52 +89,29 @@ 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); - } - - public class EpisodesPagerAdapter extends FragmentPagerAdapter { + static 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: + case POS_NEW_EPISODES: return new NewEpisodesFragment(); - case 1: + case POS_ALL_EPISODES: return new AllEpisodesFragment(); default: + case POS_FAV_EPISODES: return new FavoriteEpisodesFragment(); } } @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/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); } 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..0c7eb23d2 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.setCurrentItem(feedItemPos); + pager.setAdapter(new ItemPagerAdapter(this)); + pager.setCurrentItem(feedItemPos, false); 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/java/de/danoeh/antennapod/fragment/gpodnet/GpodnetMainFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/GpodnetMainFragment.java index 3ebdd80d8..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 @@ -4,13 +4,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.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 +22,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 +35,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 4b8cf1e1c..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 @@ -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,35 @@ 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(R.string.playback_statistics_label); + break; + case POS_SPACE_TAKEN: + tab.setText(R.string.download_statistics_label); + break; + default: + break; + } + }).attach(); rootView.findViewById(R.id.toolbar).setVisibility(View.GONE); @@ -60,39 +70,27 @@ public class StatisticsFragment extends Fragment { ((PreferenceActivity) getActivity()).getSupportActionBar().setTitle(R.string.statistics_label); } - public static class StatisticsPagerAdapter extends FragmentPagerAdapter { - - private final Resources resources; + public static class StatisticsPagerAdapter extends FragmentStateAdapter { - 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(); + public Fragment createFragment(int position) { + switch (position) { + case POS_LISTENED_HOURS: + return new PlaybackStatisticsFragment(); + default: + case POS_SPACE_TAKEN: + return new DownloadStatisticsFragment(); } } @Override - public int getCount() { + public int getItemCount() { return TOTAL_COUNT; } - - @Override - public CharSequence getPageTitle(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); - default: - return super.getPageTitle(position); - } - } } } 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..a4daa9109 --- /dev/null +++ b/app/src/main/java/de/danoeh/antennapod/view/NestedScrollableHost.java @@ -0,0 +1,111 @@ +/* + * 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 + * And modified for our need + */ + +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.view.ViewParent; +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; +import static androidx.viewpager2.widget.ViewPager2.ORIENTATION_VERTICAL; + +/** + * 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. + * + * <p>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; + } + + public boolean onInterceptTouchEvent(MotionEvent e) { + ViewPager2 parentViewPager = getParentViewPager(); + if (parentViewPager == null) { + return super.onInterceptTouchEvent(e); + } + + ViewParent parent = getParent(); + int orientation = parentViewPager.getOrientation(); + + if (e.getAction() == MotionEvent.ACTION_DOWN) { + initialX = e.getX(); + initialY = e.getY(); + parent.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) { + int value = isVpHorizontal ? dy : dx; + if (isVpHorizontal == (scaledDy > scaledDx)) { + // 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 { + parent.requestDisallowInterceptTouchEvent(child.canScrollVertically(direction)); + } + } + } + + return super.onInterceptTouchEvent(e); + } +} 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..240a565c8 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,20 @@ 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.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(); @@ -38,26 +43,31 @@ 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() { + 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 (isLocaleRtl) { + 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"/> - <androidx.viewpager.widget.ViewPager + <androidx.viewpager2.widget.ViewPager2 android:id="@+id/pager" android:layout_width="match_parent" android:layout_height="0dp" 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 @@ </LinearLayout> - <de.danoeh.antennapod.view.ShownotesWebView - android:id="@+id/webvDescription" - android:layout_width="match_parent" + <de.danoeh.antennapod.view.NestedScrollableHost android:layout_below="@id/header" - android:layout_height="match_parent" - android:foreground="?android:windowContentOverlay" /> + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <de.danoeh.antennapod.view.ShownotesWebView + android:id="@+id/webvDescription" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:foreground="?android:windowContentOverlay" /> + + </de.danoeh.antennapod.view.NestedScrollableHost> <FrameLayout android:layout_width="match_parent" 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"/> - <androidx.viewpager.widget.ViewPager + <androidx.viewpager2.widget.ViewPager2 android:id="@+id/pager" android:layout_width="match_parent" android:layout_height="match_parent"/> diff --git a/app/src/main/res/layout/item_description_fragment.xml b/app/src/main/res/layout/item_description_fragment.xml index 96382eae3..3766cf805 100644 --- a/app/src/main/res/layout/item_description_fragment.xml +++ b/app/src/main/res/layout/item_description_fragment.xml @@ -1,11 +1,18 @@ <?xml version="1.0" encoding="utf-8"?> -<androidx.core.widget.NestedScrollView -xmlns:android="http://schemas.android.com/apk/res/android" -android:layout_width="match_parent" -android:layout_height="match_parent" -android:fillViewport="false"> - <de.danoeh.antennapod.view.ShownotesWebView +<androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:fillViewport="false"> + + <de.danoeh.antennapod.view.NestedScrollableHost + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <de.danoeh.antennapod.view.ShownotesWebView android:id="@+id/webview" android:layout_width="match_parent" - android:layout_height="wrap_content"/> + android:layout_height="wrap_content" /> + + </de.danoeh.antennapod.view.NestedScrollableHost> + </androidx.core.widget.NestedScrollView>
\ No newline at end of file diff --git a/app/src/main/res/layout/pager_fragment.xml b/app/src/main/res/layout/pager_fragment.xml index 6a642caae..f7bf058c4 100644 --- a/app/src/main/res/layout/pager_fragment.xml +++ b/app/src/main/res/layout/pager_fragment.xml @@ -21,9 +21,9 @@ app:tabMode="fixed" app:tabGravity="fill"/> - <androidx.viewpager.widget.ViewPager + <androidx.viewpager2.widget.ViewPager2 android:id="@+id/viewpager" android:layout_width="match_parent" - android:layout_height="wrap_content"/> + android:layout_height="match_parent"/> </LinearLayout> diff --git a/app/src/main/res/layout/statistics_fragment.xml b/app/src/main/res/layout/statistics_fragment.xml index e69bc1a70..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" /> - <androidx.viewpager.widget.ViewPager + <androidx.viewpager2.widget.ViewPager2 android:id="@+id/viewpager" android:layout_width="match_parent" android:layout_height="0px" |