Explorations in dense grids for all apps.

- Adds sticky section headers
- Removing AppsListAdapter
- Adding search bar field
- Subtitle filtering

Bug: 20222023

Change-Id: I1eaef701b5d68f475615f09d86561eacc91c937f
This commit is contained in:
Winson Chung
2015-05-05 17:21:58 -07:00
parent 04ac4faab0
commit 83f59abc9c
20 changed files with 479 additions and 279 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 190 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 743 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 151 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 497 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 234 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 972 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 308 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 359 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

+48 -15
View File
@@ -22,22 +22,55 @@
android:elevation="15dp" android:elevation="15dp"
android:visibility="gone" android:visibility="gone"
android:focusableInTouchMode="true"> android:focusableInTouchMode="true">
<EditText <FrameLayout
android:id="@+id/app_search_box" android:id="@+id/header"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="52dp"
android:padding="16dp" android:orientation="horizontal"
android:hint="@string/apps_view_search_bar_hint" android:background="@drawable/apps_search_bg">
android:maxLines="1" <LinearLayout
android:singleLine="true" android:id="@+id/app_search_container"
android:scrollHorizontally="true" android:layout_width="match_parent"
android:gravity="fill_horizontal" android:layout_height="wrap_content"
android:textSize="16sp" android:orientation="horizontal"
android:textColor="#4c4c4c" android:visibility="invisible">
android:textColorHint="#9c9c9c" <ImageView
android:imeOptions="actionDone|flagNoExtractUi" android:id="@+id/dismiss_search_button"
android:background="@drawable/apps_search_bg" android:layout_width="wrap_content"
android:elevation="4dp" /> android:layout_height="wrap_content"
android:layout_gravity="start|center_vertical"
android:paddingTop="12dp"
android:paddingBottom="12dp"
android:contentDescription="@string/all_apps_button_label"
android:src="@drawable/ic_arrow_back_grey" />
<EditText
android:id="@+id/app_search_box"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingTop="16dp"
android:paddingBottom="16dp"
android:paddingLeft="8dp"
android:hint="@string/apps_view_search_bar_hint"
android:maxLines="1"
android:singleLine="true"
android:scrollHorizontally="true"
android:gravity="fill_horizontal"
android:textSize="16sp"
android:textColor="#4c4c4c"
android:textColorHint="#9c9c9c"
android:imeOptions="actionDone|flagNoExtractUi"
android:background="@android:color/transparent" />
</LinearLayout>
<ImageView
android:id="@+id/search_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|center_vertical"
android:paddingTop="12dp"
android:paddingBottom="12dp"
android:contentDescription="@string/apps_view_search_bar_hint"
android:src="@drawable/ic_search_grey" />
</FrameLayout>
<com.android.launcher3.AppsContainerRecyclerView <com.android.launcher3.AppsContainerRecyclerView
android:id="@+id/apps_list_view" android:id="@+id/apps_list_view"
android:layout_width="match_parent" android:layout_width="match_parent"
+1
View File
@@ -53,6 +53,7 @@
<!-- Note: This needs to match the fixed insets for the search box --> <!-- Note: This needs to match the fixed insets for the search box -->
<dimen name="apps_container_fixed_bounds_inset">8dp</dimen> <dimen name="apps_container_fixed_bounds_inset">8dp</dimen>
<dimen name="apps_grid_view_start_margin">52dp</dimen> <dimen name="apps_grid_view_start_margin">52dp</dimen>
<dimen name="apps_grid_section_y_offset">8dp</dimen>
<dimen name="apps_view_row_height">64dp</dimen> <dimen name="apps_view_row_height">64dp</dimen>
<dimen name="apps_view_section_text_size">24sp</dimen> <dimen name="apps_view_section_text_size">24sp</dimen>
<dimen name="apps_view_fast_scroll_bar_width">6dp</dimen> <dimen name="apps_view_fast_scroll_bar_width">6dp</dimen>
@@ -86,6 +86,8 @@ public class AlphabeticalAppsList {
public String sectionName; public String sectionName;
// The number of applications in this section // The number of applications in this section
public int numAppsInSection; public int numAppsInSection;
// The section AdapterItem for this section
public AdapterItem sectionItem;
// The first app AdapterItem for this section // The first app AdapterItem for this section
public AdapterItem firstAppItem; public AdapterItem firstAppItem;
@@ -137,6 +139,9 @@ public class AlphabeticalAppsList {
public boolean retainApp(AppInfo info, String sectionName); public boolean retainApp(AppInfo info, String sectionName);
} }
// The maximum number of rows allowed in a merged section before we stop merging
private static final int MAX_ROWS_IN_MERGED_SECTION = Integer.MAX_VALUE;
private List<AppInfo> mApps = new ArrayList<>(); private List<AppInfo> mApps = new ArrayList<>();
private List<AppInfo> mFilteredApps = new ArrayList<>(); private List<AppInfo> mFilteredApps = new ArrayList<>();
private List<AdapterItem> mSectionedFilteredApps = new ArrayList<>(); private List<AdapterItem> mSectionedFilteredApps = new ArrayList<>();
@@ -145,10 +150,23 @@ public class AlphabeticalAppsList {
private Filter mFilter; private Filter mFilter;
private AlphabeticIndexCompat mIndexer; private AlphabeticIndexCompat mIndexer;
private AppNameComparator mAppNameComparator; private AppNameComparator mAppNameComparator;
private int mNumAppsPerRow;
// The maximum number of section merges we allow at a given time before we stop merging
private int mMaxAllowableMerges = Integer.MAX_VALUE;
public AlphabeticalAppsList(Context context) { public AlphabeticalAppsList(Context context, int numAppsPerRow) {
mIndexer = new AlphabeticIndexCompat(context); mIndexer = new AlphabeticIndexCompat(context);
mAppNameComparator = new AppNameComparator(context); mAppNameComparator = new AppNameComparator(context);
setNumAppsPerRow(numAppsPerRow);
}
/**
* Sets the number of apps per row. Used only for AppsContainerView.SECTIONED_GRID_COALESCED.
*/
public void setNumAppsPerRow(int numAppsPerRow) {
mNumAppsPerRow = numAppsPerRow;
mMaxAllowableMerges = (int) Math.ceil(numAppsPerRow / 2f);
onAppsUpdated();
} }
/** /**
@@ -179,6 +197,13 @@ public class AlphabeticalAppsList {
return mFilteredApps.size(); return mFilteredApps.size();
} }
/**
* Returns whether there are is a filter set.
*/
public boolean hasFilter() {
return (mFilter != null);
}
/** /**
* Returns whether there are no filtered results. * Returns whether there are no filtered results.
*/ */
@@ -190,9 +215,11 @@ public class AlphabeticalAppsList {
* Sets the current filter for this list of apps. * Sets the current filter for this list of apps.
*/ */
public void setFilter(Filter f) { public void setFilter(Filter f) {
mFilter = f; if (mFilter != f) {
onAppsUpdated(); mFilter = f;
mAdapter.notifyDataSetChanged(); onAppsUpdated();
mAdapter.notifyDataSetChanged();
}
} }
/** /**
@@ -298,9 +325,13 @@ public class AlphabeticalAppsList {
lastSectionInfo = new SectionInfo(sectionName); lastSectionInfo = new SectionInfo(sectionName);
mSections.add(lastSectionInfo); mSections.add(lastSectionInfo);
// Create a new section item // Create a new section item, this item is used to break the flow of items in the
// list
AdapterItem sectionItem = AdapterItem.asSection(position++, sectionName); AdapterItem sectionItem = AdapterItem.asSection(position++, sectionName);
mSectionedFilteredApps.add(sectionItem); if (!AppsContainerView.GRID_HIDE_SECTION_HEADERS && !hasFilter()) {
lastSectionInfo.sectionItem = sectionItem;
mSectionedFilteredApps.add(sectionItem);
}
} }
// Create an app item // Create an app item
@@ -312,5 +343,53 @@ public class AlphabeticalAppsList {
mSectionedFilteredApps.add(appItem); mSectionedFilteredApps.add(appItem);
mFilteredApps.add(info); mFilteredApps.add(info);
} }
if (AppsContainerView.GRID_MERGE_SECTIONS && !hasFilter()) {
// Go through each section and try and merge some of the sections
int minNumAppsPerRow = (int) Math.ceil(mNumAppsPerRow / 2f);
int sectionAppCount = 0;
for (int i = 0; i < mSections.size(); i++) {
SectionInfo section = mSections.get(i);
String mergedSectionName = section.sectionName;
sectionAppCount = section.numAppsInSection;
int mergeCount = 1;
// Merge rows if the last app in this section is in a column that is greater than
// 0, but less than the min number of apps per row. In addition, apply the
// constraint to stop merging if the number of rows in the section is greater than
// some limit, and also if there are no lessons to merge.
while (0 < (sectionAppCount % mNumAppsPerRow) &&
(sectionAppCount % mNumAppsPerRow) < minNumAppsPerRow &&
(int) Math.ceil(sectionAppCount / mNumAppsPerRow) < MAX_ROWS_IN_MERGED_SECTION &&
(i + 1) < mSections.size()) {
SectionInfo nextSection = mSections.remove(i + 1);
// Merge the section names
if (AppsContainerView.GRID_MERGE_SECTION_HEADERS) {
mergedSectionName += nextSection.sectionName;
}
// Remove the next section break
mSectionedFilteredApps.remove(nextSection.sectionItem);
if (AppsContainerView.GRID_MERGE_SECTION_HEADERS) {
// Update the section names for the two sections
int pos = mSectionedFilteredApps.indexOf(section.firstAppItem);
for (int j = pos; j < (pos + section.numAppsInSection + nextSection.numAppsInSection); j++) {
AdapterItem item = mSectionedFilteredApps.get(j);
item.sectionName = mergedSectionName;
}
}
// Update the following adapter items of the removed section
int pos = mSectionedFilteredApps.indexOf(nextSection.firstAppItem);
for (int j = pos; j < mSectionedFilteredApps.size(); j++) {
AdapterItem item = mSectionedFilteredApps.get(j);
item.position--;
}
section.numAppsInSection += nextSection.numAppsInSection;
sectionAppCount += nextSection.numAppsInSection;
mergeCount++;
if (mergeCount >= mMaxAllowableMerges) {
break;
}
}
}
}
} }
} }
@@ -66,6 +66,7 @@ public class AppsContainerRecyclerView extends RecyclerView
private int mScrollbarWidth; private int mScrollbarWidth;
private int mScrollbarMinHeight; private int mScrollbarMinHeight;
private int mScrollbarInset; private int mScrollbarInset;
private RecyclerView.OnScrollListener mScrollListenerProxy;
public AppsContainerRecyclerView(Context context) { public AppsContainerRecyclerView(Context context) {
this(context, null); this(context, null);
@@ -102,7 +103,7 @@ public class AppsContainerRecyclerView extends RecyclerView
mDeltaThreshold = getResources().getDisplayMetrics().density * SCROLL_DELTA_THRESHOLD; mDeltaThreshold = getResources().getDisplayMetrics().density * SCROLL_DELTA_THRESHOLD;
ScrollListener listener = new ScrollListener(); ScrollListener listener = new ScrollListener();
addOnScrollListener(listener); setOnScrollListener(listener);
} }
private class ScrollListener extends RecyclerView.OnScrollListener { private class ScrollListener extends RecyclerView.OnScrollListener {
@@ -112,6 +113,7 @@ public class AppsContainerRecyclerView extends RecyclerView
@Override @Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) { public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
mDy = dy; mDy = dy;
mScrollListenerProxy.onScrolled(recyclerView, dx, dy);
} }
} }
@@ -129,6 +131,13 @@ public class AppsContainerRecyclerView extends RecyclerView
mNumAppsPerRow = rowSize; mNumAppsPerRow = rowSize;
} }
/**
* Sets an additional scroll listener, not necessary in master support lib.
*/
public void setOnScrollListenerProxy(RecyclerView.OnScrollListener listener) {
mScrollListenerProxy = listener;
}
/** /**
* Sets the fast scroller alpha. * Sets the fast scroller alpha.
*/ */
@@ -178,10 +187,6 @@ public class AppsContainerRecyclerView extends RecyclerView
handleTouchEvent(ev); handleTouchEvent(ev);
} }
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
// Do nothing
}
/** /**
* Handles the touch event and determines whether to show the fast scroller (or updates it if * Handles the touch event and determines whether to show the fast scroller (or updates it if
* it is already showing). * it is already showing).
@@ -322,6 +327,7 @@ public class AppsContainerRecyclerView extends RecyclerView
// Find the position of the first application in the section that contains the row at the // Find the position of the first application in the section that contains the row at the
// current progress // current progress
List<AlphabeticalAppsList.AdapterItem> items = mApps.getAdapterItems();
int rowAtProgress = (int) (progress * getNumRows()); int rowAtProgress = (int) (progress * getNumRows());
int rowCount = 0; int rowCount = 0;
AlphabeticalAppsList.SectionInfo lastSectionInfo = null; AlphabeticalAppsList.SectionInfo lastSectionInfo = null;
@@ -333,7 +339,7 @@ public class AppsContainerRecyclerView extends RecyclerView
} }
rowCount += numRowsInSection; rowCount += numRowsInSection;
} }
int position = mApps.getAdapterItems().indexOf(lastSectionInfo.firstAppItem); int position = items.indexOf(lastSectionInfo.firstAppItem);
// Scroll the position into view, anchored at the top of the screen if possible. We call the // Scroll the position into view, anchored at the top of the screen if possible. We call the
// scroll method on the LayoutManager directly since it is not exposed by RecyclerView. // scroll method on the LayoutManager directly since it is not exposed by RecyclerView.
@@ -342,15 +348,17 @@ public class AppsContainerRecyclerView extends RecyclerView
layoutManager.scrollToPositionWithOffset(position, 0); layoutManager.scrollToPositionWithOffset(position, 0);
// Return the section name of the row // Return the section name of the row
return mApps.getAdapterItems().get(position).sectionName; return lastSectionInfo.sectionName;
} }
/** /**
* Returns the bounds for the scrollbar. * Returns the bounds for the scrollbar.
*/ */
private void updateVerticalScrollbarBounds() { private void updateVerticalScrollbarBounds() {
List<AlphabeticalAppsList.AdapterItem> items = mApps.getAdapterItems();
// Skip early if there are no items // Skip early if there are no items
if (mApps.getAdapterItems().isEmpty()) { if (items.isEmpty()) {
mVerticalScrollbarBounds.setEmpty(); mVerticalScrollbarBounds.setEmpty();
return; return;
} }
@@ -369,7 +377,7 @@ public class AppsContainerRecyclerView extends RecyclerView
View child = getChildAt(i); View child = getChildAt(i);
int position = getChildPosition(child); int position = getChildPosition(child);
if (position != NO_POSITION) { if (position != NO_POSITION) {
AlphabeticalAppsList.AdapterItem item = mApps.getAdapterItems().get(position); AlphabeticalAppsList.AdapterItem item = items.get(position);
if (!item.isSectionHeader) { if (!item.isSectionHeader) {
rowIndex = findRowForAppIndex(item.appIndex); rowIndex = findRowForAppIndex(item.appIndex);
rowTopOffset = getLayoutManager().getDecoratedTop(child); rowTopOffset = getLayoutManager().getDecoratedTop(child);
+165 -60
View File
@@ -34,7 +34,6 @@ import android.widget.EditText;
import android.widget.FrameLayout; import android.widget.FrameLayout;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import android.widget.TextView; import android.widget.TextView;
import com.android.launcher3.util.Thunk; import com.android.launcher3.util.Thunk;
import java.util.List; import java.util.List;
@@ -45,24 +44,32 @@ import java.util.List;
*/ */
public class AppsContainerView extends FrameLayout implements DragSource, Insettable, TextWatcher, public class AppsContainerView extends FrameLayout implements DragSource, Insettable, TextWatcher,
TextView.OnEditorActionListener, LauncherTransitionable, View.OnTouchListener, TextView.OnEditorActionListener, LauncherTransitionable, View.OnTouchListener,
View.OnLongClickListener { View.OnClickListener, View.OnLongClickListener {
public static final boolean GRID_MERGE_SECTIONS = true;
public static final boolean GRID_MERGE_SECTION_HEADERS = false;
public static final boolean GRID_HIDE_SECTION_HEADERS = false;
private static final boolean ALLOW_SINGLE_APP_LAUNCH = true; private static final boolean ALLOW_SINGLE_APP_LAUNCH = true;
private static final boolean DYNAMIC_HEADER_ELEVATION = false;
private static final int GRID_LAYOUT = 0; private static final float HEADER_ELEVATION_DP = 4;
private static final int LIST_LAYOUT = 1; private static final int FADE_IN_DURATION = 175;
private static final int USE_LAYOUT = GRID_LAYOUT; private static final int FADE_OUT_DURATION = 125;
@Thunk Launcher mLauncher; @Thunk Launcher mLauncher;
@Thunk AlphabeticalAppsList mApps; @Thunk AlphabeticalAppsList mApps;
private RecyclerView.Adapter mAdapter; private AppsGridAdapter mAdapter;
private RecyclerView.LayoutManager mLayoutManager; private RecyclerView.LayoutManager mLayoutManager;
private RecyclerView.ItemDecoration mItemDecoration; private RecyclerView.ItemDecoration mItemDecoration;
private LinearLayout mContentView; private LinearLayout mContentView;
@Thunk AppsContainerRecyclerView mAppsRecyclerView; @Thunk AppsContainerRecyclerView mAppsRecyclerView;
private EditText mSearchBarView; private View mHeaderView;
private View mSearchBarContainerView;
private View mSearchButtonView;
private View mDismissSearchButtonView;
private EditText mSearchBarEditView;
private int mNumAppsPerRow; private int mNumAppsPerRow;
private Point mLastTouchDownPos = new Point(-1, -1); private Point mLastTouchDownPos = new Point(-1, -1);
private Point mLastTouchPos = new Point(); private Point mLastTouchPos = new Point();
@@ -73,6 +80,8 @@ public class AppsContainerView extends FrameLayout implements DragSource, Insett
private int mContainerInset; private int mContainerInset;
// Fixed bounds container insets // Fixed bounds container insets
private int mFixedBoundsContainerInset; private int mFixedBoundsContainerInset;
// RecyclerView scroll position
@Thunk int mRecyclerViewScrollY;
public AppsContainerView(Context context) { public AppsContainerView(Context context) {
this(context, null); this(context, null);
@@ -93,23 +102,14 @@ public class AppsContainerView extends FrameLayout implements DragSource, Insett
mFixedBoundsContainerInset = context.getResources().getDimensionPixelSize( mFixedBoundsContainerInset = context.getResources().getDimensionPixelSize(
R.dimen.apps_container_fixed_bounds_inset); R.dimen.apps_container_fixed_bounds_inset);
mLauncher = (Launcher) context; mLauncher = (Launcher) context;
mApps = new AlphabeticalAppsList(context); mNumAppsPerRow = grid.appsViewNumCols;
if (USE_LAYOUT == GRID_LAYOUT) { mApps = new AlphabeticalAppsList(context, mNumAppsPerRow);
mNumAppsPerRow = grid.appsViewNumCols; mAdapter = new AppsGridAdapter(context, mApps, mNumAppsPerRow, this, mLauncher, this);
AppsGridAdapter adapter = new AppsGridAdapter(context, mApps, mNumAppsPerRow, this, mAdapter.setEmptySearchText(res.getString(R.string.loading_apps_message));
mLauncher, this); mAdapter.setNumAppsPerRow(mNumAppsPerRow);
adapter.setEmptySearchText(res.getString(R.string.loading_apps_message)); mLayoutManager = mAdapter.getLayoutManager();
mLayoutManager = adapter.getLayoutManager(context); mItemDecoration = mAdapter.getItemDecoration();
mItemDecoration = adapter.getItemDecoration(); mContentMarginStart = mAdapter.getContentMarginStart();
mAdapter = adapter;
mContentMarginStart = adapter.getContentMarginStart();
} else if (USE_LAYOUT == LIST_LAYOUT) {
mNumAppsPerRow = 1;
AppsListAdapter adapter = new AppsListAdapter(context, mApps, this, mLauncher, this);
adapter.setEmptySearchText(res.getString(R.string.loading_apps_message));
mLayoutManager = adapter.getLayoutManager(context);
mAdapter = adapter;
}
mApps.setAdapter(mAdapter); mApps.setAdapter(mAdapter);
} }
@@ -142,10 +142,10 @@ public class AppsContainerView extends FrameLayout implements DragSource, Insett
} }
/** /**
* Hides the search bar * Hides the header bar
*/ */
public void hideSearchBar() { public void hideHeaderBar() {
mSearchBarView.setVisibility(View.GONE); mHeaderView.setVisibility(View.GONE);
updateBackgrounds(); updateBackgrounds();
updatePaddings(); updatePaddings();
} }
@@ -155,6 +155,7 @@ public class AppsContainerView extends FrameLayout implements DragSource, Insett
*/ */
public void scrollToTop() { public void scrollToTop() {
mAppsRecyclerView.scrollToPosition(0); mAppsRecyclerView.scrollToPosition(0);
mRecyclerViewScrollY = 0;
} }
/** /**
@@ -175,9 +176,7 @@ public class AppsContainerView extends FrameLayout implements DragSource, Insett
protected void onFinishInflate() { protected void onFinishInflate() {
boolean isRtl = (getResources().getConfiguration().getLayoutDirection() == boolean isRtl = (getResources().getConfiguration().getLayoutDirection() ==
LAYOUT_DIRECTION_RTL); LAYOUT_DIRECTION_RTL);
if (USE_LAYOUT == GRID_LAYOUT) { mAdapter.setRtl(isRtl);
((AppsGridAdapter) mAdapter).setRtl(isRtl);
}
// Work around the search box getting first focus and showing the cursor by // Work around the search box getting first focus and showing the cursor by
// proxying the focus from the content view to the recycler view directly // proxying the focus from the content view to the recycler view directly
@@ -190,10 +189,20 @@ public class AppsContainerView extends FrameLayout implements DragSource, Insett
} }
} }
}); });
mSearchBarView = (EditText) findViewById(R.id.app_search_box); mHeaderView = findViewById(R.id.header);
if (mSearchBarView != null) { mHeaderView.setOnClickListener(this);
mSearchBarView.addTextChangedListener(this); if (Utilities.isLmpOrAbove() && !DYNAMIC_HEADER_ELEVATION) {
mSearchBarView.setOnEditorActionListener(this); mHeaderView.setElevation(DynamicGrid.pxFromDp(HEADER_ELEVATION_DP,
getContext().getResources().getDisplayMetrics()));
}
mSearchButtonView = mHeaderView.findViewById(R.id.search_button);
mSearchBarContainerView = findViewById(R.id.app_search_container);
mDismissSearchButtonView = mSearchBarContainerView.findViewById(R.id.dismiss_search_button);
mDismissSearchButtonView.setOnClickListener(this);
mSearchBarEditView = (EditText) findViewById(R.id.app_search_box);
if (mSearchBarEditView != null) {
mSearchBarEditView.addTextChangedListener(this);
mSearchBarEditView.setOnEditorActionListener(this);
} }
mAppsRecyclerView = (AppsContainerRecyclerView) findViewById(R.id.apps_list_view); mAppsRecyclerView = (AppsContainerRecyclerView) findViewById(R.id.apps_list_view);
mAppsRecyclerView.setApps(mApps); mAppsRecyclerView.setApps(mApps);
@@ -201,6 +210,18 @@ public class AppsContainerView extends FrameLayout implements DragSource, Insett
mAppsRecyclerView.setLayoutManager(mLayoutManager); mAppsRecyclerView.setLayoutManager(mLayoutManager);
mAppsRecyclerView.setAdapter(mAdapter); mAppsRecyclerView.setAdapter(mAdapter);
mAppsRecyclerView.setHasFixedSize(true); mAppsRecyclerView.setHasFixedSize(true);
mAppsRecyclerView.setOnScrollListenerProxy(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
// Do nothing
}
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
mRecyclerViewScrollY += dy;
onRecyclerViewScrolled();
}
});
if (mItemDecoration != null) { if (mItemDecoration != null) {
mAppsRecyclerView.addItemDecoration(mItemDecoration); mAppsRecyclerView.addItemDecoration(mItemDecoration);
} }
@@ -225,12 +246,15 @@ public class AppsContainerView extends FrameLayout implements DragSource, Insett
if (grid.updateAppsViewNumCols(context.getResources(), fixedBounds.width())) { if (grid.updateAppsViewNumCols(context.getResources(), fixedBounds.width())) {
mNumAppsPerRow = grid.appsViewNumCols; mNumAppsPerRow = grid.appsViewNumCols;
mAppsRecyclerView.setNumAppsPerRow(mNumAppsPerRow); mAppsRecyclerView.setNumAppsPerRow(mNumAppsPerRow);
if (USE_LAYOUT == GRID_LAYOUT) { mAdapter.setNumAppsPerRow(mNumAppsPerRow);
((AppsGridAdapter) mAdapter).setNumAppsPerRow(mNumAppsPerRow); mApps.setNumAppsPerRow(mNumAppsPerRow);
}
} }
mFixedBounds.set(fixedBounds); mFixedBounds.set(fixedBounds);
if (Launcher.DISABLE_ALL_APPS_SEARCH_INTEGRATION) {
mFixedBounds.top = mInsets.top;
mFixedBounds.bottom = getMeasuredHeight();
}
} }
// Post the updates since they can trigger a relayout, and this call can be triggered from // Post the updates since they can trigger a relayout, and this call can be triggered from
// a layout pass itself. // a layout pass itself.
@@ -264,6 +288,15 @@ public class AppsContainerView extends FrameLayout implements DragSource, Insett
return false; return false;
} }
@Override
public void onClick(View v) {
if (v == mHeaderView) {
showSearchField();
} else if (v == mDismissSearchButtonView) {
hideSearchField(true, true);
}
}
@Override @Override
public boolean onLongClick(View v) { public boolean onLongClick(View v) {
// Return early if this is not initiated from a touch // Return early if this is not initiated from a touch
@@ -363,24 +396,27 @@ public class AppsContainerView extends FrameLayout implements DragSource, Insett
mApps.setFilter(null); mApps.setFilter(null);
} else { } else {
String formatStr = getResources().getString(R.string.apps_view_no_search_results); String formatStr = getResources().getString(R.string.apps_view_no_search_results);
if (USE_LAYOUT == GRID_LAYOUT) { mAdapter.setEmptySearchText(String.format(formatStr, s.toString()));
((AppsGridAdapter) mAdapter).setEmptySearchText(String.format(formatStr,
s.toString()));
} else {
((AppsListAdapter) mAdapter).setEmptySearchText(String.format(formatStr,
s.toString()));
}
final String filterText = s.toString().toLowerCase().replaceAll("\\s+", ""); final String filterText = s.toString().toLowerCase().replaceAll("\\s+", "");
mApps.setFilter(new AlphabeticalAppsList.Filter() { mApps.setFilter(new AlphabeticalAppsList.Filter() {
@Override @Override
public boolean retainApp(AppInfo info, String sectionName) { public boolean retainApp(AppInfo info, String sectionName) {
String title = info.title.toString(); String title = info.title.toString();
return sectionName.toLowerCase().contains(filterText) || if (sectionName.toLowerCase().contains(filterText)) {
title.toLowerCase().replaceAll("\\s+", "").contains(filterText); return true;
}
String[] words = title.toLowerCase().split("\\s+");
for (int i = 0; i < words.length; i++) {
if (words[i].startsWith(filterText)) {
return true;
}
}
return false;
} }
}); });
} }
scrollToTop();
} }
@Override @Override
@@ -396,9 +432,7 @@ public class AppsContainerView extends FrameLayout implements DragSource, Insett
AlphabeticalAppsList.AdapterItem item = items.get(i); AlphabeticalAppsList.AdapterItem item = items.get(i);
if (!item.isSectionHeader) { if (!item.isSectionHeader) {
mAppsRecyclerView.getChildAt(i).performClick(); mAppsRecyclerView.getChildAt(i).performClick();
InputMethodManager imm = (InputMethodManager) getInputMethodManager().hideSoftInputFromWindow(getWindowToken(), 0);
getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(getWindowToken(), 0);
return true; return true;
} }
} }
@@ -428,10 +462,22 @@ public class AppsContainerView extends FrameLayout implements DragSource, Insett
@Override @Override
public void onLauncherTransitionEnd(Launcher l, boolean animated, boolean toWorkspace) { public void onLauncherTransitionEnd(Launcher l, boolean animated, boolean toWorkspace) {
if (mSearchBarView != null) { if (mSearchBarEditView != null) {
if (toWorkspace) { if (toWorkspace) {
// Clear the search bar hideSearchField(false, false);
mSearchBarView.setText(""); }
}
}
/**
* Updates the container when the recycler view is scrolled.
*/
private void onRecyclerViewScrolled() {
if (DYNAMIC_HEADER_ELEVATION) {
int elevation = Math.min(mRecyclerViewScrollY, DynamicGrid.pxFromDp(HEADER_ELEVATION_DP,
getContext().getResources().getDisplayMetrics()));
if (Float.compare(mHeaderView.getElevation(), elevation) != 0) {
mHeaderView.setElevation(elevation);
} }
} }
} }
@@ -494,8 +540,8 @@ public class AppsContainerView extends FrameLayout implements DragSource, Insett
private void updatePaddings() { private void updatePaddings() {
boolean isRtl = (getResources().getConfiguration().getLayoutDirection() == boolean isRtl = (getResources().getConfiguration().getLayoutDirection() ==
LAYOUT_DIRECTION_RTL); LAYOUT_DIRECTION_RTL);
boolean hasSearchBar = (mSearchBarView != null) && boolean hasSearchBar = (mSearchBarEditView != null) &&
(mSearchBarView.getVisibility() == View.VISIBLE); (mSearchBarEditView.getVisibility() == View.VISIBLE);
if (mFixedBounds.isEmpty()) { if (mFixedBounds.isEmpty()) {
// If there are no fixed bounds, then use the default padding and insets // If there are no fixed bounds, then use the default padding and insets
@@ -516,10 +562,10 @@ public class AppsContainerView extends FrameLayout implements DragSource, Insett
mAppsRecyclerView.setPadding(inset + mContentMarginStart, inset, inset, inset); mAppsRecyclerView.setPadding(inset + mContentMarginStart, inset, inset, inset);
} }
// Update the search bar // Update the header
if (hasSearchBar) { if (hasSearchBar) {
LinearLayout.LayoutParams lp = LinearLayout.LayoutParams lp =
(LinearLayout.LayoutParams) mSearchBarView.getLayoutParams(); (LinearLayout.LayoutParams) mHeaderView.getLayoutParams();
lp.leftMargin = lp.rightMargin = inset; lp.leftMargin = lp.rightMargin = inset;
} }
} }
@@ -529,8 +575,8 @@ public class AppsContainerView extends FrameLayout implements DragSource, Insett
*/ */
private void updateBackgrounds() { private void updateBackgrounds() {
int inset = mFixedBounds.isEmpty() ? mContainerInset : mFixedBoundsContainerInset; int inset = mFixedBounds.isEmpty() ? mContainerInset : mFixedBoundsContainerInset;
boolean hasSearchBar = (mSearchBarView != null) && boolean hasSearchBar = (mSearchBarEditView != null) &&
(mSearchBarView.getVisibility() == View.VISIBLE); (mSearchBarEditView.getVisibility() == View.VISIBLE);
// Update the background of the reveal view and list to be inset with the fixed bound // Update the background of the reveal view and list to be inset with the fixed bound
// insets instead of the default insets // insets instead of the default insets
@@ -542,4 +588,63 @@ public class AppsContainerView extends FrameLayout implements DragSource, Insett
getContext().getResources().getDrawable(R.drawable.apps_reveal_bg), getContext().getResources().getDrawable(R.drawable.apps_reveal_bg),
inset, 0, inset, 0)); inset, 0, inset, 0));
} }
/**
* Shows the search field.
*/
private void showSearchField() {
// Show the search bar and focus the search
mSearchBarContainerView.setVisibility(View.VISIBLE);
mSearchBarContainerView.setAlpha(0f);
mSearchBarContainerView.animate().alpha(1f).setDuration(FADE_IN_DURATION).withLayer()
.withEndAction(new Runnable() {
@Override
public void run() {
mSearchBarEditView.requestFocus();
getInputMethodManager().showSoftInput(mSearchBarEditView,
InputMethodManager.SHOW_IMPLICIT);
}
});
mSearchButtonView.animate().alpha(0f).setDuration(FADE_OUT_DURATION).withLayer();
}
/**
* Hides the search field.
*/
private void hideSearchField(boolean animated, final boolean returnFocusToRecyclerView) {
if (animated) {
// Hide the search bar and focus the recycler view
mSearchBarContainerView.animate().alpha(0f).setDuration(FADE_IN_DURATION).withLayer()
.withEndAction(new Runnable() {
@Override
public void run() {
mSearchBarContainerView.setVisibility(View.INVISIBLE);
mSearchBarEditView.setText("");
mApps.setFilter(null);
if (returnFocusToRecyclerView) {
mAppsRecyclerView.requestFocus();
}
scrollToTop();
}
});
mSearchButtonView.animate().alpha(1f).setDuration(FADE_OUT_DURATION).withLayer();
} else {
mSearchBarContainerView.setVisibility(View.INVISIBLE);
mSearchBarEditView.setText("");
mApps.setFilter(null);
mSearchButtonView.setAlpha(1f);
if (returnFocusToRecyclerView) {
mAppsRecyclerView.requestFocus();
}
scrollToTop();
}
getInputMethodManager().hideSoftInputFromWindow(getWindowToken(), 0);
}
/**
* Returns an input method manager.
*/
private InputMethodManager getInputMethodManager() {
return (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
}
} }
+122 -27
View File
@@ -4,16 +4,18 @@ import android.content.Context;
import android.content.res.Resources; import android.content.res.Resources;
import android.graphics.Canvas; import android.graphics.Canvas;
import android.graphics.Paint; import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.Rect; import android.graphics.Rect;
import android.support.v7.widget.GridLayoutManager; import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView; import android.support.v7.widget.RecyclerView;
import android.util.Log;
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.widget.TextView; import android.widget.TextView;
import com.android.launcher3.util.Thunk; import com.android.launcher3.util.Thunk;
import java.util.HashMap;
import java.util.List; import java.util.List;
@@ -23,6 +25,7 @@ import java.util.List;
class AppsGridAdapter extends RecyclerView.Adapter<AppsGridAdapter.ViewHolder> { class AppsGridAdapter extends RecyclerView.Adapter<AppsGridAdapter.ViewHolder> {
public static final String TAG = "AppsGridAdapter"; public static final String TAG = "AppsGridAdapter";
private static final boolean DEBUG = false;
private static final int SECTION_BREAK_VIEW_TYPE = 0; private static final int SECTION_BREAK_VIEW_TYPE = 0;
private static final int ICON_VIEW_TYPE = 1; private static final int ICON_VIEW_TYPE = 1;
@@ -48,6 +51,12 @@ class AppsGridAdapter extends RecyclerView.Adapter<AppsGridAdapter.ViewHolder> {
* Helper class to size the grid items. * Helper class to size the grid items.
*/ */
public class GridSpanSizer extends GridLayoutManager.SpanSizeLookup { public class GridSpanSizer extends GridLayoutManager.SpanSizeLookup {
public GridSpanSizer() {
super();
setSpanIndexCacheEnabled(true);
}
@Override @Override
public int getSpanSize(int position) { public int getSpanSize(int position) {
if (mApps.hasNoFilteredResults()) { if (mApps.hasNoFilteredResults()) {
@@ -57,7 +66,11 @@ class AppsGridAdapter extends RecyclerView.Adapter<AppsGridAdapter.ViewHolder> {
if (mApps.getAdapterItems().get(position).isSectionHeader) { if (mApps.getAdapterItems().get(position).isSectionHeader) {
// Section break spans full width // Section break spans full width
return mAppsPerRow; if (AppsContainerView.GRID_HIDE_SECTION_HEADERS) {
return 0;
} else {
return mAppsPerRow;
}
} else { } else {
return 1; return 1;
} }
@@ -69,31 +82,88 @@ class AppsGridAdapter extends RecyclerView.Adapter<AppsGridAdapter.ViewHolder> {
*/ */
public class GridItemDecoration extends RecyclerView.ItemDecoration { public class GridItemDecoration extends RecyclerView.ItemDecoration {
private static final boolean FADE_OUT_SECTIONS = false;
private HashMap<String, Point> mCachedSectionBounds = new HashMap<>();
private Rect mTmpBounds = new Rect();
@Override @Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) { public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
if (mApps.hasFilter()) {
return;
}
List<AlphabeticalAppsList.AdapterItem> items = mApps.getAdapterItems(); List<AlphabeticalAppsList.AdapterItem> items = mApps.getAdapterItems();
String lastSectionName = null;
int appIndexInSection = 0;
int lastSectionTop = 0;
int lastSectionHeight = 0;
for (int i = 0; i < parent.getChildCount(); i++) { for (int i = 0; i < parent.getChildCount(); i++) {
View child = parent.getChildAt(i); View child = parent.getChildAt(i);
ViewHolder holder = (ViewHolder) parent.getChildViewHolder(child); ViewHolder holder = (ViewHolder) parent.getChildViewHolder(child);
if (shouldDrawItemSection(holder, child, items)) { if (shouldDrawItemSection(holder, child, i, items)) {
// Draw at the parent int cellTopOffset = (2 * child.getPaddingTop());
AlphabeticalAppsList.AdapterItem item = int pos = holder.getPosition();
items.get(holder.getPosition()); AlphabeticalAppsList.AdapterItem item = items.get(pos);
String section = item.sectionName; if (!item.sectionName.equals(lastSectionName)) {
mSectionTextPaint.getTextBounds(section, 0, section.length(), lastSectionName = item.sectionName;
mTmpBounds);
if (mIsRtl) { // Find the section code points
int left = parent.getWidth() - mPaddingStart - mStartMargin; String sectionBegin = null;
c.drawText(section, left + (mStartMargin - mTmpBounds.width()) / 2, String sectionEnd = null;
child.getTop() + (2 * child.getPaddingTop()) + int charOffset = 0;
mTmpBounds.height(), mSectionTextPaint); while (charOffset < item.sectionName.length()) {
} else { int codePoint = item.sectionName.codePointAt(charOffset);
int left = mPaddingStart; int codePointSize = Character.charCount(codePoint);
c.drawText(section, left + (mStartMargin - mTmpBounds.width()) / 2, if (charOffset == 0) {
child.getTop() + (2 * child.getPaddingTop()) + // The first code point
mTmpBounds.height(), mSectionTextPaint); sectionBegin = item.sectionName.substring(charOffset, charOffset + codePointSize);
} else if ((charOffset + codePointSize) >= item.sectionName.length()) {
// The last code point
sectionEnd = item.sectionName.substring(charOffset, charOffset + codePointSize);
}
charOffset += codePointSize;
}
Point sectionBeginBounds = getAndCacheSectionBounds(sectionBegin);
int minTop = cellTopOffset + sectionBeginBounds.y;
int top = child.getTop() + cellTopOffset + sectionBeginBounds.y;
int left = mIsRtl ? parent.getWidth() - mPaddingStart - mStartMargin :
mPaddingStart;
int col = appIndexInSection % mAppsPerRow;
int nextRowPos = Math.min(pos - col + mAppsPerRow, items.size() - 1);
int alpha = 255;
boolean fixedToRow = !items.get(nextRowPos).sectionName.equals(item.sectionName);
if (fixedToRow) {
alpha = Math.min(255, (int) (255 * (Math.max(0, top) / (float) minTop)));
} else {
// If we aren't fixed to the current row, then bound into the viewport
top = Math.max(minTop, top);
}
if (lastSectionHeight > 0 && top <= (lastSectionTop + lastSectionHeight)) {
top += lastSectionTop - top + lastSectionHeight;
}
if (FADE_OUT_SECTIONS) {
mSectionTextPaint.setAlpha(alpha);
}
if (sectionEnd != null) {
Point sectionEndBounds = getAndCacheSectionBounds(sectionEnd);
c.drawText(sectionBegin + "/" + sectionEnd,
left + (mStartMargin - sectionBeginBounds.x - sectionEndBounds.x) / 2, top,
mSectionTextPaint);
} else {
c.drawText(sectionBegin, left + (mStartMargin - sectionBeginBounds.x) / 2, top,
mSectionTextPaint);
}
lastSectionTop = top;
lastSectionHeight = sectionBeginBounds.y + mSectionHeaderOffset;
} }
} }
if (holder.mIsSectionHeader) {
appIndexInSection = 0;
} else {
appIndexInSection++;
}
} }
} }
@@ -103,7 +173,17 @@ class AppsGridAdapter extends RecyclerView.Adapter<AppsGridAdapter.ViewHolder> {
// Do nothing // Do nothing
} }
private boolean shouldDrawItemSection(ViewHolder holder, View child, private Point getAndCacheSectionBounds(String sectionName) {
Point bounds = mCachedSectionBounds.get(sectionName);
if (bounds == null) {
mSectionTextPaint.getTextBounds(sectionName, 0, sectionName.length(), mTmpBounds);
bounds = new Point(mTmpBounds.width(), mTmpBounds.height());
mCachedSectionBounds.put(sectionName, bounds);
}
return bounds;
}
private boolean shouldDrawItemSection(ViewHolder holder, View child, int childIndex,
List<AlphabeticalAppsList.AdapterItem> items) { List<AlphabeticalAppsList.AdapterItem> items) {
// Ensure item is not already removed // Ensure item is not already removed
GridLayoutManager.LayoutParams lp = (GridLayoutManager.LayoutParams) GridLayoutManager.LayoutParams lp = (GridLayoutManager.LayoutParams)
@@ -121,11 +201,19 @@ class AppsGridAdapter extends RecyclerView.Adapter<AppsGridAdapter.ViewHolder> {
} }
// Ensure we have a holder position // Ensure we have a holder position
int pos = holder.getPosition(); int pos = holder.getPosition();
if (pos <= 0 || pos >= items.size()) { if (pos < 0 || pos >= items.size()) {
return false; return false;
} }
// Only draw the first item in the section (the first one after the section header) // Ensure this is not a section header
return items.get(pos - 1).isSectionHeader && !items.get(pos).isSectionHeader; if (items.get(pos).isSectionHeader) {
return false;
}
// Only draw the header for the first item in a section, or whenever the sub-sections
// changes (if AppsContainerView.GRID_MERGE_SECTIONS is true, but
// AppsContainerView.GRID_MERGE_SECTION_HEADERS is false)
return (childIndex == 0) ||
items.get(pos - 1).isSectionHeader && !items.get(pos).isSectionHeader ||
(!items.get(pos - 1).sectionName.equals(items.get(pos).sectionName));
} }
} }
@@ -144,8 +232,8 @@ class AppsGridAdapter extends RecyclerView.Adapter<AppsGridAdapter.ViewHolder> {
// Section drawing // Section drawing
@Thunk int mPaddingStart; @Thunk int mPaddingStart;
@Thunk int mStartMargin; @Thunk int mStartMargin;
@Thunk int mSectionHeaderOffset;
@Thunk Paint mSectionTextPaint; @Thunk Paint mSectionTextPaint;
@Thunk Rect mTmpBounds = new Rect();
public AppsGridAdapter(Context context, AlphabeticalAppsList apps, int appsPerRow, public AppsGridAdapter(Context context, AlphabeticalAppsList apps, int appsPerRow,
@@ -163,7 +251,10 @@ class AppsGridAdapter extends RecyclerView.Adapter<AppsGridAdapter.ViewHolder> {
mTouchListener = touchListener; mTouchListener = touchListener;
mIconClickListener = iconClickListener; mIconClickListener = iconClickListener;
mIconLongClickListener = iconLongClickListener; mIconLongClickListener = iconLongClickListener;
mStartMargin = res.getDimensionPixelSize(R.dimen.apps_grid_view_start_margin); if (!AppsContainerView.GRID_HIDE_SECTION_HEADERS) {
mStartMargin = res.getDimensionPixelSize(R.dimen.apps_grid_view_start_margin);
mSectionHeaderOffset = res.getDimensionPixelSize(R.dimen.apps_grid_section_y_offset);
}
mPaddingStart = res.getDimensionPixelSize(R.dimen.apps_container_inset); mPaddingStart = res.getDimensionPixelSize(R.dimen.apps_container_inset);
mSectionTextPaint = new Paint(); mSectionTextPaint = new Paint();
mSectionTextPaint.setTextSize(res.getDimensionPixelSize( mSectionTextPaint.setTextSize(res.getDimensionPixelSize(
@@ -197,7 +288,7 @@ class AppsGridAdapter extends RecyclerView.Adapter<AppsGridAdapter.ViewHolder> {
/** /**
* Returns the grid layout manager. * Returns the grid layout manager.
*/ */
public GridLayoutManager getLayoutManager(Context context) { public GridLayoutManager getLayoutManager() {
return mGridLayoutMgr; return mGridLayoutMgr;
} }
@@ -205,7 +296,11 @@ class AppsGridAdapter extends RecyclerView.Adapter<AppsGridAdapter.ViewHolder> {
* Returns the item decoration for the recycler view. * Returns the item decoration for the recycler view.
*/ */
public RecyclerView.ItemDecoration getItemDecoration() { public RecyclerView.ItemDecoration getItemDecoration() {
return mItemDecoration; // We don't draw any headers when we are uncomfortably dense
if (!AppsContainerView.GRID_HIDE_SECTION_HEADERS) {
return mItemDecoration;
}
return null;
} }
/** /**
@@ -1,143 +0,0 @@
package com.android.launcher3;
import android.content.Context;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView;
/**
* The linear list view adapter for all the apps.
*/
class AppsListAdapter extends RecyclerView.Adapter<AppsListAdapter.ViewHolder> {
/**
* ViewHolder for each row.
*/
public static class ViewHolder extends RecyclerView.ViewHolder {
public View mContent;
public ViewHolder(View v) {
super(v);
mContent = v;
}
}
private static final int SECTION_BREAK_VIEW_TYPE = 0;
private static final int ICON_VIEW_TYPE = 1;
private static final int EMPTY_VIEW_TYPE = 2;
private LayoutInflater mLayoutInflater;
private AlphabeticalAppsList mApps;
private View.OnTouchListener mTouchListener;
private View.OnClickListener mIconClickListener;
private View.OnLongClickListener mIconLongClickListener;
private String mEmptySearchText;
public AppsListAdapter(Context context, AlphabeticalAppsList apps,
View.OnTouchListener touchListener, View.OnClickListener iconClickListener,
View.OnLongClickListener iconLongClickListener) {
mApps = apps;
mLayoutInflater = LayoutInflater.from(context);
mTouchListener = touchListener;
mIconClickListener = iconClickListener;
mIconLongClickListener = iconLongClickListener;
}
public RecyclerView.LayoutManager getLayoutManager(Context context) {
return new LinearLayoutManager(context);
}
/**
* Sets the text to show when there are no apps.
*/
public void setEmptySearchText(String query) {
mEmptySearchText = query;
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
switch (viewType) {
case EMPTY_VIEW_TYPE:
return new ViewHolder(mLayoutInflater.inflate(R.layout.apps_empty_view, parent,
false));
case SECTION_BREAK_VIEW_TYPE:
return new ViewHolder(new View(parent.getContext()));
case ICON_VIEW_TYPE:
// Inflate the row and all the icon children necessary
ViewGroup row = (ViewGroup) mLayoutInflater.inflate(R.layout.apps_list_row_view,
parent, false);
BubbleTextView icon = (BubbleTextView) mLayoutInflater.inflate(
R.layout.apps_list_row_icon_view, row, false);
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(0,
ViewGroup.LayoutParams.WRAP_CONTENT, 1);
lp.gravity = Gravity.CENTER_VERTICAL;
icon.setLayoutParams(lp);
icon.setOnTouchListener(mTouchListener);
icon.setOnClickListener(mIconClickListener);
icon.setOnLongClickListener(mIconLongClickListener);
icon.setFocusable(true);
row.addView(icon);
return new ViewHolder(row);
default:
throw new RuntimeException("Unexpected view type");
}
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
switch (holder.getItemViewType()) {
case ICON_VIEW_TYPE:
AlphabeticalAppsList.AdapterItem item = mApps.getAdapterItems().get(position);
ViewGroup content = (ViewGroup) holder.mContent;
String sectionDescription = item.sectionName;
// Bind the section header
boolean showSectionHeader = true;
if (position > 0) {
AlphabeticalAppsList.AdapterItem prevItem =
mApps.getAdapterItems().get(position - 1);
showSectionHeader = prevItem.isSectionHeader;
}
TextView tv = (TextView) content.findViewById(R.id.section);
if (showSectionHeader) {
tv.setText(sectionDescription);
tv.setVisibility(View.VISIBLE);
} else {
tv.setVisibility(View.INVISIBLE);
}
// Bind the icon
BubbleTextView icon = (BubbleTextView) content.getChildAt(1);
icon.applyFromApplicationInfo(item.appInfo);
break;
case EMPTY_VIEW_TYPE:
TextView emptyViewText = (TextView) holder.mContent.findViewById(R.id.empty_text);
emptyViewText.setText(mEmptySearchText);
break;
}
}
@Override
public int getItemCount() {
if (mApps.hasNoFilteredResults()) {
// For the empty view
return 1;
}
return mApps.getAdapterItems().size();
}
@Override
public int getItemViewType(int position) {
if (mApps.hasNoFilteredResults()) {
return EMPTY_VIEW_TYPE;
} else if (mApps.getAdapterItems().get(position).isSectionHeader) {
return SECTION_BREAK_VIEW_TYPE;
}
return ICON_VIEW_TYPE;
}
}
@@ -428,6 +428,13 @@ public class DeviceProfile {
} }
public boolean updateAppsViewNumCols(Resources res, int containerWidth) { public boolean updateAppsViewNumCols(Resources res, int containerWidth) {
if (AppsContainerView.GRID_HIDE_SECTION_HEADERS) {
if (appsViewNumCols != allAppsNumCols) {
appsViewNumCols = allAppsNumCols;
return true;
}
return false;
}
int appsViewLeftMarginPx = int appsViewLeftMarginPx =
res.getDimensionPixelSize(R.dimen.apps_grid_view_start_margin); res.getDimensionPixelSize(R.dimen.apps_grid_view_start_margin);
int availableAppsWidthPx = (containerWidth > 0) ? containerWidth : availableWidthPx; int availableAppsWidthPx = (containerWidth > 0) ? containerWidth : availableWidthPx;
+33 -18
View File
@@ -135,6 +135,9 @@ public class Launcher extends Activity
static final String TAG = "Launcher"; static final String TAG = "Launcher";
static final boolean LOGD = true; static final boolean LOGD = true;
// Temporary flag
static final boolean DISABLE_ALL_APPS_SEARCH_INTEGRATION = true;
static final boolean PROFILE_STARTUP = false; static final boolean PROFILE_STARTUP = false;
static final boolean DEBUG_WIDGETS = true; static final boolean DEBUG_WIDGETS = true;
static final boolean DEBUG_STRICT_MODE = false; static final boolean DEBUG_STRICT_MODE = false;
@@ -530,10 +533,12 @@ public class Launcher extends Activity
@Override @Override
public void dismissAllApps() { public void dismissAllApps() {
// Dismiss All Apps if we aren't already paused/invisible if (!DISABLE_ALL_APPS_SEARCH_INTEGRATION) {
if (!mPaused) { // Dismiss All Apps if we aren't already paused/invisible
showWorkspace(WorkspaceStateTransitionAnimation.SCROLL_TO_CURRENT_PAGE, true, if (!mPaused) {
null /* onCompleteRunnable */, false /* notifyLauncherCallbacks */); showWorkspace(WorkspaceStateTransitionAnimation.SCROLL_TO_CURRENT_PAGE, true,
null /* onCompleteRunnable */, false /* notifyLauncherCallbacks */);
}
} }
} }
}); });
@@ -1019,7 +1024,7 @@ public class Launcher extends Activity
mOnResumeState = State.NONE; mOnResumeState = State.NONE;
// Restore the apps state if we are in all apps // Restore the apps state if we are in all apps
if (mState == State.APPS) { if (!Launcher.DISABLE_ALL_APPS_SEARCH_INTEGRATION && mState == State.APPS) {
if (mLauncherCallbacks != null) { if (mLauncherCallbacks != null) {
mLauncherCallbacks.onAllAppsShown(); mLauncherCallbacks.onAllAppsShown();
} }
@@ -1453,8 +1458,8 @@ public class Launcher extends Activity
// Setup Apps // Setup Apps
mAppsView = (AppsContainerView) findViewById(R.id.apps_view); mAppsView = (AppsContainerView) findViewById(R.id.apps_view);
if (mLauncherCallbacks != null && mLauncherCallbacks.overrideAllAppsSearch()) { if (isAllAppsSearchOverridden()) {
mAppsView.hideSearchBar(); mAppsView.hideHeaderBar();
} }
// Setup AppsCustomize // Setup AppsCustomize
@@ -2877,15 +2882,22 @@ public class Launcher extends Activity
/** Updates the interaction state. */ /** Updates the interaction state. */
public void updateInteraction(Workspace.State fromState, Workspace.State toState) { public void updateInteraction(Workspace.State fromState, Workspace.State toState) {
// Only update the interacting state if we are transitioning to/from a view without an // Only update the interacting state if we are transitioning to/from a view with an
// overlay // overlay
boolean fromStateWithoutOverlay = fromState != Workspace.State.NORMAL && boolean fromStateWithOverlay;
fromState != Workspace.State.NORMAL_HIDDEN; boolean toStateWithOverlay;
boolean toStateWithoutOverlay = toState != Workspace.State.NORMAL && if (Launcher.DISABLE_ALL_APPS_SEARCH_INTEGRATION) {
toState != Workspace.State.NORMAL_HIDDEN; fromStateWithOverlay = fromState != Workspace.State.NORMAL;
if (toStateWithoutOverlay) { toStateWithOverlay = toState != Workspace.State.NORMAL;
} else {
fromStateWithOverlay = fromState != Workspace.State.NORMAL &&
fromState != Workspace.State.NORMAL_HIDDEN;
toStateWithOverlay = toState != Workspace.State.NORMAL &&
toState != Workspace.State.NORMAL_HIDDEN;
}
if (toStateWithOverlay) {
onInteractionBegin(); onInteractionBegin();
} else if (fromStateWithoutOverlay) { } else if (fromStateWithOverlay) {
onInteractionEnd(); onInteractionEnd();
} }
} }
@@ -3367,7 +3379,7 @@ public class Launcher extends Activity
.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); .sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
if (notifyLauncherCallbacks) { if (notifyLauncherCallbacks) {
// Dismiss all apps when the workspace is shown // Dismiss all apps when the workspace is shown
if (mLauncherCallbacks != null) { if (!Launcher.DISABLE_ALL_APPS_SEARCH_INTEGRATION && mLauncherCallbacks != null) {
mLauncherCallbacks.onAllAppsHidden(); mLauncherCallbacks.onAllAppsHidden();
} }
} }
@@ -3419,7 +3431,7 @@ public class Launcher extends Activity
if (toState == State.APPS) { if (toState == State.APPS) {
mStateTransitionAnimation.startAnimationToAllApps(animated); mStateTransitionAnimation.startAnimationToAllApps(animated);
if (mLauncherCallbacks != null) { if (!Launcher.DISABLE_ALL_APPS_SEARCH_INTEGRATION && mLauncherCallbacks != null) {
mLauncherCallbacks.onAllAppsShown(); mLauncherCallbacks.onAllAppsShown();
} }
} else { } else {
@@ -3472,7 +3484,7 @@ public class Launcher extends Activity
if (successfulDrop) { if (successfulDrop) {
// We need to trigger all apps hidden to notify search to update itself before the // We need to trigger all apps hidden to notify search to update itself before the
// delayed call to showWorkspace below // delayed call to showWorkspace below
if (mLauncherCallbacks != null) { if (!Launcher.DISABLE_ALL_APPS_SEARCH_INTEGRATION && mLauncherCallbacks != null) {
mLauncherCallbacks.onAllAppsHidden(); mLauncherCallbacks.onAllAppsHidden();
} }
} }
@@ -4454,9 +4466,12 @@ public class Launcher extends Activity
/** /**
* Returns whether the launcher callbacks overrides search in all apps. * Returns whether the launcher callbacks overrides search in all apps.
* @return
*/ */
@Thunk boolean isAllAppsSearchOverridden() { @Thunk boolean isAllAppsSearchOverridden() {
if (DISABLE_ALL_APPS_SEARCH_INTEGRATION) {
return false;
}
if (mLauncherCallbacks != null) { if (mLauncherCallbacks != null) {
return mLauncherCallbacks.overrideAllAppsSearch(); return mLauncherCallbacks.overrideAllAppsSearch();
} }
@@ -51,7 +51,7 @@ public class WidgetsContainerRecyclerView extends RecyclerView
mDeltaThreshold = getResources().getDisplayMetrics().density * SCROLL_DELTA_THRESHOLD; mDeltaThreshold = getResources().getDisplayMetrics().density * SCROLL_DELTA_THRESHOLD;
ScrollListener listener = new ScrollListener(); ScrollListener listener = new ScrollListener();
addOnScrollListener(listener); setOnScrollListener(listener);
} }
private class ScrollListener extends RecyclerView.OnScrollListener { private class ScrollListener extends RecyclerView.OnScrollListener {