feat: replace old searchbar implementation with official material3

This commit is contained in:
antonio 2023-06-24 11:15:04 +02:00
parent d7c5be834e
commit 3b80725673
5 changed files with 192 additions and 105 deletions

View file

@ -2,9 +2,14 @@ package com.cappielloantonio.tempo.ui.fragment;
import android.content.ComponentName; import android.content.ComponentName;
import android.os.Bundle; import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.view.inputmethod.EditorInfo;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
@ -28,17 +33,15 @@ import com.cappielloantonio.tempo.ui.adapter.AlbumAdapter;
import com.cappielloantonio.tempo.ui.adapter.ArtistAdapter; import com.cappielloantonio.tempo.ui.adapter.ArtistAdapter;
import com.cappielloantonio.tempo.ui.adapter.SongHorizontalAdapter; import com.cappielloantonio.tempo.ui.adapter.SongHorizontalAdapter;
import com.cappielloantonio.tempo.util.Constants; import com.cappielloantonio.tempo.util.Constants;
import com.cappielloantonio.tempo.util.MusicUtil;
import com.cappielloantonio.tempo.viewmodel.SearchViewModel; import com.cappielloantonio.tempo.viewmodel.SearchViewModel;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
import com.paulrybitskyi.persistentsearchview.adapters.model.SuggestionItem;
import com.paulrybitskyi.persistentsearchview.listeners.OnSuggestionChangeListener;
import com.paulrybitskyi.persistentsearchview.utils.SuggestionCreationUtil;
import java.util.Collections; import java.util.Collections;
@UnstableApi @UnstableApi
public class SearchFragment extends Fragment implements ClickCallback { public class SearchFragment extends Fragment implements ClickCallback {
private static final String TAG = "SearchFragment";
private FragmentSearchBinding bind; private FragmentSearchBinding bind;
private MainActivity activity; private MainActivity activity;
private SearchViewModel searchViewModel; private SearchViewModel searchViewModel;
@ -60,6 +63,7 @@ public class SearchFragment extends Fragment implements ClickCallback {
initSearchResultView(); initSearchResultView();
initSearchView(); initSearchView();
inputFocus();
return view; return view;
} }
@ -70,12 +74,6 @@ public class SearchFragment extends Fragment implements ClickCallback {
initializeMediaBrowser(); initializeMediaBrowser();
} }
@Override
public void onResume() {
super.onResume();
inputFocus();
}
@Override @Override
public void onStop() { public void onStop() {
releaseMediaBrowser(); releaseMediaBrowser();
@ -118,64 +116,101 @@ public class SearchFragment extends Fragment implements ClickCallback {
} }
private void initSearchView() { private void initSearchView() {
if (isQueryValid(searchViewModel.getQuery())) { setRecentSuggestions();
search(searchViewModel.getQuery());
}
bind.persistentSearchView.setInputQuery(searchViewModel.getQuery()); bind.searchView
setSuggestions(); .getEditText()
.setOnEditorActionListener((textView, actionId, keyEvent) -> {
bind.persistentSearchView.setOnSearchQueryChangeListener((searchView, oldQuery, newQuery) -> { String query = bind.searchView.getText().toString();
if (!newQuery.trim().equals("") && newQuery.trim().length() > 1) {
searchViewModel.getSearchSuggestion(newQuery).observe(getViewLifecycleOwner(), suggestions -> searchView.setSuggestions(SuggestionCreationUtil.asRegularSearchSuggestions(MusicUtil.getReadableStrings(suggestions)), false));
} else {
setSuggestions();
}
});
bind.persistentSearchView.setOnSuggestionChangeListener(new OnSuggestionChangeListener() { if (actionId == EditorInfo.IME_ACTION_DONE) {
@Override
public void onSuggestionPicked(SuggestionItem suggestion) {
search(suggestion.getItemModel().getText());
}
@Override
public void onSuggestionRemoved(SuggestionItem suggestion) {
}
});
bind.persistentSearchView.setOnSearchConfirmedListener((searchView, query) -> {
if (isQueryValid(query)) { if (isQueryValid(query)) {
searchView.collapse(); search(bind.searchView.getText().toString());
search(query); return true;
} else { } else {
Toast.makeText(requireContext(), getString(R.string.search_info_minimum_characters), Toast.LENGTH_SHORT).show(); Toast.makeText(requireContext(), getString(R.string.search_info_minimum_characters), Toast.LENGTH_SHORT).show();
return false;
} }
}
return false;
}); });
bind.persistentSearchView.setOnSuggestionChangeListener(new OnSuggestionChangeListener() { bind.searchView
.getEditText()
.addTextChangedListener(new TextWatcher() {
@Override @Override
public void onSuggestionPicked(SuggestionItem suggestion) { public void beforeTextChanged(CharSequence charSequence, int start, int count, int after) {
search(suggestion.getItemModel().getText());
} }
@Override @Override
public void onSuggestionRemoved(SuggestionItem suggestion) { public void onTextChanged(CharSequence charSequence, int start, int before, int count) {
searchViewModel.deleteRecentSearch(suggestion.getItemModel().getText()); if (count > 1) {
setSearchSuggestions(charSequence.toString());
} else {
setRecentSuggestions();
}
}
@Override
public void afterTextChanged(Editable editable) {
} }
}); });
bind.persistentSearchView.setOnClearInputBtnClickListener(v -> searchViewModel.setQuery(""));
} }
private void setSuggestions() { public void setRecentSuggestions() {
bind.persistentSearchView.setSuggestions(SuggestionCreationUtil.asRecentSearchSuggestions(searchViewModel.getRecentSearchSuggestion()), false); bind.searchViewSuggestionContainer.removeAllViews();
for (String suggestion : searchViewModel.getRecentSearchSuggestion()) {
View view = LayoutInflater.from(bind.searchViewSuggestionContainer.getContext()).inflate(R.layout.item_search_suggestion, bind.searchViewSuggestionContainer, false);
ImageView leadingImageView = view.findViewById(R.id.search_suggestion_icon);
TextView titleView = view.findViewById(R.id.search_suggestion_title);
ImageView tailingImageView = view.findViewById(R.id.search_suggestion_delete_icon);
leadingImageView.setImageDrawable(getResources().getDrawable(R.drawable.ic_history, null));
titleView.setText(suggestion);
view.setOnClickListener(v -> search(suggestion));
tailingImageView.setOnClickListener(v -> {
searchViewModel.deleteRecentSearch(suggestion);
setRecentSuggestions();
});
bind.searchViewSuggestionContainer.addView(view);
}
}
public void setSearchSuggestions(String query) {
searchViewModel.getSearchSuggestion(query).observe(getViewLifecycleOwner(), suggestions -> {
bind.searchViewSuggestionContainer.removeAllViews();
for (String suggestion : suggestions) {
View view = LayoutInflater.from(bind.searchViewSuggestionContainer.getContext()).inflate(R.layout.item_search_suggestion, bind.searchViewSuggestionContainer, false);
ImageView leadingImageView = view.findViewById(R.id.search_suggestion_icon);
TextView titleView = view.findViewById(R.id.search_suggestion_title);
ImageView tailingImageView = view.findViewById(R.id.search_suggestion_delete_icon);
leadingImageView.setImageDrawable(getResources().getDrawable(R.drawable.ic_search, null));
titleView.setText(suggestion);
tailingImageView.setVisibility(View.GONE);
view.setOnClickListener(v -> search(suggestion));
bind.searchViewSuggestionContainer.addView(view);
}
});
} }
public void search(String query) { public void search(String query) {
searchViewModel.setQuery(query); searchViewModel.setQuery(query);
bind.searchBar.setText(query);
bind.persistentSearchView.setInputQuery(query); bind.searchView.hide();
performSearch(query); performSearch(query);
} }
@ -216,7 +251,7 @@ public class SearchFragment extends Fragment implements ClickCallback {
} }
private void inputFocus() { private void inputFocus() {
bind.persistentSearchView.expand(); bind.searchView.show();
} }
private void initializeMediaBrowser() { private void initializeMediaBrowser() {

View file

@ -1,10 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" <vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp" android:width="24dp"
android:height="24dp" android:height="24dp"
android:tint="?attr/colorControlNormal" android:viewportWidth="960"
android:viewportWidth="24" android:viewportHeight="960">
android:viewportHeight="24">
<path <path
android:fillColor="@android:color/white" android:fillColor="@color/titleTextColor"
android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z" /> android:pathData="M256,760L200,704L424,480L200,256L256,200L480,424L704,200L760,256L536,480L760,704L704,760L480,536L256,760Z"/>
</vector> </vector>

View file

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960">
<path
android:fillColor="@color/titleTextColor"
android:pathData="M480,840Q342,840 239.5,748.5Q137,657 122,520L204,520Q218,624 296.5,692Q375,760 480,760Q597,760 678.5,678.5Q760,597 760,480Q760,363 678.5,281.5Q597,200 480,200Q411,200 351,232Q291,264 250,320L360,320L360,400L120,400L120,160L200,160L200,254Q251,190 324.5,155Q398,120 480,120Q555,120 620.5,148.5Q686,177 734.5,225.5Q783,274 811.5,339.5Q840,405 840,480Q840,555 811.5,620.5Q783,686 734.5,734.5Q686,783 620.5,811.5Q555,840 480,840ZM592,648L440,496L440,280L520,280L520,464L648,592L592,648Z"/>
</vector>

View file

@ -1,54 +1,14 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
<com.paulrybitskyi.persistentsearchview.PersistentSearchView
android:id="@+id/persistentSearchView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:paddingStart="12dp"
android:paddingEnd="12dp"
app:areSuggestionsDisabled="false"
app:cardBackgroundColor="?attr/colorSurface"
app:cardCornerRadius="0dp"
app:cardElevation="0dp"
app:clearInputButtonDrawable="@drawable/ic_close"
app:dividerColor="@color/dividerColor"
app:isClearInputButtonEnabled="true"
app:isDismissableOnTouchOutside="true"
app:isProgressBarEnabled="true"
app:isVoiceInputButtonEnabled="false"
app:leftButtonDrawable="@drawable/ic_search"
app:progressBarColor="?attr/colorOnSurface"
app:queryInputBarIconColor="@color/searchPlaceholderColor"
app:queryInputCursorColor="?attr/colorOnSurface"
app:queryInputHint="@string/search_hint"
app:queryInputHintColor="@color/searchPlaceholderColor"
app:queryInputTextColor="@color/searchPlaceholderColor"
app:shouldDimBehind="false"
app:suggestionRecentSearchIconColor="@color/searchColor"
app:suggestionSearchSuggestionIconColor="@color/searchColor"
app:suggestionSelectedTextColor="?attr/colorPrimary"
app:suggestionTextColor="@color/searchColor"
app:suggestionIconColor="@color/searchColor" />
<View
android:id="@+id/search_bar_divider"
android:layout_width="match_parent"
android:layout_height="0.75dp"
android:layout_marginStart="12dp"
android:layout_marginEnd="12dp"
android:layout_below="@+id/persistentSearchView"
android:background="?attr/colorSurfaceVariant" />
<androidx.core.widget.NestedScrollView <androidx.core.widget.NestedScrollView
android:id="@+id/search_result_nested_scroll_view" android:id="@+id/search_result_nested_scroll_view"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="match_parent"
android:layout_below="@id/search_bar_divider"> app:layout_behavior="@string/searchbar_scrolling_view_behavior">
<!-- Search result --> <!-- Search result -->
<LinearLayout <LinearLayout
@ -56,6 +16,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical" android:orientation="vertical"
android:paddingTop="64dp"
android:paddingBottom="@dimen/global_padding_bottom"> android:paddingBottom="@dimen/global_padding_bottom">
<!-- Artist --> <!-- Artist -->
@ -150,4 +111,39 @@
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>
</androidx.core.widget.NestedScrollView> </androidx.core.widget.NestedScrollView>
</RelativeLayout>
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.google.android.material.search.SearchBar
android:id="@+id/search_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/search_hint">
</com.google.android.material.search.SearchBar>
</com.google.android.material.appbar.AppBarLayout>
<com.google.android.material.search.SearchView
android:id="@+id/search_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:hint="@string/search_hint"
app:layout_anchor="@id/search_bar">
<!-- Content goes here (ScrollView, RecyclerView, etc.). -->
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="none"
android:paddingBottom="@dimen/global_padding_bottom">
<LinearLayout
android:id="@+id/search_view_suggestion_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"/>
</ScrollView>
</com.google.android.material.search.SearchView>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View file

@ -0,0 +1,48 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/selectableItemBackground"
android:clickable="true"
android:focusable="true"
android:paddingLeft="16dp"
android:paddingTop="16dp"
android:paddingRight="16dp"
android:paddingBottom="16dp">
<ImageView
android:id="@+id/search_suggestion_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_history"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:ignore="ContentDescription" />
<TextView
android:id="@+id/search_suggestion_title"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_marginHorizontal="12dp"
android:text="@string/label_placeholder"
android:textAppearance="?attr/textAppearanceBody2"
app:layout_constraintBottom_toBottomOf="@id/search_suggestion_icon"
app:layout_constraintEnd_toStartOf="@id/search_suggestion_delete_icon"
app:layout_constraintStart_toEndOf="@id/search_suggestion_icon"
app:layout_constraintTop_toTopOf="@id/search_suggestion_icon" />
<ImageView
android:id="@+id/search_suggestion_delete_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="?selectableItemBackgroundBorderless"
android:src="@drawable/ic_close"
app:layout_constraintBottom_toBottomOf="@id/search_suggestion_icon"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/search_suggestion_title"
app:layout_constraintTop_toTopOf="@id/search_suggestion_icon"
tools:ignore="ContentDescription" />
</androidx.constraintlayout.widget.ConstraintLayout>