Thorium-android-app/app/src/main/java/net/schueller/peertube/activity/VideoListActivity.java

591 lines
22 KiB
Java

/*
* Copyright (C) 2020 Stefan Schüller <sschueller@techdroid.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package net.schueller.peertube.activity;
import android.Manifest;
import android.app.AlertDialog;
import android.app.SearchManager;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.preference.PreferenceManager;
import android.provider.SearchRecentSuggestions;
import androidx.annotation.NonNull;
import com.google.android.material.bottomnavigation.BottomNavigationView;
import com.google.android.material.bottomnavigation.LabelVisibilityMode;
import androidx.core.app.ActivityCompat;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import android.os.Bundle;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.appcompat.widget.SearchView;
import androidx.appcompat.widget.Toolbar;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.TextView;
import com.mikepenz.iconics.IconicsDrawable;
import com.mikepenz.iconics.typeface.library.fontawesome.FontAwesome;
import net.schueller.peertube.R;
import net.schueller.peertube.adapter.VideoAdapter;
import net.schueller.peertube.helper.APIUrlHelper;
import net.schueller.peertube.helper.ErrorHelper;
import net.schueller.peertube.model.Video;
import net.schueller.peertube.model.VideoList;
import net.schueller.peertube.network.GetUserService;
import net.schueller.peertube.network.GetVideoDataService;
import net.schueller.peertube.network.RetrofitInstance;
import net.schueller.peertube.network.Session;
import net.schueller.peertube.provider.SearchSuggestionsProvider;
import net.schueller.peertube.service.VideoPlayerService;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Locale;
import java.util.Set;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
public class VideoListActivity extends CommonActivity {
private String TAG = "VideoListActivity";
public static final String EXTRA_VIDEOID = "VIDEOID";
public static final String EXTRA_ACCOUNTDISPLAYNAME = "ACCOUNTDISPLAYNAMEANDHOST";
public static final Integer SWITCH_INSTANCE = 2;
private VideoAdapter videoAdapter;
private SwipeRefreshLayout swipeRefreshLayout;
private int currentStart = 0;
private int count = 12;
private String sort = "-createdAt";
private String filter = null;
private String searchQuery = "";
private Boolean subscriptions = false;
private TextView emptyView;
private RecyclerView recyclerView;
private boolean isLoading = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_video_list);
filter = null;
createBottomBarNavigation();
// Attaching the layout to the toolbar object
Toolbar toolbar = findViewById(R.id.tool_bar);
// Setting toolbar as the ActionBar with setSupportActionBar() call
setSupportActionBar(toolbar);
// load Video List
createList();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.menu_top_videolist, menu);
// Set an icon in the ActionBar
menu.findItem(R.id.action_account).setIcon(
new IconicsDrawable(this, FontAwesome.Icon.faw_user_circle));
menu.findItem(R.id.action_server_address_book).setIcon(
new IconicsDrawable(this, FontAwesome.Icon.faw_server));
MenuItem searchMenuItem = menu.findItem(R.id.action_search);
searchMenuItem.setIcon(
new IconicsDrawable(this, FontAwesome.Icon.faw_search));
// Get the SearchView and set the searchable configuration
SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
SearchView searchView = (SearchView) searchMenuItem.getActionView();
// Assumes current activity is the searchable activity
searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName()));
searchView.setIconifiedByDefault(false); // Do not iconify the widget; expand it by default
searchView.setQueryRefinementEnabled(true);
searchMenuItem.getActionView().setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
new AlertDialog.Builder(VideoListActivity.this)
.setTitle(getString(R.string.clear_search_history))
.setMessage(getString(R.string.clear_search_history_prompt))
.setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
SearchRecentSuggestions suggestions = new SearchRecentSuggestions(getApplicationContext(),
SearchSuggestionsProvider.AUTHORITY,
SearchSuggestionsProvider.MODE);
suggestions.clearHistory();
}
})
.setNegativeButton(android.R.string.no, null)
.setIcon(android.R.drawable.ic_dialog_alert)
.show();
return true;
}
});
searchMenuItem.setOnActionExpandListener(new MenuItem.OnActionExpandListener() {
@Override
public boolean onMenuItemActionExpand(MenuItem menuItem) {
return true;
}
@Override
public boolean onMenuItemActionCollapse(MenuItem menuItem) {
searchQuery = "";
Log.d(TAG, "onMenuItemActionCollapse: ");
loadVideos(0, count, sort, filter);
return true;
}
});
// TODO, this doesn't work
searchManager.setOnDismissListener(() -> {
searchQuery = "";
Log.d(TAG, "onDismiss: ");
loadVideos(0, count, sort, filter);
});
searchView.setOnSuggestionListener(new SearchView.OnSuggestionListener() {
@Override
public boolean onSuggestionClick(int position) {
String suggestion = getSuggestion(position);
searchView.setQuery(suggestion, true);
return true;
}
private String getSuggestion(int position) {
Cursor cursor = (Cursor) searchView.getSuggestionsAdapter().getItem(
position);
return cursor.getString(cursor
.getColumnIndex(SearchManager.SUGGEST_COLUMN_TEXT_1));
}
@Override
public boolean onSuggestionSelect(int position) {
/* Required to implement */
return true;
}
});
return true;
}
@Override
protected void onDestroy() {
super.onDestroy();
stopService(new Intent(this, VideoPlayerService.class));
}
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == SWITCH_INSTANCE) {
if(resultCode == RESULT_OK) {
loadVideos(currentStart, count, sort, filter);
}
}
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
switch (item.getItemId()) {
// action with ID action_refresh was selected
case R.id.action_search:
//Toast.makeText(this, "Search Selected", Toast.LENGTH_SHORT).show();
return false;
case R.id.action_account:
// if (!Session.getInstance().isLoggedIn()) {
//Intent intentLogin = new Intent(this, ServerAddressBookActivity.class);
Intent intentMe = new Intent(this, MeActivity.class);
this.startActivity(intentMe);
//overridePendingTransition(R.anim.slide_in_bottom, 0);
// this.startActivity(intentLogin);
// } else {
// Intent intentMe = new Intent(this, MeActivity.class);
// this.startActivity(intentMe);
// }
return false;
case R.id.action_server_address_book:
Intent addressBookActivityIntent = new Intent(this, ServerAddressBookActivity.class);
this.startActivityForResult(addressBookActivityIntent, SWITCH_INSTANCE);
return false;
default:
break;
}
return super.onOptionsItemSelected(item);
}
private void createList() {
recyclerView = findViewById(R.id.recyclerView);
swipeRefreshLayout = findViewById(R.id.swipeRefreshLayout);
emptyView = findViewById(R.id.empty_view);
RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(VideoListActivity.this);
recyclerView.setLayoutManager(layoutManager);
videoAdapter = new VideoAdapter(new ArrayList<>(), VideoListActivity.this);
recyclerView.setAdapter(videoAdapter);
loadVideos(currentStart, count, sort, filter);
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
}
@Override
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
if (dy > 0) {
// is at end of list?
if (!recyclerView.canScrollVertically(RecyclerView.FOCUS_DOWN)) {
if (!isLoading) {
currentStart = currentStart + count;
loadVideos(currentStart, count, sort, filter);
}
}
}
}
});
swipeRefreshLayout.setOnRefreshListener(() -> {
// Refresh items
if (!isLoading) {
currentStart = 0;
loadVideos(currentStart, count, sort, filter);
}
});
}
private void loadVideos(int start, int count, String sort, String filter) {
isLoading = true;
SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this);
String nsfw = sharedPref.getBoolean(getString(R.string.pref_show_nsfw_key), false) ? "both" : "false";
//
// Locale locale = getResources().getConfiguration().locale;
// String country = locale.getLanguage();
//
// HashSet<String> countries = new HashSet<>(1);
// countries.add(country);
// We set this to default to null so that on initial start there are videos listed.
Set<String> languages = sharedPref.getStringSet(getString(R.string.pref_video_language_key), null);
String apiBaseURL = APIUrlHelper.getUrlWithVersion(this);
GetVideoDataService service = RetrofitInstance.getRetrofitInstance(apiBaseURL, APIUrlHelper.useInsecureConnection(this)).create(GetVideoDataService.class);
Call<VideoList> call;
if (!searchQuery.equals("")) {
call = service.searchVideosData(start, count, sort, nsfw, searchQuery, filter, languages);
} else if (subscriptions) {
GetUserService userService = RetrofitInstance.getRetrofitInstance(apiBaseURL, APIUrlHelper.useInsecureConnection(this)).create(GetUserService.class);
call = userService.getVideosSubscripions(start, count, sort);
} else {
call = service.getVideosData(start, count, sort, nsfw, filter, languages);
}
/*Log the URL called*/
Log.d("URL Called", call.request().url() + "");
// Toast.makeText(VideoListActivity.this, "URL Called: " + call.request().url(), Toast.LENGTH_SHORT).show();
call.enqueue(new Callback<VideoList>() {
@Override
public void onResponse(@NonNull Call<VideoList> call, @NonNull Response<VideoList> response) {
if (currentStart == 0) {
videoAdapter.clearData();
}
if (response.body() != null) {
ArrayList<Video> videoList = response.body().getVideoArrayList();
if (videoList != null) {
videoAdapter.setData(response.body().getVideoArrayList());
}
}
// no results show no results message
if (currentStart == 0 && videoAdapter.getItemCount() == 0) {
emptyView.setVisibility(View.VISIBLE);
recyclerView.setVisibility(View.GONE);
} else {
emptyView.setVisibility(View.GONE);
recyclerView.setVisibility(View.VISIBLE);
}
isLoading = false;
swipeRefreshLayout.setRefreshing(false);
}
@Override
public void onFailure(@NonNull Call<VideoList> call, @NonNull Throwable t) {
Log.wtf("err", t.fillInStackTrace());
ErrorHelper.showToastFromCommunicationError( VideoListActivity.this, t );
isLoading = false;
swipeRefreshLayout.setRefreshing(false);
}
});
}
@Override
protected void onResume() {
super.onResume();
// only check when we actually need the permission
SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED &&
sharedPref.getBoolean(getString(R.string.pref_torrent_player_key), false)) {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 0);
}
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
setIntent(intent);
handleIntent(intent);
}
private void handleIntent(Intent intent) {
if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
String query = intent.getStringExtra(SearchManager.QUERY);
SearchRecentSuggestions suggestions = new SearchRecentSuggestions(this,
SearchSuggestionsProvider.AUTHORITY,
SearchSuggestionsProvider.MODE);
// Save recent searches
suggestions.saveRecentQuery(query, null);
searchQuery = query;
loadVideos(0, count, sort, filter);
}
}
@Override
public boolean onSearchRequested() {
Bundle appData = new Bundle();
startSearch(null, false, appData, false);
return true;
}
private void createBottomBarNavigation() {
// Get Bottom Navigation
BottomNavigationView navigation = findViewById(R.id.navigation);
// Always show text label
navigation.setLabelVisibilityMode(LabelVisibilityMode.LABEL_VISIBILITY_LABELED);
// Add Icon font
Menu navMenu = navigation.getMenu();
navMenu.findItem(R.id.navigation_overview).setIcon(
new IconicsDrawable(this, FontAwesome.Icon.faw_globe));
navMenu.findItem(R.id.navigation_trending).setIcon(
new IconicsDrawable(this, FontAwesome.Icon.faw_fire));
navMenu.findItem(R.id.navigation_recent).setIcon(
new IconicsDrawable(this, FontAwesome.Icon.faw_plus_circle));
navMenu.findItem(R.id.navigation_local).setIcon(
new IconicsDrawable(this, FontAwesome.Icon.faw_home));
navMenu.findItem(R.id.navigation_subscriptions).setIcon(
new IconicsDrawable(this, FontAwesome.Icon.faw_folder));
// navMenu.findItem(R.id.navigation_account).setIcon(
// new IconicsDrawable(this, FontAwesome.Icon.faw_user_circle));
// Click Listener
navigation.setOnNavigationItemSelectedListener(menuItem -> {
switch (menuItem.getItemId()) {
case R.id.navigation_overview:
// TODO
if (!isLoading) {
sort = "-createdAt";
currentStart = 0;
filter = null;
subscriptions = false;
loadVideos(currentStart, count, sort, filter);
}
return true;
case R.id.navigation_trending:
//Log.v(TAG, "navigation_trending");
if (!isLoading) {
sort = "-trending";
currentStart = 0;
filter = null;
subscriptions = false;
loadVideos(currentStart, count, sort, filter);
}
return true;
case R.id.navigation_recent:
if (!isLoading) {
sort = "-createdAt";
currentStart = 0;
filter = null;
subscriptions = false;
loadVideos(currentStart, count, sort, filter);
}
return true;
case R.id.navigation_local:
//Log.v(TAG, "navigation_trending");
if (!isLoading) {
sort = "-publishedAt";
filter = "local";
currentStart = 0;
subscriptions = false;
loadVideos(currentStart, count, sort, filter);
}
return true;
case R.id.navigation_subscriptions:
//Log.v(TAG, "navigation_subscriptions");
if (!Session.getInstance().isLoggedIn()) {
// Intent intent = new Intent(this, LoginActivity.class);
// this.startActivity(intent);
Intent addressBookActivityIntent = new Intent(this, ServerAddressBookActivity.class);
this.startActivityForResult(addressBookActivityIntent, SWITCH_INSTANCE);
return false;
} else {
if (!isLoading) {
sort = "-publishedAt";
filter = null;
currentStart = 0;
subscriptions = true;
loadVideos(currentStart, count, sort, filter);
}
return true;
}
// case R.id.navigation_account:
// //Log.v(TAG, "navigation_account");
// //Toast.makeText(VideoListActivity.this, "Account Not Implemented", Toast.LENGTH_SHORT).show();
//
// if (!Session.getInstance().isLoggedIn()) {
// Intent intent = new Intent(this, LoginActivity.class);
// this.startActivity(intent);
// } else {
// Intent intent = new Intent(this, MeActivity.class);
// this.startActivity(intent);
// }
//
// return false;
}
return false;
});
// TODO: on double click jump to top and reload
// navigation.setOnNavigationItemReselectedListener(menuItemReselected -> {
// switch (menuItemReselected.getItemId()) {
// case R.id.navigation_home:
// if (!isLoading) {
// sort = "-createdAt";
// currentStart = 0;
// filter = null;
// subscriptions = false;
// loadVideos(currentStart, count, sort, filter);
// }
// case R.id.navigation_trending:
// if (!isLoading) {
// sort = "-trending";
// currentStart = 0;
// filter = null;
// subscriptions = false;
// loadVideos(currentStart, count, sort, filter);
// }
// case R.id.navigation_local:
// if (!isLoading) {
// sort = "-publishedAt";
// filter = "local";
// currentStart = 0;
// subscriptions = false;
// loadVideos(currentStart, count, sort, filter);
// }
// case R.id.navigation_subscriptions:
// if (Session.getInstance().isLoggedIn()) {
// if (!isLoading) {
// sort = "-publishedAt";
// filter = null;
// currentStart = 0;
// subscriptions = true;
// loadVideos(currentStart, count, sort, filter);
// }
// }
// }
// });
}
}