Fix issue #58 - Allow to search channels

This commit is contained in:
Thomas 2020-11-27 14:06:42 +01:00
parent 550394a5e4
commit 24ad4dbc0f
8 changed files with 287 additions and 98 deletions

View File

@ -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

View File

@ -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;
}
}
}

View File

@ -201,7 +201,7 @@ public interface PeertubeService {
Call<String> deleteHistory(
@Header("Authorization") String credentials);
//Search
//Search videos
@GET("search/videos")
Call<VideoData> 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<ChannelData> 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")

View File

@ -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<ChannelData> searchChannelsCall = peertubeService.searchChannels(getToken(), query, "local", max_id, count);
APIResponse apiResponse = new APIResponse();
try {
Response<ChannelData> 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

View File

@ -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();

View File

@ -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<ChannelData.Channel> 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<ChannelData.Channel> 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);
}

View File

@ -43,12 +43,35 @@ public class SearchVM extends AndroidViewModel {
return apiResponseMutableLiveData;
}
public LiveData<APIResponse> getChannels(String max_id, String query) {
apiResponseMutableLiveData = new MutableLiveData<>();
loadChannels(max_id, query);
return apiResponseMutableLiveData;
}
public LiveData<APIResponse> searchNextVideos(List<String> 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(() -> {

View File

@ -14,72 +14,105 @@
You should have received a copy of the GNU General Public License along with TubeLab; if not,
see <http://www.gnu.org/licenses>.
-->
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_height="match_parent">
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:context=".ShowChannelActivity">
<HorizontalScrollView
android:id="@+id/history_filter"
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:paddingTop="5dp"
android:paddingBottom="5dp"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
android:fitsSystemWindows="true"
app:theme="@style/ThemeOverlay.AppCompat.ActionBar">
<LinearLayout
android:layout_width="wrap_content"
<com.google.android.material.appbar.CollapsingToolbarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
android:fitsSystemWindows="true"
app:contentScrim="?attr/colorPrimary"
app:expandedTitleMarginEnd="64dp"
app:expandedTitleMarginStart="48dp"
app:layout_scrollFlags="scroll|exitUntilCollapsed">
<TextView
android:id="@+id/history_filter_all"
android:layout_width="wrap_content"
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/top_banner"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/background_rounded_item"
android:paddingStart="5dp"
android:paddingEnd="5dp"
android:text="@string/all"
android:textColor="?attr/colorAccent" />
android:layout_marginTop="5dp">
<TextView
android:id="@+id/history_filter_today"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:background="@drawable/background_rounded_item"
android:paddingStart="5dp"
android:paddingEnd="5dp"
android:text="@string/today"
android:textColor="?attr/colorAccent" />
<HorizontalScrollView
android:id="@+id/history_filter"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:paddingTop="5dp"
android:paddingBottom="5dp"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="@+id/history_filter_last_7_days"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:background="@drawable/background_rounded_item"
android:paddingStart="5dp"
android:paddingEnd="5dp"
android:text="@string/last_7_days"
android:textColor="?attr/colorAccent" />
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
</HorizontalScrollView>
<TextView
android:id="@+id/history_filter_all"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/background_rounded_item"
android:paddingStart="5dp"
android:paddingEnd="5dp"
android:text="@string/all"
android:textColor="?attr/colorAccent" />
<RelativeLayout
android:id="@+id/container"
<TextView
android:id="@+id/history_filter_today"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:background="@drawable/background_rounded_item"
android:paddingStart="5dp"
android:paddingEnd="5dp"
android:text="@string/today"
android:textColor="?attr/colorAccent" />
<TextView
android:id="@+id/history_filter_last_7_days"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:background="@drawable/background_rounded_item"
android:paddingStart="5dp"
android:paddingEnd="5dp"
android:text="@string/last_7_days"
android:textColor="?attr/colorAccent" />
</LinearLayout>
</HorizontalScrollView>
</androidx.constraintlayout.widget.ConstraintLayout>
</com.google.android.material.appbar.CollapsingToolbarLayout>
<com.google.android.material.tabs.TabLayout
android:id="@+id/search_tabLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:tabGravity="fill"
app:tabMode="fixed"
app:tabSelectedTextColor="?colorAccent"
app:tabTextColor="@android:color/white" />
</com.google.android.material.appbar.AppBarLayout>
<androidx.viewpager.widget.ViewPager
android:id="@+id/search_pager"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/history_filter">
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
</RelativeLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>