diff options
20 files changed, 344 insertions, 87 deletions
diff --git a/res/layout/apps_grid_row_icon_view.xml b/res/layout/apps_grid_row_icon_view.xml index 286deb454..acb3da334 100644 --- a/res/layout/apps_grid_row_icon_view.xml +++ b/res/layout/apps_grid_row_icon_view.xml @@ -21,8 +21,8 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="left|center_vertical" - android:paddingTop="8dp" - android:paddingBottom="8dp" + android:paddingTop="@dimen/apps_icon_top_bottom_padding" + android:paddingBottom="@dimen/apps_icon_top_bottom_padding" android:focusable="true" android:background="@drawable/focusable_view_bg" launcher:deferShadowGeneration="true" diff --git a/res/layout/apps_list_view.xml b/res/layout/apps_list_view.xml index a726cd8fe..ddcb639b8 100644 --- a/res/layout/apps_list_view.xml +++ b/res/layout/apps_list_view.xml @@ -25,7 +25,7 @@ <FrameLayout android:id="@+id/header" android:layout_width="match_parent" - android:layout_height="52dp" + android:layout_height="@dimen/apps_search_bar_height" android:orientation="horizontal" android:background="@drawable/apps_search_bg"> <LinearLayout @@ -39,6 +39,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="start|center_vertical" + android:layout_marginStart="4dp" android:paddingTop="12dp" android:paddingBottom="12dp" android:contentDescription="@string/all_apps_button_label" @@ -67,6 +68,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="end|center_vertical" + android:layout_marginEnd="6dp" android:paddingTop="12dp" android:paddingBottom="12dp" android:contentDescription="@string/apps_view_search_bar_hint" diff --git a/res/layout/widget_cell.xml b/res/layout/widget_cell.xml index ab23b842e..196dfca66 100644 --- a/res/layout/widget_cell.xml +++ b/res/layout/widget_cell.xml @@ -19,10 +19,11 @@ android:layout_width="@dimen/widget_preview_container_width" android:layout_height="@dimen/widget_cell_height" android:layout_weight="1" - android:layout_marginRight="@dimen/widget_row_divider" + android:layout_marginEnd="@dimen/widget_row_divider" android:orientation="vertical" android:background="@color/widgets_cell_color" - android:focusable="true"> + android:focusable="true" + android:gravity="center_horizontal"> <LinearLayout android:layout_width="wrap_content" @@ -43,7 +44,6 @@ android:singleLine="true" android:ellipsize="end" android:fadingEdge="horizontal" - android:textColor="@color/widgets_view_item_text_color" android:textSize="16sp" android:textAlignment="viewStart" diff --git a/res/layout/widgets_list_row_view.xml b/res/layout/widgets_list_row_view.xml index 12f6401e4..ea95d2407 100644 --- a/res/layout/widgets_list_row_view.xml +++ b/res/layout/widgets_list_row_view.xml @@ -55,17 +55,18 @@ </LinearLayout> <!-- Widget list --> - <RelativeLayout + <com.android.launcher3.widget.WidgetRowView + android:id="@+id/widget_row" android:layout_width="match_parent" - android:layout_height="match_parent" + android:layout_height="@dimen/widget_cell_height" android:layout_gravity="end" + android:layout_marginLeft="@dimen/widget_row_padding" android:background="@color/widgets_cell_color" > <HorizontalScrollView android:id="@+id/widgets_scroll_container" android:layout_width="match_parent" - android:layout_height="@dimen/widget_cell_height" - android:layout_marginLeft="@dimen/widget_row_padding" + android:layout_height="match_parent" android:scrollbars="none" > <LinearLayout android:id="@+id/widgets_cell_list" @@ -74,5 +75,5 @@ android:orientation="horizontal" android:background="@color/widget_text_panel"/> </HorizontalScrollView> - </RelativeLayout> + </com.android.launcher3.widget.WidgetRowView> </LinearLayout> diff --git a/res/values-sw600dp/dimens.xml b/res/values-sw600dp/dimens.xml index 9ecd07dd1..b18464292 100644 --- a/res/values-sw600dp/dimens.xml +++ b/res/values-sw600dp/dimens.xml @@ -22,6 +22,7 @@ <dimen name="apps_grid_view_start_margin">64dp</dimen> <dimen name="apps_view_section_text_size">26sp</dimen> <dimen name="apps_view_row_height">76dp</dimen> + <dimen name="apps_icon_top_bottom_padding">12dp</dimen> <!-- AppsCustomize --> <dimen name="widget_preview_label_margin_top">8dp</dimen> diff --git a/res/values-sw720dp/dimens.xml b/res/values-sw720dp/dimens.xml index 68fc1ecaf..cec6b7db0 100644 --- a/res/values-sw720dp/dimens.xml +++ b/res/values-sw720dp/dimens.xml @@ -16,6 +16,8 @@ <resources> <dimen name="app_icon_size">72dp</dimen> + <dimen name="apps_search_bar_height">56dp</dimen> + <dimen name="apps_icon_top_bottom_padding">16dp</dimen> <!-- QSB --> <dimen name="toolbar_button_vertical_padding">8dip</dimen> diff --git a/res/values/dimens.xml b/res/values/dimens.xml index 4d33b88bc..5447371a4 100644 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -59,6 +59,8 @@ <dimen name="apps_view_fast_scroll_scrubber_touch_inset">-16dp</dimen> <dimen name="apps_view_fast_scroll_popup_size">64dp</dimen> <dimen name="apps_view_fast_scroll_text_size">40dp</dimen> + <dimen name="apps_search_bar_height">52dp</dimen> + <dimen name="apps_icon_top_bottom_padding">8dp</dimen> <!-- Note: This needs to match the fixed insets for the search box. --> <dimen name="container_fixed_bounds_inset">8dp</dimen> @@ -86,7 +88,7 @@ <!-- Widget tray --> <dimen name="widget_container_inset">8dp</dimen> - <dimen name="widget_preview_size">120dp</dimen> + <dimen name="widget_preview_size">130dp</dimen> <dimen name="widget_preview_padding_top">8dp</dimen> <dimen name="widget_preview_label_vertical_padding">8dp</dimen> <dimen name="widget_preview_label_horizontal_padding">8dp</dimen> @@ -98,10 +100,10 @@ <dimen name="widget_section_icon_horizontal_padding">16dp</dimen> <!-- Equation: widget_preview_size + 2 * widget_preview_padding_horizontal --> - <dimen name="widget_preview_container_width">136dp</dimen> + <dimen name="widget_preview_container_width">146dp</dimen> <dimen name="widget_cell_height">150dp</dimen> <dimen name="widget_row_padding">8dp</dimen> - <dimen name="widget_row_divider">1dp</dimen> + <dimen name="widget_row_divider">2dp</dimen> <!-- Padding applied to shortcut previews --> <dimen name="shortcut_preview_padding_left">0dp</dimen> diff --git a/src/com/android/launcher3/AlphabeticalAppsList.java b/src/com/android/launcher3/AlphabeticalAppsList.java index f075e417d..62cb237e1 100644 --- a/src/com/android/launcher3/AlphabeticalAppsList.java +++ b/src/com/android/launcher3/AlphabeticalAppsList.java @@ -130,17 +130,21 @@ 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.position = pos; item.isSectionHeader = true; item.sectionInfo = section; + section.sectionBreakItem = item; return item; } public static AdapterItem asApp(int pos, SectionInfo section, String sectionName, - int sectionAppIndex, AppInfo appInfo, int appIndex) { + int sectionAppIndex, AppInfo appInfo, int appIndex, + boolean isPredictedApp) { AdapterItem item = new AdapterItem(); item.position = pos; item.isSectionHeader = false; @@ -149,6 +153,7 @@ public class AlphabeticalAppsList { item.sectionAppIndex = sectionAppIndex; item.appInfo = appInfo; item.appIndex = appIndex; + item.isPredictedApp = isPredictedApp; return item; } } @@ -168,6 +173,7 @@ 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 RecyclerView.Adapter mAdapter; private Filter mFilter; private AlphabeticIndexCompat mIndexer; @@ -252,6 +258,17 @@ public class AlphabeticalAppsList { } /** + * Sets the current set of predicted apps. Since this can be called before we get the full set + * of applications, we should merge the results only in onAppsUpdated() which is idempotent. + */ + public void setPredictedApps(List<ComponentName> apps) { + mPredictedApps.clear(); + mPredictedApps.addAll(apps); + onAppsUpdated(); + mAdapter.notifyDataSetChanged(); + } + + /** * Sets the current set of apps. */ public void setApps(List<AppInfo> apps) { @@ -336,7 +353,7 @@ public class AlphabeticalAppsList { // Sort the list of apps Collections.sort(mApps, mAppNameComparator.getComparator()); - // Recreate the filtered and sectioned apps (for convenience for the grid layout) + // Prepare to update the list of sections, filtered apps, etc. mFilteredApps.clear(); mSections.clear(); mSectionedFilteredApps.clear(); @@ -346,36 +363,62 @@ public class AlphabeticalAppsList { FastScrollSectionInfo lastFastScrollerSectionInfo = null; int position = 0; int appIndex = 0; - int numApps = mApps.size(); - for (AppInfo info : mApps) { - String sectionName = mIndexer.computeSectionName(info.title); + 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 + allApps.addAll(mApps); + + // Recreate the filtered and sectioned apps (for convenience for the grid layout) from the + // combined list + int numApps = allApps.size(); + for (int i = 0; i < numApps; i++) { + boolean isPredictedApp = i < numPredictedApps; + AppInfo info = allApps.get(i); + String sectionName = isPredictedApp ? "" : mIndexer.computeSectionName(info.title); // Check if we want to retain this app if (mFilter != null && !mFilter.retainApp(info, sectionName)) { continue; } - // Create a new section if necessary - if (lastSectionInfo == null || !sectionName.equals(lastSectionName)) { + // Create a new section if the section names do not match + if (lastSectionInfo == null || + (!isPredictedApp && !sectionName.equals(lastSectionName))) { lastSectionName = sectionName; lastSectionInfo = new SectionInfo(); - mSections.add(lastSectionInfo); lastFastScrollerSectionInfo = new FastScrollSectionInfo(sectionName, (float) appIndex / numApps); + mSections.add(lastSectionInfo); mFastScrollerSections.add(lastFastScrollerSectionInfo); - // Create a new section item, this item is used to break the flow of items in the - // list - AdapterItem sectionItem = AdapterItem.asSectionBreak(position++, lastSectionInfo); + // Create a new section item to break the flow of items in the list if (!AppsContainerView.GRID_HIDE_SECTION_HEADERS && !hasFilter()) { - lastSectionInfo.sectionBreakItem = sectionItem; + AdapterItem sectionItem = AdapterItem.asSectionBreak(position++, lastSectionInfo); mSectionedFilteredApps.add(sectionItem); } } // Create an app item AdapterItem appItem = AdapterItem.asApp(position++, lastSectionInfo, sectionName, - lastSectionInfo.numApps++, info, appIndex++); + lastSectionInfo.numApps++, info, appIndex++, isPredictedApp); if (lastSectionInfo.firstAppItem == null) { lastSectionInfo.firstAppItem = appItem; lastFastScrollerSectionInfo.appItem = appItem; @@ -384,8 +427,8 @@ public class AlphabeticalAppsList { mFilteredApps.add(info); } + // Go through each section and try and merge some of the sections 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++) { diff --git a/src/com/android/launcher3/AppsContainerRecyclerView.java b/src/com/android/launcher3/AppsContainerRecyclerView.java index 6556cf920..edb6f0c6e 100644 --- a/src/com/android/launcher3/AppsContainerRecyclerView.java +++ b/src/com/android/launcher3/AppsContainerRecyclerView.java @@ -229,7 +229,7 @@ public class AppsContainerRecyclerView extends BaseContainerRecyclerView { * Draws the fast scroller popup. */ private void drawFastScrollerPopup(Canvas canvas) { - if (mFastScrollAlpha > 0f) { + if (mFastScrollAlpha > 0f && !mFastScrollSectionName.isEmpty()) { int x; int y; boolean isRtl = (getResources().getConfiguration().getLayoutDirection() == @@ -381,16 +381,16 @@ public class AppsContainerRecyclerView extends BaseContainerRecyclerView { } /** - * Returns the row index for a given position in the list. + * Returns the row index for a app index in the list. */ - private int findRowForAppIndex(int position) { + private int findRowForAppIndex(int index) { List<AlphabeticalAppsList.SectionInfo> sections = mApps.getSections(); int appIndex = 0; int rowCount = 0; for (AlphabeticalAppsList.SectionInfo info : sections) { int numRowsInSection = (int) Math.ceil((float) info.numApps / mNumAppsPerRow); - if (appIndex + info.numApps > position) { - return rowCount + ((position - appIndex) / mNumAppsPerRow); + if (appIndex + info.numApps > index) { + return rowCount + ((index - appIndex) / mNumAppsPerRow); } appIndex += info.numApps; rowCount += numRowsInSection; diff --git a/src/com/android/launcher3/AppsContainerView.java b/src/com/android/launcher3/AppsContainerView.java index aa6c05993..b8d30d081 100644 --- a/src/com/android/launcher3/AppsContainerView.java +++ b/src/com/android/launcher3/AppsContainerView.java @@ -15,6 +15,7 @@ */ package com.android.launcher3; +import android.content.ComponentName; import android.content.Context; import android.content.res.Resources; import android.graphics.Point; @@ -111,6 +112,13 @@ public class AppsContainerView extends BaseContainerView implements DragSource, } /** + * Sets the current set of predicted apps. + */ + public void setPredictedApps(List<ComponentName> apps) { + mApps.setPredictedApps(apps); + } + + /** * Sets the current set of apps. */ public void setApps(List<AppInfo> apps) { diff --git a/src/com/android/launcher3/AppsGridAdapter.java b/src/com/android/launcher3/AppsGridAdapter.java index 259740c60..9ecb2eeb6 100644 --- a/src/com/android/launcher3/AppsGridAdapter.java +++ b/src/com/android/launcher3/AppsGridAdapter.java @@ -35,13 +35,11 @@ class AppsGridAdapter extends RecyclerView.Adapter<AppsGridAdapter.ViewHolder> { */ public static class ViewHolder extends RecyclerView.ViewHolder { public View mContent; - public boolean mIsSectionHeader; public boolean mIsEmptyRow; - public ViewHolder(View v, boolean isSectionHeader, boolean isEmptyRow) { + public ViewHolder(View v, boolean isEmptyRow) { super(v); mContent = v; - mIsSectionHeader = isSectionHeader; mIsEmptyRow = isEmptyRow; } } @@ -93,13 +91,29 @@ class AppsGridAdapter extends RecyclerView.Adapter<AppsGridAdapter.ViewHolder> { } List<AlphabeticalAppsList.AdapterItem> items = mApps.getAdapterItems(); + boolean hasDrawnPredictedAppDivider = false; int childCount = parent.getChildCount(); int lastSectionTop = 0; int lastSectionHeight = 0; for (int i = 0; i < childCount; i++) { View child = parent.getChildAt(i); ViewHolder holder = (ViewHolder) parent.getChildViewHolder(child); - if (shouldDrawItemSection(holder, child, i, items)) { + if (!isValidHolderAndChild(holder, child, items)) { + continue; + } + + if (shouldDrawItemDivider(holder, items) && !hasDrawnPredictedAppDivider) { + // Draw the divider under the predicted app + DeviceProfile grid = LauncherAppState.getInstance().getDynamicGrid(). + getDeviceProfile(); + int top = child.getTop() + child.getHeight(); + int left = parent.getPaddingLeft(); + int right = parent.getWidth() - parent.getPaddingRight(); + int iconInset = (((right - left) / mAppsPerRow) - grid.allAppsIconSizePx) / 2; + c.drawLine(left + iconInset, top, right - iconInset, top, mPredictedAppsDividerPaint); + hasDrawnPredictedAppDivider = true; + + } else if (shouldDrawItemSection(holder, i, items)) { // At this point, we only draw sections for each section break; int viewTopOffset = (2 * child.getPaddingTop()); int pos = holder.getPosition(); @@ -186,9 +200,9 @@ class AppsGridAdapter extends RecyclerView.Adapter<AppsGridAdapter.ViewHolder> { } /** - * Returns whether to draw the section for the given child. + * Returns whether we consider this a valid view holder for us to draw a divider or section for. */ - private boolean shouldDrawItemSection(ViewHolder holder, View child, int childIndex, + private boolean isValidHolderAndChild(ViewHolder holder, View child, List<AlphabeticalAppsList.AdapterItem> items) { // Ensure item is not already removed GridLayoutManager.LayoutParams lp = (GridLayoutManager.LayoutParams) @@ -200,18 +214,44 @@ class AppsGridAdapter extends RecyclerView.Adapter<AppsGridAdapter.ViewHolder> { if (holder == null) { return false; } + // Ensure we have a holder position + int pos = holder.getPosition(); + if (pos < 0 || pos >= items.size()) { + return false; + } + return true; + } + + /** + * Returns whether to draw the divider for a given child. + */ + private boolean shouldDrawItemDivider(ViewHolder holder, List<AlphabeticalAppsList.AdapterItem> items) { + int pos = holder.getPosition(); + return items.get(pos).isPredictedApp; + } + + /** + * Returns whether to draw the section for the given child. + */ + private boolean shouldDrawItemSection(ViewHolder holder, int childIndex, + List<AlphabeticalAppsList.AdapterItem> items) { + int pos = holder.getPosition(); + AlphabeticalAppsList.AdapterItem item = items.get(pos); + // Ensure it's not an empty row if (holder.mIsEmptyRow) { return false; } - // Ensure we have a holder position - int pos = holder.getPosition(); - if (pos <= 0 || pos >= items.size()) { + // Ensure this is not a section break + if (item.isSectionHeader) { + return false; + } + // Ensure this is not a predicted app + if (item.isPredictedApp) { return false; } // Draw the section header for the first item in each section - return (childIndex == 0) || - (items.get(pos - 1).isSectionHeader && !items.get(pos).isSectionHeader); + return (childIndex == 0) || (items.get(pos - 1).isSectionHeader && !item.isSectionHeader); } } @@ -232,6 +272,7 @@ class AppsGridAdapter extends RecyclerView.Adapter<AppsGridAdapter.ViewHolder> { @Thunk int mStartMargin; @Thunk int mSectionHeaderOffset; @Thunk Paint mSectionTextPaint; + @Thunk Paint mPredictedAppsDividerPaint; public AppsGridAdapter(Context context, AlphabeticalAppsList apps, int appsPerRow, @@ -254,11 +295,17 @@ class AppsGridAdapter extends RecyclerView.Adapter<AppsGridAdapter.ViewHolder> { mSectionHeaderOffset = res.getDimensionPixelSize(R.dimen.apps_grid_section_y_offset); } mPaddingStart = res.getDimensionPixelSize(R.dimen.apps_container_inset); + mSectionTextPaint = new Paint(); mSectionTextPaint.setTextSize(res.getDimensionPixelSize( R.dimen.apps_view_section_text_size)); mSectionTextPaint.setColor(res.getColor(R.color.apps_view_section_text_color)); mSectionTextPaint.setAntiAlias(true); + + mPredictedAppsDividerPaint = new Paint(); + mPredictedAppsDividerPaint.setStrokeWidth(DynamicGrid.pxFromDp(1.5f, res.getDisplayMetrics())); + mPredictedAppsDividerPaint.setColor(0x10000000); + mPredictedAppsDividerPaint.setAntiAlias(true); } /** @@ -313,10 +360,9 @@ 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), false /* isSectionRow */, true /* isEmptyRow */); + false), true /* isEmptyRow */); case SECTION_BREAK_VIEW_TYPE: - return new ViewHolder(new View(parent.getContext()), true /* isSectionRow */, - false /* isEmptyRow */); + return new ViewHolder(new View(parent.getContext()), false /* isEmptyRow */); case ICON_VIEW_TYPE: BubbleTextView icon = (BubbleTextView) mLayoutInflater.inflate( R.layout.apps_grid_row_icon_view, parent, false); @@ -324,7 +370,7 @@ class AppsGridAdapter extends RecyclerView.Adapter<AppsGridAdapter.ViewHolder> { icon.setOnClickListener(mIconClickListener); icon.setOnLongClickListener(mIconLongClickListener); icon.setFocusable(true); - return new ViewHolder(icon, false /* isSectionRow */, false /* isEmptyRow */); + return new ViewHolder(icon, false /* isEmptyRow */); default: throw new RuntimeException("Unexpected view type"); } diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java index 918517ebd..7a6e9a20a 100644 --- a/src/com/android/launcher3/DeviceProfile.java +++ b/src/com/android/launcher3/DeviceProfile.java @@ -69,8 +69,10 @@ public class DeviceProfile { String name; float minWidthDps; float minHeightDps; - public float numRows; - public float numColumns; + public int numRows; + public int numColumns; + public int numFolderRows; + public int numFolderColumns; float numHotseatIcons; float iconSize; private float iconTextSize; @@ -80,12 +82,11 @@ public class DeviceProfile { int defaultLayoutId; boolean isLandscape; - boolean isTablet; - boolean isLargeTablet; + public boolean isTablet; + public boolean isLargeTablet; public boolean isLayoutRtl; boolean transposeLayoutWithOrientation; - int desiredWorkspaceLeftRightMarginPx; public int edgeMarginPx; Rect defaultWidgetPadding; @@ -138,8 +139,9 @@ public class DeviceProfile { private ArrayList<DeviceProfileCallbacks> mCallbacks = new ArrayList<DeviceProfileCallbacks>(); - DeviceProfile(String n, float w, float h, float r, float c, - float is, float its, float hs, float his, int dlId) { + DeviceProfile(String n, float w, float h, + int r, int c, int fr, int fc, + float is, float its, float hs, float his, int dlId) { // Ensure that we have an odd number of hotseat items (since we need to place all apps) if (hs % 2 == 0) { throw new RuntimeException("All Device Profiles must have an odd number of hotseat spaces"); @@ -148,8 +150,12 @@ public class DeviceProfile { name = n; minWidthDps = w; minHeightDps = h; + numRows = r; numColumns = c; + numFolderRows = fr; + numFolderColumns = fc; + iconSize = is; iconTextSize = its; numHotseatIcons = hs; @@ -210,6 +216,9 @@ public class DeviceProfile { // Snap to the closest column count numColumns = closestProfile.numColumns; + numFolderRows = closestProfile.numFolderRows; + numFolderColumns = closestProfile.numFolderColumns; + // Snap to the closest hotseat size numHotseatIcons = closestProfile.numHotseatIcons; hotseatAllAppsRank = (int) (numHotseatIcons / 2); @@ -266,8 +275,8 @@ public class DeviceProfile { DeviceProfile partnerDp = p.getDeviceProfileOverride(dm); if (partnerDp != null) { if (partnerDp.numRows > 0 && partnerDp.numColumns > 0) { - numRows = partnerDp.numRows; - numColumns = partnerDp.numColumns; + numRows = numFolderRows = partnerDp.numRows; + numColumns = numFolderColumns = partnerDp.numColumns; } if (partnerDp.allAppsShortEdgeCount > 0 && partnerDp.allAppsLongEdgeCount > 0) { allAppsShortEdgeCount = partnerDp.allAppsShortEdgeCount; diff --git a/src/com/android/launcher3/DynamicGrid.java b/src/com/android/launcher3/DynamicGrid.java index 24da97fc6..d22427f44 100644 --- a/src/com/android/launcher3/DynamicGrid.java +++ b/src/com/android/launcher3/DynamicGrid.java @@ -59,30 +59,30 @@ public class DynamicGrid { DEFAULT_ICON_SIZE_PX = pxFromDp(DEFAULT_ICON_SIZE_DP, dm); // Our phone profiles include the bar sizes in each orientation deviceProfiles.add(new DeviceProfile("Super Short Stubby", - 255, 300, 2, 3, 48, 13, 3, 48, R.xml.default_workspace_4x4)); + 255, 300, 2, 3, 2, 3, 48, 13, 3, 48, R.xml.default_workspace_4x4)); deviceProfiles.add(new DeviceProfile("Shorter Stubby", - 255, 400, 3, 3, 48, 13, 3, 48, R.xml.default_workspace_4x4)); + 255, 400, 3, 3, 3, 3, 48, 13, 3, 48, R.xml.default_workspace_4x4)); deviceProfiles.add(new DeviceProfile("Short Stubby", - 275, 420, 3, 4, 48, 13, 5, 48, R.xml.default_workspace_4x4)); + 275, 420, 3, 4, 3, 4, 48, 13, 5, 48, R.xml.default_workspace_4x4)); deviceProfiles.add(new DeviceProfile("Stubby", - 255, 450, 3, 4, 48, 13, 5, 48, R.xml.default_workspace_4x4)); + 255, 450, 3, 4, 3, 4, 48, 13, 5, 48, R.xml.default_workspace_4x4)); deviceProfiles.add(new DeviceProfile("Nexus S", - 296, 491.33f, 4, 4, 48, 13, 5, 48, R.xml.default_workspace_4x4)); + 296, 491.33f, 4, 4, 4, 4, 48, 13, 5, 48, R.xml.default_workspace_4x4)); deviceProfiles.add(new DeviceProfile("Nexus 4", - 335, 567, 4, 4, DEFAULT_ICON_SIZE_DP, 13, 5, 56, R.xml.default_workspace_4x4)); + 335, 567, 4, 4, 4, 4, DEFAULT_ICON_SIZE_DP, 13, 5, 56, R.xml.default_workspace_4x4)); deviceProfiles.add(new DeviceProfile("Nexus 5", - 359, 567, 4, 4, DEFAULT_ICON_SIZE_DP, 13, 5, 56, R.xml.default_workspace_4x4)); + 359, 567, 4, 4, 4, 4, DEFAULT_ICON_SIZE_DP, 13, 5, 56, R.xml.default_workspace_4x4)); deviceProfiles.add(new DeviceProfile("Large Phone", - 406, 694, 5, 5, 64, 14.4f, 5, 56, R.xml.default_workspace_5x5)); + 406, 694, 5, 5, 4, 4, 64, 14.4f, 5, 56, R.xml.default_workspace_5x5)); // The tablet profile is odd in that the landscape orientation // also includes the nav bar on the side deviceProfiles.add(new DeviceProfile("Nexus 7", - 575, 904, 5, 6, 72, 14.4f, 7, 60, R.xml.default_workspace_5x6)); + 575, 904, 5, 6, 4, 5, 72, 14.4f, 7, 60, R.xml.default_workspace_5x6)); // Larger tablet profiles always have system bars on the top & bottom deviceProfiles.add(new DeviceProfile("Nexus 10", - 727, 1207, 5, 6, 76, 14.4f, 7, 64, R.xml.default_workspace_5x6)); + 727, 1207, 5, 6, 4, 5, 76, 14.4f, 7, 64, R.xml.default_workspace_5x6)); deviceProfiles.add(new DeviceProfile("20-inch Tablet", - 1527, 2527, 7, 7, 100, 20, 7, 72, R.xml.default_workspace_4x4)); + 1527, 2527, 7, 7, 6, 6, 100, 20, 7, 72, R.xml.default_workspace_4x4)); mMinWidth = dpiFromPx(minWidthPx, dm); mMinHeight = dpiFromPx(minHeightPx, dm); mProfile = new DeviceProfile(context, deviceProfiles, diff --git a/src/com/android/launcher3/FolderPagedView.java b/src/com/android/launcher3/FolderPagedView.java index 63550827b..05b2bbf67 100644 --- a/src/com/android/launcher3/FolderPagedView.java +++ b/src/com/android/launcher3/FolderPagedView.java @@ -51,9 +51,6 @@ public class FolderPagedView extends PagedView { private static final int[] sTempPosArray = new int[2]; - // TODO: Remove this restriction - private static final int MAX_ITEMS_PER_PAGE = 4; - public final boolean rtlLayout; private final LayoutInflater mInflater; @@ -80,13 +77,8 @@ public class FolderPagedView extends PagedView { LauncherAppState app = LauncherAppState.getInstance(); DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); - if (ALLOW_FOLDER_SCROLL) { - mMaxCountX = Math.min((int) grid.numColumns, MAX_ITEMS_PER_PAGE); - mMaxCountY = Math.min((int) grid.numRows, MAX_ITEMS_PER_PAGE); - } else { - mMaxCountX = (int) grid.numColumns; - mMaxCountY = (int) grid.numRows; - } + mMaxCountX = (int) grid.numFolderColumns; + mMaxCountY = (int) grid.numFolderRows; mMaxItemsPerPage = mMaxCountX * mMaxCountY; diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index b17a92f47..16e4ce644 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -64,6 +64,7 @@ import android.os.Handler; import android.os.Message; import android.os.StrictMode; import android.os.SystemClock; +import android.preference.PreferenceManager; import android.text.Selection; import android.text.SpannableStringBuilder; import android.text.TextUtils; @@ -1020,16 +1021,22 @@ public class Launcher extends Activity if (mOnResumeState == State.WORKSPACE) { showWorkspace(false); } else if (mOnResumeState == State.APPS) { - showAppsView(false /* animated */, false /* resetListToTop */); + // Don't update the predicted apps if the user is returning to launcher in the apps + // view as they may be depending on the UI to be static to switch to another app + showAppsView(false /* animated */, false /* resetListToTop */, + false /* updatePredictedApps */); } else if (mOnResumeState == State.WIDGETS) { showWidgetsView(false, false); } mOnResumeState = State.NONE; // Restore the apps state if we are in all apps - if (!Launcher.DISABLE_ALL_APPS_SEARCH_INTEGRATION && mState == State.APPS) { - if (mLauncherCallbacks != null) { - mLauncherCallbacks.onAllAppsShown(); + if (!Launcher.DISABLE_ALL_APPS_SEARCH_INTEGRATION) { + // Otherwise, notify the callbacks if we are in all apps mode + if (mState == State.APPS) { + if (mLauncherCallbacks != null) { + mLauncherCallbacks.onAllAppsShown(); + } } } @@ -2621,7 +2628,9 @@ public class Launcher extends Activity if (isAppsViewVisible()) { showWorkspace(true); } else { - showAppsView(true /* animated */, false /* resetListToTop */); + // Try and refresh the set of predicted apps before we enter launcher + showAppsView(true /* animated */, false /* resetListToTop */, + true /* updatePredictedApps */); } } @@ -3399,10 +3408,13 @@ public class Launcher extends Activity /** * Shows the apps view. */ - void showAppsView(boolean animated, boolean resetListToTop) { + void showAppsView(boolean animated, boolean resetListToTop, boolean updatePredictedApps) { if (resetListToTop) { mAppsView.scrollToTop(); } + if (updatePredictedApps) { + tryAndUpdatePredictedApps(); + } showAppsOrWidgets(animated, State.APPS); } @@ -3511,12 +3523,26 @@ public class Launcher extends Activity void exitSpringLoadedDragMode() { if (mState == State.APPS_SPRING_LOADED) { - showAppsView(true, false); + showAppsView(true /* animated */, false /* resetListToTop */, + false /* updatePredictedApps */); } else if (mState == State.WIDGETS_SPRING_LOADED) { showWidgetsView(true, false); } } + /** + * Updates the set of predicted apps if it hasn't been updated since the last time Launcher was + * resumed. + */ + private void tryAndUpdatePredictedApps() { + if (mLauncherCallbacks != null) { + List<ComponentName> apps = mLauncherCallbacks.getPredictedApps(); + if (!apps.isEmpty()) { + mAppsView.setPredictedApps(apps); + } + } + } + void lockAllApps() { // TODO } diff --git a/src/com/android/launcher3/LauncherCallbacks.java b/src/com/android/launcher3/LauncherCallbacks.java index 25c86c977..0124d1f28 100644 --- a/src/com/android/launcher3/LauncherCallbacks.java +++ b/src/com/android/launcher3/LauncherCallbacks.java @@ -11,6 +11,7 @@ import android.view.ViewGroup; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.List; /** * LauncherCallbacks is an interface used to extend the Launcher activity. It includes many hooks @@ -90,6 +91,7 @@ public interface LauncherCallbacks { public boolean overrideWallpaperDimensions(); public boolean isLauncherPreinstalled(); public boolean overrideAllAppsSearch(); + public List<ComponentName> getPredictedApps(); /** * Returning true will immediately result in a call to {@link #setLauncherOverlayView(ViewGroup, diff --git a/src/com/android/launcher3/LauncherExtension.java b/src/com/android/launcher3/LauncherExtension.java index 8174af045..14ad6016c 100644 --- a/src/com/android/launcher3/LauncherExtension.java +++ b/src/com/android/launcher3/LauncherExtension.java @@ -15,6 +15,7 @@ import android.view.ViewGroup; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.List; /** * This class represents a very trivial LauncherExtension. It primarily serves as a simple @@ -259,6 +260,11 @@ public class LauncherExtension extends Launcher { } @Override + public List<ComponentName> getPredictedApps() { + return new ArrayList<>(); + } + + @Override public boolean isLauncherPreinstalled() { return false; } diff --git a/src/com/android/launcher3/widget/WidgetCell.java b/src/com/android/launcher3/widget/WidgetCell.java index 27b7e6df5..877937151 100644 --- a/src/com/android/launcher3/widget/WidgetCell.java +++ b/src/com/android/launcher3/widget/WidgetCell.java @@ -49,7 +49,16 @@ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener { private static final boolean DEBUG = false; private static final int FADE_IN_DURATION_MS = 90; - private int mPresetPreviewSize; + + /** Widget cell width is calculated by multiplying this factor to grid cell width. */ + private static final float WIDTH_SCALE = 2.8f; + + /** Widget preview width is calculated by multiplying this factor to the widget cell width. */ + private static final float PREVIEW_SCALE = 0.9f; + + private static int mPresetPreviewSize; + private static int mSize; + private static int mDividerWidth; private ImageView mWidgetImage; private TextView mWidgetName; @@ -76,12 +85,22 @@ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener { final Resources r = context.getResources(); mDimensionsFormatString = r.getString(R.string.widget_dims_format); - mPresetPreviewSize = r.getDimensionPixelSize(R.dimen.widget_preview_size); + setContainerWidth(); setWillNotDraw(false); setClipToPadding(false); setAccessibilityDelegate(LauncherAppState.getInstance().getAccessibilityDelegate()); + } + private void setContainerWidth() { + // Do nothing if already set + if (mSize > 0) { + return; + } + DeviceProfile profile = LauncherAppState.getInstance().getDynamicGrid().getDeviceProfile(); + mSize = (int) (profile.cellWidthPx * WIDTH_SCALE); + mPresetPreviewSize = (int) (profile.cellWidthPx * WIDTH_SCALE * PREVIEW_SCALE); + mDividerWidth = getResources().getDimensionPixelSize(R.dimen.widget_row_divider); } @Override @@ -98,6 +117,12 @@ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener { mWidgetDims = ((TextView) findViewById(R.id.widget_dims)); } + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(MeasureSpec.makeMeasureSpec(mSize, MeasureSpec.EXACTLY), + MeasureSpec.makeMeasureSpec(mSize, MeasureSpec.EXACTLY)); + } + /** * Called to clear the view and free attached resources. (e.g., {@link Bitmap} */ @@ -108,6 +133,7 @@ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener { mWidgetImage.setImageDrawable(null); mWidgetName.setText(null); mWidgetDims.setText(null); + setSeparator(true); if (mActiveRequest != null) { mActiveRequest.cleanup(); @@ -228,4 +254,11 @@ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener { } return ""; } + + public void setSeparator(boolean enable) { + LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) getLayoutParams(); + lp.setMarginEnd(enable? mDividerWidth : 0); + setLayoutParams(lp); + requestLayout(); + } } diff --git a/src/com/android/launcher3/widget/WidgetRowView.java b/src/com/android/launcher3/widget/WidgetRowView.java new file mode 100644 index 000000000..05760ae48 --- /dev/null +++ b/src/com/android/launcher3/widget/WidgetRowView.java @@ -0,0 +1,74 @@ +/* + * 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. + */ + +package com.android.launcher3.widget; + +import android.content.Context; +import android.content.res.Resources; +import android.util.AttributeSet; +import android.widget.LinearLayout; + + +import com.android.launcher3.DeviceProfile; +import com.android.launcher3.DynamicGrid; +import com.android.launcher3.LauncherAppState; + +/** + * Represents the individual cell of the widget inside the widget tray. + */ +public class WidgetRowView extends LinearLayout { + + private static final int PRESET_INDENT_SIZE_TABLET = 56; + + /** Widget row width is calculated by multiplying this factor to grid cell width. */ + private static final float HEIGHT_SCALE = 2.8f; + + static int sIndent = 0; + static int sHeight = 0; + + public WidgetRowView(Context context) { + this(context, null); + } + + public WidgetRowView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public WidgetRowView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + setContainerHeight(); + setWillNotDraw(false); + setClipToPadding(false); + setAccessibilityDelegate(LauncherAppState.getInstance().getAccessibilityDelegate()); + } + + /** + * Sets the widget cell container size based on the physical dimension of the device. + */ + private void setContainerHeight() { + // Do nothing if already set + if (sHeight > 0) { + return; + } + + Resources r = getResources(); + DeviceProfile profile = LauncherAppState.getInstance().getDynamicGrid().getDeviceProfile(); + if (profile.isLargeTablet || profile.isTablet) { + sIndent = DynamicGrid.pxFromDp(PRESET_INDENT_SIZE_TABLET, r.getDisplayMetrics()); + } + sHeight = (int) (profile.cellWidthPx * HEIGHT_SCALE); + } +} diff --git a/src/com/android/launcher3/widget/WidgetsListAdapter.java b/src/com/android/launcher3/widget/WidgetsListAdapter.java index a7728a11b..8b0a43b2f 100644 --- a/src/com/android/launcher3/widget/WidgetsListAdapter.java +++ b/src/com/android/launcher3/widget/WidgetsListAdapter.java @@ -23,6 +23,7 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; +import android.widget.LinearLayout; import android.widget.TextView; import com.android.launcher3.IconCache; @@ -105,6 +106,10 @@ public class WidgetsListAdapter extends Adapter<WidgetsRowViewHolder> { // set up touch. widget.setOnClickListener(mIconClickListener); widget.setOnLongClickListener(mIconLongClickListener); + // Add a devider if it is not the last item. + if (i == diff - 1) { + widget.setSeparator(false); + } row.addView(widget); } } else if (diff < 0) { @@ -156,6 +161,11 @@ public class WidgetsListAdapter extends Adapter<WidgetsRowViewHolder> { ViewGroup container = (ViewGroup) mLayoutInflater.inflate( R.layout.widgets_list_row_view, parent, false); + WidgetRowView row = (WidgetRowView) container.findViewById(R.id.widget_row); + LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) row.getLayoutParams(); + lp.setMarginStart(WidgetRowView.sIndent); + lp.height = WidgetRowView.sHeight; + row.setLayoutParams(lp); return new WidgetsRowViewHolder(container); } |