Bug fixes for widget picker search.

SearchBarController
- Use Extended Edit Text to handle close keyboard action well.
- On press enter while search remove focus from bar and hide keyboard.
- On cancel button press hide keyboard and also reset search targets to empty so that on next search session the previous results dont flash (show for a short time before reflecting users query).

WidgetsFullSheet
- Make sure expanded header are reset when user leaves personal/work recycler views. Search recycler view resets its expanded header on every search already.
- Show 'no search results' view if no search result present.
- Update WidgetListBaseRowEntryComparator to show personal profile widgets first.

Test: Tested prototype locally.
Bug: b/157286785
Change-Id: Ibaa208c4091783e14ac0887caf559e867185df5a
This commit is contained in:
Alina Zaidi
2021-03-12 17:39:38 +00:00
parent 1632752a72
commit 2e732e95d9
8 changed files with 77 additions and 18 deletions

View File

@@ -9,7 +9,7 @@
android:background="@drawable/bg_widgets_searchbox"
android:padding="12dp">
<EditText
<com.android.launcher3.ExtendedEditText
android:id="@+id/widgets_search_bar_edit_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"

View File

@@ -84,6 +84,9 @@
<!-- Text shown when there is no widgets shown in the popup view showing all available widgets
installed on the device. [CHAR_LIMIT=none] -->
<string name="no_widgets_available">No widgets available</string>
<!-- Text shown when there are no matching widget search results for user's search query.
[CHAR_LIMIT=none] -->
<string name="no_search_results">No search results</string>
<!-- All Apps -->
<!-- Search bar text in the apps view. [CHAR_LIMIT=50] -->

View File

@@ -24,9 +24,9 @@ import static org.mockito.Mockito.verifyNoMoreInteractions;
import android.content.Context;
import android.view.View;
import android.widget.EditText;
import android.widget.ImageButton;
import com.android.launcher3.ExtendedEditText;
import com.android.launcher3.search.SearchAlgorithm;
import com.android.launcher3.widget.model.WidgetsListBaseEntry;
@@ -45,7 +45,7 @@ public class WidgetsSearchBarControllerTest {
private WidgetsSearchBarController mController;
private Context mContext;
private EditText mEditText;
private ExtendedEditText mEditText;
private ImageButton mCancelButton;
@Mock
private SearchModeListener mSearchModeListener;
@@ -56,7 +56,7 @@ public class WidgetsSearchBarControllerTest {
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application;
mEditText = new EditText(mContext);
mEditText = new ExtendedEditText(mContext);
mCancelButton = new ImageButton(mContext);
mController = new WidgetsSearchBarController(
mSearchAlgorithm, mEditText, mCancelButton, mSearchModeListener);
@@ -116,11 +116,10 @@ public class WidgetsSearchBarControllerTest {
}
@Test
public void cancelSearch_shouldInformSearchModeListenerToExitSearch() {
public void cancelSearch_shouldInformSearchModeListenerToClearResultsAndExitSearch() {
mCancelButton.performClick();
verify(mSearchModeListener).exitSearchMode();
verifyNoMoreInteractions(mSearchModeListener);
}
@Test

View File

@@ -57,6 +57,7 @@ import com.android.launcher3.widget.picker.search.WidgetsSearchBar;
import com.android.launcher3.workprofile.PersonalWorkPagedView;
import com.android.launcher3.workprofile.PersonalWorkSlidingTabStrip.OnActivePageChangedListener;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;
@@ -161,8 +162,8 @@ public class WidgetsFullSheet extends BaseWidgetSheet
mAdapters.get(currentActivePage).mWidgetsRecyclerView;
updateNoWidgetsView(currentAdapterHolder);
attachScrollbarToRecyclerView(currentRecyclerView);
resetExpandedHeaders();
}
private void attachScrollbarToRecyclerView(WidgetsRecyclerView recyclerView) {
@@ -180,6 +181,13 @@ public class WidgetsFullSheet extends BaseWidgetSheet
mNoWidgetsView.setVisibility(isWidgetAvailable ? GONE : VISIBLE);
}
private void updateNoSearchResultsView(boolean isVisible) {
mNoWidgetsView.setVisibility(isVisible ? VISIBLE : GONE);
if (isVisible) {
mNoWidgetsView.setText(R.string.no_search_results);
}
}
private void reset() {
mAdapters.get(AdapterHolder.PRIMARY).mWidgetsRecyclerView.scrollToTop();
if (mHasWorkProfile) {
@@ -323,10 +331,12 @@ public class WidgetsFullSheet extends BaseWidgetSheet
if (mIsInSearchMode) return;
setViewVisibilityBasedOnSearch(/*isInSearchMode= */ true);
attachScrollbarToRecyclerView(mAdapters.get(AdapterHolder.SEARCH).mWidgetsRecyclerView);
resetExpandedHeaders();
}
@Override
public void exitSearchMode() {
onSearchResults(new ArrayList<>());
setViewVisibilityBasedOnSearch(/*isInSearchMode=*/ false);
if (mHasWorkProfile) {
mViewPager.snapToPage(AdapterHolder.PRIMARY);
@@ -337,6 +347,8 @@ public class WidgetsFullSheet extends BaseWidgetSheet
@Override
public void onSearchResults(List<WidgetsListBaseEntry> entries) {
mAdapters.get(AdapterHolder.SEARCH).mWidgetsListAdapter.setWidgetsOnSearch(entries);
updateNoSearchResultsView(
mAdapters.get(AdapterHolder.SEARCH).mWidgetsListAdapter.getItemCount() == 0);
}
private void setViewVisibilityBasedOnSearch(boolean isInSearchMode) {
@@ -350,6 +362,12 @@ public class WidgetsFullSheet extends BaseWidgetSheet
}
mAdapters.get(AdapterHolder.SEARCH).mWidgetsRecyclerView
.setVisibility(mIsInSearchMode ? VISIBLE : GONE);
mNoWidgetsView.setVisibility(GONE);
}
private void resetExpandedHeaders() {
mAdapters.get(AdapterHolder.PRIMARY).mWidgetsListAdapter.resetExpandedHeader();
mAdapters.get(AdapterHolder.WORK).mWidgetsListAdapter.resetExpandedHeader();
}
private void open(boolean animate) {

View File

@@ -16,6 +16,7 @@
package com.android.launcher3.widget.picker;
import android.content.Context;
import android.os.Process;
import android.util.Log;
import android.util.SparseArray;
import android.view.LayoutInflater;
@@ -81,7 +82,7 @@ public class WidgetsListAdapter extends Adapter<ViewHolder> implements OnHeaderC
entry instanceof WidgetsListHeaderEntry
|| entry instanceof WidgetsListSearchHeaderEntry
|| new PackageUserKey(entry.mPkgItem.packageName, entry.mPkgItem.user)
.equals(mWidgetsContentVisiblePackageUserKey);
.equals(mWidgetsContentVisiblePackageUserKey);
@Nullable private Predicate<WidgetsListBaseEntry> mFilter = null;
public WidgetsListAdapter(Context context, LayoutInflater layoutInflater,
@@ -175,6 +176,14 @@ public class WidgetsListAdapter extends Adapter<ViewHolder> implements OnHeaderC
mDiffReporter.process(mVisibleEntries, newVisibleEntries, mRowComparator);
}
/**
* Resets any expanded widget header.
*/
public void resetExpandedHeader() {
mWidgetsContentVisiblePackageUserKey = null;
updateVisibleEntries();
}
@Override
public void onBindViewHolder(ViewHolder holder, int pos) {
ViewHolderBinder viewHolderBinder = mViewHolderBinders.get(getItemViewType(pos));
@@ -258,7 +267,14 @@ public class WidgetsListAdapter extends Adapter<ViewHolder> implements OnHeaderC
@Override
public int compare(WidgetsListBaseEntry a, WidgetsListBaseEntry b) {
return mComparator.compare(a.mPkgItem.title.toString(), b.mPkgItem.title.toString());
int i = mComparator.compare(a.mPkgItem.title.toString(), b.mPkgItem.title.toString());
if (i != 0) {
return i;
}
// Prioritize entries from current user over other users if the entries are same.
if (a.mPkgItem.user.equals(b.mPkgItem.user)) return 0;
if (a.mPkgItem.user.equals(Process.myUserHandle())) return -1;
return 1;
}
}
}

View File

@@ -220,8 +220,8 @@ public class WidgetsRecyclerView extends BaseRecyclerView implements OnItemTouch
int totalItemsHeight = 0;
for (int i = 0; i < untilIndex; i++) {
WidgetsListBaseEntry entry = mAdapter.getItems().get(i);
if (entry instanceof WidgetsListHeaderEntry ||
entry instanceof WidgetsListSearchHeaderEntry) {
if (entry instanceof WidgetsListHeaderEntry
|| entry instanceof WidgetsListSearchHeaderEntry) {
totalItemsHeight += mEstimatedWidgetListHeaderHeight;
} else if (entry instanceof WidgetsListContentEntry) {
totalItemsHeight += mLastVisibleWidgetContentTableHeight;

View File

@@ -18,13 +18,13 @@ package com.android.launcher3.widget.picker.search;
import android.content.Context;
import android.util.AttributeSet;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.launcher3.ExtendedEditText;
import com.android.launcher3.R;
import com.android.launcher3.search.SearchAlgorithm;
import com.android.launcher3.widget.model.WidgetsListBaseEntry;
@@ -36,7 +36,7 @@ import java.util.List;
*/
public class LauncherWidgetsSearchBar extends LinearLayout implements WidgetsSearchBar {
private WidgetsSearchBarController mController;
private EditText mEditText;
private ExtendedEditText mEditText;
private ImageButton mCancelButton;
public LauncherWidgetsSearchBar(Context context) {

View File

@@ -22,9 +22,11 @@ import static android.view.View.VISIBLE;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.Log;
import android.widget.EditText;
import android.view.KeyEvent;
import android.view.View;
import android.widget.ImageButton;
import com.android.launcher3.ExtendedEditText;
import com.android.launcher3.search.SearchAlgorithm;
import com.android.launcher3.search.SearchCallback;
import com.android.launcher3.widget.model.WidgetsListBaseEntry;
@@ -35,22 +37,25 @@ import java.util.ArrayList;
* Controller for a search bar with an edit text and a cancel button.
*/
public class WidgetsSearchBarController implements TextWatcher,
SearchCallback<WidgetsListBaseEntry> {
SearchCallback<WidgetsListBaseEntry>, ExtendedEditText.OnBackKeyListener,
View.OnKeyListener {
private static final String TAG = "WidgetsSearchBarController";
private static final boolean DEBUG = false;
protected SearchAlgorithm<WidgetsListBaseEntry> mSearchAlgorithm;
protected EditText mInput;
protected ExtendedEditText mInput;
protected ImageButton mCancelButton;
protected SearchModeListener mSearchModeListener;
protected String mQuery;
public WidgetsSearchBarController(
SearchAlgorithm<WidgetsListBaseEntry> algo, EditText editText, ImageButton cancelButton,
SearchModeListener searchModeListener) {
SearchAlgorithm<WidgetsListBaseEntry> algo, ExtendedEditText editText,
ImageButton cancelButton, SearchModeListener searchModeListener) {
mSearchAlgorithm = algo;
mInput = editText;
mInput.addTextChangedListener(this);
mInput.setOnBackKeyListener(this);
mInput.setOnKeyListener(this);
mCancelButton = cancelButton;
mCancelButton.setOnClickListener(v -> clearSearchResult());
mSearchModeListener = searchModeListener;
@@ -99,6 +104,7 @@ public class WidgetsSearchBarController implements TextWatcher,
mSearchAlgorithm.cancel(/* interruptActiveRequests= */ true);
mInput.getText().clear();
mInput.clearFocus();
mInput.hideKeyboard();
mSearchModeListener.exitSearchMode();
}
@@ -108,4 +114,21 @@ public class WidgetsSearchBarController implements TextWatcher,
public void onDestroy() {
mSearchAlgorithm.destroy();
}
@Override
public boolean onBackKey() {
mInput.clearFocus();
mInput.hideKeyboard();
return true;
}
@Override
public boolean onKey(View view, int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_ENTER && event.getAction() == KeyEvent.ACTION_UP) {
mInput.clearFocus();
mInput.hideKeyboard();
return true;
}
return false;
}
}