From 6e7d85c74ad008c268df25e66c9b74570e63dad8 Mon Sep 17 00:00:00 2001 From: Stefan Schueller Date: Sat, 10 Nov 2018 22:40:27 +0100 Subject: [PATCH] - Implemented basic search --- app/build.gradle | 4 + app/src/main/AndroidManifest.xml | 36 +++- .../peertube/activity/SearchActivity.java | 177 ++++++++++++++++++ .../peertube/activity/VideoListActivity.java | 38 ++-- .../helper/BottomNavigationViewHelper.java | 34 ---- .../peertube/network/GetVideoDataService.java | 5 +- .../provider/SearchSuggestionsProvider.java | 17 ++ app/src/main/res/color/bottom_bar.xml | 21 +++ app/src/main/res/layout/activity_search.xml | 38 ++++ .../main/res/layout/activity_video_list.xml | 8 +- app/src/main/res/layout/row_video.xml | 2 + app/src/main/res/layout/video_list.xml | 63 ++++--- app/src/main/res/values/strings.xml | 3 +- app/src/main/res/xml/searchable.xml | 7 + 14 files changed, 360 insertions(+), 93 deletions(-) create mode 100644 app/src/main/java/net/schueller/peertube/activity/SearchActivity.java delete mode 100644 app/src/main/java/net/schueller/peertube/helper/BottomNavigationViewHelper.java create mode 100644 app/src/main/java/net/schueller/peertube/provider/SearchSuggestionsProvider.java create mode 100644 app/src/main/res/color/bottom_bar.xml create mode 100644 app/src/main/res/layout/activity_search.xml create mode 100644 app/src/main/res/xml/searchable.xml diff --git a/app/build.gradle b/app/build.gradle index dc4d622..a0f046f 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -45,6 +45,10 @@ dependencies { // implementation 'org.webrtc:google-webrtc:1.0.+' implementation 'com.android.support:design:28.0.0' + // BottomNavigationViewEx -> https://github.com/ittianyu/BottomNavigationViewEx + implementation 'com.github.ittianyu:BottomNavigationViewEx:2.0.2' + + implementation 'com.blackboardtheory:android-iconify-fontawesome:3.0.1-SNAPSHOT' implementation 'com.github.TorrentStream:TorrentStream-Android:2.5.0' diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index baeaede..9e3b3af 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,5 +1,6 @@ @@ -11,30 +12,59 @@ - + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/java/net/schueller/peertube/activity/SearchActivity.java b/app/src/main/java/net/schueller/peertube/activity/SearchActivity.java new file mode 100644 index 0000000..e8622d0 --- /dev/null +++ b/app/src/main/java/net/schueller/peertube/activity/SearchActivity.java @@ -0,0 +1,177 @@ +package net.schueller.peertube.activity; + +import android.app.SearchManager; +import android.content.Intent; +import android.content.SharedPreferences; +import android.preference.PreferenceManager; +import android.provider.SearchRecentSuggestions; +import android.support.annotation.NonNull; +import android.support.v4.widget.SwipeRefreshLayout; +import android.support.v7.app.AppCompatActivity; +import android.os.Bundle; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.support.v7.widget.Toolbar; +import android.util.Log; +import android.widget.Toast; + +import net.schueller.peertube.R; +import net.schueller.peertube.adapter.VideoAdapter; +import net.schueller.peertube.helper.APIUrlHelper; +import net.schueller.peertube.model.VideoList; +import net.schueller.peertube.network.GetVideoDataService; +import net.schueller.peertube.network.RetrofitInstance; +import net.schueller.peertube.provider.SearchSuggestionsProvider; + +import java.util.ArrayList; + +import retrofit2.Call; +import retrofit2.Callback; +import retrofit2.Response; + +// TODO: cleanup, this code partially is duplicated from VideoList Activity and should be seperated out + +public class SearchActivity extends AppCompatActivity { + + private VideoAdapter videoAdapter; + private SwipeRefreshLayout swipeRefreshLayout; + + private int currentStart = 0; + private int count = 12; + private String sort = "-match"; + private String filter = ""; + + private boolean isLoading = false; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_search); + + Intent intent = getIntent(); + + // do search + handleIntent(intent); + + // handle search suggestions + 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); + } + + } + + private void createList(String query) { + RecyclerView recyclerView = findViewById(R.id.recyclerView); + swipeRefreshLayout = findViewById(R.id.swipeRefreshLayout); + + RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(SearchActivity.this); + recyclerView.setLayoutManager(layoutManager); + + videoAdapter = new VideoAdapter(new ArrayList<>(), SearchActivity.this); + recyclerView.setAdapter(videoAdapter); + + loadVideos(currentStart, count, sort, query, 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, query, filter); + } + } + } + + } + }); + + swipeRefreshLayout.setOnRefreshListener(() -> { + // Refresh items + if (!isLoading) { + currentStart = 0; + loadVideos(currentStart, count, sort, query, filter); + } + }); + + } + + + private void loadVideos(int start, int count, String sort, String search, String filter) { + + isLoading = true; + + SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this); + String nsfw = sharedPref.getBoolean("pref_show_nsfw", true) ? "both" : "false"; + + String apiBaseURL = APIUrlHelper.getUrl(this); + + GetVideoDataService service = RetrofitInstance.getRetrofitInstance(apiBaseURL + "/api/v1/").create(GetVideoDataService.class); + + Call call = service.searchVideosData(start, count, sort, nsfw, search); + + /*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() { + @Override + public void onResponse(@NonNull Call call, @NonNull Response response) { + + if (currentStart == 0) { + videoAdapter.clearData(); + } + + if (response.body() != null) { + videoAdapter.setData(response.body().getVideoArrayList()); + } + isLoading = false; + swipeRefreshLayout.setRefreshing(false); + } + + @Override + public void onFailure(@NonNull Call call, @NonNull Throwable t) { + Log.wtf("err", t.fillInStackTrace()); + Toast.makeText(SearchActivity.this, "Something went wrong...Please try later!", Toast.LENGTH_SHORT).show(); + isLoading = false; + swipeRefreshLayout.setRefreshing(false); + } + }); + } + + + + @Override + protected void onNewIntent(Intent intent) { + setIntent(intent); + handleIntent(intent); + } + + private void handleIntent(Intent intent) { + if (Intent.ACTION_SEARCH.equals(intent.getAction())) { + String query = intent.getStringExtra(SearchManager.QUERY); + Log.v("Search Activity", query); + createList(query); + } + } + + @Override + public boolean onSearchRequested() { + Bundle appData = new Bundle(); + startSearch(null, false, appData, false); + return true; + } +} diff --git a/app/src/main/java/net/schueller/peertube/activity/VideoListActivity.java b/app/src/main/java/net/schueller/peertube/activity/VideoListActivity.java index 2ac5625..7853b3e 100644 --- a/app/src/main/java/net/schueller/peertube/activity/VideoListActivity.java +++ b/app/src/main/java/net/schueller/peertube/activity/VideoListActivity.java @@ -7,8 +7,8 @@ import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.preference.PreferenceManager; import android.support.annotation.NonNull; -import android.support.design.widget.BottomNavigationView; import android.support.v4.app.ActivityCompat; +import android.support.v4.content.ContextCompat; import android.support.v4.widget.SwipeRefreshLayout; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; @@ -26,6 +26,7 @@ import com.google.android.gms.common.GooglePlayServicesNotAvailableException; import com.google.android.gms.common.GooglePlayServicesRepairableException; import com.google.android.gms.common.GooglePlayServicesUtil; import com.google.android.gms.security.ProviderInstaller; +import com.ittianyu.bottomnavigationviewex.BottomNavigationViewEx; import com.joanzapata.iconify.IconDrawable; import com.joanzapata.iconify.Iconify; import com.joanzapata.iconify.fonts.FontAwesomeIcons; @@ -34,7 +35,6 @@ import com.joanzapata.iconify.fonts.FontAwesomeModule; import net.schueller.peertube.R; import net.schueller.peertube.adapter.VideoAdapter; import net.schueller.peertube.helper.APIUrlHelper; -import net.schueller.peertube.helper.BottomNavigationViewHelper; import net.schueller.peertube.model.VideoList; import net.schueller.peertube.network.GetVideoDataService; import net.schueller.peertube.network.RetrofitInstance; @@ -53,19 +53,16 @@ public class VideoListActivity extends AppCompatActivity { public static final String EXTRA_VIDEOID = "VIDEOID "; private VideoAdapter videoAdapter; - private RecyclerView recyclerView; private SwipeRefreshLayout swipeRefreshLayout; - private Toolbar toolbar; private int currentStart = 0; private int count = 12; private String sort = "-createdAt"; private String filter = ""; - private String nsfw = "false"; private boolean isLoading = false; - private BottomNavigationView.OnNavigationItemSelectedListener mOnNavigationItemSelectedListener + private BottomNavigationViewEx.OnNavigationItemSelectedListener mOnNavigationItemSelectedListener = item -> { switch (item.getItemId()) { case R.id.navigation_home: @@ -82,20 +79,20 @@ public class VideoListActivity extends AppCompatActivity { //Log.v(TAG, "navigation_trending"); if (!isLoading) { - sort = "-views"; + sort = "-trending"; currentStart = 0; loadVideos(currentStart, count, sort, filter); } return true; case R.id.navigation_subscriptions: - Log.v(TAG, "navigation_subscriptions"); + //Log.v(TAG, "navigation_subscriptions"); Toast.makeText(VideoListActivity.this, "Subscriptions Not Implemented", Toast.LENGTH_SHORT).show(); return false; case R.id.navigation_account: - Log.v(TAG, "navigation_account"); + //Log.v(TAG, "navigation_account"); Toast.makeText(VideoListActivity.this, "Account Not Implemented", Toast.LENGTH_SHORT).show(); return false; @@ -114,7 +111,7 @@ public class VideoListActivity extends AppCompatActivity { Iconify.with(new FontAwesomeModule()); // Attaching the layout to the toolbar object - toolbar = findViewById(R.id.tool_bar); + Toolbar toolbar = findViewById(R.id.tool_bar); // Setting toolbar as the ActionBar with setSupportActionBar() call setSupportActionBar(toolbar); @@ -122,7 +119,12 @@ public class VideoListActivity extends AppCompatActivity { updateAndroidSecurityProvider(this); // Bottom Navigation - BottomNavigationView navigation = findViewById(R.id.navigation); + BottomNavigationViewEx navigation = findViewById(R.id.navigation); + + navigation.enableAnimation(false); + navigation.enableShiftingMode(false); + navigation.enableItemShiftingMode(false); + Menu navMenu = navigation.getMenu(); navMenu.findItem(R.id.navigation_home).setIcon( new IconDrawable(this, FontAwesomeIcons.fa_home)); @@ -133,8 +135,6 @@ public class VideoListActivity extends AppCompatActivity { navMenu.findItem(R.id.navigation_account).setIcon( new IconDrawable(this, FontAwesomeIcons.fa_user_circle)); - BottomNavigationViewHelper.removeShiftMode(navigation); - navigation.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener); @@ -172,8 +172,8 @@ public class VideoListActivity extends AppCompatActivity { switch (item.getItemId()) { // action with ID action_refresh was selected case R.id.action_search: - // TODO: implement - Toast.makeText(this, "Search Selected", Toast.LENGTH_SHORT).show(); + //Toast.makeText(this, "Search Selected", Toast.LENGTH_SHORT).show(); + onSearchRequested(); return false; case R.id.action_settings: @@ -190,7 +190,7 @@ public class VideoListActivity extends AppCompatActivity { } private void createList() { - recyclerView = findViewById(R.id.recyclerView); + RecyclerView recyclerView = findViewById(R.id.recyclerView); swipeRefreshLayout = findViewById(R.id.swipeRefreshLayout); RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(VideoListActivity.this); @@ -203,12 +203,12 @@ public class VideoListActivity extends AppCompatActivity { recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { @Override - public void onScrollStateChanged(RecyclerView recyclerView, int newState) { + public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) { super.onScrollStateChanged(recyclerView, newState); } @Override - public void onScrolled(RecyclerView recyclerView, int dx, int dy) { + public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) { if (dy > 0) { // is at end of list? @@ -238,7 +238,7 @@ public class VideoListActivity extends AppCompatActivity { isLoading = true; SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this); - nsfw = sharedPref.getBoolean("pref_show_nsfw", true) ? "both" : "false"; + String nsfw = sharedPref.getBoolean("pref_show_nsfw", true) ? "both" : "false"; String apiBaseURL = APIUrlHelper.getUrl(this); diff --git a/app/src/main/java/net/schueller/peertube/helper/BottomNavigationViewHelper.java b/app/src/main/java/net/schueller/peertube/helper/BottomNavigationViewHelper.java deleted file mode 100644 index 7e9642b..0000000 --- a/app/src/main/java/net/schueller/peertube/helper/BottomNavigationViewHelper.java +++ /dev/null @@ -1,34 +0,0 @@ -package net.schueller.peertube.helper; - -import android.annotation.SuppressLint; -import android.support.design.internal.BottomNavigationItemView; -import android.support.design.internal.BottomNavigationMenuView; -import android.support.design.widget.BottomNavigationView; -import android.util.Log; - -import java.lang.reflect.Field; - -public class BottomNavigationViewHelper { - @SuppressLint("RestrictedApi") - public static void removeShiftMode(BottomNavigationView view) { - BottomNavigationMenuView menuView = (BottomNavigationMenuView) view.getChildAt(0); - try { - Field shiftingMode = menuView.getClass().getDeclaredField("mShiftingMode"); - shiftingMode.setAccessible(true); - shiftingMode.setBoolean(menuView, false); - shiftingMode.setAccessible(false); - for (int i = 0; i < menuView.getChildCount(); i++) { - BottomNavigationItemView item = (BottomNavigationItemView) menuView.getChildAt(i); - //noinspection RestrictedApi - //item.setShiftingMode(false); - // set once again checked value, so view will be updated - //noinspection RestrictedApi - item.setChecked(item.getItemData().isChecked()); - } - } catch (NoSuchFieldException e) { - Log.e("BottomNav", "Unable to get shift mode field", e); - } catch (IllegalAccessException e) { - Log.e("BottomNav", "Unable to change value of shift mode", e); - } - } -} \ No newline at end of file diff --git a/app/src/main/java/net/schueller/peertube/network/GetVideoDataService.java b/app/src/main/java/net/schueller/peertube/network/GetVideoDataService.java index ce7ad2e..97ce8f2 100644 --- a/app/src/main/java/net/schueller/peertube/network/GetVideoDataService.java +++ b/app/src/main/java/net/schueller/peertube/network/GetVideoDataService.java @@ -23,12 +23,13 @@ public interface GetVideoDataService { @Path(value = "id", encoded = true) String id ); - @GET("videos/search/") + @GET("search/videos/") Call searchVideosData( @Query("start") int start, @Query("count") int count, @Query("sort") String sort, - @Query("filter") String filter, + @Query("nsfw") String nsfw, @Query("search") String search +// @Query("filter") String filter ); } \ No newline at end of file diff --git a/app/src/main/java/net/schueller/peertube/provider/SearchSuggestionsProvider.java b/app/src/main/java/net/schueller/peertube/provider/SearchSuggestionsProvider.java new file mode 100644 index 0000000..2101fb4 --- /dev/null +++ b/app/src/main/java/net/schueller/peertube/provider/SearchSuggestionsProvider.java @@ -0,0 +1,17 @@ +package net.schueller.peertube.provider; + +import android.content.SearchRecentSuggestionsProvider; + +public class SearchSuggestionsProvider extends SearchRecentSuggestionsProvider { + + public final static String AUTHORITY = SearchSuggestionsProvider.class.getName(); + public static final int MODE = SearchRecentSuggestionsProvider.DATABASE_MODE_QUERIES; + + public SearchSuggestionsProvider() + { + setupSuggestions(AUTHORITY, MODE); + } + + // TODO: add search suggestions once they become available in peertube server + +} diff --git a/app/src/main/res/color/bottom_bar.xml b/app/src/main/res/color/bottom_bar.xml new file mode 100644 index 0000000..e0f548d --- /dev/null +++ b/app/src/main/res/color/bottom_bar.xml @@ -0,0 +1,21 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_search.xml b/app/src/main/res/layout/activity_search.xml new file mode 100644 index 0000000..66a4a72 --- /dev/null +++ b/app/src/main/res/layout/activity_search.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/activity_video_list.xml b/app/src/main/res/layout/activity_video_list.xml index 1380f10..787af75 100644 --- a/app/src/main/res/layout/activity_video_list.xml +++ b/app/src/main/res/layout/activity_video_list.xml @@ -37,17 +37,19 @@ - + app:itemIconTint="@color/bottom_bar" + android:background="?android:attr/windowBackground" + /> \ No newline at end of file diff --git a/app/src/main/res/layout/row_video.xml b/app/src/main/res/layout/row_video.xml index 609a759..19df68e 100644 --- a/app/src/main/res/layout/row_video.xml +++ b/app/src/main/res/layout/row_video.xml @@ -19,8 +19,10 @@ android:id="@+id/thumb" android:layout_width="fill_parent" android:layout_height="wrap_content" + android:maxHeight="300dp" android:contentDescription="@string/video_row_video_thumbnail" android:scaleType="fitXY" + android:adjustViewBounds="true" /> + - - android:layout_margin="16dp" - android:layout_width="220dp" - android:layout_gravity="center" - android:layout_height="wrap_content"> + + + + - + + + + + + - - + + + + + + + + + + + + + - + + + + + - - + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 62fbcb6..92814a4 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -42,7 +42,6 @@ \@ - Video Thumbnail Account Avatar @@ -54,5 +53,7 @@ License \nGNU Affero General Public License v3.0\n\nPermissions of this strongest copyleft license are conditioned on making available complete source code of licensed works and modifications, which include larger works using a licensed work, under the same license. Copyright and license notices must be preserved. Contributors provide an express grant of patent rights. When a modified version is used to provide a service over a network, the complete source code of the modified version must be made available. Version + Search PeerTube + Search diff --git a/app/src/main/res/xml/searchable.xml b/app/src/main/res/xml/searchable.xml new file mode 100644 index 0000000..a89bfa9 --- /dev/null +++ b/app/src/main/res/xml/searchable.xml @@ -0,0 +1,7 @@ + + + \ No newline at end of file