summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWinson Chung <winsonc@google.com>2015-05-12 19:05:30 -0700
committerWinson Chung <winsonc@google.com>2015-05-13 09:10:31 -0700
commit208ed75cfdb02e571273d73d056d8ed7f6f756eb (patch)
tree7996096c9fd3ad65a58f1ff6b254436195421dd2
parent11509ad61afb7424ce83057b0d2a4b09f853651f (diff)
downloadandroid_packages_apps_Trebuchet-208ed75cfdb02e571273d73d056d8ed7f6f756eb.tar.gz
android_packages_apps_Trebuchet-208ed75cfdb02e571273d73d056d8ed7f6f756eb.tar.bz2
android_packages_apps_Trebuchet-208ed75cfdb02e571273d73d056d8ed7f6f756eb.zip
Pulling out predictions into another row view.
Change-Id: Iba0d74457a1314cf0c00a88f9b07df049334e542
-rw-r--r--res/layout/all_apps_button.xml2
-rw-r--r--res/layout/application.xml2
-rw-r--r--res/layout/apps_grid_icon_view.xml (renamed from res/layout/apps_grid_row_icon_view.xml)2
-rw-r--r--res/layout/apps_list_view.xml43
-rw-r--r--res/layout/apps_prediction_bar_icon_view.xml29
-rw-r--r--res/layout/folder_application.xml2
-rw-r--r--res/layout/folder_icon.xml2
-rw-r--r--res/values/dimens.xml1
-rw-r--r--res/values/styles.xml10
-rw-r--r--src/com/android/launcher3/AlphabeticalAppsList.java160
-rw-r--r--src/com/android/launcher3/AppsContainerRecyclerView.java65
-rw-r--r--src/com/android/launcher3/AppsContainerView.java117
-rw-r--r--src/com/android/launcher3/AppsGridAdapter.java102
-rw-r--r--src/com/android/launcher3/DeviceProfile.java12
14 files changed, 385 insertions, 164 deletions
diff --git a/res/layout/all_apps_button.xml b/res/layout/all_apps_button.xml
index 9d6d82bb2..68cc10932 100644
--- a/res/layout/all_apps_button.xml
+++ b/res/layout/all_apps_button.xml
@@ -15,5 +15,5 @@
-->
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
- style="@style/WorkspaceIcon"
+ style="@style/Icon"
android:focusable="true" />
diff --git a/res/layout/application.xml b/res/layout/application.xml
index c21dea070..831cee5b0 100644
--- a/res/layout/application.xml
+++ b/res/layout/application.xml
@@ -15,5 +15,5 @@
-->
<com.android.launcher3.BubbleTextView xmlns:android="http://schemas.android.com/apk/res/android"
- style="@style/WorkspaceIcon"
+ style="@style/Icon"
android:focusable="true" />
diff --git a/res/layout/apps_grid_row_icon_view.xml b/res/layout/apps_grid_icon_view.xml
index acb3da334..67d7d50e7 100644
--- a/res/layout/apps_grid_row_icon_view.xml
+++ b/res/layout/apps_grid_icon_view.xml
@@ -16,7 +16,7 @@
<com.android.launcher3.BubbleTextView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:launcher="http://schemas.android.com/apk/res-auto"
- style="@style/WorkspaceIcon.AppsCustomize"
+ style="@style/Icon.AllApps"
android:id="@+id/icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
diff --git a/res/layout/apps_list_view.xml b/res/layout/apps_list_view.xml
index ddcb639b8..ef20323c1 100644
--- a/res/layout/apps_list_view.xml
+++ b/res/layout/apps_list_view.xml
@@ -13,20 +13,37 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<LinearLayout
+<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/apps_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:orientation="vertical"
android:elevation="15dp"
android:visibility="gone"
android:focusableInTouchMode="true">
+ <com.android.launcher3.AppsContainerRecyclerView
+ android:id="@+id/apps_list_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_marginTop="@dimen/apps_search_bar_height"
+ android:layout_gravity="center_horizontal|top"
+ android:clipToPadding="false"
+ android:focusable="true"
+ android:descendantFocusability="afterDescendants" />
+ <LinearLayout
+ android:id="@+id/prediction_bar"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/apps_search_bar_height"
+ android:orientation="horizontal"
+ android:visibility="invisible">
+ </LinearLayout>
+
+ <!-- We always want the search bar on top, so it goes last. -->
<FrameLayout
android:id="@+id/header"
android:layout_width="match_parent"
android:layout_height="@dimen/apps_search_bar_height"
- android:orientation="horizontal"
android:background="@drawable/apps_search_bg">
<LinearLayout
android:id="@+id/app_search_container"
@@ -40,8 +57,8 @@
android:layout_height="wrap_content"
android:layout_gravity="start|center_vertical"
android:layout_marginStart="4dp"
- android:paddingTop="12dp"
- android:paddingBottom="12dp"
+ android:paddingTop="13dp"
+ android:paddingBottom="13dp"
android:contentDescription="@string/all_apps_button_label"
android:src="@drawable/ic_arrow_back_grey" />
<com.android.launcher3.AppsContainerSearchEditTextView
@@ -69,19 +86,9 @@
android:layout_height="wrap_content"
android:layout_gravity="end|center_vertical"
android:layout_marginEnd="6dp"
- android:paddingTop="12dp"
- android:paddingBottom="12dp"
+ android:paddingTop="13dp"
+ android:paddingBottom="13dp"
android:contentDescription="@string/apps_view_search_bar_hint"
android:src="@drawable/ic_search_grey" />
</FrameLayout>
- <com.android.launcher3.AppsContainerRecyclerView
- android:id="@+id/apps_list_view"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_gravity="center"
- android:paddingTop="12dp"
- android:paddingBottom="12dp"
- android:clipToPadding="false"
- android:focusable="true"
- android:descendantFocusability="afterDescendants" />
-</LinearLayout> \ No newline at end of file
+</FrameLayout> \ No newline at end of file
diff --git a/res/layout/apps_prediction_bar_icon_view.xml b/res/layout/apps_prediction_bar_icon_view.xml
new file mode 100644
index 000000000..4a6f1574b
--- /dev/null
+++ b/res/layout/apps_prediction_bar_icon_view.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<com.android.launcher3.BubbleTextView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:launcher="http://schemas.android.com/apk/res-auto"
+ style="@style/Icon.AllApps"
+ android:id="@+id/icon"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_gravity="center"
+ android:layout_weight="1"
+ android:focusable="true"
+ android:background="@drawable/focusable_view_bg"
+ launcher:deferShadowGeneration="true"
+ launcher:iconDisplay="all_apps" />
+
diff --git a/res/layout/folder_application.xml b/res/layout/folder_application.xml
index b48b61331..4d003313e 100644
--- a/res/layout/folder_application.xml
+++ b/res/layout/folder_application.xml
@@ -15,5 +15,5 @@
-->
<com.android.launcher3.BubbleTextView xmlns:android="http://schemas.android.com/apk/res/android"
- style="@style/WorkspaceIcon.Folder"
+ style="@style/Icon.Folder"
android:focusable="true" />
diff --git a/res/layout/folder_icon.xml b/res/layout/folder_icon.xml
index fd45d7685..d9a7671af 100644
--- a/res/layout/folder_icon.xml
+++ b/res/layout/folder_icon.xml
@@ -28,7 +28,7 @@
android:antialias="true"
android:src="@drawable/portal_ring_inner_holo"/>
<com.android.launcher3.BubbleTextView
- style="@style/WorkspaceIcon"
+ style="@style/Icon"
android:id="@+id/folder_icon_name"
android:layout_gravity="top"
android:layout_width="match_parent"
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 90d709887..1a9254575 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -60,6 +60,7 @@
<dimen name="apps_view_fast_scroll_text_size">48dp</dimen>
<dimen name="apps_search_bar_height">52dp</dimen>
<dimen name="apps_icon_top_bottom_padding">8dp</dimen>
+ <dimen name="apps_prediction_icon_top_bottom_padding">12dp</dimen>
<!-- Note: This needs to match the fixed insets for the search box. -->
<dimen name="container_fixed_bounds_inset">8dp</dimen>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 16d4cbeed..78cc0837f 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -19,7 +19,7 @@
<resources>
- <style name="WorkspaceIcon">
+ <style name="Icon">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">match_parent</item>
<item name="android:layout_gravity">center</item>
@@ -32,11 +32,7 @@
<item name="android:fontFamily">sans-serif-condensed</item>
</style>
- <style name="WorkspaceIcon.Portrait"></style>
-
- <style name="WorkspaceIcon.Landscape"></style>
-
- <style name="WorkspaceIcon.AppsCustomize">
+ <style name="Icon.AllApps">
<item name="android:background">@null</item>
<item name="android:textColor">@color/quantum_panel_text_color</item>
<item name="android:drawablePadding">@dimen/dynamic_grid_icon_drawable_padding</item>
@@ -44,7 +40,7 @@
<item name="customShadows">false</item>
</style>
- <style name="WorkspaceIcon.Folder">
+ <style name="Icon.Folder">
<item name="android:background">@null</item>
<item name="android:textColor">@color/quantum_panel_text_color</item>
<item name="android:shadowRadius">0</item>
diff --git a/src/com/android/launcher3/AlphabeticalAppsList.java b/src/com/android/launcher3/AlphabeticalAppsList.java
index dc75637e5..eff7b0625 100644
--- a/src/com/android/launcher3/AlphabeticalAppsList.java
+++ b/src/com/android/launcher3/AlphabeticalAppsList.java
@@ -151,11 +151,13 @@ public class AlphabeticalAppsList {
* Info about a particular adapter item (can be either section or app)
*/
public static class AdapterItem {
- /** Section & App properties */
+ /** Common properties */
// The index of this adapter item in the list
public int position;
- // Whether or not the item at this adapter position is a section or not
- public boolean isSectionHeader;
+ // The type of this item
+ public int viewType;
+
+ /** Section & App properties */
// The section for this item
public SectionInfo sectionInfo;
@@ -169,30 +171,33 @@ public class AlphabeticalAppsList {
public AppInfo appInfo = null;
// The index of this app not including sections
public int appIndex = -1;
- // Whether or not this is a predicted app
- public boolean isPredictedApp;
public static AdapterItem asSectionBreak(int pos, SectionInfo section) {
AdapterItem item = new AdapterItem();
+ item.viewType = AppsGridAdapter.SECTION_BREAK_VIEW_TYPE;
item.position = pos;
- item.isSectionHeader = true;
item.sectionInfo = section;
section.sectionBreakItem = item;
return item;
}
+ public static AdapterItem asPredictionBarSpacer(int pos) {
+ AdapterItem item = new AdapterItem();
+ item.viewType = AppsGridAdapter.PREDICTION_BAR_SPACER_TYPE;
+ item.position = pos;
+ return item;
+ }
+
public static AdapterItem asApp(int pos, SectionInfo section, String sectionName,
- int sectionAppIndex, AppInfo appInfo, int appIndex,
- boolean isPredictedApp) {
+ int sectionAppIndex, AppInfo appInfo, int appIndex) {
AdapterItem item = new AdapterItem();
+ item.viewType = AppsGridAdapter.ICON_VIEW_TYPE;
item.position = pos;
- item.isSectionHeader = false;
item.sectionInfo = section;
item.sectionName = sectionName;
item.sectionAppIndex = sectionAppIndex;
item.appInfo = appInfo;
item.appIndex = appIndex;
- item.isPredictedApp = isPredictedApp;
return item;
}
}
@@ -205,6 +210,13 @@ public class AlphabeticalAppsList {
}
/**
+ * A callback to notify of changes to the filter.
+ */
+ public interface FilterChangedCallback {
+ void onFilterChanged();
+ }
+
+ /**
* Common interface for different merging strategies.
*/
private interface MergeAlgorithm {
@@ -260,28 +272,31 @@ public class AlphabeticalAppsList {
private List<AdapterItem> mSectionedFilteredApps = new ArrayList<>();
private List<SectionInfo> mSections = new ArrayList<>();
private List<FastScrollSectionInfo> mFastScrollerSections = new ArrayList<>();
- private List<ComponentName> mPredictedApps = new ArrayList<>();
+ private List<ComponentName> mPredictedAppComponents = new ArrayList<>();
+ private List<AppInfo> mPredictedApps = new ArrayList<>();
private HashMap<CharSequence, String> mCachedSectionNames = new HashMap<>();
private RecyclerView.Adapter mAdapter;
private Filter mFilter;
private AlphabeticIndexCompat mIndexer;
private AppNameComparator mAppNameComparator;
private MergeAlgorithm mMergeAlgorithm;
+ private FilterChangedCallback mFilterChangedCallback;
private int mNumAppsPerRow;
+ private int mNumPredictedAppsPerRow;
- public AlphabeticalAppsList(Context context, int numAppsPerRow) {
+ public AlphabeticalAppsList(Context context, FilterChangedCallback cb, int numAppsPerRow,
+ int numPredictedAppsPerRow) {
mContext = context;
mIndexer = new AlphabeticIndexCompat(context);
mAppNameComparator = new AppNameComparator(context);
- setNumAppsPerRow(numAppsPerRow);
+ mFilterChangedCallback = cb;
+ setNumAppsPerRow(numAppsPerRow, numPredictedAppsPerRow);
}
/**
* Sets the number of apps per row. Used only for AppsContainerView.SECTIONED_GRID_COALESCED.
*/
- public void setNumAppsPerRow(int numAppsPerRow) {
- mNumAppsPerRow = numAppsPerRow;
-
+ public void setNumAppsPerRow(int numAppsPerRow, int numPredictedAppsPerRow) {
// Update the merge algorithm
DeviceProfile grid = LauncherAppState.getInstance().getDynamicGrid().getDeviceProfile();
if (grid.isPhone()) {
@@ -291,6 +306,9 @@ public class AlphabeticalAppsList {
mMergeAlgorithm = new TabletMergeAlgorithm();
}
+ mNumAppsPerRow = numAppsPerRow;
+ mNumPredictedAppsPerRow = numPredictedAppsPerRow;
+
onAppsUpdated();
}
@@ -351,6 +369,9 @@ public class AlphabeticalAppsList {
mFilter = f;
onAppsUpdated();
mAdapter.notifyDataSetChanged();
+ if (mFilterChangedCallback != null){
+ mFilterChangedCallback.onFilterChanged();
+ }
}
}
@@ -359,13 +380,20 @@ public class AlphabeticalAppsList {
* of applications, we should merge the results only in onAppsUpdated() which is idempotent.
*/
public void setPredictedApps(List<ComponentName> apps) {
- mPredictedApps.clear();
- mPredictedApps.addAll(apps);
+ mPredictedAppComponents.clear();
+ mPredictedAppComponents.addAll(apps);
onAppsUpdated();
mAdapter.notifyDataSetChanged();
}
/**
+ * Returns the current set of predicted apps.
+ */
+ public List<AppInfo> getPredictedApps() {
+ return mPredictedApps;
+ }
+
+ /**
* Sets the current set of apps.
*/
public void setApps(List<AppInfo> apps) {
@@ -450,6 +478,42 @@ public class AlphabeticalAppsList {
// Sort the list of apps
Collections.sort(mApps, mAppNameComparator.getAppInfoComparator());
+ // Prepare to update the list of sections, filtered apps, etc.
+ mFilteredApps.clear();
+ mSections.clear();
+ mSectionedFilteredApps.clear();
+ mFastScrollerSections.clear();
+ SectionInfo lastSectionInfo = null;
+ String lastSectionName = null;
+ FastScrollSectionInfo lastFastScrollerSectionInfo = null;
+ int position = 0;
+ int appIndex = 0;
+ List<AppInfo> allApps = new ArrayList<>();
+
+
+ // Process the predicted app components
+ mPredictedApps.clear();
+ if (mPredictedAppComponents != null && !mPredictedAppComponents.isEmpty() && !hasFilter()) {
+ for (ComponentName cn : mPredictedAppComponents) {
+ for (AppInfo info : mApps) {
+ if (cn.equals(info.componentName)) {
+ mPredictedApps.add(info);
+ break;
+ }
+ }
+ // Stop at the number of predicted apps
+ if (mPredictedApps.size() == mNumPredictedAppsPerRow) {
+ break;
+ }
+ }
+
+ if (!mPredictedApps.isEmpty()) {
+ // Create a new spacer for the prediction bar
+ AdapterItem sectionItem = AdapterItem.asPredictionBarSpacer(position++);
+ mSectionedFilteredApps.add(sectionItem);
+ }
+ }
+
// As a special case for some languages (currently only Simplified Chinese), we may need to
// coalesce sections
Locale curLocale = mContext.getResources().getConfiguration().locale;
@@ -475,6 +539,11 @@ public class AlphabeticalAppsList {
}
sectionApps.add(info);
}
+
+ // Add it to the list
+ for (Map.Entry<String, ArrayList<AppInfo>> entry : sectionMap.entrySet()) {
+ allApps.addAll(entry.getValue());
+ }
} else {
// Just compute the section headers for use below
for (AppInfo info : mApps) {
@@ -485,44 +554,7 @@ public class AlphabeticalAppsList {
mCachedSectionNames.put(info.title, sectionName);
}
}
- }
-
- // Prepare to update the list of sections, filtered apps, etc.
- mFilteredApps.clear();
- mSections.clear();
- mSectionedFilteredApps.clear();
- mFastScrollerSections.clear();
- SectionInfo lastSectionInfo = null;
- String lastSectionName = null;
- FastScrollSectionInfo lastFastScrollerSectionInfo = null;
- int position = 0;
- int appIndex = 0;
- List<AppInfo> allApps = new ArrayList<>();
-
- // Add the predicted apps to the combined list
- int numPredictedApps = 0;
- if (mPredictedApps != null && !mPredictedApps.isEmpty() && !hasFilter()) {
- for (ComponentName cn : mPredictedApps) {
- for (AppInfo info : mApps) {
- if (cn.equals(info.componentName)) {
- allApps.add(info);
- numPredictedApps++;
- break;
- }
- }
- // Stop at the number of predicted apps
- if (numPredictedApps == mNumAppsPerRow) {
- break;
- }
- }
- }
-
- // Add all the other apps to the combined list
- if (localeRequiresSectionSorting) {
- for (Map.Entry<String, ArrayList<AppInfo>> entry : sectionMap.entrySet()) {
- allApps.addAll(entry.getValue());
- }
- } else {
+ // Add it to the list
allApps.addAll(mApps);
}
@@ -530,10 +562,9 @@ public class AlphabeticalAppsList {
// ordered set of sections
int numApps = allApps.size();
for (int i = 0; i < numApps; i++) {
- boolean isPredictedApp = i < numPredictedApps;
AppInfo info = allApps.get(i);
// The section name was computed above so this should be find
- String sectionName = isPredictedApp ? "" : mCachedSectionNames.get(info.title);
+ String sectionName = mCachedSectionNames.get(info.title);
// Check if we want to retain this app
if (mFilter != null && !mFilter.retainApp(info, sectionName)) {
@@ -541,8 +572,7 @@ public class AlphabeticalAppsList {
}
// Create a new section if the section names do not match
- if (lastSectionInfo == null ||
- (!isPredictedApp && !sectionName.equals(lastSectionName))) {
+ if (lastSectionInfo == null || !sectionName.equals(lastSectionName)) {
lastSectionName = sectionName;
lastSectionInfo = new SectionInfo();
lastFastScrollerSectionInfo = new FastScrollSectionInfo(sectionName,
@@ -559,7 +589,7 @@ public class AlphabeticalAppsList {
// Create an app item
AdapterItem appItem = AdapterItem.asApp(position++, lastSectionInfo, sectionName,
- lastSectionInfo.numApps++, info, appIndex++, isPredictedApp);
+ lastSectionInfo.numApps++, info, appIndex++);
if (lastSectionInfo.firstAppItem == null) {
lastSectionInfo.firstAppItem = appItem;
lastFastScrollerSectionInfo.appItem = appItem;
@@ -568,6 +598,14 @@ public class AlphabeticalAppsList {
mFilteredApps.add(info);
}
+ // Merge multiple sections together as requested by the merge strategy for this device
+ mergeSections();
+ }
+
+ /**
+ * Merges multiple sections to reduce visual raggedness.
+ */
+ private void mergeSections() {
// Go through each section and try and merge some of the sections
if (AppsContainerView.GRID_MERGE_SECTIONS && !hasFilter()) {
int sectionAppCount = 0;
diff --git a/src/com/android/launcher3/AppsContainerRecyclerView.java b/src/com/android/launcher3/AppsContainerRecyclerView.java
index e918bc2ee..3952923af 100644
--- a/src/com/android/launcher3/AppsContainerRecyclerView.java
+++ b/src/com/android/launcher3/AppsContainerRecyclerView.java
@@ -45,8 +45,6 @@ public class AppsContainerRecyclerView extends BaseContainerRecyclerView {
* scroller.
*/
private static class ScrollPositionState {
- // The index of the first app in the row (Note that is this not the position)
- int rowFirstAppIndex;
// The index of the first visible row
int rowIndex;
// The offset of the first visible row
@@ -59,6 +57,7 @@ public class AppsContainerRecyclerView extends BaseContainerRecyclerView {
private AlphabeticalAppsList mApps;
private int mNumAppsPerRow;
+ private int mNumPredictedAppsPerRow;
private Drawable mScrollbar;
private Drawable mFastScrollerBg;
@@ -68,6 +67,7 @@ public class AppsContainerRecyclerView extends BaseContainerRecyclerView {
private Paint mFastScrollTextPaint;
private Rect mFastScrollTextBounds = new Rect();
private float mFastScrollAlpha;
+ private int mPredictionBarHeight;
private int mDownX;
private int mDownY;
private int mLastX;
@@ -123,8 +123,9 @@ public class AppsContainerRecyclerView extends BaseContainerRecyclerView {
/**
* Sets the number of apps per row in this recycler view.
*/
- public void setNumAppsPerRow(int rowSize) {
- mNumAppsPerRow = rowSize;
+ public void setNumAppsPerRow(int numAppsPerRow, int numPredictedAppsPerRow) {
+ mNumAppsPerRow = numAppsPerRow;
+ mNumPredictedAppsPerRow = numPredictedAppsPerRow;
}
@Override
@@ -134,6 +135,13 @@ public class AppsContainerRecyclerView extends BaseContainerRecyclerView {
}
/**
+ * Sets the prediction bar height.
+ */
+ public void setPredictionBarHeight(int height) {
+ mPredictionBarHeight = height;
+ }
+
+ /**
* Sets the fast scroller alpha.
*/
public void setFastScrollerAlpha(float alpha) {
@@ -330,6 +338,26 @@ public class AppsContainerRecyclerView extends BaseContainerRecyclerView {
return "";
}
+ // Stop the scroller if it is scrolling
+ LinearLayoutManager layoutManager = (LinearLayoutManager) getLayoutManager();
+ stopScroll();
+
+ // If there is a prediction bar, then capture the appropriate area for the prediction bar
+ float predictionBarFraction = 0f;
+ if (mPredictionBarHeight > 0) {
+ predictionBarFraction = (float) mNumPredictedAppsPerRow / mApps.getSize();
+ if (touchFraction <= predictionBarFraction) {
+ // Scroll to the top of the view, where the prediction bar is
+ layoutManager.scrollToPositionWithOffset(0, 0);
+ updateScrollY(0);
+ return "";
+ }
+ }
+
+ // Since the app ranges are from 0..1, we need to map the touch fraction back to 0..1 from
+ // predictionBarFraction..1
+ touchFraction = (touchFraction - predictionBarFraction) *
+ (1f / (1f - predictionBarFraction));
AlphabeticalAppsList.FastScrollSectionInfo lastScrollSection = fastScrollSections.get(0);
for (int i = 1; i < fastScrollSections.size(); i++) {
AlphabeticalAppsList.FastScrollSectionInfo scrollSection = fastScrollSections.get(i);
@@ -340,21 +368,19 @@ public class AppsContainerRecyclerView extends BaseContainerRecyclerView {
lastScrollSection = scrollSection;
}
- // 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.
- LinearLayoutManager layoutManager = (LinearLayoutManager) getLayoutManager();
- stopScroll();
- layoutManager.scrollToPositionWithOffset(lastScrollSection.appItem.position, 0);
-
- // We need to workaround the RecyclerView to get the right scroll position after scrolling
+ // We need to workaround the RecyclerView to get the right scroll position
List<AlphabeticalAppsList.AdapterItem> items = mApps.getAdapterItems();
getCurScrollState(mScrollPosState, items);
if (mScrollPosState.rowIndex != -1) {
- int rowIndex = findRowForAppIndex(mScrollPosState.rowFirstAppIndex);
- int y = (rowIndex * mScrollPosState.rowHeight) - mScrollPosState.rowTopOffset;
- updateScrollY(y);
+ int scrollY = getPaddingTop() + (mScrollPosState.rowIndex * mScrollPosState.rowHeight) +
+ mPredictionBarHeight - mScrollPosState.rowTopOffset;
+ updateScrollY(scrollY);
}
+ // Scroll to the view at the position, anchored at the top of the screen. We call the scroll
+ // method on the LayoutManager directly since it is not exposed by RecyclerView.
+ layoutManager.scrollToPositionWithOffset(lastScrollSection.appItem.position, 0);
+
return lastScrollSection.sectionName;
}
@@ -377,10 +403,9 @@ public class AppsContainerRecyclerView extends BaseContainerRecyclerView {
LAYOUT_DIRECTION_RTL);
int rowCount = getNumRows();
getCurScrollState(mScrollPosState, items);
-
if (mScrollPosState.rowIndex != -1) {
int height = getHeight() - getPaddingTop() - getPaddingBottom();
- int totalScrollHeight = rowCount * mScrollPosState.rowHeight;
+ int totalScrollHeight = rowCount * mScrollPosState.rowHeight + mPredictionBarHeight;
if (totalScrollHeight > height) {
int scrollbarHeight = Math.max(mScrollbarMinHeight,
(int) (height / ((float) totalScrollHeight / height)));
@@ -396,8 +421,8 @@ public class AppsContainerRecyclerView extends BaseContainerRecyclerView {
// that the user has already scrolled and then map that to the scroll bar bounds
int availableY = totalScrollHeight - height;
int availableScrollY = height - scrollbarHeight;
- y = (mScrollPosState.rowIndex * mScrollPosState.rowHeight) -
- mScrollPosState.rowTopOffset;
+ y = (mScrollPosState.rowIndex * mScrollPosState.rowHeight) + mPredictionBarHeight
+ - mScrollPosState.rowTopOffset;
y = getPaddingTop() +
(int) (((float) (getPaddingTop() + y) / availableY) * availableScrollY);
@@ -444,7 +469,6 @@ public class AppsContainerRecyclerView extends BaseContainerRecyclerView {
*/
private void getCurScrollState(ScrollPositionState stateOut,
List<AlphabeticalAppsList.AdapterItem> items) {
- stateOut.rowFirstAppIndex = -1;
stateOut.rowIndex = -1;
stateOut.rowTopOffset = -1;
stateOut.rowHeight = -1;
@@ -454,8 +478,7 @@ public class AppsContainerRecyclerView extends BaseContainerRecyclerView {
int position = getChildPosition(child);
if (position != NO_POSITION) {
AlphabeticalAppsList.AdapterItem item = items.get(position);
- if (!item.isSectionHeader) {
- stateOut.rowFirstAppIndex = item.appIndex;
+ if (item.viewType == AppsGridAdapter.ICON_VIEW_TYPE) {
stateOut.rowIndex = findRowForAppIndex(item.appIndex);
stateOut.rowTopOffset = getLayoutManager().getDecoratedTop(child);
stateOut.rowHeight = child.getHeight();
diff --git a/src/com/android/launcher3/AppsContainerView.java b/src/com/android/launcher3/AppsContainerView.java
index 5dac9f1e8..21dd7cbc4 100644
--- a/src/com/android/launcher3/AppsContainerView.java
+++ b/src/com/android/launcher3/AppsContainerView.java
@@ -26,12 +26,14 @@ import android.text.Editable;
import android.text.TextWatcher;
import android.util.AttributeSet;
import android.view.KeyEvent;
+import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
+import android.view.ViewGroup;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
-import android.widget.EditText;
+import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.android.launcher3.util.Thunk;
@@ -44,11 +46,11 @@ import java.util.regex.Pattern;
* The all apps view container.
*/
public class AppsContainerView extends BaseContainerView implements DragSource, Insettable,
- TextWatcher, TextView.OnEditorActionListener, LauncherTransitionable, View.OnTouchListener,
- View.OnClickListener, View.OnLongClickListener {
+ TextWatcher, TextView.OnEditorActionListener, LauncherTransitionable,
+ AlphabeticalAppsList.FilterChangedCallback, AppsGridAdapter.PredictionBarSpacerCallbacks,
+ View.OnTouchListener, View.OnClickListener, View.OnLongClickListener {
public static final boolean GRID_MERGE_SECTIONS = true;
- public static final boolean GRID_HIDE_SECTION_HEADERS = false;
private static final boolean ALLOW_SINGLE_APP_LAUNCH = true;
private static final boolean DYNAMIC_HEADER_ELEVATION = true;
@@ -64,12 +66,14 @@ public class AppsContainerView extends BaseContainerView implements DragSource,
@Thunk Launcher mLauncher;
@Thunk AlphabeticalAppsList mApps;
+ private LayoutInflater mLayoutInflater;
private AppsGridAdapter mAdapter;
private RecyclerView.LayoutManager mLayoutManager;
private RecyclerView.ItemDecoration mItemDecoration;
- private LinearLayout mContentView;
+ private FrameLayout mContentView;
@Thunk AppsContainerRecyclerView mAppsRecyclerView;
+ private ViewGroup mPredictionBarView;
private View mHeaderView;
private View mSearchBarContainerView;
private View mSearchButtonView;
@@ -77,11 +81,13 @@ public class AppsContainerView extends BaseContainerView implements DragSource,
private AppsContainerSearchEditTextView mSearchBarEditView;
private int mNumAppsPerRow;
+ private int mNumPredictedAppsPerRow;
private Point mLastTouchDownPos = new Point(-1, -1);
private Point mLastTouchPos = new Point();
private int mContentMarginStart;
// Normal container insets
private int mContainerInset;
+ private int mPredictionBarHeight;
// RecyclerView scroll position
@Thunk int mRecyclerViewScrollY;
@@ -101,12 +107,17 @@ public class AppsContainerView extends BaseContainerView implements DragSource,
mContainerInset = context.getResources().getDimensionPixelSize(
R.dimen.apps_container_inset);
+ mPredictionBarHeight = grid.allAppsCellHeightPx +
+ 2 * res.getDimensionPixelSize(R.dimen.apps_prediction_icon_top_bottom_padding);
mLauncher = (Launcher) context;
+ mLayoutInflater = LayoutInflater.from(context);
mNumAppsPerRow = grid.appsViewNumCols;
- mApps = new AlphabeticalAppsList(context, mNumAppsPerRow);
- mAdapter = new AppsGridAdapter(context, mApps, mNumAppsPerRow, this, mLauncher, this);
+ mNumPredictedAppsPerRow = grid.appsViewNumPredictiveCols;
+ mApps = new AlphabeticalAppsList(context, this, mNumAppsPerRow, mNumPredictedAppsPerRow);
+ mAdapter = new AppsGridAdapter(context, mApps, mNumAppsPerRow, this, this, mLauncher, this);
mAdapter.setEmptySearchText(res.getString(R.string.loading_apps_message));
mAdapter.setNumAppsPerRow(mNumAppsPerRow);
+ mAdapter.setPredictionRowHeight(mPredictionBarHeight);
mLayoutManager = mAdapter.getLayoutManager();
mItemDecoration = mAdapter.getItemDecoration();
mContentMarginStart = mAdapter.getContentMarginStart();
@@ -186,7 +197,7 @@ public class AppsContainerView extends BaseContainerView implements DragSource,
// 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
- mContentView = (LinearLayout) findViewById(R.id.apps_list);
+ mContentView = (FrameLayout) findViewById(R.id.apps_list);
mContentView.setOnFocusChangeListener(new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
@@ -195,12 +206,20 @@ public class AppsContainerView extends BaseContainerView implements DragSource,
}
}
});
+
+ // Fix the header view elevation if not dynamically calculating it
mHeaderView = findViewById(R.id.header);
mHeaderView.setOnClickListener(this);
if (Utilities.isLmpOrAbove() && !DYNAMIC_HEADER_ELEVATION) {
mHeaderView.setElevation(DynamicGrid.pxFromDp(HEADER_ELEVATION_DP,
getContext().getResources().getDisplayMetrics()));
}
+
+ // Fix the prediction bar size
+ mPredictionBarView = (ViewGroup) findViewById(R.id.prediction_bar);
+ FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mPredictionBarView.getLayoutParams();
+ lp.height = mPredictionBarHeight;
+
mSearchButtonView = mHeaderView.findViewById(R.id.search_button);
mSearchBarContainerView = findViewById(R.id.app_search_container);
mDismissSearchButtonView = mSearchBarContainerView.findViewById(R.id.dismiss_search_button);
@@ -227,7 +246,8 @@ public class AppsContainerView extends BaseContainerView implements DragSource,
}
mAppsRecyclerView = (AppsContainerRecyclerView) findViewById(R.id.apps_list_view);
mAppsRecyclerView.setApps(mApps);
- mAppsRecyclerView.setNumAppsPerRow(mNumAppsPerRow);
+ mAppsRecyclerView.setNumAppsPerRow(mNumAppsPerRow, mNumPredictedAppsPerRow);
+ mAppsRecyclerView.setPredictionBarHeight(mPredictionBarHeight);
mAppsRecyclerView.setLayoutManager(mLayoutManager);
mAppsRecyclerView.setAdapter(mAdapter);
mAppsRecyclerView.setHasFixedSize(true);
@@ -247,15 +267,52 @@ public class AppsContainerView extends BaseContainerView implements DragSource,
}
@Override
+ public void onBindPredictionBar() {
+ if (!updatePredictionBarVisibility()) {
+ return;
+ }
+
+ List<AppInfo> predictedApps = mApps.getPredictedApps();
+ int childCount = mPredictionBarView.getChildCount();
+ for (int i = 0; i < mNumPredictedAppsPerRow; i++) {
+ BubbleTextView icon;
+ if (i < childCount) {
+ // If a child at that index exists, then get that child
+ icon = (BubbleTextView) mPredictionBarView.getChildAt(i);
+ } else {
+ // Otherwise, inflate a new icon
+ icon = (BubbleTextView) mLayoutInflater.inflate(
+ R.layout.apps_prediction_bar_icon_view, mPredictionBarView, false);
+ icon.setOnTouchListener(this);
+ icon.setOnClickListener(mLauncher);
+ icon.setOnLongClickListener(this);
+ icon.setFocusable(true);
+ mPredictionBarView.addView(icon);
+ }
+
+ // Either apply the app info to the child, or hide the view
+ if (i < predictedApps.size()) {
+ if (icon.getVisibility() != View.VISIBLE) {
+ icon.setVisibility(View.VISIBLE);
+ }
+ icon.applyFromApplicationInfo(predictedApps.get(i));
+ } else {
+ icon.setVisibility(View.INVISIBLE);
+ }
+ }
+ }
+
+ @Override
protected void onFixedBoundsUpdated() {
// Update the number of items in the grid
LauncherAppState app = LauncherAppState.getInstance();
DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
if (grid.updateAppsViewNumCols(getContext().getResources(), mFixedBounds.width())) {
mNumAppsPerRow = grid.appsViewNumCols;
- mAppsRecyclerView.setNumAppsPerRow(mNumAppsPerRow);
+ mNumPredictedAppsPerRow = grid.appsViewNumPredictiveCols;
+ mAppsRecyclerView.setNumAppsPerRow(mNumAppsPerRow, mNumPredictedAppsPerRow);
mAdapter.setNumAppsPerRow(mNumAppsPerRow);
- mApps.setNumAppsPerRow(mNumAppsPerRow);
+ mApps.setNumAppsPerRow(mNumAppsPerRow, mNumPredictedAppsPerRow);
}
}
@@ -297,10 +354,16 @@ public class AppsContainerView extends BaseContainerView implements DragSource,
// Update the header bar
if (hasSearchBar) {
- LinearLayout.LayoutParams lp =
- (LinearLayout.LayoutParams) mHeaderView.getLayoutParams();
+ FrameLayout.LayoutParams lp =
+ (FrameLayout.LayoutParams) mHeaderView.getLayoutParams();
lp.leftMargin = lp.rightMargin = inset;
+ mHeaderView.requestLayout();
}
+
+ FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mPredictionBarView.getLayoutParams();
+ lp.leftMargin = inset + mAppsRecyclerView.getScrollbarWidth();
+ lp.rightMargin = inset + mAppsRecyclerView.getScrollbarWidth();
+ mPredictionBarView.requestLayout();
}
/**
@@ -499,7 +562,7 @@ public class AppsContainerView extends BaseContainerView implements DragSource,
List<AlphabeticalAppsList.AdapterItem> items = mApps.getAdapterItems();
for (int i = 0; i < items.size(); i++) {
AlphabeticalAppsList.AdapterItem item = items.get(i);
- if (!item.isSectionHeader) {
+ if (item.viewType == AppsGridAdapter.ICON_VIEW_TYPE) {
mAppsRecyclerView.getChildAt(i).performClick();
getInputMethodManager().hideSoftInputFromWindow(getWindowToken(), 0);
return true;
@@ -510,6 +573,11 @@ public class AppsContainerView extends BaseContainerView implements DragSource,
}
@Override
+ public void onFilterChanged() {
+ updatePredictionBarVisibility();
+ }
+
+ @Override
public View getContent() {
return null;
}
@@ -554,6 +622,12 @@ public class AppsContainerView extends BaseContainerView implements DragSource,
mHeaderView.setElevation(newElevation);
}
}
+
+ // XXX: Optimize this, stop once we are out of bounds
+ if (mRecyclerViewScrollY < 0) {
+ new Throwable().printStackTrace();
+ }
+ mPredictionBarView.setTranslationY(-mRecyclerViewScrollY + mAppsRecyclerView.getPaddingTop());
}
/**
@@ -683,6 +757,21 @@ public class AppsContainerView extends BaseContainerView implements DragSource,
}
/**
+ * Updates the visibility of the prediction bar.
+ * @return whether the prediction bar is visible
+ */
+ private boolean updatePredictionBarVisibility() {
+ boolean showPredictionBar = !mApps.getPredictedApps().isEmpty() && (!mApps.hasFilter() ||
+ mSearchBarEditView.getEditableText().toString().isEmpty());
+ if (showPredictionBar) {
+ mPredictionBarView.setVisibility(View.VISIBLE);
+ } else if (!showPredictionBar) {
+ mPredictionBarView.setVisibility(View.INVISIBLE);
+ }
+ return showPredictionBar;
+ }
+
+ /**
* Returns an input method manager.
*/
private InputMethodManager getInputMethodManager() {
diff --git a/src/com/android/launcher3/AppsGridAdapter.java b/src/com/android/launcher3/AppsGridAdapter.java
index 4014e3804..dfbfa01da 100644
--- a/src/com/android/launcher3/AppsGridAdapter.java
+++ b/src/com/android/launcher3/AppsGridAdapter.java
@@ -6,6 +6,7 @@ import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PointF;
import android.graphics.Rect;
+import android.os.Handler;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
@@ -26,21 +27,31 @@ class AppsGridAdapter extends RecyclerView.Adapter<AppsGridAdapter.ViewHolder> {
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 ICON_VIEW_TYPE = 1;
- private static final int EMPTY_VIEW_TYPE = 2;
+ // A section break in the grid
+ public static final int SECTION_BREAK_VIEW_TYPE = 0;
+ // A normal icon
+ public static final int ICON_VIEW_TYPE = 1;
+ // The message shown when there are no filtered results
+ public static final int EMPTY_VIEW_TYPE = 2;
+ // The spacer used for the prediction bar
+ public static final int PREDICTION_BAR_SPACER_TYPE = 3;
+
+ /**
+ * Callback for when the prediction bar spacer is bound.
+ */
+ public interface PredictionBarSpacerCallbacks {
+ void onBindPredictionBar();
+ }
/**
* ViewHolder for each icon.
*/
public static class ViewHolder extends RecyclerView.ViewHolder {
public View mContent;
- public boolean mIsEmptyRow;
- public ViewHolder(View v, boolean isEmptyRow) {
+ public ViewHolder(View v) {
super(v);
mContent = v;
- mIsEmptyRow = isEmptyRow;
}
}
@@ -61,8 +72,8 @@ class AppsGridAdapter extends RecyclerView.Adapter<AppsGridAdapter.ViewHolder> {
return mAppsPerRow;
}
- if (mApps.getAdapterItems().get(position).isSectionHeader) {
- // Section break spans full width
+ if (mApps.getAdapterItems().get(position).viewType != AppsGridAdapter.ICON_VIEW_TYPE) {
+ // Both the section breaks and predictive bar span the full width
return mAppsPerRow;
} else {
return 1;
@@ -88,7 +99,7 @@ class AppsGridAdapter extends RecyclerView.Adapter<AppsGridAdapter.ViewHolder> {
DeviceProfile grid = LauncherAppState.getInstance().getDynamicGrid().getDeviceProfile();
List<AlphabeticalAppsList.AdapterItem> items = mApps.getAdapterItems();
- boolean hasDrawnPredictedAppDivider = false;
+ boolean hasDrawnPredictedAppsDivider = false;
int childCount = parent.getChildCount();
int lastSectionTop = 0;
int lastSectionHeight = 0;
@@ -99,13 +110,13 @@ class AppsGridAdapter extends RecyclerView.Adapter<AppsGridAdapter.ViewHolder> {
continue;
}
- if (shouldDrawItemDivider(holder, items) && !hasDrawnPredictedAppDivider) {
- // Draw the divider under the predicted app
+ if (shouldDrawItemDivider(holder, items) && !hasDrawnPredictedAppsDivider) {
+ // Draw the divider under the predicted apps
parent.getBackground().getPadding(mTmpBounds);
int top = child.getTop() + child.getHeight();
c.drawLine(mTmpBounds.left, top, parent.getWidth() - mTmpBounds.right, top,
mPredictedAppsDividerPaint);
- hasDrawnPredictedAppDivider = true;
+ hasDrawnPredictedAppsDivider = true;
} else if (grid.isPhone() && shouldDrawItemSection(holder, i, items)) {
// At this point, we only draw sections for each section break;
@@ -220,9 +231,10 @@ class AppsGridAdapter extends RecyclerView.Adapter<AppsGridAdapter.ViewHolder> {
/**
* Returns whether to draw the divider for a given child.
*/
- private boolean shouldDrawItemDivider(ViewHolder holder, List<AlphabeticalAppsList.AdapterItem> items) {
+ private boolean shouldDrawItemDivider(ViewHolder holder,
+ List<AlphabeticalAppsList.AdapterItem> items) {
int pos = holder.getPosition();
- return items.get(pos).isPredictedApp;
+ return items.get(pos).viewType == AppsGridAdapter.PREDICTION_BAR_SPACER_TYPE;
}
/**
@@ -233,31 +245,27 @@ class AppsGridAdapter extends RecyclerView.Adapter<AppsGridAdapter.ViewHolder> {
int pos = holder.getPosition();
AlphabeticalAppsList.AdapterItem item = items.get(pos);
- // Ensure it's not an empty row
- if (holder.mIsEmptyRow) {
- return false;
- }
- // Ensure this is not a section break
- if (item.isSectionHeader) {
- return false;
- }
- // Ensure this is not a predicted app
- if (item.isPredictedApp) {
+ // Ensure it's an icon
+ if (item.viewType != AppsGridAdapter.ICON_VIEW_TYPE) {
return false;
}
// Draw the section header for the first item in each section
- return (childIndex == 0) || (items.get(pos - 1).isSectionHeader && !item.isSectionHeader);
+ return (childIndex == 0) ||
+ (items.get(pos - 1).viewType == AppsGridAdapter.SECTION_BREAK_VIEW_TYPE);
}
}
+ private Handler mHandler;
private LayoutInflater mLayoutInflater;
@Thunk AlphabeticalAppsList mApps;
private GridLayoutManager mGridLayoutMgr;
private GridSpanSizer mGridSizer;
private GridItemDecoration mItemDecoration;
+ private PredictionBarSpacerCallbacks mPredictionBarCb;
private View.OnTouchListener mTouchListener;
private View.OnClickListener mIconClickListener;
private View.OnLongClickListener mIconLongClickListener;
+ @Thunk int mPredictionBarHeight;
@Thunk int mAppsPerRow;
@Thunk boolean mIsRtl;
private String mEmptySearchText;
@@ -271,11 +279,13 @@ class AppsGridAdapter extends RecyclerView.Adapter<AppsGridAdapter.ViewHolder> {
public AppsGridAdapter(Context context, AlphabeticalAppsList apps, int appsPerRow,
- View.OnTouchListener touchListener, View.OnClickListener iconClickListener,
- View.OnLongClickListener iconLongClickListener) {
+ PredictionBarSpacerCallbacks pbCb, View.OnTouchListener touchListener,
+ View.OnClickListener iconClickListener, View.OnLongClickListener iconLongClickListener) {
Resources res = context.getResources();
+ mHandler = new Handler();
mApps = apps;
mAppsPerRow = appsPerRow;
+ mPredictionBarCb = pbCb;
mGridSizer = new GridSpanSizer();
mGridLayoutMgr = new GridLayoutManager(context, appsPerRow, GridLayoutManager.VERTICAL,
false);
@@ -310,6 +320,13 @@ class AppsGridAdapter extends RecyclerView.Adapter<AppsGridAdapter.ViewHolder> {
}
/**
+ * Sets the prediction row height.
+ */
+ public void setPredictionRowHeight(int height) {
+ mPredictionBarHeight = height;
+ }
+
+ /**
* Sets whether we are in RTL mode.
*/
public void setRtl(boolean rtl) {
@@ -350,17 +367,24 @@ class AppsGridAdapter extends RecyclerView.Adapter<AppsGridAdapter.ViewHolder> {
switch (viewType) {
case EMPTY_VIEW_TYPE:
return new ViewHolder(mLayoutInflater.inflate(R.layout.apps_empty_view, parent,
- false), true /* isEmptyRow */);
+ false));
case SECTION_BREAK_VIEW_TYPE:
- return new ViewHolder(new View(parent.getContext()), false /* isEmptyRow */);
+ return new ViewHolder(new View(parent.getContext()));
+ case PREDICTION_BAR_SPACER_TYPE:
+ // Create a view of a specific height to match the floating prediction bar
+ View v = new View(parent.getContext());
+ ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT, mPredictionBarHeight);
+ v.setLayoutParams(lp);
+ return new ViewHolder(v);
case ICON_VIEW_TYPE:
BubbleTextView icon = (BubbleTextView) mLayoutInflater.inflate(
- R.layout.apps_grid_row_icon_view, parent, false);
+ R.layout.apps_grid_icon_view, parent, false);
icon.setOnTouchListener(mTouchListener);
icon.setOnClickListener(mIconClickListener);
icon.setOnLongClickListener(mIconLongClickListener);
icon.setFocusable(true);
- return new ViewHolder(icon, false /* isEmptyRow */);
+ return new ViewHolder(icon);
default:
throw new RuntimeException("Unexpected view type");
}
@@ -374,6 +398,16 @@ class AppsGridAdapter extends RecyclerView.Adapter<AppsGridAdapter.ViewHolder> {
BubbleTextView icon = (BubbleTextView) holder.mContent;
icon.applyFromApplicationInfo(info);
break;
+ case PREDICTION_BAR_SPACER_TYPE:
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ if (mPredictionBarCb != null) {
+ mPredictionBarCb.onBindPredictionBar();
+ }
+ }
+ });
+ break;
case EMPTY_VIEW_TYPE:
TextView emptyViewText = (TextView) holder.mContent.findViewById(R.id.empty_text);
emptyViewText.setText(mEmptySearchText);
@@ -394,9 +428,9 @@ class AppsGridAdapter extends RecyclerView.Adapter<AppsGridAdapter.ViewHolder> {
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;
+
+ AlphabeticalAppsList.AdapterItem item = mApps.getAdapterItems().get(position);
+ return item.viewType;
}
}
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 3bbf0e7d8..dc6de0736 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -127,6 +127,7 @@ public class DeviceProfile {
int allAppsNumRows;
int allAppsNumCols;
int appsViewNumCols;
+ int appsViewNumPredictiveCols;
int searchBarSpaceWidthPx;
int searchBarSpaceHeightPx;
int pageIndicatorHeightPx;
@@ -411,7 +412,7 @@ public class DeviceProfile {
// All Apps
allAppsCellWidthPx = allAppsIconSizePx;
- allAppsCellHeightPx = allAppsIconSizePx + drawablePadding + iconTextSizePx;
+ allAppsCellHeightPx = allAppsIconSizePx + drawablePadding + allAppsIconTextSizePx;
int maxLongEdgeCellCount =
res.getInteger(R.integer.config_dynamic_grid_max_long_edge_cell_count);
int maxShortEdgeCellCount =
@@ -440,10 +441,13 @@ public class DeviceProfile {
int appsViewLeftMarginPx =
res.getDimensionPixelSize(R.dimen.apps_grid_view_start_margin);
int availableAppsWidthPx = (containerWidth > 0) ? containerWidth : availableWidthPx;
- int numCols = (availableAppsWidthPx - appsViewLeftMarginPx) /
+ int numAppsCols = (availableAppsWidthPx - appsViewLeftMarginPx) /
(allAppsCellWidthPx + 2 * allAppsCellPaddingPx);
- if (numCols != appsViewNumCols) {
- appsViewNumCols = numCols;
+ int numPredictiveAppCols = isPhone() ? numColumns : numAppsCols;
+ if ((numAppsCols != appsViewNumCols) ||
+ (numPredictiveAppCols != appsViewNumPredictiveCols)) {
+ appsViewNumCols = numAppsCols;
+ appsViewNumPredictiveCols = numPredictiveAppCols;
return true;
}
return false;