Merge pull request #4814 from Isira-Seneviratne/Use_view_binding_in_fragments

Use view binding in fragments.
This commit is contained in:
Stypox 2021-01-14 14:40:19 +01:00 committed by GitHub
commit 94b086de20
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 545 additions and 533 deletions

View file

@ -7,13 +7,14 @@ import android.view.MenuInflater;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import org.schabi.newpipe.R; import org.schabi.newpipe.R;
import org.schabi.newpipe.databinding.FragmentLicensesBinding;
import org.schabi.newpipe.databinding.ItemSoftwareComponentBinding;
import org.schabi.newpipe.util.ShareUtils; import org.schabi.newpipe.util.ShareUtils;
import java.io.Serializable; import java.io.Serializable;
@ -67,43 +68,42 @@ public class LicenseFragment extends Fragment {
@Nullable @Nullable
@Override @Override
public View onCreateView(final LayoutInflater inflater, @Nullable final ViewGroup container, public View onCreateView(@NonNull final LayoutInflater inflater,
@Nullable final ViewGroup container,
@Nullable final Bundle savedInstanceState) { @Nullable final Bundle savedInstanceState) {
final View rootView = inflater.inflate(R.layout.fragment_licenses, container, false); final FragmentLicensesBinding binding = FragmentLicensesBinding
final ViewGroup softwareComponentsView = rootView.findViewById(R.id.software_components); .inflate(inflater, container, false);
final View licenseLink = rootView.findViewById(R.id.app_read_license); binding.appReadLicense.setOnClickListener(v -> {
licenseLink.setOnClickListener(v -> {
activeLicense = StandardLicenses.GPL3; activeLicense = StandardLicenses.GPL3;
compositeDisposable.add(LicenseFragmentHelper.showLicense(getActivity(), compositeDisposable.add(LicenseFragmentHelper.showLicense(getActivity(),
StandardLicenses.GPL3)); StandardLicenses.GPL3));
}); });
for (final SoftwareComponent component : softwareComponents) { for (final SoftwareComponent component : softwareComponents) {
final View componentView = inflater final ItemSoftwareComponentBinding componentBinding = ItemSoftwareComponentBinding
.inflate(R.layout.item_software_component, container, false); .inflate(inflater, container, false);
final TextView softwareName = componentView.findViewById(R.id.name); componentBinding.name.setText(component.getName());
final TextView copyright = componentView.findViewById(R.id.copyright); componentBinding.copyright.setText(getString(R.string.copyright,
softwareName.setText(component.getName());
copyright.setText(getString(R.string.copyright,
component.getYears(), component.getYears(),
component.getCopyrightOwner(), component.getCopyrightOwner(),
component.getLicense().getAbbreviation())); component.getLicense().getAbbreviation()));
componentView.setTag(component); final View root = componentBinding.getRoot();
componentView.setOnClickListener(v -> { root.setTag(component);
root.setOnClickListener(v -> {
activeLicense = component.getLicense(); activeLicense = component.getLicense();
compositeDisposable.add(LicenseFragmentHelper.showLicense(getActivity(), compositeDisposable.add(LicenseFragmentHelper.showLicense(getActivity(),
component.getLicense())); component.getLicense()));
}); });
softwareComponentsView.addView(componentView); binding.softwareComponents.addView(root);
registerForContextMenu(componentView); registerForContextMenu(root);
} }
if (activeLicense != null) { if (activeLicense != null) {
compositeDisposable.add(LicenseFragmentHelper.showLicense(getActivity(), compositeDisposable.add(LicenseFragmentHelper.showLicense(getActivity(),
activeLicense)); activeLicense));
} }
return rootView; return binding.getRoot();
} }
@Override @Override

View file

@ -16,12 +16,8 @@ import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.AdapterView; import android.widget.AdapterView;
import android.widget.EditText;
import android.widget.RadioButton;
import android.widget.RadioGroup; import android.widget.RadioGroup;
import android.widget.SeekBar; import android.widget.SeekBar;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import androidx.annotation.IdRes; import androidx.annotation.IdRes;
@ -40,6 +36,7 @@ import com.nononsenseapps.filepicker.Utils;
import org.schabi.newpipe.MainActivity; import org.schabi.newpipe.MainActivity;
import org.schabi.newpipe.R; import org.schabi.newpipe.R;
import org.schabi.newpipe.RouterActivity; import org.schabi.newpipe.RouterActivity;
import org.schabi.newpipe.databinding.DownloadDialogBinding;
import org.schabi.newpipe.extractor.MediaFormat; import org.schabi.newpipe.extractor.MediaFormat;
import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.localization.Localization; import org.schabi.newpipe.extractor.localization.Localization;
@ -116,11 +113,7 @@ public class DownloadDialog extends DialogFragment
private final CompositeDisposable disposables = new CompositeDisposable(); private final CompositeDisposable disposables = new CompositeDisposable();
private EditText nameEditText; private DownloadDialogBinding dialogBinding;
private Spinner streamsSpinner;
private RadioGroup radioStreamsGroup;
private TextView threadsCountTextView;
private SeekBar threadsSeekBar;
private SharedPreferences prefs; private SharedPreferences prefs;
@ -277,38 +270,35 @@ public class DownloadDialog extends DialogFragment
@Override @Override
public void onViewCreated(@NonNull final View view, @Nullable final Bundle savedInstanceState) { public void onViewCreated(@NonNull final View view, @Nullable final Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState); super.onViewCreated(view, savedInstanceState);
nameEditText = view.findViewById(R.id.file_name); dialogBinding = DownloadDialogBinding.bind(view);
nameEditText.setText(FilenameUtils.createFilename(getContext(), currentInfo.getName()));
dialogBinding.fileName.setText(FilenameUtils.createFilename(getContext(),
currentInfo.getName()));
selectedAudioIndex = ListHelper selectedAudioIndex = ListHelper
.getDefaultAudioFormat(getContext(), currentInfo.getAudioStreams()); .getDefaultAudioFormat(getContext(), currentInfo.getAudioStreams());
selectedSubtitleIndex = getSubtitleIndexBy(subtitleStreamsAdapter.getAll()); selectedSubtitleIndex = getSubtitleIndexBy(subtitleStreamsAdapter.getAll());
streamsSpinner = view.findViewById(R.id.quality_spinner); dialogBinding.qualitySpinner.setOnItemSelectedListener(this);
streamsSpinner.setOnItemSelectedListener(this);
threadsCountTextView = view.findViewById(R.id.threads_count); dialogBinding.videoAudioGroup.setOnCheckedChangeListener(this);
threadsSeekBar = view.findViewById(R.id.threads);
radioStreamsGroup = view.findViewById(R.id.video_audio_group); initToolbar(dialogBinding.toolbarLayout.toolbar);
radioStreamsGroup.setOnCheckedChangeListener(this);
initToolbar(view.findViewById(R.id.toolbar));
setupDownloadOptions(); setupDownloadOptions();
prefs = PreferenceManager.getDefaultSharedPreferences(requireContext()); prefs = PreferenceManager.getDefaultSharedPreferences(requireContext());
final int threads = prefs.getInt(getString(R.string.default_download_threads), 3); final int threads = prefs.getInt(getString(R.string.default_download_threads), 3);
threadsCountTextView.setText(String.valueOf(threads)); dialogBinding.threadsCount.setText(String.valueOf(threads));
threadsSeekBar.setProgress(threads - 1); dialogBinding.threads.setProgress(threads - 1);
threadsSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { dialogBinding.threads.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override @Override
public void onProgressChanged(final SeekBar seekbar, final int progress, public void onProgressChanged(final SeekBar seekbar, final int progress,
final boolean fromUser) { final boolean fromUser) {
final int newProgress = progress + 1; final int newProgress = progress + 1;
prefs.edit().putInt(getString(R.string.default_download_threads), newProgress) prefs.edit().putInt(getString(R.string.default_download_threads), newProgress)
.apply(); .apply();
threadsCountTextView.setText(String.valueOf(newProgress)); dialogBinding.threadsCount.setText(String.valueOf(newProgress));
} }
@Override @Override
@ -326,19 +316,19 @@ public class DownloadDialog extends DialogFragment
disposables.add(StreamSizeWrapper.fetchSizeForWrapper(wrappedVideoStreams) disposables.add(StreamSizeWrapper.fetchSizeForWrapper(wrappedVideoStreams)
.subscribe(result -> { .subscribe(result -> {
if (radioStreamsGroup.getCheckedRadioButtonId() == R.id.video_button) { if (dialogBinding.videoAudioGroup.getCheckedRadioButtonId() == R.id.video_button) {
setupVideoSpinner(); setupVideoSpinner();
} }
})); }));
disposables.add(StreamSizeWrapper.fetchSizeForWrapper(wrappedAudioStreams) disposables.add(StreamSizeWrapper.fetchSizeForWrapper(wrappedAudioStreams)
.subscribe(result -> { .subscribe(result -> {
if (radioStreamsGroup.getCheckedRadioButtonId() == R.id.audio_button) { if (dialogBinding.videoAudioGroup.getCheckedRadioButtonId() == R.id.audio_button) {
setupAudioSpinner(); setupAudioSpinner();
} }
})); }));
disposables.add(StreamSizeWrapper.fetchSizeForWrapper(wrappedSubtitleStreams) disposables.add(StreamSizeWrapper.fetchSizeForWrapper(wrappedSubtitleStreams)
.subscribe(result -> { .subscribe(result -> {
if (radioStreamsGroup.getCheckedRadioButtonId() == R.id.subtitle_button) { if (dialogBinding.videoAudioGroup.getCheckedRadioButtonId() == R.id.subtitle_button) {
setupSubtitleSpinner(); setupSubtitleSpinner();
} }
})); }));
@ -350,6 +340,12 @@ public class DownloadDialog extends DialogFragment
disposables.clear(); disposables.clear();
} }
@Override
public void onDestroyView() {
dialogBinding = null;
super.onDestroyView();
}
/*////////////////////////////////////////////////////////////////////////// /*//////////////////////////////////////////////////////////////////////////
// Radio group Video&Audio options - Listener // Radio group Video&Audio options - Listener
//////////////////////////////////////////////////////////////////////////*/ //////////////////////////////////////////////////////////////////////////*/
@ -429,8 +425,8 @@ public class DownloadDialog extends DialogFragment
return; return;
} }
streamsSpinner.setAdapter(audioStreamsAdapter); dialogBinding.qualitySpinner.setAdapter(audioStreamsAdapter);
streamsSpinner.setSelection(selectedAudioIndex); dialogBinding.qualitySpinner.setSelection(selectedAudioIndex);
setRadioButtonsState(true); setRadioButtonsState(true);
} }
@ -439,8 +435,8 @@ public class DownloadDialog extends DialogFragment
return; return;
} }
streamsSpinner.setAdapter(videoStreamsAdapter); dialogBinding.qualitySpinner.setAdapter(videoStreamsAdapter);
streamsSpinner.setSelection(selectedVideoIndex); dialogBinding.qualitySpinner.setSelection(selectedVideoIndex);
setRadioButtonsState(true); setRadioButtonsState(true);
} }
@ -449,8 +445,8 @@ public class DownloadDialog extends DialogFragment
return; return;
} }
streamsSpinner.setAdapter(subtitleStreamsAdapter); dialogBinding.qualitySpinner.setAdapter(subtitleStreamsAdapter);
streamsSpinner.setSelection(selectedSubtitleIndex); dialogBinding.qualitySpinner.setSelection(selectedSubtitleIndex);
setRadioButtonsState(true); setRadioButtonsState(true);
} }
@ -475,7 +471,7 @@ public class DownloadDialog extends DialogFragment
break; break;
} }
threadsSeekBar.setEnabled(flag); dialogBinding.threads.setEnabled(flag);
} }
@Override @Override
@ -486,7 +482,7 @@ public class DownloadDialog extends DialogFragment
+ "parent = [" + parent + "], view = [" + view + "], " + "parent = [" + parent + "], view = [" + view + "], "
+ "position = [" + position + "], id = [" + id + "]"); + "position = [" + position + "], id = [" + id + "]");
} }
switch (radioStreamsGroup.getCheckedRadioButtonId()) { switch (dialogBinding.videoAudioGroup.getCheckedRadioButtonId()) {
case R.id.audio_button: case R.id.audio_button:
selectedAudioIndex = position; selectedAudioIndex = position;
break; break;
@ -506,16 +502,14 @@ public class DownloadDialog extends DialogFragment
protected void setupDownloadOptions() { protected void setupDownloadOptions() {
setRadioButtonsState(false); setRadioButtonsState(false);
final RadioButton audioButton = radioStreamsGroup.findViewById(R.id.audio_button);
final RadioButton videoButton = radioStreamsGroup.findViewById(R.id.video_button);
final RadioButton subtitleButton = radioStreamsGroup.findViewById(R.id.subtitle_button);
final boolean isVideoStreamsAvailable = videoStreamsAdapter.getCount() > 0; final boolean isVideoStreamsAvailable = videoStreamsAdapter.getCount() > 0;
final boolean isAudioStreamsAvailable = audioStreamsAdapter.getCount() > 0; final boolean isAudioStreamsAvailable = audioStreamsAdapter.getCount() > 0;
final boolean isSubtitleStreamsAvailable = subtitleStreamsAdapter.getCount() > 0; final boolean isSubtitleStreamsAvailable = subtitleStreamsAdapter.getCount() > 0;
audioButton.setVisibility(isAudioStreamsAvailable ? View.VISIBLE : View.GONE); dialogBinding.audioButton.setVisibility(isAudioStreamsAvailable ? View.VISIBLE : View.GONE);
videoButton.setVisibility(isVideoStreamsAvailable ? View.VISIBLE : View.GONE); dialogBinding.videoButton.setVisibility(isVideoStreamsAvailable ? View.VISIBLE : View.GONE);
subtitleButton.setVisibility(isSubtitleStreamsAvailable ? View.VISIBLE : View.GONE); dialogBinding.subtitleButton.setVisibility(isSubtitleStreamsAvailable
? View.VISIBLE : View.GONE);
prefs = PreferenceManager.getDefaultSharedPreferences(getContext()); prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
final String defaultMedia = prefs.getString(getString(R.string.last_used_download_type), final String defaultMedia = prefs.getString(getString(R.string.last_used_download_type),
@ -523,24 +517,24 @@ public class DownloadDialog extends DialogFragment
if (isVideoStreamsAvailable if (isVideoStreamsAvailable
&& (defaultMedia.equals(getString(R.string.last_download_type_video_key)))) { && (defaultMedia.equals(getString(R.string.last_download_type_video_key)))) {
videoButton.setChecked(true); dialogBinding.videoButton.setChecked(true);
setupVideoSpinner(); setupVideoSpinner();
} else if (isAudioStreamsAvailable } else if (isAudioStreamsAvailable
&& (defaultMedia.equals(getString(R.string.last_download_type_audio_key)))) { && (defaultMedia.equals(getString(R.string.last_download_type_audio_key)))) {
audioButton.setChecked(true); dialogBinding.audioButton.setChecked(true);
setupAudioSpinner(); setupAudioSpinner();
} else if (isSubtitleStreamsAvailable } else if (isSubtitleStreamsAvailable
&& (defaultMedia.equals(getString(R.string.last_download_type_subtitle_key)))) { && (defaultMedia.equals(getString(R.string.last_download_type_subtitle_key)))) {
subtitleButton.setChecked(true); dialogBinding.subtitleButton.setChecked(true);
setupSubtitleSpinner(); setupSubtitleSpinner();
} else if (isVideoStreamsAvailable) { } else if (isVideoStreamsAvailable) {
videoButton.setChecked(true); dialogBinding.videoButton.setChecked(true);
setupVideoSpinner(); setupVideoSpinner();
} else if (isAudioStreamsAvailable) { } else if (isAudioStreamsAvailable) {
audioButton.setChecked(true); dialogBinding.audioButton.setChecked(true);
setupAudioSpinner(); setupAudioSpinner();
} else if (isSubtitleStreamsAvailable) { } else if (isSubtitleStreamsAvailable) {
subtitleButton.setChecked(true); dialogBinding.subtitleButton.setChecked(true);
setupSubtitleSpinner(); setupSubtitleSpinner();
} else { } else {
Toast.makeText(getContext(), R.string.no_streams_available_download, Toast.makeText(getContext(), R.string.no_streams_available_download,
@ -550,9 +544,9 @@ public class DownloadDialog extends DialogFragment
} }
private void setRadioButtonsState(final boolean enabled) { private void setRadioButtonsState(final boolean enabled) {
radioStreamsGroup.findViewById(R.id.audio_button).setEnabled(enabled); dialogBinding.audioButton.setEnabled(enabled);
radioStreamsGroup.findViewById(R.id.video_button).setEnabled(enabled); dialogBinding.videoButton.setEnabled(enabled);
radioStreamsGroup.findViewById(R.id.subtitle_button).setEnabled(enabled); dialogBinding.subtitleButton.setEnabled(enabled);
} }
private int getSubtitleIndexBy(final List<SubtitlesStream> streams) { private int getSubtitleIndexBy(final List<SubtitlesStream> streams) {
@ -582,7 +576,7 @@ public class DownloadDialog extends DialogFragment
} }
private String getNameEditText() { private String getNameEditText() {
final String str = nameEditText.getText().toString().trim(); final String str = dialogBinding.fileName.getText().toString().trim();
return FilenameUtils.createFilename(context, str.isEmpty() ? currentInfo.getName() : str); return FilenameUtils.createFilename(context, str.isEmpty() ? currentInfo.getName() : str);
} }
@ -619,7 +613,7 @@ public class DownloadDialog extends DialogFragment
String filename = getNameEditText().concat("."); String filename = getNameEditText().concat(".");
switch (radioStreamsGroup.getCheckedRadioButtonId()) { switch (dialogBinding.videoAudioGroup.getCheckedRadioButtonId()) {
case R.id.audio_button: case R.id.audio_button:
selectedMediaType = getString(R.string.last_download_type_audio_key); selectedMediaType = getString(R.string.last_download_type_audio_key);
mainStorage = mainStorageAudio; mainStorage = mainStorageAudio;
@ -669,7 +663,7 @@ public class DownloadDialog extends DialogFragment
filename, mime); filename, mime);
} else { } else {
File initialSavePath; File initialSavePath;
if (radioStreamsGroup.getCheckedRadioButtonId() == R.id.audio_button) { if (dialogBinding.videoAudioGroup.getCheckedRadioButtonId() == R.id.audio_button) {
initialSavePath = NewPipeSettings.getDir(Environment.DIRECTORY_MUSIC); initialSavePath = NewPipeSettings.getDir(Environment.DIRECTORY_MUSIC);
} else { } else {
initialSavePath = NewPipeSettings.getDir(Environment.DIRECTORY_MOVIES); initialSavePath = NewPipeSettings.getDir(Environment.DIRECTORY_MOVIES);
@ -862,7 +856,7 @@ public class DownloadDialog extends DialogFragment
final Stream selectedStream; final Stream selectedStream;
Stream secondaryStream = null; Stream secondaryStream = null;
final char kind; final char kind;
int threads = threadsSeekBar.getProgress() + 1; int threads = dialogBinding.threads.getProgress() + 1;
final String[] urls; final String[] urls;
final MissionRecoveryInfo[] recoveryInfo; final MissionRecoveryInfo[] recoveryInfo;
String psName = null; String psName = null;
@ -870,7 +864,7 @@ public class DownloadDialog extends DialogFragment
long nearLength = 0; long nearLength = 0;
// more download logic: select muxer, subtitle converter, etc. // more download logic: select muxer, subtitle converter, etc.
switch (radioStreamsGroup.getCheckedRadioButtonId()) { switch (dialogBinding.videoAudioGroup.getCheckedRadioButtonId()) {
case R.id.audio_button: case R.id.audio_button:
kind = 'a'; kind = 'a';
selectedStream = audioStreamsAdapter.getItem(selectedAudioIndex); selectedStream = audioStreamsAdapter.getItem(selectedAudioIndex);

View file

@ -19,12 +19,12 @@ import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentStatePagerAdapterMenuWorkaround; import androidx.fragment.app.FragmentStatePagerAdapterMenuWorkaround;
import androidx.preference.PreferenceManager; import androidx.preference.PreferenceManager;
import androidx.viewpager.widget.ViewPager;
import com.google.android.material.tabs.TabLayout; import com.google.android.material.tabs.TabLayout;
import org.schabi.newpipe.BaseFragment; import org.schabi.newpipe.BaseFragment;
import org.schabi.newpipe.R; import org.schabi.newpipe.R;
import org.schabi.newpipe.databinding.FragmentMainBinding;
import org.schabi.newpipe.extractor.exceptions.ExtractionException; import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.report.ErrorActivity; import org.schabi.newpipe.report.ErrorActivity;
import org.schabi.newpipe.report.ErrorInfo; import org.schabi.newpipe.report.ErrorInfo;
@ -34,15 +34,13 @@ import org.schabi.newpipe.settings.tabs.TabsManager;
import org.schabi.newpipe.util.NavigationHelper; import org.schabi.newpipe.util.NavigationHelper;
import org.schabi.newpipe.util.ServiceHelper; import org.schabi.newpipe.util.ServiceHelper;
import org.schabi.newpipe.util.ThemeHelper; import org.schabi.newpipe.util.ThemeHelper;
import org.schabi.newpipe.views.ScrollableTabLayout;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
public class MainFragment extends BaseFragment implements TabLayout.OnTabSelectedListener { public class MainFragment extends BaseFragment implements TabLayout.OnTabSelectedListener {
private ViewPager viewPager; private FragmentMainBinding binding;
private SelectedTabsPagerAdapter pagerAdapter; private SelectedTabsPagerAdapter pagerAdapter;
private ScrollableTabLayout tabLayout;
private final List<Tab> tabsList = new ArrayList<>(); private final List<Tab> tabsList = new ArrayList<>();
private TabsManager tabsManager; private TabsManager tabsManager;
@ -90,13 +88,12 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte
protected void initViews(final View rootView, final Bundle savedInstanceState) { protected void initViews(final View rootView, final Bundle savedInstanceState) {
super.initViews(rootView, savedInstanceState); super.initViews(rootView, savedInstanceState);
tabLayout = rootView.findViewById(R.id.main_tab_layout); binding = FragmentMainBinding.bind(rootView);
viewPager = rootView.findViewById(R.id.pager);
tabLayout.setTabIconTint(ColorStateList.valueOf( binding.mainTabLayout.setTabIconTint(ColorStateList.valueOf(
ThemeHelper.resolveColorFromAttr(requireContext(), R.attr.colorAccent))); ThemeHelper.resolveColorFromAttr(requireContext(), R.attr.colorAccent)));
tabLayout.setupWithViewPager(viewPager); binding.mainTabLayout.setupWithViewPager(binding.pager);
tabLayout.addOnTabSelectedListener(this); binding.mainTabLayout.addOnTabSelectedListener(this);
setupTabs(); setupTabs();
} }
@ -120,8 +117,14 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte
public void onDestroy() { public void onDestroy() {
super.onDestroy(); super.onDestroy();
tabsManager.unsetSavedTabsListener(); tabsManager.unsetSavedTabsListener();
if (viewPager != null) { }
viewPager.setAdapter(null);
@Override
public void onDestroyView() {
super.onDestroyView();
if (binding != null) {
binding.pager.setAdapter(null);
binding = null;
} }
} }
@ -172,19 +175,19 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte
getChildFragmentManager(), tabsList); getChildFragmentManager(), tabsList);
} }
viewPager.setAdapter(null); binding.pager.setAdapter(null);
viewPager.setOffscreenPageLimit(tabsList.size()); binding.pager.setOffscreenPageLimit(tabsList.size());
viewPager.setAdapter(pagerAdapter); binding.pager.setAdapter(pagerAdapter);
updateTabsIconAndDescription(); updateTabsIconAndDescription();
updateTitleForTab(viewPager.getCurrentItem()); updateTitleForTab(binding.pager.getCurrentItem());
hasTabsChanged = false; hasTabsChanged = false;
} }
private void updateTabsIconAndDescription() { private void updateTabsIconAndDescription() {
for (int i = 0; i < tabsList.size(); i++) { for (int i = 0; i < tabsList.size(); i++) {
final TabLayout.Tab tabToSet = tabLayout.getTabAt(i); final TabLayout.Tab tabToSet = binding.mainTabLayout.getTabAt(i);
if (tabToSet != null) { if (tabToSet != null) {
final Tab tab = tabsList.get(i); final Tab tab = tabsList.get(i);
tabToSet.setIcon(tab.getTabIconRes(requireContext())); tabToSet.setIcon(tab.getTabIconRes(requireContext()));

View file

@ -12,13 +12,16 @@ import android.view.MenuInflater;
import android.view.View; import android.view.View;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatActivity;
import androidx.preference.PreferenceManager; import androidx.preference.PreferenceManager;
import androidx.recyclerview.widget.GridLayoutManager; import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import androidx.viewbinding.ViewBinding;
import org.schabi.newpipe.R; import org.schabi.newpipe.R;
import org.schabi.newpipe.databinding.PignateFooterBinding;
import org.schabi.newpipe.extractor.InfoItem; import org.schabi.newpipe.extractor.InfoItem;
import org.schabi.newpipe.extractor.channel.ChannelInfoItem; import org.schabi.newpipe.extractor.channel.ChannelInfoItem;
import org.schabi.newpipe.extractor.comments.CommentsInfoItem; import org.schabi.newpipe.extractor.comments.CommentsInfoItem;
@ -215,12 +218,13 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I>
// Init // Init
//////////////////////////////////////////////////////////////////////////*/ //////////////////////////////////////////////////////////////////////////*/
protected View getListHeader() { @Nullable
protected ViewBinding getListHeader() {
return null; return null;
} }
protected View getListFooter() { protected ViewBinding getListFooter() {
return activity.getLayoutInflater().inflate(R.layout.pignate_footer, itemsList, false); return PignateFooterBinding.inflate(activity.getLayoutInflater(), itemsList, false);
} }
protected RecyclerView.LayoutManager getListLayoutManager() { protected RecyclerView.LayoutManager getListLayoutManager() {
@ -247,8 +251,12 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I>
itemsList.setLayoutManager(useGrid ? getGridLayoutManager() : getListLayoutManager()); itemsList.setLayoutManager(useGrid ? getGridLayoutManager() : getListLayoutManager());
infoListAdapter.setUseGridVariant(useGrid); infoListAdapter.setUseGridVariant(useGrid);
infoListAdapter.setFooter(getListFooter()); infoListAdapter.setFooter(getListFooter().getRoot());
infoListAdapter.setHeader(getListHeader());
final ViewBinding listHeader = getListHeader();
if (listHeader != null) {
infoListAdapter.setHeader(listHeader.getRoot());
}
itemsList.setAdapter(infoListAdapter); itemsList.setAdapter(infoListAdapter);
} }

View file

@ -14,20 +14,21 @@ import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.Button; import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatActivity;
import androidx.core.content.ContextCompat; import androidx.core.content.ContextCompat;
import androidx.viewbinding.ViewBinding;
import com.jakewharton.rxbinding4.view.RxView; import com.jakewharton.rxbinding4.view.RxView;
import org.schabi.newpipe.R; import org.schabi.newpipe.R;
import org.schabi.newpipe.database.subscription.SubscriptionEntity; import org.schabi.newpipe.database.subscription.SubscriptionEntity;
import org.schabi.newpipe.databinding.ChannelHeaderBinding;
import org.schabi.newpipe.databinding.FragmentChannelBinding;
import org.schabi.newpipe.databinding.PlaylistControlBinding;
import org.schabi.newpipe.extractor.InfoItem; import org.schabi.newpipe.extractor.InfoItem;
import org.schabi.newpipe.extractor.ListExtractor; import org.schabi.newpipe.extractor.ListExtractor;
import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.NewPipe;
@ -78,22 +79,12 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo>
//////////////////////////////////////////////////////////////////////////*/ //////////////////////////////////////////////////////////////////////////*/
private SubscriptionManager subscriptionManager; private SubscriptionManager subscriptionManager;
private View headerRootLayout;
private ImageView headerChannelBanner; private FragmentChannelBinding channelBinding;
private ImageView headerAvatarView; private ChannelHeaderBinding headerBinding;
private TextView headerTitleView; private PlaylistControlBinding playlistControlBinding;
private ImageView headerSubChannelAvatarView;
private TextView headerSubChannelTitleView;
private TextView headerSubscribersTextView;
private Button headerSubscribeButton;
private View playlistCtrl;
private LinearLayout headerPlayAllButton;
private LinearLayout headerPopupButton;
private LinearLayout headerBackgroundButton;
private MenuItem menuRssButton; private MenuItem menuRssButton;
private TextView contentNotSupportedTextView;
private TextView kaomojiTextView;
private TextView noVideosTextView;
public static ChannelFragment getInstance(final int serviceId, final String url, public static ChannelFragment getInstance(final int serviceId, final String url,
final String name) { final String name) {
@ -132,53 +123,45 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo>
@Override @Override
public void onViewCreated(final View rootView, final Bundle savedInstanceState) { public void onViewCreated(final View rootView, final Bundle savedInstanceState) {
super.onViewCreated(rootView, savedInstanceState); super.onViewCreated(rootView, savedInstanceState);
contentNotSupportedTextView = rootView.findViewById(R.id.error_content_not_supported); channelBinding = FragmentChannelBinding.bind(rootView);
kaomojiTextView = rootView.findViewById(R.id.channel_kaomoji);
noVideosTextView = rootView.findViewById(R.id.channel_no_videos);
} }
@Override @Override
public void onDestroy() { public void onDestroy() {
super.onDestroy(); super.onDestroy();
if (disposables != null) {
disposables.clear(); disposables.clear();
}
if (subscribeButtonMonitor != null) { if (subscribeButtonMonitor != null) {
subscribeButtonMonitor.dispose(); subscribeButtonMonitor.dispose();
} }
} }
@Override
public void onDestroyView() {
channelBinding = null;
headerBinding = null;
playlistControlBinding = null;
super.onDestroyView();
}
/*////////////////////////////////////////////////////////////////////////// /*//////////////////////////////////////////////////////////////////////////
// Init // Init
//////////////////////////////////////////////////////////////////////////*/ //////////////////////////////////////////////////////////////////////////*/
protected View getListHeader() { @Override
headerRootLayout = activity.getLayoutInflater() protected ViewBinding getListHeader() {
.inflate(R.layout.channel_header, itemsList, false); headerBinding = ChannelHeaderBinding
headerChannelBanner = headerRootLayout.findViewById(R.id.channel_banner_image); .inflate(activity.getLayoutInflater(), itemsList, false);
headerAvatarView = headerRootLayout.findViewById(R.id.channel_avatar_view); playlistControlBinding = headerBinding.playlistControl;
headerTitleView = headerRootLayout.findViewById(R.id.channel_title_view);
headerSubscribersTextView = headerRootLayout.findViewById(R.id.channel_subscriber_view);
headerSubscribeButton = headerRootLayout.findViewById(R.id.channel_subscribe_button);
playlistCtrl = headerRootLayout.findViewById(R.id.playlist_control);
headerSubChannelAvatarView =
headerRootLayout.findViewById(R.id.sub_channel_avatar_view);
headerSubChannelTitleView =
headerRootLayout.findViewById(R.id.sub_channel_title_view);
headerPlayAllButton = headerRootLayout.findViewById(R.id.playlist_ctrl_play_all_button); return headerBinding;
headerPopupButton = headerRootLayout.findViewById(R.id.playlist_ctrl_play_popup_button);
headerBackgroundButton = headerRootLayout.findViewById(R.id.playlist_ctrl_play_bg_button);
return headerRootLayout;
} }
@Override @Override
protected void initListeners() { protected void initListeners() {
super.initListeners(); super.initListeners();
headerSubChannelTitleView.setOnClickListener(this); headerBinding.subChannelTitleView.setOnClickListener(this);
headerSubChannelAvatarView.setOnClickListener(this); headerBinding.subChannelAvatarView.setOnClickListener(this);
} }
/*////////////////////////////////////////////////////////////////////////// /*//////////////////////////////////////////////////////////////////////////
@ -241,7 +224,7 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo>
private void monitorSubscription(final ChannelInfo info) { private void monitorSubscription(final ChannelInfo info) {
final Consumer<Throwable> onError = (Throwable throwable) -> { final Consumer<Throwable> onError = (Throwable throwable) -> {
animateView(headerSubscribeButton, false, 100); animateView(headerBinding.channelSubscribeButton, false, 100);
showSnackBarError(throwable, UserAction.SUBSCRIPTION, showSnackBarError(throwable, UserAction.SUBSCRIPTION,
NewPipe.getNameOfService(currentInfo.getServiceId()), NewPipe.getNameOfService(currentInfo.getServiceId()),
"Get subscription status", 0); "Get subscription status", 0);
@ -351,15 +334,15 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo>
info.getAvatarUrl(), info.getAvatarUrl(),
info.getDescription(), info.getDescription(),
info.getSubscriberCount()); info.getSubscriberCount());
subscribeButtonMonitor = monitorSubscribeButton(headerSubscribeButton, subscribeButtonMonitor = monitorSubscribeButton(
mapOnSubscribe(channel, info)); headerBinding.channelSubscribeButton, mapOnSubscribe(channel, info));
} else { } else {
if (DEBUG) { if (DEBUG) {
Log.d(TAG, "Found subscription to this channel!"); Log.d(TAG, "Found subscription to this channel!");
} }
final SubscriptionEntity subscription = subscriptionEntities.get(0); final SubscriptionEntity subscription = subscriptionEntities.get(0);
subscribeButtonMonitor = monitorSubscribeButton(headerSubscribeButton, subscribeButtonMonitor = monitorSubscribeButton(
mapOnUnsubscribe(subscription)); headerBinding.channelSubscribeButton, mapOnUnsubscribe(subscription));
} }
}; };
} }
@ -370,7 +353,8 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo>
+ "isSubscribed = [" + isSubscribed + "]"); + "isSubscribed = [" + isSubscribed + "]");
} }
final boolean isButtonVisible = headerSubscribeButton.getVisibility() == View.VISIBLE; final boolean isButtonVisible = headerBinding.channelSubscribeButton.getVisibility()
== View.VISIBLE;
final int backgroundDuration = isButtonVisible ? 300 : 0; final int backgroundDuration = isButtonVisible ? 300 : 0;
final int textDuration = isButtonVisible ? 200 : 0; final int textDuration = isButtonVisible ? 200 : 0;
@ -382,18 +366,21 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo>
final int subscribedText = ContextCompat.getColor(activity, R.color.subscribed_text_color); final int subscribedText = ContextCompat.getColor(activity, R.color.subscribed_text_color);
if (!isSubscribed) { if (!isSubscribed) {
headerSubscribeButton.setText(R.string.subscribe_button_title); headerBinding.channelSubscribeButton.setText(R.string.subscribe_button_title);
animateBackgroundColor(headerSubscribeButton, backgroundDuration, subscribedBackground, animateBackgroundColor(headerBinding.channelSubscribeButton, backgroundDuration,
subscribeBackground); subscribedBackground, subscribeBackground);
animateTextColor(headerSubscribeButton, textDuration, subscribedText, subscribeText); animateTextColor(headerBinding.channelSubscribeButton, textDuration, subscribedText,
subscribeText);
} else { } else {
headerSubscribeButton.setText(R.string.subscribed_button_title); headerBinding.channelSubscribeButton.setText(R.string.subscribed_button_title);
animateBackgroundColor(headerSubscribeButton, backgroundDuration, subscribeBackground, animateBackgroundColor(headerBinding.channelSubscribeButton, backgroundDuration,
subscribedBackground); subscribeBackground, subscribedBackground);
animateTextColor(headerSubscribeButton, textDuration, subscribeText, subscribedText); animateTextColor(headerBinding.channelSubscribeButton, textDuration, subscribeText,
subscribedText);
} }
animateView(headerSubscribeButton, AnimationUtils.Type.LIGHT_SCALE_AND_ALPHA, true, 100); animateView(headerBinding.channelSubscribeButton, AnimationUtils.Type.LIGHT_SCALE_AND_ALPHA,
true, 100);
} }
/*////////////////////////////////////////////////////////////////////////// /*//////////////////////////////////////////////////////////////////////////
@ -446,48 +433,49 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo>
public void showLoading() { public void showLoading() {
super.showLoading(); super.showLoading();
IMAGE_LOADER.cancelDisplayTask(headerChannelBanner); IMAGE_LOADER.cancelDisplayTask(headerBinding.channelBannerImage);
IMAGE_LOADER.cancelDisplayTask(headerAvatarView); IMAGE_LOADER.cancelDisplayTask(headerBinding.channelAvatarView);
IMAGE_LOADER.cancelDisplayTask(headerSubChannelAvatarView); IMAGE_LOADER.cancelDisplayTask(headerBinding.subChannelAvatarView);
animateView(headerSubscribeButton, false, 100); animateView(headerBinding.channelSubscribeButton, false, 100);
} }
@Override @Override
public void handleResult(@NonNull final ChannelInfo result) { public void handleResult(@NonNull final ChannelInfo result) {
super.handleResult(result); super.handleResult(result);
headerRootLayout.setVisibility(View.VISIBLE); headerBinding.getRoot().setVisibility(View.VISIBLE);
IMAGE_LOADER.displayImage(result.getBannerUrl(), headerChannelBanner, IMAGE_LOADER.displayImage(result.getBannerUrl(), headerBinding.channelBannerImage,
ImageDisplayConstants.DISPLAY_BANNER_OPTIONS); ImageDisplayConstants.DISPLAY_BANNER_OPTIONS);
IMAGE_LOADER.displayImage(result.getAvatarUrl(), headerAvatarView, IMAGE_LOADER.displayImage(result.getAvatarUrl(), headerBinding.channelAvatarView,
ImageDisplayConstants.DISPLAY_AVATAR_OPTIONS); ImageDisplayConstants.DISPLAY_AVATAR_OPTIONS);
IMAGE_LOADER.displayImage(result.getParentChannelAvatarUrl(), headerSubChannelAvatarView, IMAGE_LOADER.displayImage(result.getParentChannelAvatarUrl(),
headerBinding.subChannelAvatarView,
ImageDisplayConstants.DISPLAY_AVATAR_OPTIONS); ImageDisplayConstants.DISPLAY_AVATAR_OPTIONS);
headerSubscribersTextView.setVisibility(View.VISIBLE); headerBinding.channelSubscriberView.setVisibility(View.VISIBLE);
if (result.getSubscriberCount() >= 0) { if (result.getSubscriberCount() >= 0) {
headerSubscribersTextView.setText(Localization headerBinding.channelSubscriberView.setText(Localization
.shortSubscriberCount(activity, result.getSubscriberCount())); .shortSubscriberCount(activity, result.getSubscriberCount()));
} else { } else {
headerSubscribersTextView.setText(R.string.subscribers_count_not_available); headerBinding.channelSubscriberView.setText(R.string.subscribers_count_not_available);
} }
if (!TextUtils.isEmpty(currentInfo.getParentChannelName())) { if (!TextUtils.isEmpty(currentInfo.getParentChannelName())) {
headerSubChannelTitleView.setText(String.format( headerBinding.subChannelTitleView.setText(String.format(
getString(R.string.channel_created_by), getString(R.string.channel_created_by),
currentInfo.getParentChannelName()) currentInfo.getParentChannelName())
); );
headerSubChannelTitleView.setVisibility(View.VISIBLE); headerBinding.subChannelTitleView.setVisibility(View.VISIBLE);
headerSubChannelAvatarView.setVisibility(View.VISIBLE); headerBinding.subChannelAvatarView.setVisibility(View.VISIBLE);
} else { } else {
headerSubChannelTitleView.setVisibility(View.GONE); headerBinding.subChannelTitleView.setVisibility(View.GONE);
} }
if (menuRssButton != null) { if (menuRssButton != null) {
menuRssButton.setVisible(!TextUtils.isEmpty(result.getFeedUrl())); menuRssButton.setVisible(!TextUtils.isEmpty(result.getFeedUrl()));
} }
playlistCtrl.setVisibility(View.VISIBLE); playlistControlBinding.getRoot().setVisibility(View.VISIBLE);
final List<Throwable> errors = new ArrayList<>(result.getErrors()); final List<Throwable> errors = new ArrayList<>(result.getErrors());
if (!errors.isEmpty()) { if (!errors.isEmpty()) {
@ -516,29 +504,32 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo>
updateSubscription(result); updateSubscription(result);
monitorSubscription(result); monitorSubscription(result);
headerPlayAllButton.setOnClickListener(view -> NavigationHelper playlistControlBinding.playlistCtrlPlayAllButton
.setOnClickListener(view -> NavigationHelper
.playOnMainPlayer(activity, getPlayQueue())); .playOnMainPlayer(activity, getPlayQueue()));
headerPopupButton.setOnClickListener(view -> NavigationHelper playlistControlBinding.playlistCtrlPlayPopupButton
.setOnClickListener(view -> NavigationHelper
.playOnPopupPlayer(activity, getPlayQueue(), false)); .playOnPopupPlayer(activity, getPlayQueue(), false));
headerBackgroundButton.setOnClickListener(view -> NavigationHelper playlistControlBinding.playlistCtrlPlayBgButton
.setOnClickListener(view -> NavigationHelper
.playOnBackgroundPlayer(activity, getPlayQueue(), false)); .playOnBackgroundPlayer(activity, getPlayQueue(), false));
headerPopupButton.setOnLongClickListener(view -> { playlistControlBinding.playlistCtrlPlayPopupButton.setOnLongClickListener(view -> {
NavigationHelper.enqueueOnPopupPlayer(activity, getPlayQueue(), true); NavigationHelper.enqueueOnPopupPlayer(activity, getPlayQueue(), true);
return true; return true;
}); });
headerBackgroundButton.setOnLongClickListener(view -> { playlistControlBinding.playlistCtrlPlayBgButton.setOnLongClickListener(view -> {
NavigationHelper.enqueueOnBackgroundPlayer(activity, getPlayQueue(), true); NavigationHelper.enqueueOnBackgroundPlayer(activity, getPlayQueue(), true);
return true; return true;
}); });
} }
private void showContentNotSupported() { private void showContentNotSupported() {
contentNotSupportedTextView.setVisibility(View.VISIBLE); channelBinding.errorContentNotSupported.setVisibility(View.VISIBLE);
kaomojiTextView.setText("(︶︹︺)"); channelBinding.channelKaomoji.setText("(︶︹︺)");
kaomojiTextView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 45f); channelBinding.channelKaomoji.setTextSize(TypedValue.COMPLEX_UNIT_SP, 45f);
noVideosTextView.setVisibility(View.GONE); channelBinding.channelNoVideos.setVisibility(View.GONE);
} }
private PlayQueue getPlayQueue() { private PlayQueue getPlayQueue() {
@ -596,7 +587,7 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo>
public void setTitle(final String title) { public void setTitle(final String title) {
super.setTitle(title); super.setTitle(title);
if (!useAsFrontPage) { if (!useAsFrontPage) {
headerTitleView.setText(title); headerBinding.channelTitleView.setText(title);
} }
} }
} }

View file

@ -11,18 +11,20 @@ import android.view.MenuInflater;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.content.res.AppCompatResources; import androidx.appcompat.content.res.AppCompatResources;
import androidx.viewbinding.ViewBinding;
import org.reactivestreams.Subscriber; import org.reactivestreams.Subscriber;
import org.reactivestreams.Subscription; import org.reactivestreams.Subscription;
import org.schabi.newpipe.NewPipeDatabase; import org.schabi.newpipe.NewPipeDatabase;
import org.schabi.newpipe.R; import org.schabi.newpipe.R;
import org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity; import org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity;
import org.schabi.newpipe.databinding.PlaylistControlBinding;
import org.schabi.newpipe.databinding.PlaylistHeaderBinding;
import org.schabi.newpipe.extractor.InfoItem; import org.schabi.newpipe.extractor.InfoItem;
import org.schabi.newpipe.extractor.ListExtractor; import org.schabi.newpipe.extractor.ListExtractor;
import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.NewPipe;
@ -53,7 +55,6 @@ import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import de.hdodenhof.circleimageview.CircleImageView;
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
import io.reactivex.rxjava3.core.Flowable; import io.reactivex.rxjava3.core.Flowable;
import io.reactivex.rxjava3.core.Single; import io.reactivex.rxjava3.core.Single;
@ -74,17 +75,8 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
// Views // Views
//////////////////////////////////////////////////////////////////////////*/ //////////////////////////////////////////////////////////////////////////*/
private View headerRootLayout; private PlaylistHeaderBinding headerBinding;
private TextView headerTitleView; private PlaylistControlBinding playlistControlBinding;
private View headerUploaderLayout;
private TextView headerUploaderName;
private CircleImageView headerUploaderAvatar;
private TextView headerStreamCount;
private View playlistCtrl;
private View headerPlayAllButton;
private View headerPopupButton;
private View headerBackgroundButton;
private MenuItem playlistBookmarkButton; private MenuItem playlistBookmarkButton;
@ -119,22 +111,13 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
// Init // Init
//////////////////////////////////////////////////////////////////////////*/ //////////////////////////////////////////////////////////////////////////*/
protected View getListHeader() { @Override
headerRootLayout = activity.getLayoutInflater() protected ViewBinding getListHeader() {
.inflate(R.layout.playlist_header, itemsList, false); headerBinding = PlaylistHeaderBinding
headerTitleView = headerRootLayout.findViewById(R.id.playlist_title_view); .inflate(activity.getLayoutInflater(), itemsList, false);
headerUploaderLayout = headerRootLayout.findViewById(R.id.uploader_layout); playlistControlBinding = headerBinding.playlistControl;
headerUploaderName = headerRootLayout.findViewById(R.id.uploader_name);
headerUploaderAvatar = headerRootLayout.findViewById(R.id.uploader_avatar_view);
headerStreamCount = headerRootLayout.findViewById(R.id.playlist_stream_count);
playlistCtrl = headerRootLayout.findViewById(R.id.playlist_control);
headerPlayAllButton = headerRootLayout.findViewById(R.id.playlist_ctrl_play_all_button); return headerBinding;
headerPopupButton = headerRootLayout.findViewById(R.id.playlist_ctrl_play_popup_button);
headerBackgroundButton = headerRootLayout.findViewById(R.id.playlist_ctrl_play_bg_button);
return headerRootLayout;
} }
@Override @Override
@ -203,6 +186,9 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
@Override @Override
public void onDestroyView() { public void onDestroyView() {
headerBinding = null;
playlistControlBinding = null;
super.onDestroyView(); super.onDestroyView();
if (isBookmarkButtonReady != null) { if (isBookmarkButtonReady != null) {
isBookmarkButtonReady.set(false); isBookmarkButtonReady.set(false);
@ -275,25 +261,25 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
@Override @Override
public void showLoading() { public void showLoading() {
super.showLoading(); super.showLoading();
animateView(headerRootLayout, false, 200); animateView(headerBinding.getRoot(), false, 200);
animateView(itemsList, false, 100); animateView(itemsList, false, 100);
IMAGE_LOADER.cancelDisplayTask(headerUploaderAvatar); IMAGE_LOADER.cancelDisplayTask(headerBinding.uploaderAvatarView);
animateView(headerUploaderLayout, false, 200); animateView(headerBinding.uploaderLayout, false, 200);
} }
@Override @Override
public void handleResult(@NonNull final PlaylistInfo result) { public void handleResult(@NonNull final PlaylistInfo result) {
super.handleResult(result); super.handleResult(result);
animateView(headerRootLayout, true, 100); animateView(headerBinding.getRoot(), true, 100);
animateView(headerUploaderLayout, true, 300); animateView(headerBinding.uploaderLayout, true, 300);
headerUploaderLayout.setOnClickListener(null); headerBinding.uploaderLayout.setOnClickListener(null);
// If we have an uploader put them into the UI // If we have an uploader put them into the UI
if (!TextUtils.isEmpty(result.getUploaderName())) { if (!TextUtils.isEmpty(result.getUploaderName())) {
headerUploaderName.setText(result.getUploaderName()); headerBinding.uploaderName.setText(result.getUploaderName());
if (!TextUtils.isEmpty(result.getUploaderUrl())) { if (!TextUtils.isEmpty(result.getUploaderUrl())) {
headerUploaderLayout.setOnClickListener(v -> { headerBinding.uploaderLayout.setOnClickListener(v -> {
try { try {
NavigationHelper.openChannelFragment(getFM(), result.getServiceId(), NavigationHelper.openChannelFragment(getFM(), result.getServiceId(),
result.getUploaderUrl(), result.getUploaderName()); result.getUploaderUrl(), result.getUploaderName());
@ -303,28 +289,29 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
}); });
} }
} else { // Otherwise say we have no uploader } else { // Otherwise say we have no uploader
headerUploaderName.setText(R.string.playlist_no_uploader); headerBinding.uploaderName.setText(R.string.playlist_no_uploader);
} }
playlistCtrl.setVisibility(View.VISIBLE); playlistControlBinding.getRoot().setVisibility(View.VISIBLE);
final String avatarUrl = result.getUploaderAvatarUrl(); final String avatarUrl = result.getUploaderAvatarUrl();
if (result.getServiceId() == ServiceList.YouTube.getServiceId() if (result.getServiceId() == ServiceList.YouTube.getServiceId()
&& (YoutubeParsingHelper.isYoutubeMixId(result.getId()) && (YoutubeParsingHelper.isYoutubeMixId(result.getId())
|| YoutubeParsingHelper.isYoutubeMusicMixId(result.getId()))) { || YoutubeParsingHelper.isYoutubeMusicMixId(result.getId()))) {
// this is an auto-generated playlist (e.g. Youtube mix), so a radio is shown // this is an auto-generated playlist (e.g. Youtube mix), so a radio is shown
headerUploaderAvatar.setDisableCircularTransformation(true); headerBinding.uploaderAvatarView.setDisableCircularTransformation(true);
headerUploaderAvatar.setBorderColor( headerBinding.uploaderAvatarView.setBorderColor(
getResources().getColor(R.color.transparent_background_color)); getResources().getColor(R.color.transparent_background_color));
headerUploaderAvatar.setImageDrawable(AppCompatResources.getDrawable(requireContext(), headerBinding.uploaderAvatarView.setImageDrawable(
resolveResourceIdFromAttr(requireContext(), R.attr.ic_radio))); AppCompatResources.getDrawable(requireContext(),
resolveResourceIdFromAttr(requireContext(), R.attr.ic_radio))
);
} else { } else {
IMAGE_LOADER.displayImage(avatarUrl, headerUploaderAvatar, IMAGE_LOADER.displayImage(avatarUrl, headerBinding.uploaderAvatarView,
ImageDisplayConstants.DISPLAY_AVATAR_OPTIONS); ImageDisplayConstants.DISPLAY_AVATAR_OPTIONS);
} }
headerStreamCount.setText(Localization headerBinding.playlistStreamCount.setText(Localization
.localizeStreamCount(getContext(), result.getStreamCount())); .localizeStreamCount(getContext(), result.getStreamCount()));
if (!result.getErrors().isEmpty()) { if (!result.getErrors().isEmpty()) {
@ -338,19 +325,19 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.subscribe(getPlaylistBookmarkSubscriber()); .subscribe(getPlaylistBookmarkSubscriber());
headerPlayAllButton.setOnClickListener(view -> playlistControlBinding.playlistCtrlPlayAllButton.setOnClickListener(view ->
NavigationHelper.playOnMainPlayer(activity, getPlayQueue())); NavigationHelper.playOnMainPlayer(activity, getPlayQueue()));
headerPopupButton.setOnClickListener(view -> playlistControlBinding.playlistCtrlPlayPopupButton.setOnClickListener(view ->
NavigationHelper.playOnPopupPlayer(activity, getPlayQueue(), false)); NavigationHelper.playOnPopupPlayer(activity, getPlayQueue(), false));
headerBackgroundButton.setOnClickListener(view -> playlistControlBinding.playlistCtrlPlayBgButton.setOnClickListener(view ->
NavigationHelper.playOnBackgroundPlayer(activity, getPlayQueue(), false)); NavigationHelper.playOnBackgroundPlayer(activity, getPlayQueue(), false));
headerPopupButton.setOnLongClickListener(view -> { playlistControlBinding.playlistCtrlPlayPopupButton.setOnLongClickListener(view -> {
NavigationHelper.enqueueOnPopupPlayer(activity, getPlayQueue(), true); NavigationHelper.enqueueOnPopupPlayer(activity, getPlayQueue(), true);
return true; return true;
}); });
headerBackgroundButton.setOnLongClickListener(view -> { playlistControlBinding.playlistCtrlPlayBgButton.setOnLongClickListener(view -> {
NavigationHelper.enqueueOnBackgroundPlayer(activity, getPlayQueue(), true); NavigationHelper.enqueueOnBackgroundPlayer(activity, getPlayQueue(), true);
return true; return true;
}); });
@ -459,7 +446,7 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
@Override @Override
public void setTitle(final String title) { public void setTitle(final String title) {
super.setTitle(title); super.setTitle(title);
headerTitleView.setText(title); headerBinding.playlistTitleView.setText(title);
} }
private void onBookmarkClicked() { private void onBookmarkClicked() {

View file

@ -37,6 +37,7 @@ import androidx.recyclerview.widget.RecyclerView;
import org.schabi.newpipe.R; import org.schabi.newpipe.R;
import org.schabi.newpipe.ReCaptchaActivity; import org.schabi.newpipe.ReCaptchaActivity;
import org.schabi.newpipe.database.history.model.SearchHistoryEntry; import org.schabi.newpipe.database.history.model.SearchHistoryEntry;
import org.schabi.newpipe.databinding.FragmentSearchBinding;
import org.schabi.newpipe.extractor.InfoItem; import org.schabi.newpipe.extractor.InfoItem;
import org.schabi.newpipe.extractor.ListExtractor; import org.schabi.newpipe.extractor.ListExtractor;
import org.schabi.newpipe.extractor.MetaInfo; import org.schabi.newpipe.extractor.MetaInfo;
@ -155,6 +156,8 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
// Views // Views
//////////////////////////////////////////////////////////////////////////*/ //////////////////////////////////////////////////////////////////////////*/
private FragmentSearchBinding searchBinding;
private View searchToolbarContainer; private View searchToolbarContainer;
private EditText searchEditText; private EditText searchEditText;
private View searchClear; private View searchClear;
@ -165,7 +168,6 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
private View suggestionsPanel; private View suggestionsPanel;
private boolean suggestionsPanelVisible = false; private boolean suggestionsPanelVisible = false;
private RecyclerView suggestionsRecyclerView;
/*////////////////////////////////////////////////////////////////////////*/ /*////////////////////////////////////////////////////////////////////////*/
@ -301,6 +303,8 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
Log.d(TAG, "onDestroyView() called"); Log.d(TAG, "onDestroyView() called");
} }
unsetSearchListeners(); unsetSearchListeners();
searchBinding = null;
super.onDestroyView(); super.onDestroyView();
} }
@ -337,9 +341,9 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
@Override @Override
protected void initViews(final View rootView, final Bundle savedInstanceState) { protected void initViews(final View rootView, final Bundle savedInstanceState) {
super.initViews(rootView, savedInstanceState); super.initViews(rootView, savedInstanceState);
suggestionsPanel = rootView.findViewById(R.id.suggestions_panel); searchBinding = FragmentSearchBinding.bind(rootView);
suggestionsRecyclerView = rootView.findViewById(R.id.suggestions_list);
suggestionsRecyclerView.setAdapter(suggestionListAdapter); searchBinding.suggestionsList.setAdapter(suggestionListAdapter);
new ItemTouchHelper(new ItemTouchHelper.Callback() { new ItemTouchHelper(new ItemTouchHelper.Callback() {
@Override @Override
public int getMovementFlags(@NonNull final RecyclerView recyclerView, public int getMovementFlags(@NonNull final RecyclerView recyclerView,
@ -358,7 +362,7 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
public void onSwiped(@NonNull final RecyclerView.ViewHolder viewHolder, final int i) { public void onSwiped(@NonNull final RecyclerView.ViewHolder viewHolder, final int i) {
onSuggestionItemSwiped(viewHolder); onSuggestionItemSwiped(viewHolder);
} }
}).attachToRecyclerView(suggestionsRecyclerView); }).attachToRecyclerView(searchBinding.suggestionsList);
searchToolbarContainer = activity.findViewById(R.id.toolbar_search_container); searchToolbarContainer = activity.findViewById(R.id.toolbar_search_container);
searchEditText = searchToolbarContainer.findViewById(R.id.toolbar_search_edit_text); searchEditText = searchToolbarContainer.findViewById(R.id.toolbar_search_edit_text);
@ -523,7 +527,7 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
return; return;
} }
correctSuggestion.setVisibility(View.GONE); searchBinding.correctSuggestion.setVisibility(View.GONE);
searchEditText.setText(""); searchEditText.setText("");
suggestionListAdapter.setItems(new ArrayList<>()); suggestionListAdapter.setItems(new ArrayList<>());
@ -640,7 +644,8 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
Log.d(TAG, "showSuggestionsPanel() called"); Log.d(TAG, "showSuggestionsPanel() called");
} }
suggestionsPanelVisible = true; suggestionsPanelVisible = true;
animateView(suggestionsPanel, AnimationUtils.Type.LIGHT_SLIDE_AND_ALPHA, true, 200); animateView(searchBinding.suggestionsPanel, AnimationUtils.Type.LIGHT_SLIDE_AND_ALPHA,
true, 200);
} }
private void hideSuggestionsPanel() { private void hideSuggestionsPanel() {
@ -648,7 +653,8 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
Log.d(TAG, "hideSuggestionsPanel() called"); Log.d(TAG, "hideSuggestionsPanel() called");
} }
suggestionsPanelVisible = false; suggestionsPanelVisible = false;
animateView(suggestionsPanel, AnimationUtils.Type.LIGHT_SLIDE_AND_ALPHA, false, 200); animateView(searchBinding.suggestionsPanel, AnimationUtils.Type.LIGHT_SLIDE_AND_ALPHA,
false, 200);
} }
private void showKeyboardSearch() { private void showKeyboardSearch() {
@ -936,8 +942,8 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
if (DEBUG) { if (DEBUG) {
Log.d(TAG, "handleSuggestions() called with: suggestions = [" + suggestions + "]"); Log.d(TAG, "handleSuggestions() called with: suggestions = [" + suggestions + "]");
} }
suggestionsRecyclerView.smoothScrollToPosition(0); searchBinding.suggestionsList.smoothScrollToPosition(0);
suggestionsRecyclerView.post(() -> suggestionListAdapter.setItems(suggestions)); searchBinding.suggestionsList.post(() -> suggestionListAdapter.setItems(suggestions));
if (suggestionsPanelVisible && errorPanelRoot.getVisibility() == View.VISIBLE) { if (suggestionsPanelVisible && errorPanelRoot.getVisibility() == View.VISIBLE) {
hideLoading(); hideLoading();
@ -1019,7 +1025,7 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
private void handleSearchSuggestion() { private void handleSearchSuggestion() {
if (TextUtils.isEmpty(searchSuggestion)) { if (TextUtils.isEmpty(searchSuggestion)) {
correctSuggestion.setVisibility(View.GONE); searchBinding.correctSuggestion.setVisibility(View.GONE);
} else { } else {
final String helperText = getString(isCorrectedSearch final String helperText = getString(isCorrectedSearch
? R.string.search_showing_result_for ? R.string.search_showing_result_for
@ -1028,22 +1034,23 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
final String highlightedSearchSuggestion = final String highlightedSearchSuggestion =
"<b><i>" + Html.escapeHtml(searchSuggestion) + "</i></b>"; "<b><i>" + Html.escapeHtml(searchSuggestion) + "</i></b>";
final String text = String.format(helperText, highlightedSearchSuggestion); final String text = String.format(helperText, highlightedSearchSuggestion);
correctSuggestion.setText(HtmlCompat.fromHtml(text, HtmlCompat.FROM_HTML_MODE_LEGACY)); searchBinding.correctSuggestion.setText(HtmlCompat.fromHtml(text,
HtmlCompat.FROM_HTML_MODE_LEGACY));
correctSuggestion.setOnClickListener(v -> { searchBinding.correctSuggestion.setOnClickListener(v -> {
correctSuggestion.setVisibility(View.GONE); searchBinding.correctSuggestion.setVisibility(View.GONE);
search(searchSuggestion, contentFilter, sortFilter); search(searchSuggestion, contentFilter, sortFilter);
searchEditText.setText(searchSuggestion); searchEditText.setText(searchSuggestion);
}); });
correctSuggestion.setOnLongClickListener(v -> { searchBinding.correctSuggestion.setOnLongClickListener(v -> {
searchEditText.setText(searchSuggestion); searchEditText.setText(searchSuggestion);
searchEditText.setSelection(searchSuggestion.length()); searchEditText.setSelection(searchSuggestion.length());
showKeyboardSearch(); showKeyboardSearch();
return true; return true;
}); });
correctSuggestion.setVisibility(View.VISIBLE); searchBinding.correctSuggestion.setVisibility(View.VISIBLE);
} }
} }

View file

@ -8,13 +8,14 @@ import android.view.Menu;
import android.view.MenuInflater; import android.view.MenuInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.Switch;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.preference.PreferenceManager; import androidx.preference.PreferenceManager;
import androidx.viewbinding.ViewBinding;
import org.schabi.newpipe.R; import org.schabi.newpipe.R;
import org.schabi.newpipe.databinding.RelatedStreamsHeaderBinding;
import org.schabi.newpipe.extractor.ListExtractor; import org.schabi.newpipe.extractor.ListExtractor;
import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.stream.StreamInfo; import org.schabi.newpipe.extractor.stream.StreamInfo;
@ -38,8 +39,7 @@ public class RelatedVideosFragment extends BaseListInfoFragment<RelatedStreamInf
// Views // Views
//////////////////////////////////////////////////////////////////////////*/ //////////////////////////////////////////////////////////////////////////*/
private View headerRootLayout; private RelatedStreamsHeaderBinding headerBinding;
private Switch autoplaySwitch;
public static RelatedVideosFragment getInstance(final StreamInfo info) { public static RelatedVideosFragment getInstance(final StreamInfo info) {
final RelatedVideosFragment instance = new RelatedVideosFragment(); final RelatedVideosFragment instance = new RelatedVideosFragment();
@ -66,25 +66,29 @@ public class RelatedVideosFragment extends BaseListInfoFragment<RelatedStreamInf
@Override @Override
public void onDestroy() { public void onDestroy() {
super.onDestroy(); super.onDestroy();
if (disposables != null) {
disposables.clear(); disposables.clear();
} }
@Override
public void onDestroyView() {
headerBinding = null;
super.onDestroyView();
} }
protected View getListHeader() { @Override
protected ViewBinding getListHeader() {
if (relatedStreamInfo != null && relatedStreamInfo.getRelatedItems() != null) { if (relatedStreamInfo != null && relatedStreamInfo.getRelatedItems() != null) {
headerRootLayout = activity.getLayoutInflater() headerBinding = RelatedStreamsHeaderBinding
.inflate(R.layout.related_streams_header, itemsList, false); .inflate(activity.getLayoutInflater(), itemsList, false);
autoplaySwitch = headerRootLayout.findViewById(R.id.autoplay_switch);
final SharedPreferences pref = PreferenceManager final SharedPreferences pref = PreferenceManager
.getDefaultSharedPreferences(requireContext()); .getDefaultSharedPreferences(requireContext());
final boolean autoplay = pref.getBoolean(getString(R.string.auto_queue_key), false); final boolean autoplay = pref.getBoolean(getString(R.string.auto_queue_key), false);
autoplaySwitch.setChecked(autoplay); headerBinding.autoplaySwitch.setChecked(autoplay);
autoplaySwitch.setOnCheckedChangeListener((compoundButton, b) -> headerBinding.autoplaySwitch.setOnCheckedChangeListener((compoundButton, b) ->
PreferenceManager.getDefaultSharedPreferences(requireContext()).edit() PreferenceManager.getDefaultSharedPreferences(requireContext()).edit()
.putBoolean(getString(R.string.auto_queue_key), b).apply()); .putBoolean(getString(R.string.auto_queue_key), b).apply());
return headerRootLayout; return headerBinding;
} else { } else {
return null; return null;
} }
@ -107,8 +111,8 @@ public class RelatedVideosFragment extends BaseListInfoFragment<RelatedStreamInf
@Override @Override
public void showLoading() { public void showLoading() {
super.showLoading(); super.showLoading();
if (headerRootLayout != null) { if (headerBinding != null) {
headerRootLayout.setVisibility(View.INVISIBLE); headerBinding.getRoot().setVisibility(View.INVISIBLE);
} }
} }
@ -116,8 +120,8 @@ public class RelatedVideosFragment extends BaseListInfoFragment<RelatedStreamInf
public void handleResult(@NonNull final RelatedStreamInfo result) { public void handleResult(@NonNull final RelatedStreamInfo result) {
super.handleResult(result); super.handleResult(result);
if (headerRootLayout != null) { if (headerBinding != null) {
headerRootLayout.setVisibility(View.VISIBLE); headerBinding.getRoot().setVisibility(View.VISIBLE);
} }
AnimationUtils.slideUp(getView(), 120, 96, 0.06f); AnimationUtils.slideUp(getView(), 120, 96, 0.06f);
@ -126,10 +130,8 @@ public class RelatedVideosFragment extends BaseListInfoFragment<RelatedStreamInf
NewPipe.getNameOfService(result.getServiceId()), result.getUrl(), 0); NewPipe.getNameOfService(result.getServiceId()), result.getUrl(), 0);
} }
if (disposables != null) {
disposables.clear(); disposables.clear();
} }
}
@Override @Override
public void handleNextItems(final ListExtractor.InfoItemsPage result) { public void handleNextItems(final ListExtractor.InfoItemsPage result) {
@ -202,8 +204,8 @@ public class RelatedVideosFragment extends BaseListInfoFragment<RelatedStreamInf
final SharedPreferences pref = final SharedPreferences pref =
PreferenceManager.getDefaultSharedPreferences(requireContext()); PreferenceManager.getDefaultSharedPreferences(requireContext());
final boolean autoplay = pref.getBoolean(getString(R.string.auto_queue_key), false); final boolean autoplay = pref.getBoolean(getString(R.string.auto_queue_key), false);
if (autoplaySwitch != null) { if (headerBinding != null) {
autoplaySwitch.setChecked(autoplay); headerBinding.autoplaySwitch.setChecked(autoplay);
} }
} }

View file

@ -4,6 +4,8 @@ import android.content.SharedPreferences;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.content.res.Resources; import android.content.res.Resources;
import android.os.Bundle; import android.os.Bundle;
import androidx.annotation.Nullable;
import androidx.preference.PreferenceManager; import androidx.preference.PreferenceManager;
import android.util.Log; import android.util.Log;
import android.view.Menu; import android.view.Menu;
@ -15,8 +17,10 @@ import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.GridLayoutManager; import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import androidx.viewbinding.ViewBinding;
import org.schabi.newpipe.R; import org.schabi.newpipe.R;
import org.schabi.newpipe.databinding.PignateFooterBinding;
import org.schabi.newpipe.fragments.BaseStateFragment; import org.schabi.newpipe.fragments.BaseStateFragment;
import org.schabi.newpipe.fragments.list.ListViewContract; import org.schabi.newpipe.fragments.list.ListViewContract;
@ -42,8 +46,8 @@ public abstract class BaseLocalListFragment<I, N> extends BaseStateFragment<I>
//////////////////////////////////////////////////////////////////////////*/ //////////////////////////////////////////////////////////////////////////*/
private static final int LIST_MODE_UPDATE_FLAG = 0x32; private static final int LIST_MODE_UPDATE_FLAG = 0x32;
private View headerRootView; private ViewBinding headerRootBinding;
private View footerRootView; private ViewBinding footerRootBinding;
protected LocalItemListAdapter itemListAdapter; protected LocalItemListAdapter itemListAdapter;
protected RecyclerView itemsList; protected RecyclerView itemsList;
private int updateFlags = 0; private int updateFlags = 0;
@ -86,12 +90,13 @@ public abstract class BaseLocalListFragment<I, N> extends BaseStateFragment<I>
// Lifecycle - View // Lifecycle - View
//////////////////////////////////////////////////////////////////////////*/ //////////////////////////////////////////////////////////////////////////*/
protected View getListHeader() { @Nullable
protected ViewBinding getListHeader() {
return null; return null;
} }
protected View getListFooter() { protected ViewBinding getListFooter() {
return activity.getLayoutInflater().inflate(R.layout.pignate_footer, itemsList, false); return PignateFooterBinding.inflate(activity.getLayoutInflater(), itemsList, false);
} }
protected RecyclerView.LayoutManager getGridLayoutManager() { protected RecyclerView.LayoutManager getGridLayoutManager() {
@ -120,10 +125,12 @@ public abstract class BaseLocalListFragment<I, N> extends BaseStateFragment<I>
itemsList.setLayoutManager(useGrid ? getGridLayoutManager() : getListLayoutManager()); itemsList.setLayoutManager(useGrid ? getGridLayoutManager() : getListLayoutManager());
itemListAdapter.setUseGridVariant(useGrid); itemListAdapter.setUseGridVariant(useGrid);
headerRootView = getListHeader(); headerRootBinding = getListHeader();
itemListAdapter.setHeader(headerRootView); if (headerRootBinding != null) {
footerRootView = getListFooter(); itemListAdapter.setHeader(headerRootBinding.getRoot());
itemListAdapter.setFooter(footerRootView); }
footerRootBinding = getListFooter();
itemListAdapter.setFooter(footerRootBinding.getRoot());
itemsList.setAdapter(itemListAdapter); itemsList.setAdapter(itemListAdapter);
} }
@ -180,8 +187,8 @@ public abstract class BaseLocalListFragment<I, N> extends BaseStateFragment<I>
if (itemsList != null) { if (itemsList != null) {
animateView(itemsList, false, 200); animateView(itemsList, false, 200);
} }
if (headerRootView != null) { if (headerRootBinding != null) {
animateView(headerRootView, false, 200); animateView(headerRootBinding.getRoot(), false, 200);
} }
} }
@ -191,8 +198,8 @@ public abstract class BaseLocalListFragment<I, N> extends BaseStateFragment<I>
if (itemsList != null) { if (itemsList != null) {
animateView(itemsList, true, 200); animateView(itemsList, true, 200);
} }
if (headerRootView != null) { if (headerRootBinding != null) {
animateView(headerRootView, true, 200); animateView(headerRootBinding.getRoot(), true, 200);
} }
} }
@ -204,8 +211,8 @@ public abstract class BaseLocalListFragment<I, N> extends BaseStateFragment<I>
if (itemsList != null) { if (itemsList != null) {
animateView(itemsList, false, 200); animateView(itemsList, false, 200);
} }
if (headerRootView != null) { if (headerRootBinding != null) {
animateView(headerRootView, false, 200); animateView(headerRootBinding.getRoot(), false, 200);
} }
} }

View file

@ -37,18 +37,10 @@ import androidx.lifecycle.ViewModelProvider
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import icepick.State import icepick.State
import kotlinx.android.synthetic.main.error_retry.error_button_retry
import kotlinx.android.synthetic.main.error_retry.error_message_view
import kotlinx.android.synthetic.main.fragment_feed.empty_state_view
import kotlinx.android.synthetic.main.fragment_feed.error_panel
import kotlinx.android.synthetic.main.fragment_feed.items_list
import kotlinx.android.synthetic.main.fragment_feed.loading_progress_bar
import kotlinx.android.synthetic.main.fragment_feed.loading_progress_text
import kotlinx.android.synthetic.main.fragment_feed.refresh_root_view
import kotlinx.android.synthetic.main.fragment_feed.refresh_subtitle_text
import kotlinx.android.synthetic.main.fragment_feed.refresh_text
import org.schabi.newpipe.R import org.schabi.newpipe.R
import org.schabi.newpipe.database.feed.model.FeedGroupEntity import org.schabi.newpipe.database.feed.model.FeedGroupEntity
import org.schabi.newpipe.databinding.ErrorRetryBinding
import org.schabi.newpipe.databinding.FragmentFeedBinding
import org.schabi.newpipe.fragments.list.BaseListFragment import org.schabi.newpipe.fragments.list.BaseListFragment
import org.schabi.newpipe.local.feed.service.FeedLoadService import org.schabi.newpipe.local.feed.service.FeedLoadService
import org.schabi.newpipe.report.UserAction import org.schabi.newpipe.report.UserAction
@ -57,6 +49,12 @@ import org.schabi.newpipe.util.Localization
import java.util.Calendar import java.util.Calendar
class FeedFragment : BaseListFragment<FeedState, Unit>() { class FeedFragment : BaseListFragment<FeedState, Unit>() {
private var _feedBinding: FragmentFeedBinding? = null
private val feedBinding get() = _feedBinding!!
private var _errorBinding: ErrorRetryBinding? = null
private val errorBinding get() = _errorBinding!!
private lateinit var viewModel: FeedViewModel private lateinit var viewModel: FeedViewModel
private lateinit var swipeRefreshLayout: SwipeRefreshLayout private lateinit var swipeRefreshLayout: SwipeRefreshLayout
@State @State
@ -86,15 +84,17 @@ class FeedFragment : BaseListFragment<FeedState, Unit>() {
override fun onViewCreated(rootView: View, savedInstanceState: Bundle?) { override fun onViewCreated(rootView: View, savedInstanceState: Bundle?) {
super.onViewCreated(rootView, savedInstanceState) super.onViewCreated(rootView, savedInstanceState)
swipeRefreshLayout = requireView().findViewById(R.id.swiperefresh) _feedBinding = FragmentFeedBinding.bind(rootView)
swipeRefreshLayout.setOnRefreshListener { reloadContent() } _errorBinding = feedBinding.errorPanel
feedBinding.swiperefresh.setOnRefreshListener { reloadContent() }
viewModel = ViewModelProvider(this, FeedViewModel.Factory(requireContext(), groupId)).get(FeedViewModel::class.java) viewModel = ViewModelProvider(this, FeedViewModel.Factory(requireContext(), groupId)).get(FeedViewModel::class.java)
viewModel.stateLiveData.observe(viewLifecycleOwner, Observer { it?.let(::handleResult) }) viewModel.stateLiveData.observe(viewLifecycleOwner, Observer { it?.let(::handleResult) })
} }
override fun onPause() { override fun onPause() {
super.onPause() super.onPause()
listState = items_list?.layoutManager?.onSaveInstanceState() listState = _feedBinding?.itemsList?.layoutManager?.onSaveInstanceState()
} }
override fun onResume() { override fun onResume() {
@ -112,7 +112,8 @@ class FeedFragment : BaseListFragment<FeedState, Unit>() {
override fun initListeners() { override fun initListeners() {
super.initListeners() super.initListeners()
refresh_root_view.setOnClickListener { // Using the non-null property may result in a NullPointerException
_feedBinding?.refreshRootView?.setOnClickListener {
triggerUpdate() triggerUpdate()
} }
} }
@ -169,55 +170,60 @@ class FeedFragment : BaseListFragment<FeedState, Unit>() {
activity?.supportActionBar?.subtitle = null activity?.supportActionBar?.subtitle = null
} }
override fun onDestroyView() {
_feedBinding = null
super.onDestroyView()
}
// ///////////////////////////////////////////////////////////////////////// // /////////////////////////////////////////////////////////////////////////
// Handling // Handling
// ///////////////////////////////////////////////////////////////////////// // /////////////////////////////////////////////////////////////////////////
override fun showLoading() { override fun showLoading() {
animateView(refresh_root_view, false, 0) animateView(feedBinding.refreshRootView, false, 0)
animateView(items_list, false, 0) animateView(feedBinding.itemsList, false, 0)
animateView(loading_progress_bar, true, 200) animateView(feedBinding.loadingProgressBar, true, 200)
animateView(loading_progress_text, true, 200) animateView(feedBinding.loadingProgressText, true, 200)
empty_state_view?.let { animateView(it, false, 0) } animateView(feedBinding.emptyStateView.root, false, 0)
animateView(error_panel, false, 0) animateView(errorBinding.root, false, 0)
} }
override fun hideLoading() { override fun hideLoading() {
animateView(refresh_root_view, true, 200) animateView(feedBinding.refreshRootView, true, 200)
animateView(items_list, true, 300) animateView(feedBinding.itemsList, true, 300)
animateView(loading_progress_bar, false, 0) animateView(feedBinding.loadingProgressBar, false, 0)
animateView(loading_progress_text, false, 0) animateView(feedBinding.loadingProgressText, false, 0)
empty_state_view?.let { animateView(it, false, 0) } animateView(feedBinding.emptyStateView.root, false, 0)
animateView(error_panel, false, 0) animateView(errorBinding.root, false, 0)
swipeRefreshLayout.isRefreshing = false feedBinding.swiperefresh.isRefreshing = false
} }
override fun showEmptyState() { override fun showEmptyState() {
animateView(refresh_root_view, true, 200) animateView(feedBinding.refreshRootView, true, 200)
animateView(items_list, false, 0) animateView(feedBinding.itemsList, false, 0)
animateView(loading_progress_bar, false, 0) animateView(feedBinding.loadingProgressBar, false, 0)
animateView(loading_progress_text, false, 0) animateView(feedBinding.loadingProgressText, false, 0)
empty_state_view?.let { animateView(it, true, 800) } animateView(feedBinding.emptyStateView.root, true, 800)
animateView(error_panel, false, 0) animateView(errorBinding.root, false, 0)
} }
override fun showError(message: String, showRetryButton: Boolean) { override fun showError(message: String, showRetryButton: Boolean) {
infoListAdapter.clearStreamItemList() infoListAdapter.clearStreamItemList()
animateView(refresh_root_view, false, 120) animateView(feedBinding.refreshRootView, false, 120)
animateView(items_list, false, 120) animateView(feedBinding.itemsList, false, 120)
animateView(loading_progress_bar, false, 120) animateView(feedBinding.loadingProgressBar, false, 120)
animateView(loading_progress_text, false, 120) animateView(feedBinding.loadingProgressText, false, 120)
error_message_view.text = message errorBinding.errorMessageView.text = message
animateView(error_button_retry, showRetryButton, if (showRetryButton) 600 else 0) animateView(errorBinding.errorButtonRetry, showRetryButton, if (showRetryButton) 600 else 0)
animateView(error_panel, true, 300) animateView(errorBinding.root, true, 300)
} }
override fun handleResult(result: FeedState) { override fun handleResult(result: FeedState) {
@ -237,33 +243,34 @@ class FeedFragment : BaseListFragment<FeedState, Unit>() {
progressState.maxProgress == -1 progressState.maxProgress == -1
if (!isIndeterminate) { if (!isIndeterminate) {
loading_progress_text.text = "${progressState.currentProgress}/${progressState.maxProgress}" feedBinding.loadingProgressText.text = "${progressState.currentProgress}/${progressState.maxProgress}"
} else if (progressState.progressMessage > 0) { } else if (progressState.progressMessage > 0) {
loading_progress_text?.setText(progressState.progressMessage) _feedBinding?.loadingProgressText?.setText(progressState.progressMessage)
} else { } else {
loading_progress_text?.text = "∞/∞" _feedBinding?.loadingProgressText?.text = "∞/∞"
} }
loading_progress_bar.isIndeterminate = isIndeterminate || feedBinding.loadingProgressBar.isIndeterminate = isIndeterminate ||
(progressState.maxProgress > 0 && progressState.currentProgress == 0) (progressState.maxProgress > 0 && progressState.currentProgress == 0)
loading_progress_bar.progress = progressState.currentProgress feedBinding.loadingProgressBar.progress = progressState.currentProgress
loading_progress_bar.max = progressState.maxProgress feedBinding.loadingProgressBar.max = progressState.maxProgress
} }
private fun handleLoadedState(loadedState: FeedState.LoadedState) { private fun handleLoadedState(loadedState: FeedState.LoadedState) {
infoListAdapter.setInfoItemList(loadedState.items) infoListAdapter.setInfoItemList(loadedState.items)
listState?.run { listState?.run {
items_list.layoutManager?.onRestoreInstanceState(listState) feedBinding.itemsList.layoutManager?.onRestoreInstanceState(listState)
listState = null listState = null
} }
oldestSubscriptionUpdate = loadedState.oldestUpdate oldestSubscriptionUpdate = loadedState.oldestUpdate
val loadedCount = loadedState.notLoadedCount > 0 val loadedCount = loadedState.notLoadedCount > 0
refresh_subtitle_text.isVisible = loadedCount feedBinding.refreshSubtitleText.isVisible = loadedCount
if (loadedCount) { if (loadedCount) {
refresh_subtitle_text.text = getString(R.string.feed_subscription_not_loaded_count, loadedState.notLoadedCount) feedBinding.refreshSubtitleText.text = getString(R.string.feed_subscription_not_loaded_count,
loadedState.notLoadedCount)
} }
if (loadedState.itemsErrors.isNotEmpty()) { if (loadedState.itemsErrors.isNotEmpty()) {
@ -300,7 +307,7 @@ class FeedFragment : BaseListFragment<FeedState, Unit>() {
else -> "" else -> ""
} }
refresh_text?.text = getString(R.string.feed_oldest_subscription_update, oldestSubscriptionUpdateText) feedBinding.refreshText.text = getString(R.string.feed_oldest_subscription_update, oldestSubscriptionUpdateText)
} }
// ///////////////////////////////////////////////////////////////////////// // /////////////////////////////////////////////////////////////////////////

View file

@ -10,13 +10,12 @@ import android.view.MenuInflater;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
import androidx.viewbinding.ViewBinding;
import com.google.android.material.snackbar.Snackbar; import com.google.android.material.snackbar.Snackbar;
@ -26,6 +25,8 @@ import org.schabi.newpipe.R;
import org.schabi.newpipe.database.LocalItem; import org.schabi.newpipe.database.LocalItem;
import org.schabi.newpipe.database.stream.StreamStatisticsEntry; import org.schabi.newpipe.database.stream.StreamStatisticsEntry;
import org.schabi.newpipe.database.stream.model.StreamEntity; import org.schabi.newpipe.database.stream.model.StreamEntity;
import org.schabi.newpipe.databinding.PlaylistControlBinding;
import org.schabi.newpipe.databinding.StatisticPlaylistControlBinding;
import org.schabi.newpipe.extractor.stream.StreamInfoItem; import org.schabi.newpipe.extractor.stream.StreamInfoItem;
import org.schabi.newpipe.extractor.stream.StreamType; import org.schabi.newpipe.extractor.stream.StreamType;
import org.schabi.newpipe.info_list.InfoItemDialog; import org.schabi.newpipe.info_list.InfoItemDialog;
@ -60,13 +61,10 @@ public class StatisticsPlaylistFragment
@State @State
Parcelable itemsListState; Parcelable itemsListState;
private StatisticSortMode sortMode = StatisticSortMode.LAST_PLAYED; private StatisticSortMode sortMode = StatisticSortMode.LAST_PLAYED;
private View headerPlayAllButton;
private View headerPopupButton; private StatisticPlaylistControlBinding headerBinding;
private View headerBackgroundButton; private PlaylistControlBinding playlistControlBinding;
private View playlistCtrl;
private View sortButton;
private ImageView sortButtonIcon;
private TextView sortButtonText;
/* Used for independent events */ /* Used for independent events */
private Subscription databaseSubscription; private Subscription databaseSubscription;
private HistoryRecordManager recordManager; private HistoryRecordManager recordManager;
@ -131,17 +129,12 @@ public class StatisticsPlaylistFragment
} }
@Override @Override
protected View getListHeader() { protected ViewBinding getListHeader() {
final View headerRootLayout = activity.getLayoutInflater() headerBinding = StatisticPlaylistControlBinding.inflate(activity.getLayoutInflater(),
.inflate(R.layout.statistic_playlist_control, itemsList, false); itemsList, false);
playlistCtrl = headerRootLayout.findViewById(R.id.playlist_control); playlistControlBinding = headerBinding.playlistControl;
headerPlayAllButton = headerRootLayout.findViewById(R.id.playlist_ctrl_play_all_button);
headerPopupButton = headerRootLayout.findViewById(R.id.playlist_ctrl_play_popup_button); return headerBinding;
headerBackgroundButton = headerRootLayout.findViewById(R.id.playlist_ctrl_play_bg_button);
sortButton = headerRootLayout.findViewById(R.id.sortButton);
sortButtonIcon = headerRootLayout.findViewById(R.id.sortButtonIcon);
sortButtonText = headerRootLayout.findViewById(R.id.sortButtonText);
return headerRootLayout;
} }
@Override @Override
@ -245,14 +238,13 @@ public class StatisticsPlaylistFragment
if (itemListAdapter != null) { if (itemListAdapter != null) {
itemListAdapter.unsetSelectedListener(); itemListAdapter.unsetSelectedListener();
} }
if (headerBackgroundButton != null) { if (playlistControlBinding != null) {
headerBackgroundButton.setOnClickListener(null); playlistControlBinding.playlistCtrlPlayBgButton.setOnClickListener(null);
} playlistControlBinding.playlistCtrlPlayAllButton.setOnClickListener(null);
if (headerPlayAllButton != null) { playlistControlBinding.playlistCtrlPlayPopupButton.setOnClickListener(null);
headerPlayAllButton.setOnClickListener(null);
} headerBinding = null;
if (headerPopupButton != null) { playlistControlBinding = null;
headerPopupButton.setOnClickListener(null);
} }
if (databaseSubscription != null) { if (databaseSubscription != null) {
@ -311,7 +303,7 @@ public class StatisticsPlaylistFragment
return; return;
} }
playlistCtrl.setVisibility(View.VISIBLE); playlistControlBinding.getRoot().setVisibility(View.VISIBLE);
itemListAdapter.clearStreamItemList(); itemListAdapter.clearStreamItemList();
@ -326,13 +318,13 @@ public class StatisticsPlaylistFragment
itemsListState = null; itemsListState = null;
} }
headerPlayAllButton.setOnClickListener(view -> playlistControlBinding.playlistCtrlPlayAllButton.setOnClickListener(view ->
NavigationHelper.playOnMainPlayer(activity, getPlayQueue())); NavigationHelper.playOnMainPlayer(activity, getPlayQueue()));
headerPopupButton.setOnClickListener(view -> playlistControlBinding.playlistCtrlPlayPopupButton.setOnClickListener(view ->
NavigationHelper.playOnPopupPlayer(activity, getPlayQueue(), false)); NavigationHelper.playOnPopupPlayer(activity, getPlayQueue(), false));
headerBackgroundButton.setOnClickListener(view -> playlistControlBinding.playlistCtrlPlayBgButton.setOnClickListener(view ->
NavigationHelper.playOnBackgroundPlayer(activity, getPlayQueue(), false)); NavigationHelper.playOnBackgroundPlayer(activity, getPlayQueue(), false));
sortButton.setOnClickListener(view -> toggleSortMode()); headerBinding.sortButton.setOnClickListener(view -> toggleSortMode());
hideLoading(); hideLoading();
} }
@ -368,15 +360,15 @@ public class StatisticsPlaylistFragment
if (sortMode == StatisticSortMode.LAST_PLAYED) { if (sortMode == StatisticSortMode.LAST_PLAYED) {
sortMode = StatisticSortMode.MOST_PLAYED; sortMode = StatisticSortMode.MOST_PLAYED;
setTitle(getString(R.string.title_most_played)); setTitle(getString(R.string.title_most_played));
sortButtonIcon.setImageResource( headerBinding.sortButtonIcon.setImageResource(
ThemeHelper.resolveResourceIdFromAttr(requireContext(), R.attr.ic_history)); ThemeHelper.resolveResourceIdFromAttr(requireContext(), R.attr.ic_history));
sortButtonText.setText(R.string.title_last_played); headerBinding.sortButtonText.setText(R.string.title_last_played);
} else { } else {
sortMode = StatisticSortMode.LAST_PLAYED; sortMode = StatisticSortMode.LAST_PLAYED;
setTitle(getString(R.string.title_last_played)); setTitle(getString(R.string.title_last_played));
sortButtonIcon.setImageResource( headerBinding.sortButtonIcon.setImageResource(
ThemeHelper.resolveResourceIdFromAttr(requireContext(), R.attr.ic_filter_list)); ThemeHelper.resolveResourceIdFromAttr(requireContext(), R.attr.ic_filter_list));
sortButtonText.setText(R.string.title_most_played); headerBinding.sortButtonText.setText(R.string.title_most_played);
} }
startLoading(true); startLoading(true);
} }

View file

@ -14,7 +14,6 @@ import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.EditText; import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
@ -22,6 +21,7 @@ import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
import androidx.recyclerview.widget.ItemTouchHelper; import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import androidx.viewbinding.ViewBinding;
import org.reactivestreams.Subscriber; import org.reactivestreams.Subscriber;
import org.reactivestreams.Subscription; import org.reactivestreams.Subscription;
@ -32,6 +32,8 @@ import org.schabi.newpipe.database.history.model.StreamHistoryEntry;
import org.schabi.newpipe.database.playlist.PlaylistStreamEntry; import org.schabi.newpipe.database.playlist.PlaylistStreamEntry;
import org.schabi.newpipe.database.stream.model.StreamEntity; import org.schabi.newpipe.database.stream.model.StreamEntity;
import org.schabi.newpipe.database.stream.model.StreamStateEntity; import org.schabi.newpipe.database.stream.model.StreamStateEntity;
import org.schabi.newpipe.databinding.LocalPlaylistHeaderBinding;
import org.schabi.newpipe.databinding.PlaylistControlBinding;
import org.schabi.newpipe.extractor.stream.StreamInfoItem; import org.schabi.newpipe.extractor.stream.StreamInfoItem;
import org.schabi.newpipe.extractor.stream.StreamType; import org.schabi.newpipe.extractor.stream.StreamType;
import org.schabi.newpipe.info_list.InfoItemDialog; import org.schabi.newpipe.info_list.InfoItemDialog;
@ -77,13 +79,8 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt
@State @State
Parcelable itemsListState; Parcelable itemsListState;
private View headerRootLayout; private LocalPlaylistHeaderBinding headerBinding;
private TextView headerTitleView; private PlaylistControlBinding playlistControlBinding;
private TextView headerStreamCount;
private View playlistControl;
private View headerPlayAllButton;
private View headerPopupButton;
private View headerBackgroundButton;
private ItemTouchHelper itemTouchHelper; private ItemTouchHelper itemTouchHelper;
@ -137,8 +134,8 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt
public void setTitle(final String title) { public void setTitle(final String title) {
super.setTitle(title); super.setTitle(title);
if (headerTitleView != null) { if (headerBinding != null) {
headerTitleView.setText(title); headerBinding.playlistTitleView.setText(title);
} }
} }
@ -149,28 +146,21 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt
} }
@Override @Override
protected View getListHeader() { protected ViewBinding getListHeader() {
headerRootLayout = activity.getLayoutInflater() headerBinding = LocalPlaylistHeaderBinding.inflate(activity.getLayoutInflater(), itemsList,
.inflate(R.layout.local_playlist_header, itemsList, false); false);
playlistControlBinding = headerBinding.playlistControl;
headerTitleView = headerRootLayout.findViewById(R.id.playlist_title_view); headerBinding.playlistTitleView.setSelected(true);
headerTitleView.setSelected(true);
headerStreamCount = headerRootLayout.findViewById(R.id.playlist_stream_count); return headerBinding;
playlistControl = headerRootLayout.findViewById(R.id.playlist_control);
headerPlayAllButton = headerRootLayout.findViewById(R.id.playlist_ctrl_play_all_button);
headerPopupButton = headerRootLayout.findViewById(R.id.playlist_ctrl_play_popup_button);
headerBackgroundButton = headerRootLayout.findViewById(R.id.playlist_ctrl_play_bg_button);
return headerRootLayout;
} }
@Override @Override
protected void initListeners() { protected void initListeners() {
super.initListeners(); super.initListeners();
headerTitleView.setOnClickListener(view -> createRenameDialog()); headerBinding.playlistTitleView.setOnClickListener(view -> createRenameDialog());
itemTouchHelper = new ItemTouchHelper(getItemTouchCallback()); itemTouchHelper = new ItemTouchHelper(getItemTouchCallback());
itemTouchHelper.attachToRecyclerView(itemsList); itemTouchHelper.attachToRecyclerView(itemsList);
@ -210,22 +200,18 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt
@Override @Override
public void showLoading() { public void showLoading() {
super.showLoading(); super.showLoading();
if (headerRootLayout != null) { if (headerBinding != null) {
animateView(headerRootLayout, false, 200); animateView(headerBinding.getRoot(), false, 200);
} animateView(playlistControlBinding.getRoot(), false, 200);
if (playlistControl != null) {
animateView(playlistControl, false, 200);
} }
} }
@Override @Override
public void hideLoading() { public void hideLoading() {
super.hideLoading(); super.hideLoading();
if (headerRootLayout != null) { if (headerBinding != null) {
animateView(headerRootLayout, true, 200); animateView(headerBinding.getRoot(), true, 200);
} animateView(playlistControlBinding.getRoot(), true, 200);
if (playlistControl != null) {
animateView(playlistControl, true, 200);
} }
} }
@ -277,14 +263,13 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt
if (itemListAdapter != null) { if (itemListAdapter != null) {
itemListAdapter.unsetSelectedListener(); itemListAdapter.unsetSelectedListener();
} }
if (headerBackgroundButton != null) { if (playlistControlBinding != null) {
headerBackgroundButton.setOnClickListener(null); playlistControlBinding.playlistCtrlPlayBgButton.setOnClickListener(null);
} playlistControlBinding.playlistCtrlPlayAllButton.setOnClickListener(null);
if (headerPlayAllButton != null) { playlistControlBinding.playlistCtrlPlayPopupButton.setOnClickListener(null);
headerPlayAllButton.setOnClickListener(null);
} headerBinding = null;
if (headerPopupButton != null) { playlistControlBinding = null;
headerPopupButton.setOnClickListener(null);
} }
if (databaseSubscription != null) { if (databaseSubscription != null) {
@ -494,19 +479,19 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt
} }
setVideoCount(itemListAdapter.getItemsList().size()); setVideoCount(itemListAdapter.getItemsList().size());
headerPlayAllButton.setOnClickListener(view -> playlistControlBinding.playlistCtrlPlayAllButton.setOnClickListener(view ->
NavigationHelper.playOnMainPlayer(activity, getPlayQueue())); NavigationHelper.playOnMainPlayer(activity, getPlayQueue()));
headerPopupButton.setOnClickListener(view -> playlistControlBinding.playlistCtrlPlayPopupButton.setOnClickListener(view ->
NavigationHelper.playOnPopupPlayer(activity, getPlayQueue(), false)); NavigationHelper.playOnPopupPlayer(activity, getPlayQueue(), false));
headerBackgroundButton.setOnClickListener(view -> playlistControlBinding.playlistCtrlPlayBgButton.setOnClickListener(view ->
NavigationHelper.playOnBackgroundPlayer(activity, getPlayQueue(), false)); NavigationHelper.playOnBackgroundPlayer(activity, getPlayQueue(), false));
headerPopupButton.setOnLongClickListener(view -> { playlistControlBinding.playlistCtrlPlayPopupButton.setOnLongClickListener(view -> {
NavigationHelper.enqueueOnPopupPlayer(activity, getPlayQueue(), true); NavigationHelper.enqueueOnPopupPlayer(activity, getPlayQueue(), true);
return true; return true;
}); });
headerBackgroundButton.setOnLongClickListener(view -> { playlistControlBinding.playlistCtrlPlayBgButton.setOnLongClickListener(view -> {
NavigationHelper.enqueueOnBackgroundPlayer(activity, getPlayQueue(), true); NavigationHelper.enqueueOnBackgroundPlayer(activity, getPlayQueue(), true);
return true; return true;
}); });
@ -806,8 +791,9 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt
} }
private void setVideoCount(final long count) { private void setVideoCount(final long count) {
if (activity != null && headerStreamCount != null) { if (activity != null && headerBinding != null) {
headerStreamCount.setText(Localization.localizeStreamCount(activity, count)); headerBinding.playlistStreamCount.setText(Localization
.localizeStreamCount(activity, count));
} }
} }

View file

@ -17,6 +17,7 @@ import android.view.MenuInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.Toast import android.widget.Toast
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.localbroadcastmanager.content.LocalBroadcastManager import androidx.localbroadcastmanager.content.LocalBroadcastManager
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
@ -29,11 +30,10 @@ import com.xwray.groupie.Section
import com.xwray.groupie.kotlinandroidextensions.GroupieViewHolder import com.xwray.groupie.kotlinandroidextensions.GroupieViewHolder
import icepick.State import icepick.State
import io.reactivex.rxjava3.disposables.CompositeDisposable import io.reactivex.rxjava3.disposables.CompositeDisposable
import kotlinx.android.synthetic.main.dialog_title.view.itemAdditionalDetails
import kotlinx.android.synthetic.main.dialog_title.view.itemTitleView
import kotlinx.android.synthetic.main.fragment_subscription.items_list
import org.schabi.newpipe.R import org.schabi.newpipe.R
import org.schabi.newpipe.database.feed.model.FeedGroupEntity import org.schabi.newpipe.database.feed.model.FeedGroupEntity
import org.schabi.newpipe.databinding.DialogTitleBinding
import org.schabi.newpipe.databinding.FragmentSubscriptionBinding
import org.schabi.newpipe.extractor.channel.ChannelInfoItem import org.schabi.newpipe.extractor.channel.ChannelInfoItem
import org.schabi.newpipe.fragments.BaseStateFragment import org.schabi.newpipe.fragments.BaseStateFragment
import org.schabi.newpipe.local.subscription.SubscriptionViewModel.SubscriptionState import org.schabi.newpipe.local.subscription.SubscriptionViewModel.SubscriptionState
@ -70,6 +70,9 @@ import kotlin.math.floor
import kotlin.math.max import kotlin.math.max
class SubscriptionFragment : BaseStateFragment<SubscriptionState>() { class SubscriptionFragment : BaseStateFragment<SubscriptionState>() {
private var _binding: FragmentSubscriptionBinding? = null
private val binding get() = _binding!!
private lateinit var viewModel: SubscriptionViewModel private lateinit var viewModel: SubscriptionViewModel
private lateinit var subscriptionManager: SubscriptionManager private lateinit var subscriptionManager: SubscriptionManager
private val disposables: CompositeDisposable = CompositeDisposable() private val disposables: CompositeDisposable = CompositeDisposable()
@ -129,7 +132,7 @@ class SubscriptionFragment : BaseStateFragment<SubscriptionState>() {
override fun onPause() { override fun onPause() {
super.onPause() super.onPause()
itemsListState = items_list.layoutManager?.onSaveInstanceState() itemsListState = binding.itemsList.layoutManager?.onSaveInstanceState()
feedGroupsListState = feedGroupsCarousel?.onSaveInstanceState() feedGroupsListState = feedGroupsCarousel?.onSaveInstanceState()
importExportItemExpandedState = importExportItem.isExpanded importExportItemExpandedState = importExportItem.isExpanded
@ -169,7 +172,7 @@ class SubscriptionFragment : BaseStateFragment<SubscriptionState>() {
filters.addAction(IMPORT_COMPLETE_ACTION) filters.addAction(IMPORT_COMPLETE_ACTION)
subscriptionBroadcastReceiver = object : BroadcastReceiver() { subscriptionBroadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) { override fun onReceive(context: Context, intent: Intent) {
items_list?.post { _binding?.itemsList?.post {
importExportItem.isExpanded = false importExportItem.isExpanded = false
importExportItem.notifyChanged(FeedImportExportItem.REFRESH_EXPANDED_STATUS) importExportItem.notifyChanged(FeedImportExportItem.REFRESH_EXPANDED_STATUS)
} }
@ -275,17 +278,18 @@ class SubscriptionFragment : BaseStateFragment<SubscriptionState>() {
override fun initViews(rootView: View, savedInstanceState: Bundle?) { override fun initViews(rootView: View, savedInstanceState: Bundle?) {
super.initViews(rootView, savedInstanceState) super.initViews(rootView, savedInstanceState)
_binding = FragmentSubscriptionBinding.bind(rootView)
val shouldUseGridLayout = shouldUseGridLayout() val shouldUseGridLayout = shouldUseGridLayout()
groupAdapter.spanCount = if (shouldUseGridLayout) getGridSpanCount() else 1 groupAdapter.spanCount = if (shouldUseGridLayout) getGridSpanCount() else 1
items_list.layoutManager = GridLayoutManager(requireContext(), groupAdapter.spanCount).apply { binding.itemsList.layoutManager = GridLayoutManager(requireContext(), groupAdapter.spanCount).apply {
spanSizeLookup = groupAdapter.spanSizeLookup spanSizeLookup = groupAdapter.spanSizeLookup
} }
items_list.adapter = groupAdapter binding.itemsList.adapter = groupAdapter
viewModel = ViewModelProvider(this).get(SubscriptionViewModel::class.java) viewModel = ViewModelProvider(this).get(SubscriptionViewModel::class.java)
viewModel.stateLiveData.observe(viewLifecycleOwner, androidx.lifecycle.Observer { it?.let(this::handleResult) }) viewModel.stateLiveData.observe(viewLifecycleOwner, Observer { it?.let(this::handleResult) })
viewModel.feedGroupsLiveData.observe(viewLifecycleOwner, androidx.lifecycle.Observer { it?.let(this::handleFeedGroups) }) viewModel.feedGroupsLiveData.observe(viewLifecycleOwner, Observer { it?.let(this::handleFeedGroups) })
} }
private fun showLongTapDialog(selectedItem: ChannelInfoItem) { private fun showLongTapDialog(selectedItem: ChannelInfoItem) {
@ -301,13 +305,13 @@ class SubscriptionFragment : BaseStateFragment<SubscriptionState>() {
} }
} }
val bannerView = View.inflate(requireContext(), R.layout.dialog_title, null) val dialogTitleBinding = DialogTitleBinding.inflate(LayoutInflater.from(requireContext()))
bannerView.isSelected = true dialogTitleBinding.root.isSelected = true
bannerView.itemTitleView.text = selectedItem.name dialogTitleBinding.itemTitleView.text = selectedItem.name
bannerView.itemAdditionalDetails.visibility = View.GONE dialogTitleBinding.itemAdditionalDetails.visibility = View.GONE
AlertDialog.Builder(requireContext()) AlertDialog.Builder(requireContext())
.setCustomTitle(bannerView) .setCustomTitle(dialogTitleBinding.root)
.setItems(commands, actions) .setItems(commands, actions)
.create() .create()
.show() .show()
@ -368,14 +372,14 @@ class SubscriptionFragment : BaseStateFragment<SubscriptionState>() {
subscriptionsSection.setHideWhenEmpty(false) subscriptionsSection.setHideWhenEmpty(false)
if (result.subscriptions.isEmpty() && importExportItemExpandedState == null) { if (result.subscriptions.isEmpty() && importExportItemExpandedState == null) {
items_list.post { binding.itemsList.post {
importExportItem.isExpanded = true importExportItem.isExpanded = true
importExportItem.notifyChanged(FeedImportExportItem.REFRESH_EXPANDED_STATUS) importExportItem.notifyChanged(FeedImportExportItem.REFRESH_EXPANDED_STATUS)
} }
} }
if (itemsListState != null) { if (itemsListState != null) {
items_list.layoutManager?.onRestoreInstanceState(itemsListState) binding.itemsList.layoutManager?.onRestoreInstanceState(itemsListState)
itemsListState = null itemsListState = null
} }
} }
@ -394,7 +398,7 @@ class SubscriptionFragment : BaseStateFragment<SubscriptionState>() {
} }
feedGroupsSortMenuItem.showMenuItem = groups.size > 1 feedGroupsSortMenuItem.showMenuItem = groups.size > 1
items_list.post { feedGroupsSortMenuItem.notifyChanged(PAYLOAD_UPDATE_VISIBILITY_MENU_ITEM) } binding.itemsList.post { feedGroupsSortMenuItem.notifyChanged(PAYLOAD_UPDATE_VISIBILITY_MENU_ITEM) }
} }
// ///////////////////////////////////////////////////////////////////////// // /////////////////////////////////////////////////////////////////////////
@ -403,12 +407,12 @@ class SubscriptionFragment : BaseStateFragment<SubscriptionState>() {
override fun showLoading() { override fun showLoading() {
super.showLoading() super.showLoading()
animateView(items_list, false, 100) animateView(binding.itemsList, false, 100)
} }
override fun hideLoading() { override fun hideLoading() {
super.hideLoading() super.hideLoading()
animateView(items_list, true, 200) animateView(binding.itemsList, true, 200)
} }
// ///////////////////////////////////////////////////////////////////////// // /////////////////////////////////////////////////////////////////////////

View file

@ -24,10 +24,10 @@ import com.xwray.groupie.Section
import com.xwray.groupie.kotlinandroidextensions.GroupieViewHolder import com.xwray.groupie.kotlinandroidextensions.GroupieViewHolder
import icepick.Icepick import icepick.Icepick
import icepick.State import icepick.State
import kotlinx.android.synthetic.main.dialog_feed_group_create.*
import kotlinx.android.synthetic.main.toolbar_search_layout.*
import org.schabi.newpipe.R import org.schabi.newpipe.R
import org.schabi.newpipe.database.feed.model.FeedGroupEntity import org.schabi.newpipe.database.feed.model.FeedGroupEntity
import org.schabi.newpipe.databinding.DialogFeedGroupCreateBinding
import org.schabi.newpipe.databinding.ToolbarSearchLayoutBinding
import org.schabi.newpipe.fragments.BackPressable import org.schabi.newpipe.fragments.BackPressable
import org.schabi.newpipe.local.subscription.FeedGroupIcon import org.schabi.newpipe.local.subscription.FeedGroupIcon
import org.schabi.newpipe.local.subscription.dialog.FeedGroupDialog.ScreenState.DeleteScreen import org.schabi.newpipe.local.subscription.dialog.FeedGroupDialog.ScreenState.DeleteScreen
@ -45,6 +45,12 @@ import java.io.Serializable
import kotlin.collections.contains import kotlin.collections.contains
class FeedGroupDialog : DialogFragment(), BackPressable { class FeedGroupDialog : DialogFragment(), BackPressable {
private var _feedGroupCreateBinding: DialogFeedGroupCreateBinding? = null
private val feedGroupCreateBinding get() = _feedGroupCreateBinding!!
private var _searchLayoutBinding: ToolbarSearchLayoutBinding? = null
private val searchLayoutBinding get() = _searchLayoutBinding!!
private lateinit var viewModel: FeedGroupDialogViewModel private lateinit var viewModel: FeedGroupDialogViewModel
private var groupId: Long = NO_GROUP_SELECTED private var groupId: Long = NO_GROUP_SELECTED
private var groupIcon: FeedGroupIcon? = null private var groupIcon: FeedGroupIcon? = null
@ -107,14 +113,16 @@ class FeedGroupDialog : DialogFragment(), BackPressable {
override fun onSaveInstanceState(outState: Bundle) { override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState) super.onSaveInstanceState(outState)
iconsListState = icon_selector.layoutManager?.onSaveInstanceState() iconsListState = feedGroupCreateBinding.iconSelector.layoutManager?.onSaveInstanceState()
subscriptionsListState = subscriptions_selector_list.layoutManager?.onSaveInstanceState() subscriptionsListState = feedGroupCreateBinding.subscriptionsSelectorList.layoutManager?.onSaveInstanceState()
Icepick.saveInstanceState(this, outState) Icepick.saveInstanceState(this, outState)
} }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
_feedGroupCreateBinding = DialogFeedGroupCreateBinding.bind(view)
_searchLayoutBinding = feedGroupCreateBinding.subscriptionsHeaderSearchContainer
viewModel = ViewModelProvider( viewModel = ViewModelProvider(
this, this,
@ -146,7 +154,7 @@ class FeedGroupDialog : DialogFragment(), BackPressable {
add(subscriptionEmptyFooter) add(subscriptionEmptyFooter)
spanCount = 4 spanCount = 4
} }
subscriptions_selector_list.apply { feedGroupCreateBinding.subscriptionsSelectorList.apply {
// Disable animations, too distracting. // Disable animations, too distracting.
itemAnimator = null itemAnimator = null
adapter = subscriptionGroupAdapter adapter = subscriptionGroupAdapter
@ -172,8 +180,11 @@ class FeedGroupDialog : DialogFragment(), BackPressable {
override fun onDestroyView() { override fun onDestroyView() {
super.onDestroyView() super.onDestroyView()
subscriptions_selector_list?.adapter = null feedGroupCreateBinding.subscriptionsSelectorList.adapter = null
icon_selector?.adapter = null feedGroupCreateBinding.iconSelector.adapter = null
_feedGroupCreateBinding = null
_searchLayoutBinding = null
} }
/*/////////////////////////////////////////////////////////////////////////// /*///////////////////////////////////////////////////////////////////////////
@ -193,30 +204,30 @@ class FeedGroupDialog : DialogFragment(), BackPressable {
} }
private fun setupListeners() { private fun setupListeners() {
delete_button.setOnClickListener { showScreen(DeleteScreen) } feedGroupCreateBinding.deleteButton.setOnClickListener { showScreen(DeleteScreen) }
cancel_button.setOnClickListener { feedGroupCreateBinding.cancelButton.setOnClickListener {
when (currentScreen) { when (currentScreen) {
InitialScreen -> dismiss() InitialScreen -> dismiss()
else -> showScreen(InitialScreen) else -> showScreen(InitialScreen)
} }
} }
group_name_input_container.error = null feedGroupCreateBinding.groupNameInputContainer.error = null
group_name_input.doOnTextChanged { text, _, _, _ -> feedGroupCreateBinding.groupNameInput.doOnTextChanged { text, _, _, _ ->
if (group_name_input_container.isErrorEnabled && !text.isNullOrBlank()) { if (feedGroupCreateBinding.groupNameInputContainer.isErrorEnabled && !text.isNullOrBlank()) {
group_name_input_container.error = null feedGroupCreateBinding.groupNameInputContainer.error = null
} }
} }
confirm_button.setOnClickListener { handlePositiveButton() } feedGroupCreateBinding.confirmButton.setOnClickListener { handlePositiveButton() }
select_channel_button.setOnClickListener { feedGroupCreateBinding.selectChannelButton.setOnClickListener {
subscriptions_selector_list.scrollToPosition(0) feedGroupCreateBinding.subscriptionsSelectorList.scrollToPosition(0)
showScreen(SubscriptionsPickerScreen) showScreen(SubscriptionsPickerScreen)
} }
val headerMenu = subscriptions_header_toolbar.menu val headerMenu = feedGroupCreateBinding.subscriptionsHeaderToolbar.menu
requireActivity().menuInflater.inflate(R.menu.menu_feed_group_dialog, headerMenu) requireActivity().menuInflater.inflate(R.menu.menu_feed_group_dialog, headerMenu)
headerMenu.findItem(R.id.action_search).setOnMenuItemClickListener { headerMenu.findItem(R.id.action_search).setOnMenuItemClickListener {
@ -234,8 +245,8 @@ class FeedGroupDialog : DialogFragment(), BackPressable {
} }
} }
toolbar_search_clear.setOnClickListener { searchLayoutBinding.toolbarSearchClear.setOnClickListener {
if (toolbar_search_edit_text.text.isEmpty()) { if (searchLayoutBinding.toolbarSearchEditText.text.isNullOrEmpty()) {
hideSearch() hideSearch()
return@setOnClickListener return@setOnClickListener
} }
@ -243,14 +254,14 @@ class FeedGroupDialog : DialogFragment(), BackPressable {
showKeyboardSearch() showKeyboardSearch()
} }
toolbar_search_edit_text.setOnClickListener { searchLayoutBinding.toolbarSearchEditText.setOnClickListener {
if (DeviceUtils.isTv(context)) { if (DeviceUtils.isTv(context)) {
showKeyboardSearch() showKeyboardSearch()
} }
} }
toolbar_search_edit_text.doOnTextChanged { _, _, _, _ -> searchLayoutBinding.toolbarSearchEditText.doOnTextChanged { _, _, _, _ ->
val newQuery: String = toolbar_search_edit_text.text.toString() val newQuery: String = searchLayoutBinding.toolbarSearchEditText.text.toString()
subscriptionsCurrentSearchQuery = newQuery subscriptionsCurrentSearchQuery = newQuery
viewModel.filterSubscriptionsBy(newQuery) viewModel.filterSubscriptionsBy(newQuery)
} }
@ -266,16 +277,16 @@ class FeedGroupDialog : DialogFragment(), BackPressable {
} }
private fun handlePositiveButtonInitialScreen() { private fun handlePositiveButtonInitialScreen() {
val name = group_name_input.text.toString().trim() val name = feedGroupCreateBinding.groupNameInput.text.toString().trim()
val icon = selectedIcon ?: groupIcon ?: FeedGroupIcon.ALL val icon = selectedIcon ?: groupIcon ?: FeedGroupIcon.ALL
if (name.isBlank()) { if (name.isBlank()) {
group_name_input_container.error = getString(R.string.feed_group_dialog_empty_name) feedGroupCreateBinding.groupNameInputContainer.error = getString(R.string.feed_group_dialog_empty_name)
group_name_input.text = null feedGroupCreateBinding.groupNameInput.text = null
group_name_input.requestFocus() feedGroupCreateBinding.groupNameInput.requestFocus()
return return
} else { } else {
group_name_input_container.error = null feedGroupCreateBinding.groupNameInputContainer.error = null
} }
if (selectedSubscriptions.isEmpty()) { if (selectedSubscriptions.isEmpty()) {
@ -296,10 +307,10 @@ class FeedGroupDialog : DialogFragment(), BackPressable {
groupSortOrder = feedGroupEntity?.sortOrder ?: -1 groupSortOrder = feedGroupEntity?.sortOrder ?: -1
val feedGroupIcon = if (selectedIcon == null) icon else selectedIcon!! val feedGroupIcon = if (selectedIcon == null) icon else selectedIcon!!
icon_preview.setImageResource(feedGroupIcon.getDrawableRes(requireContext())) feedGroupCreateBinding.iconPreview.setImageResource(feedGroupIcon.getDrawableRes(requireContext()))
if (group_name_input.text.isNullOrBlank()) { if (feedGroupCreateBinding.groupNameInput.text.isNullOrBlank()) {
group_name_input.setText(name) feedGroupCreateBinding.groupNameInput.setText(name)
} }
} }
@ -346,10 +357,10 @@ class FeedGroupDialog : DialogFragment(), BackPressable {
subscriptionMainSection.update(subscriptions, false) subscriptionMainSection.update(subscriptions, false)
if (subscriptionsListState != null) { if (subscriptionsListState != null) {
subscriptions_selector_list.layoutManager?.onRestoreInstanceState(subscriptionsListState) feedGroupCreateBinding.subscriptionsSelectorList.layoutManager?.onRestoreInstanceState(subscriptionsListState)
subscriptionsListState = null subscriptionsListState = null
} else { } else {
subscriptions_selector_list.scrollToPosition(0) feedGroupCreateBinding.subscriptionsSelectorList.scrollToPosition(0)
} }
} }
@ -357,17 +368,16 @@ class FeedGroupDialog : DialogFragment(), BackPressable {
val selectedCount = this.selectedSubscriptions.size val selectedCount = this.selectedSubscriptions.size
val selectedCountText = resources.getQuantityString( val selectedCountText = resources.getQuantityString(
R.plurals.feed_group_dialog_selection_count, R.plurals.feed_group_dialog_selection_count,
selectedCount, selectedCount selectedCount, selectedCount)
) feedGroupCreateBinding.selectedSubscriptionCountView.text = selectedCountText
selected_subscription_count_view.text = selectedCountText feedGroupCreateBinding.subscriptionsHeaderInfo.text = selectedCountText
subscriptions_header_info.text = selectedCountText
} }
private fun setupIconPicker() { private fun setupIconPicker() {
val groupAdapter = GroupAdapter<GroupieViewHolder>() val groupAdapter = GroupAdapter<GroupieViewHolder>()
groupAdapter.addAll(FeedGroupIcon.values().map { PickerIconItem(requireContext(), it) }) groupAdapter.addAll(FeedGroupIcon.values().map { PickerIconItem(requireContext(), it) })
icon_selector.apply { feedGroupCreateBinding.iconSelector.apply {
layoutManager = GridLayoutManager(requireContext(), 7, RecyclerView.VERTICAL, false) layoutManager = GridLayoutManager(requireContext(), 7, RecyclerView.VERTICAL, false)
adapter = groupAdapter adapter = groupAdapter
@ -381,20 +391,20 @@ class FeedGroupDialog : DialogFragment(), BackPressable {
when (item) { when (item) {
is PickerIconItem -> { is PickerIconItem -> {
selectedIcon = item.icon selectedIcon = item.icon
icon_preview.setImageResource(item.iconRes) feedGroupCreateBinding.iconPreview.setImageResource(item.iconRes)
showScreen(InitialScreen) showScreen(InitialScreen)
} }
} }
} }
icon_preview.setOnClickListener { feedGroupCreateBinding.iconPreview.setOnClickListener {
icon_selector.scrollToPosition(0) feedGroupCreateBinding.iconSelector.scrollToPosition(0)
showScreen(IconPickerScreen) showScreen(IconPickerScreen)
} }
if (groupId == NO_GROUP_SELECTED) { if (groupId == NO_GROUP_SELECTED) {
val icon = selectedIcon ?: FeedGroupIcon.ALL val icon = selectedIcon ?: FeedGroupIcon.ALL
icon_preview.setImageResource(icon.getDrawableRes(requireContext())) feedGroupCreateBinding.iconPreview.setImageResource(icon.getDrawableRes(requireContext()))
} }
} }
@ -405,22 +415,20 @@ class FeedGroupDialog : DialogFragment(), BackPressable {
private fun showScreen(screen: ScreenState) { private fun showScreen(screen: ScreenState) {
currentScreen = screen currentScreen = screen
options_root.onlyVisibleIn(InitialScreen) feedGroupCreateBinding.optionsRoot.onlyVisibleIn(InitialScreen)
icon_selector.onlyVisibleIn(IconPickerScreen) feedGroupCreateBinding.iconSelector.onlyVisibleIn(IconPickerScreen)
subscriptions_selector.onlyVisibleIn(SubscriptionsPickerScreen) feedGroupCreateBinding.subscriptionsSelector.onlyVisibleIn(SubscriptionsPickerScreen)
delete_screen_message.onlyVisibleIn(DeleteScreen) feedGroupCreateBinding.deleteScreenMessage.onlyVisibleIn(DeleteScreen)
separator.onlyVisibleIn(SubscriptionsPickerScreen, IconPickerScreen) feedGroupCreateBinding.separator.onlyVisibleIn(SubscriptionsPickerScreen, IconPickerScreen)
cancel_button.onlyVisibleIn(InitialScreen, DeleteScreen) feedGroupCreateBinding.cancelButton.onlyVisibleIn(InitialScreen, DeleteScreen)
confirm_button.setText( feedGroupCreateBinding.confirmButton.setText(when {
when {
currentScreen == InitialScreen && groupId == NO_GROUP_SELECTED -> R.string.create currentScreen == InitialScreen && groupId == NO_GROUP_SELECTED -> R.string.create
else -> android.R.string.ok else -> android.R.string.ok
} })
)
delete_button.isGone = currentScreen != InitialScreen || groupId == NO_GROUP_SELECTED feedGroupCreateBinding.deleteButton.isGone = currentScreen != InitialScreen || groupId == NO_GROUP_SELECTED
hideKeyboard() hideKeyboard()
hideSearch() hideSearch()
@ -434,26 +442,26 @@ class FeedGroupDialog : DialogFragment(), BackPressable {
// Utils // Utils
////////////////////////////////////////////////////////////////////////// */ ////////////////////////////////////////////////////////////////////////// */
private fun isSearchVisible() = subscriptions_header_search_container?.visibility == View.VISIBLE private fun isSearchVisible() = _searchLayoutBinding?.root?.visibility == View.VISIBLE
private fun resetSearch() { private fun resetSearch() {
toolbar_search_edit_text.setText("") searchLayoutBinding.toolbarSearchEditText.setText("")
subscriptionsCurrentSearchQuery = "" subscriptionsCurrentSearchQuery = ""
viewModel.clearSubscriptionsFilter() viewModel.clearSubscriptionsFilter()
} }
private fun hideSearch() { private fun hideSearch() {
resetSearch() resetSearch()
subscriptions_header_search_container.visibility = View.GONE searchLayoutBinding.root.visibility = View.GONE
subscriptions_header_info_container.visibility = View.VISIBLE feedGroupCreateBinding.subscriptionsHeaderInfoContainer.visibility = View.VISIBLE
subscriptions_header_toolbar.menu.findItem(R.id.action_search).isVisible = true feedGroupCreateBinding.subscriptionsHeaderToolbar.menu.findItem(R.id.action_search).isVisible = true
hideKeyboardSearch() hideKeyboardSearch()
} }
private fun showSearch() { private fun showSearch() {
subscriptions_header_search_container.visibility = View.VISIBLE searchLayoutBinding.root.visibility = View.VISIBLE
subscriptions_header_info_container.visibility = View.GONE feedGroupCreateBinding.subscriptionsHeaderInfoContainer.visibility = View.GONE
subscriptions_header_toolbar.menu.findItem(R.id.action_search).isVisible = false feedGroupCreateBinding.subscriptionsHeaderToolbar.menu.findItem(R.id.action_search).isVisible = false
showKeyboardSearch() showKeyboardSearch()
} }
@ -462,37 +470,35 @@ class FeedGroupDialog : DialogFragment(), BackPressable {
} }
private fun showKeyboardSearch() { private fun showKeyboardSearch() {
if (toolbar_search_edit_text.requestFocus()) { if (searchLayoutBinding.toolbarSearchEditText.requestFocus()) {
inputMethodManager.showSoftInput(toolbar_search_edit_text, InputMethodManager.SHOW_IMPLICIT) inputMethodManager.showSoftInput(searchLayoutBinding.toolbarSearchEditText,
InputMethodManager.SHOW_IMPLICIT)
} }
} }
private fun hideKeyboardSearch() { private fun hideKeyboardSearch() {
inputMethodManager.hideSoftInputFromWindow( inputMethodManager.hideSoftInputFromWindow(searchLayoutBinding.toolbarSearchEditText.windowToken,
toolbar_search_edit_text.windowToken, InputMethodManager.RESULT_UNCHANGED_SHOWN)
InputMethodManager.RESULT_UNCHANGED_SHOWN searchLayoutBinding.toolbarSearchEditText.clearFocus()
)
toolbar_search_edit_text.clearFocus()
} }
private fun showKeyboard() { private fun showKeyboard() {
if (group_name_input.requestFocus()) { if (feedGroupCreateBinding.groupNameInput.requestFocus()) {
inputMethodManager.showSoftInput(group_name_input, InputMethodManager.SHOW_IMPLICIT) inputMethodManager.showSoftInput(feedGroupCreateBinding.groupNameInput,
InputMethodManager.SHOW_IMPLICIT)
} }
} }
private fun hideKeyboard() { private fun hideKeyboard() {
inputMethodManager.hideSoftInputFromWindow( inputMethodManager.hideSoftInputFromWindow(feedGroupCreateBinding.groupNameInput.windowToken,
group_name_input.windowToken, InputMethodManager.RESULT_UNCHANGED_SHOWN)
InputMethodManager.RESULT_UNCHANGED_SHOWN feedGroupCreateBinding.groupNameInput.clearFocus()
)
group_name_input.clearFocus()
} }
private fun disableInput() { private fun disableInput() {
delete_button?.isEnabled = false _feedGroupCreateBinding?.deleteButton?.isEnabled = false
confirm_button?.isEnabled = false _feedGroupCreateBinding?.confirmButton?.isEnabled = false
cancel_button?.isEnabled = false _feedGroupCreateBinding?.cancelButton?.isEnabled = false
isCancelable = false isCancelable = false
hideKeyboard() hideKeyboard()

View file

@ -16,10 +16,9 @@ import com.xwray.groupie.TouchCallback
import com.xwray.groupie.kotlinandroidextensions.GroupieViewHolder import com.xwray.groupie.kotlinandroidextensions.GroupieViewHolder
import icepick.Icepick import icepick.Icepick
import icepick.State import icepick.State
import kotlinx.android.synthetic.main.dialog_feed_group_reorder.confirm_button
import kotlinx.android.synthetic.main.dialog_feed_group_reorder.feed_groups_list
import org.schabi.newpipe.R import org.schabi.newpipe.R
import org.schabi.newpipe.database.feed.model.FeedGroupEntity import org.schabi.newpipe.database.feed.model.FeedGroupEntity
import org.schabi.newpipe.databinding.DialogFeedGroupReorderBinding
import org.schabi.newpipe.local.subscription.dialog.FeedGroupReorderDialogViewModel.DialogEvent.ProcessingEvent import org.schabi.newpipe.local.subscription.dialog.FeedGroupReorderDialogViewModel.DialogEvent.ProcessingEvent
import org.schabi.newpipe.local.subscription.dialog.FeedGroupReorderDialogViewModel.DialogEvent.SuccessEvent import org.schabi.newpipe.local.subscription.dialog.FeedGroupReorderDialogViewModel.DialogEvent.SuccessEvent
import org.schabi.newpipe.local.subscription.item.FeedGroupReorderItem import org.schabi.newpipe.local.subscription.item.FeedGroupReorderItem
@ -27,6 +26,9 @@ import org.schabi.newpipe.util.ThemeHelper
import java.util.Collections import java.util.Collections
class FeedGroupReorderDialog : DialogFragment() { class FeedGroupReorderDialog : DialogFragment() {
private var _binding: DialogFeedGroupReorderBinding? = null
private val binding get() = _binding!!
private lateinit var viewModel: FeedGroupReorderDialogViewModel private lateinit var viewModel: FeedGroupReorderDialogViewModel
@State @State
@ -48,6 +50,7 @@ class FeedGroupReorderDialog : DialogFragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
_binding = DialogFeedGroupReorderBinding.bind(view)
viewModel = ViewModelProvider(this).get(FeedGroupReorderDialogViewModel::class.java) viewModel = ViewModelProvider(this).get(FeedGroupReorderDialogViewModel::class.java)
viewModel.groupsLiveData.observe(viewLifecycleOwner, Observer(::handleGroups)) viewModel.groupsLiveData.observe(viewLifecycleOwner, Observer(::handleGroups))
@ -61,15 +64,20 @@ class FeedGroupReorderDialog : DialogFragment() {
} }
) )
feed_groups_list.layoutManager = LinearLayoutManager(requireContext()) binding.feedGroupsList.layoutManager = LinearLayoutManager(requireContext())
feed_groups_list.adapter = groupAdapter binding.feedGroupsList.adapter = groupAdapter
itemTouchHelper.attachToRecyclerView(feed_groups_list) itemTouchHelper.attachToRecyclerView(binding.feedGroupsList)
confirm_button.setOnClickListener { binding.confirmButton.setOnClickListener {
viewModel.updateOrder(groupOrderedIdList) viewModel.updateOrder(groupOrderedIdList)
} }
} }
override fun onDestroyView() {
_binding = null
super.onDestroyView()
}
override fun onSaveInstanceState(outState: Bundle) { override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState) super.onSaveInstanceState(outState)
Icepick.saveInstanceState(this, outState) Icepick.saveInstanceState(this, outState)
@ -89,7 +97,7 @@ class FeedGroupReorderDialog : DialogFragment() {
} }
private fun disableInput() { private fun disableInput() {
confirm_button?.isEnabled = false _binding?.confirmButton?.isEnabled = false
isCancelable = false isCancelable = false
} }

View file

@ -119,7 +119,10 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_below="@id/channel_metadata"> android:layout_below="@id/channel_metadata">
<include layout="@layout/playlist_control" /> <include
android:id="@+id/playlist_control"
layout="@layout/playlist_control" />
</LinearLayout> </LinearLayout>
</RelativeLayout> </RelativeLayout>

View file

@ -40,7 +40,10 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_below="@id/playlist_stream_count"> android:layout_below="@id/playlist_stream_count">
<include layout="@layout/playlist_control" /> <include
android:id="@+id/playlist_control"
layout="@layout/playlist_control" />
</LinearLayout> </LinearLayout>
</RelativeLayout> </RelativeLayout>

View file

@ -83,7 +83,9 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_below="@id/playlist_meta"> android:layout_below="@id/playlist_meta">
<include layout="@layout/playlist_control" /> <include
android:id="@+id/playlist_control"
layout="@layout/playlist_control" />
</LinearLayout> </LinearLayout>
</RelativeLayout> </RelativeLayout>

View file

@ -38,6 +38,8 @@
tools:ignore="RtlHardcoded" /> tools:ignore="RtlHardcoded" />
</RelativeLayout> </RelativeLayout>
<include layout="@layout/playlist_control" /> <include
android:id="@+id/playlist_control"
layout="@layout/playlist_control" />
</LinearLayout> </LinearLayout>