From 24ad4dbc0fb7080ef0f1b46142547568c2cdd5f7 Mon Sep 17 00:00:00 2001 From: Thomas Date: Fri, 27 Nov 2020 14:06:42 +0100 Subject: [PATCH] Fix issue #58 - Allow to search channels --- .../app/fedilab/fedilabtube/MainActivity.java | 4 +- .../fedilab/fedilabtube/SearchActivity.java | 116 ++++++++++++-- .../fedilabtube/client/PeertubeService.java | 10 +- .../client/RetrofitPeertubeAPI.java | 28 ++++ .../fragment/DisplayAccountsFragment.java | 3 +- .../fragment/DisplayChannelsFragment.java | 58 +++---- .../fedilabtube/viewmodel/SearchVM.java | 23 +++ .../res/layout/activity_search_result.xml | 143 +++++++++++------- 8 files changed, 287 insertions(+), 98 deletions(-) diff --git a/app/src/main/java/app/fedilab/fedilabtube/MainActivity.java b/app/src/main/java/app/fedilab/fedilabtube/MainActivity.java index 8c477ac..4d00c94 100644 --- a/app/src/main/java/app/fedilab/fedilabtube/MainActivity.java +++ b/app/src/main/java/app/fedilab/fedilabtube/MainActivity.java @@ -187,7 +187,9 @@ public class MainActivity extends AppCompatActivity { private void setTitleCustom(int titleRId) { Toolbar toolbar = findViewById(R.id.toolbar); TextView mTitle = toolbar.findViewById(R.id.toolbar_title); - mTitle.setText(getString(titleRId)); + if (mTitle != null) { + mTitle.setText(getString(titleRId)); + } } @Override diff --git a/app/src/main/java/app/fedilab/fedilabtube/SearchActivity.java b/app/src/main/java/app/fedilab/fedilabtube/SearchActivity.java index 4df50cf..636ca09 100644 --- a/app/src/main/java/app/fedilab/fedilabtube/SearchActivity.java +++ b/app/src/main/java/app/fedilab/fedilabtube/SearchActivity.java @@ -16,11 +16,22 @@ package app.fedilab.fedilabtube; import android.os.Bundle; import android.view.MenuItem; +import android.view.View; import android.widget.Toast; import androidx.appcompat.app.AppCompatActivity; -import androidx.fragment.app.FragmentTransaction; +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentManager; +import androidx.fragment.app.FragmentStatePagerAdapter; +import androidx.viewpager.widget.PagerAdapter; +import androidx.viewpager.widget.ViewPager; +import com.google.android.material.tabs.TabLayout; + +import org.jetbrains.annotations.NotNull; + +import app.fedilab.fedilabtube.databinding.ActivitySearchResultBinding; +import app.fedilab.fedilabtube.fragment.DisplayChannelsFragment; import app.fedilab.fedilabtube.fragment.DisplayVideosFragment; import es.dmoral.toasty.Toasty; @@ -29,12 +40,15 @@ public class SearchActivity extends AppCompatActivity { private String search; + private ActivitySearchResultBinding binding; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + binding = ActivitySearchResultBinding.inflate(getLayoutInflater()); + View view = binding.getRoot(); + setContentView(view); - setContentView(R.layout.activity_search_result); Bundle b = getIntent().getExtras(); if (b != null) { search = b.getString("search"); @@ -46,15 +60,64 @@ public class SearchActivity extends AppCompatActivity { getSupportActionBar().setDisplayHomeAsUpEnabled(true); setTitle(search); + binding.searchTabLayout.addTab(binding.searchTabLayout.newTab().setText(getString(R.string.videos))); + binding.searchTabLayout.addTab(binding.searchTabLayout.newTab().setText(getString(R.string.channels))); + binding.searchPager.setOffscreenPageLimit(2); + + PagerAdapter mPagerAdapter = new ScreenSlidePagerAdapter(getSupportFragmentManager()); + binding.searchPager.setAdapter(mPagerAdapter); + binding.searchPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { + @Override + public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { + + } + + @Override + public void onPageSelected(int position) { + TabLayout.Tab tab = binding.searchTabLayout.getTabAt(position); + if (tab != null) + tab.select(); + } + + @Override + public void onPageScrollStateChanged(int state) { + + } + }); + + binding.searchTabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() { + @Override + public void onTabSelected(TabLayout.Tab tab) { + binding.searchPager.setCurrentItem(tab.getPosition()); + } + + @Override + public void onTabUnselected(TabLayout.Tab tab) { + + } + + @Override + public void onTabReselected(TabLayout.Tab tab) { + Fragment fragment = null; + if (binding.searchPager.getAdapter() != null) + fragment = (Fragment) binding.searchPager.getAdapter().instantiateItem(binding.searchPager, tab.getPosition()); + switch (tab.getPosition()) { + case 0: + if (fragment != null) { + DisplayVideosFragment displayVideosFragment = ((DisplayVideosFragment) fragment); + displayVideosFragment.scrollToTop(); + } + break; + case 1: + if (fragment != null) { + DisplayChannelsFragment displayChannelsFragment = ((DisplayChannelsFragment) fragment); + displayChannelsFragment.scrollToTop(); + } + break; + } + } + }); - if (savedInstanceState == null) { - DisplayVideosFragment displayVideosFragment = new DisplayVideosFragment(); - Bundle bundle = new Bundle(); - bundle.putString("search_peertube", search); - displayVideosFragment.setArguments(bundle); - FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); - ft.add(R.id.container, displayVideosFragment).commit(); - } } @@ -67,4 +130,37 @@ public class SearchActivity extends AppCompatActivity { return super.onOptionsItemSelected(item); } + + /** + * Pager adapter for the 2 fragments + */ + private class ScreenSlidePagerAdapter extends FragmentStatePagerAdapter { + + ScreenSlidePagerAdapter(FragmentManager fm) { + super(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT); + } + + @NotNull + @Override + public Fragment getItem(int position) { + Bundle bundle = new Bundle(); + if (position == 0) { + DisplayVideosFragment displayVideosFragment = new DisplayVideosFragment(); + bundle.putString("search_peertube", search); + displayVideosFragment.setArguments(bundle); + return displayVideosFragment; + } + DisplayChannelsFragment displayChannelsFragment = new DisplayChannelsFragment(); + bundle.putString("search_peertube", search); + displayChannelsFragment.setArguments(bundle); + return displayChannelsFragment; + } + + + @Override + public int getCount() { + return 2; + } + } + } diff --git a/app/src/main/java/app/fedilab/fedilabtube/client/PeertubeService.java b/app/src/main/java/app/fedilab/fedilabtube/client/PeertubeService.java index b240da3..1510613 100644 --- a/app/src/main/java/app/fedilab/fedilabtube/client/PeertubeService.java +++ b/app/src/main/java/app/fedilab/fedilabtube/client/PeertubeService.java @@ -201,7 +201,7 @@ public interface PeertubeService { Call deleteHistory( @Header("Authorization") String credentials); - //Search + //Search videos @GET("search/videos") Call searchVideos( @Header("Authorization") String credentials, @@ -209,6 +209,14 @@ public interface PeertubeService { @Query("start") String maxId, @Query("count") String count); + //Search channels + @GET("search/video-channels") + Call searchChannels( + @Header("Authorization") String credentials, + @Query("search") String search, + @Query("searcharget") String searchTarget, + @Query("start") String maxId, + @Query("count") String count); //Search @GET("search/videos") diff --git a/app/src/main/java/app/fedilab/fedilabtube/client/RetrofitPeertubeAPI.java b/app/src/main/java/app/fedilab/fedilabtube/client/RetrofitPeertubeAPI.java index ee4ecbb..72d8c33 100644 --- a/app/src/main/java/app/fedilab/fedilabtube/client/RetrofitPeertubeAPI.java +++ b/app/src/main/java/app/fedilab/fedilabtube/client/RetrofitPeertubeAPI.java @@ -946,6 +946,34 @@ public class RetrofitPeertubeAPI { return apiResponse; } + + /** + * Retrieves channels search *synchronously* + * + * @param query String search + * @return APIResponse + */ + public APIResponse searchChannels(String query, String max_id) { + PeertubeService peertubeService = init(); + Call searchChannelsCall = peertubeService.searchChannels(getToken(), query, "local", max_id, count); + APIResponse apiResponse = new APIResponse(); + try { + Response response = searchChannelsCall.execute(); + if (response.isSuccessful() && response.body() != null) { + apiResponse.setChannels(response.body().data); + } else { + setError(apiResponse, response.code(), response.errorBody()); + } + } catch (IOException e) { + Error error = new Error(); + error.setError(_context.getString(R.string.toast_error)); + apiResponse.setError(error); + e.printStackTrace(); + } + return apiResponse; + } + + /*** * Verifiy credential of the authenticated user *synchronously* * @return Account diff --git a/app/src/main/java/app/fedilab/fedilabtube/fragment/DisplayAccountsFragment.java b/app/src/main/java/app/fedilab/fedilabtube/fragment/DisplayAccountsFragment.java index 24b9f18..dc798c2 100644 --- a/app/src/main/java/app/fedilab/fedilabtube/fragment/DisplayAccountsFragment.java +++ b/app/src/main/java/app/fedilab/fedilabtube/fragment/DisplayAccountsFragment.java @@ -182,8 +182,7 @@ public class DisplayAccountsFragment extends Fragment implements AccountsListAda textviewNoAction.setVisibility(View.VISIBLE); else textviewNoAction.setVisibility(View.GONE); - - max_id = apiResponse.getMax_id(); + max_id = String.valueOf(Integer.parseInt(max_id) + 20); if (accounts != null && accounts.size() > 0) { int previousPosition = this.accounts.size(); int currentPosition = this.accounts.size(); diff --git a/app/src/main/java/app/fedilab/fedilabtube/fragment/DisplayChannelsFragment.java b/app/src/main/java/app/fedilab/fedilabtube/fragment/DisplayChannelsFragment.java index 2e72c87..77b195b 100644 --- a/app/src/main/java/app/fedilab/fedilabtube/fragment/DisplayChannelsFragment.java +++ b/app/src/main/java/app/fedilab/fedilabtube/fragment/DisplayChannelsFragment.java @@ -29,7 +29,6 @@ import android.view.ViewGroup; import android.view.WindowManager; import android.view.inputmethod.InputMethodManager; import android.widget.Button; -import android.widget.RelativeLayout; import android.widget.Toast; import androidx.annotation.NonNull; @@ -60,6 +59,7 @@ import app.fedilab.fedilabtube.databinding.FragmentRecyclerviewBinding; import app.fedilab.fedilabtube.drawer.ChannelListAdapter; import app.fedilab.fedilabtube.helper.Helper; import app.fedilab.fedilabtube.viewmodel.ChannelsVM; +import app.fedilab.fedilabtube.viewmodel.SearchVM; import es.dmoral.toasty.Toasty; import static app.fedilab.fedilabtube.PeertubeUploadActivity.MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE; @@ -71,7 +71,6 @@ public class DisplayChannelsFragment extends Fragment implements ChannelListAdap private static final int PICK_AVATAR = 467; private ChannelListAdapter channelListAdapter; private List channels; - private RelativeLayout mainLoader, nextElementLoader, textviewNoAction; private String name; private View rootView; private boolean myChannels; @@ -79,6 +78,8 @@ public class DisplayChannelsFragment extends Fragment implements ChannelListAdap private FragmentRecyclerviewBinding binding; private AddChannelBinding bindingDialog; private Uri inputData; + private String search_peertube; + private String max_id; @Override public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { @@ -90,13 +91,13 @@ public class DisplayChannelsFragment extends Fragment implements ChannelListAdap Bundle bundle = this.getArguments(); channels = new ArrayList<>(); myChannels = true; + max_id = "0"; if (bundle != null) { name = bundle.getString("name", null); myChannels = bundle.getBoolean("myChannels", true); + search_peertube = bundle.getString("search_peertube", null); } - - if (getActivity() != null) { action_button = getActivity().findViewById(R.id.action_button); if (action_button != null) { @@ -106,11 +107,6 @@ public class DisplayChannelsFragment extends Fragment implements ChannelListAdap } binding.lvElements.addItemDecoration(new DividerItemDecoration(context, DividerItemDecoration.VERTICAL)); - mainLoader = rootView.findViewById(R.id.loader); - nextElementLoader = rootView.findViewById(R.id.loading_next); - textviewNoAction = rootView.findViewById(R.id.no_action); - mainLoader.setVisibility(View.VISIBLE); - nextElementLoader.setVisibility(View.GONE); channelListAdapter = new ChannelListAdapter(this.channels, myChannels); channelListAdapter.allChannelRemoved = this; channelListAdapter.editAlertDialog = this; @@ -121,15 +117,24 @@ public class DisplayChannelsFragment extends Fragment implements ChannelListAdap binding.lvElements.setLayoutManager(mLayoutManager); binding.swipeContainer.setOnRefreshListener(this::pullToRefresh); - ChannelsVM viewModel = new ViewModelProvider(this).get(ChannelsVM.class); - if (name != null) { - viewModel.get(RetrofitPeertubeAPI.DataType.CHANNELS_FOR_ACCOUNT, name).observe(DisplayChannelsFragment.this.requireActivity(), this::manageViewChannels); - } else { - viewModel.get(RetrofitPeertubeAPI.DataType.MY_CHANNELS, null).observe(DisplayChannelsFragment.this.requireActivity(), this::manageViewChannels); - } + + loadChannels(max_id); return rootView; } + private void loadChannels(String max_id) { + if (search_peertube == null) { + ChannelsVM viewModel = new ViewModelProvider(this).get(ChannelsVM.class); + if (name != null) { + viewModel.get(RetrofitPeertubeAPI.DataType.CHANNELS_FOR_ACCOUNT, name).observe(DisplayChannelsFragment.this.requireActivity(), this::manageViewChannels); + } else { + viewModel.get(RetrofitPeertubeAPI.DataType.MY_CHANNELS, null).observe(DisplayChannelsFragment.this.requireActivity(), this::manageViewChannels); + } + } else { + SearchVM viewModelSearch = new ViewModelProvider(this).get(SearchVM.class); + viewModelSearch.getChannels(max_id, search_peertube).observe(this.requireActivity(), this::manageViewChannels); + } + } @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { @@ -187,19 +192,20 @@ public class DisplayChannelsFragment extends Fragment implements ChannelListAdap } private void manageViewChannels(APIResponse apiResponse) { - mainLoader.setVisibility(View.GONE); - nextElementLoader.setVisibility(View.GONE); + binding.loader.setVisibility(View.GONE); + binding.loadingNext.setVisibility(View.GONE); if (apiResponse.getError() != null) { Toasty.error(context, apiResponse.getError().getError(), Toast.LENGTH_LONG).show(); binding.swipeContainer.setRefreshing(false); return; } List channels = apiResponse.getChannels(); - if ((channels == null || channels.size() == 0)) - textviewNoAction.setVisibility(View.VISIBLE); - else - textviewNoAction.setVisibility(View.GONE); + if ((channels == null || channels.size() == 0)) + binding.noAction.setVisibility(View.VISIBLE); + else + binding.noAction.setVisibility(View.GONE); + max_id = String.valueOf(Integer.parseInt(max_id) + 20); if (channels != null && channels.size() > 0) { int currentPosition = this.channels.size(); this.channels.addAll(channels); @@ -217,19 +223,13 @@ public class DisplayChannelsFragment extends Fragment implements ChannelListAdap public void pullToRefresh() { channels = new ArrayList<>(); - binding.swipeContainer.setRefreshing(true); - ChannelsVM viewModel = new ViewModelProvider(this).get(ChannelsVM.class); - if (name != null) { - viewModel.get(RetrofitPeertubeAPI.DataType.CHANNELS_FOR_ACCOUNT, name).observe(DisplayChannelsFragment.this.requireActivity(), this::manageViewChannels); - } else { - viewModel.get(RetrofitPeertubeAPI.DataType.MY_CHANNELS, null).observe(DisplayChannelsFragment.this.requireActivity(), this::manageViewChannels); - } + loadChannels("0"); } @Override public void onAllChannelRemoved() { - textviewNoAction.setVisibility(View.VISIBLE); + binding.noAction.setVisibility(View.VISIBLE); } diff --git a/app/src/main/java/app/fedilab/fedilabtube/viewmodel/SearchVM.java b/app/src/main/java/app/fedilab/fedilabtube/viewmodel/SearchVM.java index a46eb30..051f1e8 100644 --- a/app/src/main/java/app/fedilab/fedilabtube/viewmodel/SearchVM.java +++ b/app/src/main/java/app/fedilab/fedilabtube/viewmodel/SearchVM.java @@ -43,12 +43,35 @@ public class SearchVM extends AndroidViewModel { return apiResponseMutableLiveData; } + + public LiveData getChannels(String max_id, String query) { + apiResponseMutableLiveData = new MutableLiveData<>(); + loadChannels(max_id, query); + return apiResponseMutableLiveData; + } + public LiveData searchNextVideos(List tags) { apiResponseMutableLiveData = new MutableLiveData<>(); loadNextVideos(tags); return apiResponseMutableLiveData; } + + private void loadChannels(String max_id, String query) { + Context _mContext = getApplication().getApplicationContext(); + new Thread(() -> { + try { + RetrofitPeertubeAPI api = new RetrofitPeertubeAPI(_mContext); + APIResponse apiResponse = api.searchChannels(query, max_id); + Handler mainHandler = new Handler(Looper.getMainLooper()); + Runnable myRunnable = () -> apiResponseMutableLiveData.setValue(apiResponse); + mainHandler.post(myRunnable); + } catch (Exception e) { + e.printStackTrace(); + } + }).start(); + } + private void loadVideos(String max_id, String query) { Context _mContext = getApplication().getApplicationContext(); new Thread(() -> { diff --git a/app/src/main/res/layout/activity_search_result.xml b/app/src/main/res/layout/activity_search_result.xml index dcd181a..bdf76e4 100644 --- a/app/src/main/res/layout/activity_search_result.xml +++ b/app/src/main/res/layout/activity_search_result.xml @@ -14,72 +14,105 @@ You should have received a copy of the GNU General Public License along with TubeLab; if not, see . --> - + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:fitsSystemWindows="true" + tools:context=".ShowChannelActivity"> - + android:fitsSystemWindows="true" + app:theme="@style/ThemeOverlay.AppCompat.ActionBar"> - + android:fitsSystemWindows="true" + app:contentScrim="?attr/colorPrimary" + app:expandedTitleMarginEnd="64dp" + app:expandedTitleMarginStart="48dp" + app:layout_scrollFlags="scroll|exitUntilCollapsed"> - + android:layout_marginTop="5dp"> - + - - + - + - + + + + + + + + + + + + + + android:layout_height="wrap_content" + android:layout_marginTop="5dp" + app:layout_behavior="@string/appbar_scrolling_view_behavior" /> - - - \ No newline at end of file +