Merge "Updating folder grid sizes for various devices" into ub-launcher3-burnaby
diff --git a/res/layout/apps_grid_row_icon_view.xml b/res/layout/apps_grid_row_icon_view.xml
index 81e74b9..149e28e 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_row_icon_view.xml b/res/layout/apps_list_row_icon_view.xml
deleted file mode 100644
index 867dbdc..0000000
--- a/res/layout/apps_list_row_icon_view.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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/WorkspaceIcon.AppsCustomize"
- android:id="@+id/application_icon"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:focusable="true"
- android:background="@drawable/focusable_view_bg"
- launcher:iconPaddingOverride="24dp"
- launcher:textSizeOverride="16dp"
- launcher:layoutHorizontal="true"
- launcher:deferShadowGeneration="true" />
diff --git a/res/layout/apps_list_view.xml b/res/layout/apps_list_view.xml
index a726cd8..ddcb639 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/user_folder.xml b/res/layout/user_folder.xml
index cd3a051..5bacc96 100644
--- a/res/layout/user_folder.xml
+++ b/res/layout/user_folder.xml
@@ -19,7 +19,7 @@
xmlns:launcher="http://schemas.android.com/apk/res-auto"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:background="@drawable/quantum_panel"
+ android:elevation="5dp"
android:orientation="vertical" >
<FrameLayout
diff --git a/res/layout/widget_cell.xml b/res/layout/widget_cell.xml
index ab23b84..196dfca 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 12f6401..ea95d24 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 9ecd07dd..b184642 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 68fc1ec..cec6b7d 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/config.xml b/res/values/config.xml
index 6ef8635..84ccef1 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -102,4 +102,5 @@
<item type="id" name="action_move_to_workspace" />
<item type="id" name="action_move_screen_backwards" />
<item type="id" name="action_move_screen_forwards" />
+ <item type="id" name="action_resize" />
</resources>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 46830d6..5447371 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>
@@ -76,13 +78,17 @@
or right while you're dragging. -->
<dimen name="scroll_zone">20dp</dimen>
- <!-- When dragging an item, how much bigger (fixed dps) the dragged view
- should be. If 0, it will not be scaled at all. -->
+ <!-- When dragging an item, how much bigger (fixed dps) the dragged view
+ should be. If 0, it will not be scaled at all. -->
<dimen name="dragViewScale">12dp</dimen>
+ <!-- Elevation for the drag view. It should be larger than elevation of all other drag sources
+ and drop targets like all-apps and folders -->
+ <dimen name="drag_elevation">30dp</dimen>
+
<!-- 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>
@@ -94,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>
@@ -108,6 +114,7 @@
<!-- The amount that the preview contents are inset from the preview background -->
<dimen name="folder_preview_padding">4dp</dimen>
<dimen name="folder_name_padding">10dp</dimen>
+ <dimen name="folder_shadow_padding">8dp</dimen>
<!-- Sizes for managed profile badges -->
<dimen name="profile_badge_size">24dp</dimen>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 1681fc6..5962584 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -252,4 +252,23 @@
<!-- Accessibility confirmation when a screen was moved [DO NOT TRANSLATE] -->
<string name="screen_moved">Screen moved</string>
+
+ <!-- Accessibility action to resize a widget [DO NOT TRANSLATE] -->
+ <string name="action_resize">Resize</string>
+
+ <!-- Accessibility action to increase width of a widget [DO NOT TRANSLATE] -->
+ <string name="action_increase_width">Increase width</string>
+
+ <!-- Accessibility action to increase height of a widget [DO NOT TRANSLATE] -->
+ <string name="action_increase_height">Increase height</string>
+
+ <!-- Accessibility action to decrease width of a widget [DO NOT TRANSLATE] -->
+ <string name="action_decrease_width">Decrease width</string>
+
+ <!-- Accessibility action to decrease height of a widget [DO NOT TRANSLATE] -->
+ <string name="action_decrease_height">Decrease height</string>
+
+ <!-- Accessibility confirmation for widget resize [DO NOT TRANSLATE]-->
+ <string name="widget_resized">Widget resized to width <xliff:g id="number" example="2">%1$s</xliff:g> height <xliff:g id="number" example="1">%2$s</xliff:g></string>
+
</resources>
diff --git a/src/com/android/launcher3/AlphabeticalAppsList.java b/src/com/android/launcher3/AlphabeticalAppsList.java
index 70e36a7..62cb237 100644
--- a/src/com/android/launcher3/AlphabeticalAppsList.java
+++ b/src/com/android/launcher3/AlphabeticalAppsList.java
@@ -29,8 +29,7 @@
mAppNameComparator = new Comparator<AppInfo>() {
public final int compare(AppInfo a, AppInfo b) {
// Order by the title
- int result = collator.compare(a.title.toString().trim(),
- b.title.toString().trim());
+ int result = collator.compare(a.title.toString(), b.title.toString());
if (result == 0) {
// If two apps have the same title, then order by the component name
result = a.componentName.compareTo(b.componentName);
@@ -131,17 +130,21 @@
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;
@@ -150,6 +153,7 @@
item.sectionAppIndex = sectionAppIndex;
item.appInfo = appInfo;
item.appIndex = appIndex;
+ item.isPredictedApp = isPredictedApp;
return item;
}
}
@@ -169,6 +173,7 @@
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;
@@ -253,6 +258,17 @@
}
/**
+ * 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) {
@@ -337,7 +353,7 @@
// 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();
@@ -347,36 +363,62 @@
FastScrollSectionInfo lastFastScrollerSectionInfo = null;
int position = 0;
int appIndex = 0;
- int numApps = mApps.size();
- for (AppInfo info : mApps) {
- String sectionName = mIndexer.computeSectionName(info.title.toString().trim());
+ 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;
@@ -385,8 +427,8 @@
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/AppInfo.java b/src/com/android/launcher3/AppInfo.java
index 7c6b066..58a57a1 100644
--- a/src/com/android/launcher3/AppInfo.java
+++ b/src/com/android/launcher3/AppInfo.java
@@ -105,7 +105,7 @@
public AppInfo(AppInfo info) {
super(info);
componentName = info.componentName;
- title = info.title.toString();
+ title = Utilities.trim(info.title);
intent = new Intent(info.intent);
flags = info.flags;
firstInstallTime = info.firstInstallTime;
@@ -114,7 +114,7 @@
@Override
public String toString() {
- return "ApplicationInfo(title=" + title.toString() + " id=" + this.id
+ return "ApplicationInfo(title=" + title + " id=" + this.id
+ " type=" + this.itemType + " container=" + this.container
+ " screen=" + screenId + " cellX=" + cellX + " cellY=" + cellY
+ " spanX=" + spanX + " spanY=" + spanY + " dropPos=" + Arrays.toString(dropPos)
diff --git a/src/com/android/launcher3/AppsContainerRecyclerView.java b/src/com/android/launcher3/AppsContainerRecyclerView.java
index 6556cf9..edb6f0c 100644
--- a/src/com/android/launcher3/AppsContainerRecyclerView.java
+++ b/src/com/android/launcher3/AppsContainerRecyclerView.java
@@ -229,7 +229,7 @@
* 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 @@
}
/**
- * 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 993f9c8..b8d30d0 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;
@@ -36,6 +37,7 @@
import com.android.launcher3.util.Thunk;
import java.util.List;
+import java.util.regex.Pattern;
/**
@@ -56,6 +58,8 @@
private static final int FADE_OUT_DURATION = 100;
private static final int SEARCH_TRANSLATION_X_DP = 18;
+ private static final Pattern SPLIT_PATTERN = Pattern.compile("[\\s|\\p{javaSpaceChar}]+");
+
@Thunk Launcher mLauncher;
@Thunk AlphabeticalAppsList mApps;
private AppsGridAdapter mAdapter;
@@ -108,6 +112,13 @@
}
/**
+ * 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) {
@@ -430,23 +441,24 @@
@Override
public void afterTextChanged(final Editable s) {
- if (s.toString().isEmpty()) {
+ String queryText = s.toString();
+ if (queryText.isEmpty()) {
mApps.setFilter(null);
} else {
String formatStr = getResources().getString(R.string.apps_view_no_search_results);
- mAdapter.setEmptySearchText(String.format(formatStr, s.toString()));
+ mAdapter.setEmptySearchText(String.format(formatStr, queryText));
- final String filterText = s.toString().toLowerCase().replaceAll("\\s+", "");
+ final String queryTextLower = queryText.toLowerCase();
mApps.setFilter(new AlphabeticalAppsList.Filter() {
@Override
public boolean retainApp(AppInfo info, String sectionName) {
- String title = info.title.toString();
- if (sectionName.toLowerCase().contains(filterText)) {
+ if (sectionName.toLowerCase().contains(queryTextLower)) {
return true;
}
- String[] words = title.toLowerCase().split("\\s+");
+ String title = info.title.toString();
+ String[] words = SPLIT_PATTERN.split(title.toLowerCase());
for (int i = 0; i < words.length; i++) {
- if (words[i].startsWith(filterText)) {
+ if (words[i].startsWith(queryTextLower)) {
return true;
}
}
diff --git a/src/com/android/launcher3/AppsGridAdapter.java b/src/com/android/launcher3/AppsGridAdapter.java
index 259740c..9ecb2ee 100644
--- a/src/com/android/launcher3/AppsGridAdapter.java
+++ b/src/com/android/launcher3/AppsGridAdapter.java
@@ -35,13 +35,11 @@
*/
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 @@
}
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 @@
}
/**
- * 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 @@
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 @@
@Thunk int mStartMargin;
@Thunk int mSectionHeaderOffset;
@Thunk Paint mSectionTextPaint;
+ @Thunk Paint mPredictedAppsDividerPaint;
public AppsGridAdapter(Context context, AlphabeticalAppsList apps, int appsPerRow,
@@ -254,11 +295,17 @@
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 @@
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 @@
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/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index 65c6702..72eabf1 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -3051,4 +3051,21 @@
public boolean findVacantCell(int spanX, int spanY, int[] outXY) {
return Utilities.findVacantCell(outXY, spanX, spanY, mCountX, mCountY, mOccupied);
}
+
+ public boolean isRegionVacant(int x, int y, int spanX, int spanY) {
+ int x2 = x + spanX - 1;
+ int y2 = y + spanY - 1;
+ if (x < 0 || y < 0 || x2 >= mCountX || y2 >= mCountY) {
+ return false;
+ }
+ for (int i = x; i <= x2; i++) {
+ for (int j = y; j <= y2; j++) {
+ if (mOccupied[i][j]) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
}
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 94589ad..7a6e9a2 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -82,12 +82,11 @@
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;
diff --git a/src/com/android/launcher3/DragLayer.java b/src/com/android/launcher3/DragLayer.java
index 91f97fa..2efdb06 100644
--- a/src/com/android/launcher3/DragLayer.java
+++ b/src/com/android/launcher3/DragLayer.java
@@ -38,7 +38,6 @@
import android.widget.FrameLayout;
import android.widget.TextView;
-import com.android.launcher3.InsettableFrameLayout.LayoutParams;
import com.android.launcher3.util.Thunk;
import java.util.ArrayList;
@@ -153,6 +152,14 @@
return false;
}
+ private boolean isEventOverDropTargetBar(MotionEvent ev) {
+ getDescendantRectRelativeToSelf(mLauncher.getSearchBar(), mHitRect);
+ if (mHitRect.contains((int) ev.getX(), (int) ev.getY())) {
+ return true;
+ }
+ return false;
+ }
+
public void setBlockTouch(boolean block) {
mBlockTouches = block;
}
@@ -188,10 +195,16 @@
}
}
- getDescendantRectRelativeToSelf(currentFolder, hitRect);
if (!isEventOverFolder(currentFolder, ev)) {
- mLauncher.closeFolder();
- return true;
+ if (isInAccessibleDrag()) {
+ // Do not close the folder if in drag and drop.
+ if (!isEventOverDropTargetBar(ev)) {
+ return true;
+ }
+ } else {
+ mLauncher.closeFolder();
+ return true;
+ }
}
}
return false;
@@ -228,11 +241,12 @@
getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
if (accessibilityManager.isTouchExplorationEnabled()) {
final int action = ev.getAction();
- boolean isOverFolder;
+ boolean isOverFolderOrSearchBar;
switch (action) {
case MotionEvent.ACTION_HOVER_ENTER:
- isOverFolder = isEventOverFolder(currentFolder, ev);
- if (!isOverFolder) {
+ isOverFolderOrSearchBar = isEventOverFolder(currentFolder, ev) ||
+ (isInAccessibleDrag() && isEventOverDropTargetBar(ev));
+ if (!isOverFolderOrSearchBar) {
sendTapOutsideFolderAccessibilityEvent(currentFolder.isEditingName());
mHoverPointClosesFolder = true;
return true;
@@ -240,12 +254,13 @@
mHoverPointClosesFolder = false;
break;
case MotionEvent.ACTION_HOVER_MOVE:
- isOverFolder = isEventOverFolder(currentFolder, ev);
- if (!isOverFolder && !mHoverPointClosesFolder) {
+ isOverFolderOrSearchBar = isEventOverFolder(currentFolder, ev) ||
+ (isInAccessibleDrag() && isEventOverDropTargetBar(ev));
+ if (!isOverFolderOrSearchBar && !mHoverPointClosesFolder) {
sendTapOutsideFolderAccessibilityEvent(currentFolder.isEditingName());
mHoverPointClosesFolder = true;
return true;
- } else if (!isOverFolder) {
+ } else if (!isOverFolderOrSearchBar) {
return true;
}
mHoverPointClosesFolder = false;
@@ -268,6 +283,12 @@
}
}
+ private boolean isInAccessibleDrag() {
+ LauncherAccessibilityDelegate delegate = LauncherAppState
+ .getInstance().getAccessibilityDelegate();
+ return delegate != null && delegate.isInAccessibleDrag();
+ }
+
@Override
public boolean onRequestSendAccessibilityEvent(View child, AccessibilityEvent event) {
Folder currentFolder = mLauncher.getWorkspace().getOpenFolder();
@@ -275,6 +296,10 @@
if (child == currentFolder) {
return super.onRequestSendAccessibilityEvent(child, event);
}
+
+ if (isInAccessibleDrag() && child instanceof SearchDropTargetBar) {
+ return super.onRequestSendAccessibilityEvent(child, event);
+ }
// Skip propagating onRequestSendAccessibilityEvent all for other children
// when a folder is open
return false;
@@ -288,6 +313,10 @@
if (currentFolder != null) {
// Only add the folder as a child for accessibility when it is open
childrenForAccessibility.add(currentFolder);
+
+ if (isInAccessibleDrag()) {
+ childrenForAccessibility.add(mLauncher.getSearchBar());
+ }
} else {
super.addChildrenForAccessibility(childrenForAccessibility);
}
diff --git a/src/com/android/launcher3/DragView.java b/src/com/android/launcher3/DragView.java
index a4b6704..3eec3d9 100644
--- a/src/com/android/launcher3/DragView.java
+++ b/src/com/android/launcher3/DragView.java
@@ -129,6 +129,10 @@
int ms = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
measure(ms, ms);
mPaint = new Paint(Paint.FILTER_BITMAP_FLAG);
+
+ if (Utilities.isLmpOrAbove()) {
+ setElevation(getResources().getDimension(R.dimen.drag_elevation));
+ }
}
/** Sets the scale of the view over the normal workspace icon size. */
diff --git a/src/com/android/launcher3/Folder.java b/src/com/android/launcher3/Folder.java
index a282805..6ca4888 100644
--- a/src/com/android/launcher3/Folder.java
+++ b/src/com/android/launcher3/Folder.java
@@ -27,6 +27,7 @@
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
+import android.graphics.drawable.InsetDrawable;
import android.os.Build;
import android.text.InputType;
import android.text.Selection;
@@ -180,6 +181,15 @@
// name is complete, we have something to focus on, thus hiding the cursor and giving
// reliable behavior when clicking the text field (since it will always gain focus on click).
setFocusableInTouchMode(true);
+
+ if (Utilities.isLmpOrAbove()) {
+ int padding = getResources().getDimensionPixelSize(R.dimen.folder_shadow_padding);
+ setBackground(new InsetDrawable(
+ getResources().getDrawable(R.drawable.apps_list_bg),
+ padding, padding, padding, padding));
+ } else {
+ setBackgroundResource(R.drawable.quantum_panel);
+ }
}
@Override
@@ -275,6 +285,9 @@
for (int i = 0; i < mContent.getChildCount(); i++) {
mContent.getPageAt(i).enableAccessibleDrag(enable, CellLayout.FOLDER_ACCESSIBILITY_DRAG);
}
+
+ mFooter.setImportantForAccessibility(enable ? IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS :
+ IMPORTANT_FOR_ACCESSIBILITY_AUTO);
mLauncher.getWorkspace().setAddNewPageOnDrag(!enable);
}
@@ -296,13 +309,14 @@
mFolderName.setHint(sHintText);
// Convert to a string here to ensure that no other state associated with the text field
// gets saved.
- String newTitle = mFolderName.getText().toString();
+ CharSequence newTitle = mFolderName.getText();
mInfo.setTitle(newTitle);
LauncherModel.updateItemInDatabase(mLauncher, mInfo);
if (commit) {
sendCustomAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED,
- String.format(getContext().getString(R.string.folder_renamed), newTitle));
+ String.format(getContext().getString(R.string.folder_renamed),
+ newTitle.toString()));
}
// In order to clear the focus from the text field, we set the focus on ourself. This
// ensures that every time the field is clicked, focus is gained, giving reliable behavior.
@@ -470,9 +484,15 @@
PropertyValuesHolder tx = PropertyValuesHolder.ofFloat("translationX", transX, 0);
PropertyValuesHolder ty = PropertyValuesHolder.ofFloat("translationY", transY, 0);
+ Animator drift = LauncherAnimUtils.ofPropertyValuesHolder(this, tx, ty);
+ drift.setDuration(mMaterialExpandDuration);
+ drift.setStartDelay(mMaterialExpandStagger);
+ drift.setInterpolator(new LogDecelerateInterpolator(100, 0));
+
int rx = (int) Math.max(Math.max(width - getPivotX(), 0), getPivotX());
int ry = (int) Math.max(Math.max(height - getPivotY(), 0), getPivotY());
float radius = (float) Math.hypot(rx, ry);
+
AnimatorSet anim = LauncherAnimUtils.createAnimatorSet();
Animator reveal = LauncherAnimUtils.createCircularReveal(this, (int) getPivotX(),
(int) getPivotY(), 0, radius);
@@ -491,10 +511,6 @@
textAlpha.setStartDelay(mMaterialExpandStagger);
textAlpha.setInterpolator(new AccelerateInterpolator(1.5f));
- Animator drift = LauncherAnimUtils.ofPropertyValuesHolder(this, tx, ty);
- drift.setDuration(mMaterialExpandDuration);
- drift.setStartDelay(mMaterialExpandStagger);
- drift.setInterpolator(new LogDecelerateInterpolator(60, 0));
anim.play(drift);
anim.play(iconsAlpha);
@@ -504,10 +520,12 @@
openFolderAnim = anim;
mContentWrapper.setLayerType(LAYER_TYPE_HARDWARE, null);
+ mFooter.setLayerType(LAYER_TYPE_HARDWARE, null);
onCompleteRunnable = new Runnable() {
@Override
public void run() {
mContentWrapper.setLayerType(LAYER_TYPE_NONE, null);
+ mContentWrapper.setLayerType(LAYER_TYPE_NONE, null);
}
};
}
diff --git a/src/com/android/launcher3/FolderIcon.java b/src/com/android/launcher3/FolderIcon.java
index f5836c2..b161b1c 100644
--- a/src/com/android/launcher3/FolderIcon.java
+++ b/src/com/android/launcher3/FolderIcon.java
@@ -710,7 +710,7 @@
}
public void onTitleChanged(CharSequence title) {
- mFolderName.setText(title.toString());
+ mFolderName.setText(title);
setContentDescription(String.format(getContext().getString(R.string.folder_name_format),
title));
}
diff --git a/src/com/android/launcher3/FolderInfo.java b/src/com/android/launcher3/FolderInfo.java
index 80b1564..9675371 100644
--- a/src/com/android/launcher3/FolderInfo.java
+++ b/src/com/android/launcher3/FolderInfo.java
@@ -87,7 +87,7 @@
}
public void setTitle(CharSequence title) {
- this.title = title;
+ this.title = Utilities.trim(title);
for (int i = 0; i < listeners.size(); i++) {
listeners.get(i).onTitleChanged(title);
}
diff --git a/src/com/android/launcher3/FolderPagedView.java b/src/com/android/launcher3/FolderPagedView.java
index 9f2f225..348655f 100644
--- a/src/com/android/launcher3/FolderPagedView.java
+++ b/src/com/android/launcher3/FolderPagedView.java
@@ -496,6 +496,10 @@
}
}
+ public int getAllocatedContentSize() {
+ return mAllocatedContentSize;
+ }
+
/**
* Reorders the items such that the {@param empty} spot moves to {@param target}
*/
diff --git a/src/com/android/launcher3/IconCache.java b/src/com/android/launcher3/IconCache.java
index 6c2aa39..0596fbe 100644
--- a/src/com/android/launcher3/IconCache.java
+++ b/src/com/android/launcher3/IconCache.java
@@ -400,7 +400,7 @@
UserHandleCompat user = info == null ? application.user : info.getUser();
CacheEntry entry = cacheLocked(application.componentName, info, user,
false, useLowResIcon);
- application.title = entry.title;
+ application.title = Utilities.trim(entry.title);
application.iconBitmap = getNonNullIcon(entry, user);
application.contentDescription = entry.contentDescription;
application.usingLowResIcon = entry.isLowResIcon;
@@ -413,7 +413,7 @@
CacheEntry entry = cacheLocked(application.componentName, null, application.user,
false, application.usingLowResIcon);
if (entry.icon != null && !isDefaultIcon(entry.icon, application.user)) {
- application.title = entry.title;
+ application.title = Utilities.trim(entry.title);
application.iconBitmap = entry.icon;
application.contentDescription = entry.contentDescription;
application.usingLowResIcon = entry.isLowResIcon;
@@ -464,7 +464,7 @@
UserHandleCompat user, boolean usePkgIcon, boolean useLowResIcon) {
CacheEntry entry = cacheLocked(component, info, user, usePkgIcon, useLowResIcon);
shortcutInfo.setIcon(getNonNullIcon(entry, user));
- shortcutInfo.title = entry.title;
+ shortcutInfo.title = Utilities.trim(entry.title);
shortcutInfo.usingFallbackIcon = isDefaultIcon(entry.icon, user);
shortcutInfo.usingLowResIcon = entry.isLowResIcon;
}
@@ -477,7 +477,7 @@
PackageItemInfo infoOut) {
CacheEntry entry = getEntryForPackageLocked(packageName, user, useLowResIcon);
infoOut.iconBitmap = getNonNullIcon(entry, user);
- infoOut.title = entry.title;
+ infoOut.title = Utilities.trim(entry.title);
infoOut.usingLowResIcon = entry.isLowResIcon;
infoOut.contentDescription = entry.contentDescription;
}
@@ -530,7 +530,7 @@
}
if (TextUtils.isEmpty(entry.title) && info != null) {
- entry.title = info.getLabel().toString();
+ entry.title = info.getLabel();
entry.contentDescription = mUserManager.getBadgedLabelForUser(entry.title, user);
}
}
diff --git a/src/com/android/launcher3/InstallShortcutReceiver.java b/src/com/android/launcher3/InstallShortcutReceiver.java
index 23bcc85..115598f 100644
--- a/src/com/android/launcher3/InstallShortcutReceiver.java
+++ b/src/com/android/launcher3/InstallShortcutReceiver.java
@@ -247,7 +247,7 @@
try {
PackageManager pm = context.getPackageManager();
ActivityInfo info = pm.getActivityInfo(intent.getComponent(), 0);
- name = info.loadLabel(pm).toString();
+ name = info.loadLabel(pm);
} catch (PackageManager.NameNotFoundException nnfe) {
return "";
}
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 5645759..11b1e9a 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -64,6 +64,7 @@
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;
@@ -1018,16 +1019,22 @@
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();
+ }
}
}
@@ -2619,7 +2626,9 @@
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 */);
}
}
@@ -3397,10 +3406,13 @@
/**
* 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);
}
@@ -3509,12 +3521,26 @@
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/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/LauncherAccessibilityDelegate.java
index 8a9a050..3992e63 100644
--- a/src/com/android/launcher3/LauncherAccessibilityDelegate.java
+++ b/src/com/android/launcher3/LauncherAccessibilityDelegate.java
@@ -1,6 +1,9 @@
package com.android.launcher3;
import android.annotation.TargetApi;
+import android.app.AlertDialog;
+import android.appwidget.AppWidgetProviderInfo;
+import android.content.DialogInterface;
import android.graphics.Rect;
import android.os.Build;
import android.os.Bundle;
@@ -28,6 +31,7 @@
private static final int ADD_TO_WORKSPACE = R.id.action_add_to_workspace;
private static final int MOVE = R.id.action_move;
private static final int MOVE_TO_WORKSPACE = R.id.action_move_to_workspace;
+ private static final int RESIZE = R.id.action_resize;
public enum DragType {
ICON,
@@ -62,6 +66,8 @@
launcher.getText(R.string.action_move)));
mActions.put(MOVE_TO_WORKSPACE, new AccessibilityAction(MOVE_TO_WORKSPACE,
launcher.getText(R.string.action_move_to_workspace)));
+ mActions.put(RESIZE, new AccessibilityAction(RESIZE,
+ launcher.getText(R.string.action_resize)));
}
@Override
@@ -87,6 +93,10 @@
if (item.container >= 0) {
info.addAction(mActions.get(MOVE_TO_WORKSPACE));
+ } else if (item instanceof LauncherAppWidgetInfo) {
+ if (!getSupportedResizeActions(host, (LauncherAppWidgetInfo) item).isEmpty()) {
+ info.addAction(mActions.get(RESIZE));
+ }
}
} if ((item instanceof AppInfo) || (item instanceof PendingAddItemInfo)) {
info.addAction(mActions.get(ADD_TO_WORKSPACE));
@@ -102,7 +112,7 @@
return super.performAccessibilityAction(host, action, args);
}
- public boolean performAction(View host, final ItemInfo item, int action) {
+ public boolean performAction(final View host, final ItemInfo item, int action) {
if (action == REMOVE) {
if (DeleteDropTarget.removeWorkspaceOrFolderItem(mLauncher, item, host)) {
announceConfirmation(R.string.item_removed);
@@ -167,10 +177,97 @@
announceConfirmation(R.string.item_moved);
}
});
+ } else if (action == RESIZE) {
+ final LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) item;
+ final ArrayList<Integer> actions = getSupportedResizeActions(host, info);
+ CharSequence[] labels = new CharSequence[actions.size()];
+ for (int i = 0; i < actions.size(); i++) {
+ labels[i] = mLauncher.getText(actions.get(i));
+ }
+
+ new AlertDialog.Builder(mLauncher)
+ .setTitle(R.string.action_resize)
+ .setItems(labels, new DialogInterface.OnClickListener() {
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ performResizeAction(actions.get(which), host, info);
+ dialog.dismiss();
+ }
+ })
+ .show();
}
return false;
}
+ private ArrayList<Integer> getSupportedResizeActions(View host, LauncherAppWidgetInfo info) {
+ AppWidgetProviderInfo providerInfo = ((LauncherAppWidgetHostView) host).getAppWidgetInfo();
+ ArrayList<Integer> actions = new ArrayList<>();
+
+ CellLayout layout = (CellLayout) host.getParent().getParent();
+ if ((providerInfo.resizeMode & AppWidgetProviderInfo.RESIZE_HORIZONTAL) != 0) {
+ if (layout.isRegionVacant(info.cellX + info.spanX, info.cellY, 1, info.spanY) ||
+ layout.isRegionVacant(info.cellX - 1, info.cellY, 1, info.spanY)) {
+ actions.add(R.string.action_increase_width);
+ }
+
+ if (info.spanX > info.minSpanX && info.spanX > 1) {
+ actions.add(R.string.action_decrease_width);
+ }
+ }
+
+ if ((providerInfo.resizeMode & AppWidgetProviderInfo.RESIZE_VERTICAL) != 0) {
+ if (layout.isRegionVacant(info.cellX, info.cellY + info.spanY, info.spanX, 1) ||
+ layout.isRegionVacant(info.cellX, info.cellY - 1, info.spanX, 1)) {
+ actions.add(R.string.action_increase_height);
+ }
+
+ if (info.spanY > info.minSpanY && info.spanY > 1) {
+ actions.add(R.string.action_decrease_height);
+ }
+ }
+ return actions;
+ }
+
+ private void performResizeAction(int action, View host, LauncherAppWidgetInfo info) {
+ CellLayout.LayoutParams lp = (CellLayout.LayoutParams) host.getLayoutParams();
+ CellLayout layout = (CellLayout) host.getParent().getParent();
+ layout.markCellsAsUnoccupiedForView(host);
+
+ if (action == R.string.action_increase_width) {
+ if (((host.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL)
+ && layout.isRegionVacant(info.cellX - 1, info.cellY, 1, info.spanY))
+ || !layout.isRegionVacant(info.cellX + info.spanX, info.cellY, 1, info.spanY)) {
+ lp.cellX --;
+ info.cellX --;
+ }
+ lp.cellHSpan ++;
+ info.spanX ++;
+ } else if (action == R.string.action_decrease_width) {
+ lp.cellHSpan --;
+ info.spanX --;
+ } else if (action == R.string.action_increase_height) {
+ if (!layout.isRegionVacant(info.cellX, info.cellY + info.spanY, info.spanX, 1)) {
+ lp.cellY --;
+ info.cellY --;
+ }
+ lp.cellVSpan ++;
+ info.spanY ++;
+ } else if (action == R.string.action_decrease_height) {
+ lp.cellVSpan --;
+ info.spanY --;
+ }
+
+ layout.markCellsAsOccupiedForView(host);
+ Rect sizeRange = new Rect();
+ AppWidgetResizeFrame.getWidgetSizeRanges(mLauncher, info.spanX, info.spanY, sizeRange);
+ ((LauncherAppWidgetHostView) host).updateAppWidgetSize(null,
+ sizeRange.left, sizeRange.top, sizeRange.right, sizeRange.bottom);
+ host.requestLayout();
+ LauncherModel.updateItemInDatabase(mLauncher, info);
+ announceConfirmation(mLauncher.getString(R.string.widget_resized, info.spanX, info.spanY));
+ }
+
@Thunk void announceConfirmation(int resId) {
announceConfirmation(mLauncher.getResources().getString(resId));
}
diff --git a/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java b/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java
index bb4580c..af680f2 100644
--- a/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java
+++ b/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java
@@ -66,7 +66,7 @@
public String getLabel(PackageManager packageManager) {
if (isCustomWidget) {
- return label.toString().trim();
+ return Utilities.trim(label);
}
return super.loadLabel(packageManager);
}
diff --git a/src/com/android/launcher3/LauncherCallbacks.java b/src/com/android/launcher3/LauncherCallbacks.java
index 25c86c9..0124d1f 100644
--- a/src/com/android/launcher3/LauncherCallbacks.java
+++ b/src/com/android/launcher3/LauncherCallbacks.java
@@ -11,6 +11,7 @@
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 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 8174af0..14ad601 100644
--- a/src/com/android/launcher3/LauncherExtension.java
+++ b/src/com/android/launcher3/LauncherExtension.java
@@ -15,6 +15,7 @@
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 @@
}
@Override
+ public List<ComponentName> getPredictedApps() {
+ return new ArrayList<>();
+ }
+
+ @Override
public boolean isLauncherPreinstalled() {
return false;
}
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index e81c8c2..3987c02 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -969,7 +969,7 @@
break;
}
- folderInfo.title = c.getString(titleIndex);
+ folderInfo.title = Utilities.trim(c.getString(titleIndex));
folderInfo.id = id;
folderInfo.container = c.getInt(containerIndex);
folderInfo.screenId = c.getInt(screenIndex);
@@ -2144,7 +2144,7 @@
id = c.getLong(idIndex);
FolderInfo folderInfo = findOrMakeFolder(sBgFolders, id);
- folderInfo.title = c.getString(titleIndex);
+ folderInfo.title = Utilities.trim(c.getString(titleIndex));
folderInfo.id = id;
container = c.getInt(containerIndex);
folderInfo.container = container;
@@ -3199,7 +3199,7 @@
if (appInfo != null && Intent.ACTION_MAIN.equals(si.intent.getAction())
&& si.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
si.updateIcon(mIconCache);
- si.title = appInfo.title.toString();
+ si.title = Utilities.trim(appInfo.title);
si.contentDescription = appInfo.contentDescription;
infoUpdated = true;
}
@@ -3428,18 +3428,17 @@
if ((promiseType & ShortcutInfo.FLAG_RESTORED_ICON) != 0) {
String title = (cursor != null) ? cursor.getString(titleIndex) : null;
if (!TextUtils.isEmpty(title)) {
- info.title = title;
+ info.title = Utilities.trim(title);
}
} else if ((promiseType & ShortcutInfo.FLAG_AUTOINTALL_ICON) != 0) {
if (TextUtils.isEmpty(info.title)) {
- info.title = (cursor != null) ? cursor.getString(titleIndex) : "";
+ info.title = (cursor != null) ? Utilities.trim(cursor.getString(titleIndex)) : "";
}
} else {
throw new InvalidParameterException("Invalid restoreType " + promiseType);
}
- info.contentDescription = mUserManager.getBadgedLabelForUser(
- info.title.toString(), info.user);
+ info.contentDescription = mUserManager.getBadgedLabelForUser(info.title, info.user);
info.itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
info.promisedIntent = intent;
info.status = promiseType;
@@ -3501,7 +3500,7 @@
// from the db
if (TextUtils.isEmpty(info.title) && c != null) {
- info.title = c.getString(titleIndex);
+ info.title = Utilities.trim(c.getString(titleIndex));
}
// fall back to the class name of the activity
@@ -3511,8 +3510,7 @@
info.itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
info.user = user;
- info.contentDescription = mUserManager.getBadgedLabelForUser(
- info.title.toString(), info.user);
+ info.contentDescription = mUserManager.getBadgedLabelForUser(info.title, info.user);
if (lai != null) {
info.flags = AppInfo.initFlags(lai);
}
@@ -3578,7 +3576,7 @@
// TODO: If there's an explicit component and we can't install that, delete it.
- info.title = c.getString(titleIndex);
+ info.title = Utilities.trim(c.getString(titleIndex));
int iconType = c.getInt(iconTypeIndex);
switch (iconType) {
@@ -3656,9 +3654,8 @@
}
info.setIcon(icon);
- info.title = name;
- info.contentDescription = mUserManager.getBadgedLabelForUser(
- info.title.toString(), info.user);
+ info.title = Utilities.trim(name);
+ info.contentDescription = mUserManager.getBadgedLabelForUser(info.title, info.user);
info.intent = intent;
info.customIcon = customIcon;
info.iconResource = iconResource;
@@ -3699,16 +3696,16 @@
labelA = mLabelCache.get(a);
} else {
labelA = (a instanceof LauncherAppWidgetProviderInfo)
- ? mManager.loadLabel((LauncherAppWidgetProviderInfo) a)
- : ((ResolveInfo) a).loadLabel(mPackageManager).toString().trim();
+ ? Utilities.trim(mManager.loadLabel((LauncherAppWidgetProviderInfo) a))
+ : Utilities.trim(((ResolveInfo) a).loadLabel(mPackageManager));
mLabelCache.put(a, labelA);
}
if (mLabelCache.containsKey(b)) {
labelB = mLabelCache.get(b);
} else {
labelB = (b instanceof LauncherAppWidgetProviderInfo)
- ? mManager.loadLabel((LauncherAppWidgetProviderInfo) b)
- : ((ResolveInfo) b).loadLabel(mPackageManager).toString().trim();
+ ? Utilities.trim(mManager.loadLabel((LauncherAppWidgetProviderInfo) b))
+ : Utilities.trim(((ResolveInfo) b).loadLabel(mPackageManager));
mLabelCache.put(b, labelB);
}
return mCollator.compare(labelA, labelB);
diff --git a/src/com/android/launcher3/ShortcutInfo.java b/src/com/android/launcher3/ShortcutInfo.java
index 6354fcd..8be4872 100644
--- a/src/com/android/launcher3/ShortcutInfo.java
+++ b/src/com/android/launcher3/ShortcutInfo.java
@@ -153,7 +153,7 @@
Bitmap icon, UserHandleCompat user) {
this();
this.intent = intent;
- this.title = title;
+ this.title = Utilities.trim(title);
this.contentDescription = contentDescription;
mIcon = icon;
this.user = user;
@@ -161,7 +161,7 @@
public ShortcutInfo(Context context, ShortcutInfo info) {
super(info);
- title = info.title.toString();
+ title = Utilities.trim(info.title);
intent = new Intent(info.intent);
if (info.iconResource != null) {
iconResource = new Intent.ShortcutIconResource();
@@ -179,7 +179,7 @@
/** TODO: Remove this. It's only called by ApplicationInfo.makeShortcut. */
public ShortcutInfo(AppInfo info) {
super(info);
- title = info.title.toString();
+ title = Utilities.trim(info.title);
intent = new Intent(info.intent);
customIcon = false;
flags = info.flags;
@@ -281,7 +281,7 @@
public static ShortcutInfo fromActivityInfo(LauncherActivityInfoCompat info, Context context) {
final ShortcutInfo shortcut = new ShortcutInfo();
shortcut.user = info.getUser();
- shortcut.title = info.getLabel().toString();
+ shortcut.title = Utilities.trim(info.getLabel());
shortcut.contentDescription = UserManagerCompat.getInstance(context)
.getBadgedLabelForUser(info.getLabel(), info.getUser());
shortcut.customIcon = false;
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index 2dbf078..2981747 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -54,6 +54,8 @@
import java.io.IOException;
import java.util.ArrayList;
import java.util.Comparator;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
/**
* Various utilities shared amongst the Launcher's classes.
@@ -67,6 +69,9 @@
private static final Rect sOldBounds = new Rect();
private static final Canvas sCanvas = new Canvas();
+ private static final Pattern sTrimPattern =
+ Pattern.compile("^[\\s|\\p{javaSpaceChar}]*(.*)[\\s|\\p{javaSpaceChar}]*$");
+
static {
sCanvas.setDrawFilter(new PaintFlagsDrawFilter(Paint.DITHER_FLAG,
Paint.FILTER_BITMAP_FLAG));
@@ -616,4 +621,14 @@
return false;
}
+
+ /**
+ * Trims the string, removing all whitespace at the beginning and end of the string.
+ * Non-breaking whitespaces are also removed.
+ */
+ public static String trim(CharSequence s) {
+ // Just strip any sequence of whitespace or java space characters from the beginning and end
+ Matcher m = sTrimPattern.matcher(s);
+ return m.replaceAll("$1");
+ }
}
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 5c2121a..55deb85 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -4185,7 +4185,7 @@
&& packageNames.contains(cn.getPackageName())) {
shortcutInfo.isDisabled |= reason;
BubbleTextView shortcut = (BubbleTextView) v;
- shortcut.applyFromShortcutInfo(shortcutInfo, mIconCache, true, false);
+ shortcut.applyFromShortcutInfo(shortcutInfo, mIconCache, false);
if (parent != null) {
parent.invalidate();
@@ -4371,7 +4371,7 @@
BubbleTextView shortcut = (BubbleTextView) v;
boolean oldPromiseState = getTextViewIcon(shortcut)
instanceof PreloadIconDrawable;
- shortcut.applyFromShortcutInfo(si, mIconCache, true,
+ shortcut.applyFromShortcutInfo(si, mIconCache, false,
si.isPromise() != oldPromiseState);
if (parent != null) {
diff --git a/src/com/android/launcher3/accessibility/FolderAccessibilityHelper.java b/src/com/android/launcher3/accessibility/FolderAccessibilityHelper.java
index fc105b4..ff99890 100644
--- a/src/com/android/launcher3/accessibility/FolderAccessibilityHelper.java
+++ b/src/com/android/launcher3/accessibility/FolderAccessibilityHelper.java
@@ -24,23 +24,29 @@
* Implementation of {@link DragAndDropAccessibilityDelegate} to support DnD in a folder.
*/
public class FolderAccessibilityHelper extends DragAndDropAccessibilityDelegate {
+
+ /**
+ * 0-index position for the first cell in {@link #mView} in {@link #mParent}.
+ */
private final int mStartPosition;
+ private final FolderPagedView mParent;
+
public FolderAccessibilityHelper(CellLayout layout) {
super(layout);
- FolderPagedView parent = (FolderPagedView) layout.getParent();
+ mParent = (FolderPagedView) layout.getParent();
- int index = parent.indexOfChild(layout);
- mStartPosition = 1 + index * layout.getCountX() * layout.getCountY();
+ int index = mParent.indexOfChild(layout);
+ mStartPosition = index * layout.getCountX() * layout.getCountY();
}
@Override
protected int intersectsValidDropTarget(int id) {
- return id;
+ return Math.min(id, mParent.getAllocatedContentSize() - mStartPosition - 1);
}
@Override
protected String getLocationDescriptionForIconDrop(int id) {
- return mContext.getString(R.string.move_to_position, id + mStartPosition);
+ return mContext.getString(R.string.move_to_position, id + mStartPosition + 1);
}
@Override
diff --git a/src/com/android/launcher3/accessibility/WorkspaceAccessibilityHelper.java b/src/com/android/launcher3/accessibility/WorkspaceAccessibilityHelper.java
index 42e9e3c..6f89d0e 100644
--- a/src/com/android/launcher3/accessibility/WorkspaceAccessibilityHelper.java
+++ b/src/com/android/launcher3/accessibility/WorkspaceAccessibilityHelper.java
@@ -145,7 +145,7 @@
if (info instanceof ShortcutInfo) {
return mContext.getString(R.string.create_folder_with, info.title);
} else if (info instanceof FolderInfo) {
- if (TextUtils.isEmpty(info.title.toString().trim())) {
+ if (TextUtils.isEmpty(info.title)) {
// Find the first item in the folder.
FolderInfo folder = (FolderInfo) info;
ShortcutInfo firstItem = null;
diff --git a/src/com/android/launcher3/compat/AlphabeticIndexCompat.java b/src/com/android/launcher3/compat/AlphabeticIndexCompat.java
index f890706..18cdc81 100644
--- a/src/com/android/launcher3/compat/AlphabeticIndexCompat.java
+++ b/src/com/android/launcher3/compat/AlphabeticIndexCompat.java
@@ -1,6 +1,7 @@
package com.android.launcher3.compat;
import android.content.Context;
+import com.android.launcher3.Utilities;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
@@ -102,10 +103,11 @@
/**
* Computes the section name for an given string {@param s}.
*/
- public String computeSectionName(String s) {
+ public String computeSectionName(CharSequence cs) {
+ String s = Utilities.trim(cs);
String sectionName = getBucketLabel(getBucketIndex(s));
- if (sectionName.trim().isEmpty() && s.length() > 0) {
- boolean startsWithDigit = Character.isDigit(Character.codePointAt(s.trim(), 0));
+ if (Utilities.trim(sectionName).isEmpty() && s.length() > 0) {
+ boolean startsWithDigit = Character.isDigit(s.codePointAt(0));
if (startsWithDigit) {
// Digit section
return "#";
diff --git a/src/com/android/launcher3/compat/AppWidgetManagerCompatV16.java b/src/com/android/launcher3/compat/AppWidgetManagerCompatV16.java
index 767f16f..967b53b 100644
--- a/src/com/android/launcher3/compat/AppWidgetManagerCompatV16.java
+++ b/src/com/android/launcher3/compat/AppWidgetManagerCompatV16.java
@@ -46,7 +46,7 @@
@Override
public String loadLabel(LauncherAppWidgetProviderInfo info) {
- return info.label.trim();
+ return Utilities.trim(info.label);
}
@Override
diff --git a/src/com/android/launcher3/widget/PackageItemInfo.java b/src/com/android/launcher3/widget/PackageItemInfo.java
index 1a1de55..8f45a77 100644
--- a/src/com/android/launcher3/widget/PackageItemInfo.java
+++ b/src/com/android/launcher3/widget/PackageItemInfo.java
@@ -49,7 +49,7 @@
@Override
public String toString() {
- return "PackageItemInfo(title=" + title.toString() + " id=" + this.id
+ return "PackageItemInfo(title=" + title + " id=" + this.id
+ " type=" + this.itemType + " container=" + this.container
+ " screen=" + screenId + " cellX=" + cellX + " cellY=" + cellY
+ " spanX=" + spanX + " spanY=" + spanY + " dropPos=" + Arrays.toString(dropPos)
diff --git a/src/com/android/launcher3/widget/WidgetCell.java b/src/com/android/launcher3/widget/WidgetCell.java
index 2df170e..8779371 100644
--- a/src/com/android/launcher3/widget/WidgetCell.java
+++ b/src/com/android/launcher3/widget/WidgetCell.java
@@ -49,7 +49,16 @@
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 @@
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 @@
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 @@
mWidgetImage.setImageDrawable(null);
mWidgetName.setText(null);
mWidgetDims.setText(null);
+ setSeparator(true);
if (mActiveRequest != null) {
mActiveRequest.cleanup();
@@ -222,11 +248,17 @@
* Helper method to get the string info of the tag.
*/
private String getTagToString() {
- if (getTag() instanceof PendingAddWidgetInfo) {
- return ((PendingAddWidgetInfo)getTag()).toString();
- } else if (getTag() instanceof PendingAddShortcutInfo) {
- return ((PendingAddShortcutInfo)getTag()).toString();
+ if (getTag() instanceof PendingAddWidgetInfo ||
+ getTag() instanceof PendingAddShortcutInfo) {
+ return getTag().toString();
}
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 0000000..05760ae
--- /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 a7728a1..8b0a43b 100644
--- a/src/com/android/launcher3/widget/WidgetsListAdapter.java
+++ b/src/com/android/launcher3/widget/WidgetsListAdapter.java
@@ -23,6 +23,7 @@
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 @@
// 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 @@
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);
}