Explorations in dense grids for all apps.
- Adds sticky section headers - Removing AppsListAdapter - Adding search bar field - Subtitle filtering Bug: 20222023 Change-Id: I1eaef701b5d68f475615f09d86561eacc91c937f
|
After Width: | Height: | Size: 190 B |
|
After Width: | Height: | Size: 743 B |
|
After Width: | Height: | Size: 151 B |
|
After Width: | Height: | Size: 497 B |
|
After Width: | Height: | Size: 234 B |
|
After Width: | Height: | Size: 972 B |
|
After Width: | Height: | Size: 308 B |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 359 B |
|
After Width: | Height: | Size: 1.9 KiB |
@@ -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"
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||