Merge "Exploring dense all apps layout." into ub-launcher3-burnaby

This commit is contained in:
Winson Chung
2015-05-12 18:40:31 +00:00
committed by Android (Google) Code Review
9 changed files with 115 additions and 36 deletions
+1 -1
View File
@@ -18,7 +18,7 @@
android:id="@+id/empty_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:paddingTop="24dp"
android:paddingBottom="24dp"
android:paddingRight="@dimen/apps_grid_view_start_margin"
+3 -3
View File
@@ -18,10 +18,10 @@
<dimen name="app_icon_size">64dp</dimen>
<!-- Apps view -->
<dimen name="apps_container_inset">24dp</dimen>
<dimen name="apps_grid_view_start_margin">64dp</dimen>
<dimen name="apps_container_inset">18dp</dimen>
<dimen name="apps_grid_view_start_margin">0dp</dimen>
<dimen name="apps_view_section_text_size">26sp</dimen>
<dimen name="apps_view_row_height">76dp</dimen>
<dimen name="apps_view_row_height">72dp</dimen>
<dimen name="apps_icon_top_bottom_padding">12dp</dimen>
<!-- AppsCustomize -->
+1 -1
View File
@@ -16,7 +16,7 @@
<resources>
<dimen name="app_icon_size">72dp</dimen>
<dimen name="apps_search_bar_height">56dp</dimen>
<dimen name="apps_search_bar_height">54dp</dimen>
<dimen name="apps_icon_top_bottom_padding">16dp</dimen>
<!-- QSB -->
@@ -3,6 +3,7 @@ package com.android.launcher3;
import android.content.ComponentName;
import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import com.android.launcher3.compat.AlphabeticIndexCompat;
import com.android.launcher3.compat.UserHandleCompat;
import com.android.launcher3.compat.UserManagerCompat;
@@ -77,6 +78,9 @@ class AppNameComparator {
*/
public class AlphabeticalAppsList {
public static final String TAG = "AlphabeticalAppsList";
private static final boolean DEBUG = false;
/**
* Info about a section in the alphabetic list
*/
@@ -162,11 +166,58 @@ public class AlphabeticalAppsList {
* A filter interface to limit the set of applications in the apps list.
*/
public interface Filter {
public boolean retainApp(AppInfo info, String sectionName);
boolean retainApp(AppInfo info, String sectionName);
}
// The maximum number of rows allowed in a merged section before we stop merging
private static final int MAX_ROWS_IN_MERGED_SECTION = 3;
/**
* Common interface for different merging strategies.
*/
private interface MergeAlgorithm {
boolean continueMerging(int sectionAppCount, int numAppsPerRow, int mergeCount);
}
/**
* The logic we use to merge sections on tablets.
*/
private static class TabletMergeAlgorithm implements MergeAlgorithm {
@Override
public boolean continueMerging(int sectionAppCount, int numAppsPerRow, int mergeCount) {
// Merge EVERYTHING
return true;
}
}
/**
* The logic we use to merge sections on phones.
*/
private static class PhoneMergeAlgorithm implements MergeAlgorithm {
private int mMinAppsPerRow;
private int mMinRowsInMergedSection;
private int mMaxAllowableMerges;
public PhoneMergeAlgorithm(int minAppsPerRow, int minRowsInMergedSection, int maxNumMerges) {
mMinAppsPerRow = minAppsPerRow;
mMinRowsInMergedSection = minRowsInMergedSection;
mMaxAllowableMerges = maxNumMerges;
}
@Override
public boolean continueMerging(int sectionAppCount, int numAppsPerRow, int mergeCount) {
// Continue merging if the number of hanging apps on the final row is less than some
// fixed number (ragged), the merged rows has yet to exceed some minimum row count,
// and while the number of merged sections is less than some fixed number of merges
int rows = sectionAppCount / numAppsPerRow;
int cols = sectionAppCount % numAppsPerRow;
return (0 < cols && cols < mMinAppsPerRow) &&
rows < mMinRowsInMergedSection &&
mergeCount < mMaxAllowableMerges;
}
}
private static final int MIN_ROWS_IN_MERGED_SECTION_PHONE = 3;
private static final int MAX_NUM_MERGES_PHONE = 2;
private List<AppInfo> mApps = new ArrayList<>();
private List<AppInfo> mFilteredApps = new ArrayList<>();
@@ -174,13 +225,13 @@ public class AlphabeticalAppsList {
private List<SectionInfo> mSections = new ArrayList<>();
private List<FastScrollSectionInfo> mFastScrollerSections = new ArrayList<>();
private List<ComponentName> mPredictedApps = new ArrayList<>();
private HashMap<CharSequence, String> mCachedSectionNames = new HashMap<>();
private RecyclerView.Adapter mAdapter;
private Filter mFilter;
private AlphabeticIndexCompat mIndexer;
private AppNameComparator mAppNameComparator;
private MergeAlgorithm mMergeAlgorithm;
private int mNumAppsPerRow;
// The maximum number of section merges we allow at a given time before we stop merging
private int mMaxAllowableMerges = Integer.MAX_VALUE;
public AlphabeticalAppsList(Context context, int numAppsPerRow) {
mIndexer = new AlphabeticIndexCompat(context);
@@ -193,7 +244,16 @@ public class AlphabeticalAppsList {
*/
public void setNumAppsPerRow(int numAppsPerRow) {
mNumAppsPerRow = numAppsPerRow;
mMaxAllowableMerges = (int) Math.ceil(numAppsPerRow / 2f);
// Update the merge algorithm
DeviceProfile grid = LauncherAppState.getInstance().getDynamicGrid().getDeviceProfile();
if (grid.isPhone()) {
mMergeAlgorithm = new PhoneMergeAlgorithm((int) Math.ceil(numAppsPerRow / 2f),
MIN_ROWS_IN_MERGED_SECTION_PHONE, MAX_NUM_MERGES_PHONE);
} else {
mMergeAlgorithm = new TabletMergeAlgorithm();
}
onAppsUpdated();
}
@@ -392,7 +452,15 @@ public class AlphabeticalAppsList {
for (int i = 0; i < numApps; i++) {
boolean isPredictedApp = i < numPredictedApps;
AppInfo info = allApps.get(i);
String sectionName = isPredictedApp ? "" : mIndexer.computeSectionName(info.title);
String sectionName = "";
if (!isPredictedApp) {
// Only cache section names from non-predicted apps
sectionName = mCachedSectionNames.get(info.title);
if (sectionName == null) {
sectionName = mIndexer.computeSectionName(info.title);
mCachedSectionNames.put(info.title, sectionName);
}
}
// Check if we want to retain this app
if (mFilter != null && !mFilter.retainApp(info, sectionName)) {
@@ -429,20 +497,14 @@ public class AlphabeticalAppsList {
// Go through each section and try and merge some of the sections
if (AppsContainerView.GRID_MERGE_SECTIONS && !hasFilter()) {
int minNumAppsPerRow = (int) Math.ceil(mNumAppsPerRow / 2f);
int sectionAppCount = 0;
for (int i = 0; i < mSections.size(); i++) {
SectionInfo section = mSections.get(i);
sectionAppCount = section.numApps;
int mergeCount = 1;
// Merge rows if the last app in this section is in a column that is greater than
// 0, but less than the min number of apps per row. In addition, apply the
// constraint to stop merging if the number of rows in the section is greater than
// some limit, and also if there are no lessons to merge.
while (0 < (sectionAppCount % mNumAppsPerRow) &&
(sectionAppCount % mNumAppsPerRow) < minNumAppsPerRow &&
(sectionAppCount / mNumAppsPerRow) < MAX_ROWS_IN_MERGED_SECTION &&
// Merge rows based on the current strategy
while (mMergeAlgorithm.continueMerging(sectionAppCount, mNumAppsPerRow, mergeCount) &&
(i + 1) < mSections.size()) {
SectionInfo nextSection = mSections.remove(i + 1);
@@ -465,10 +527,13 @@ public class AlphabeticalAppsList {
}
section.numApps += nextSection.numApps;
sectionAppCount += nextSection.numApps;
mergeCount++;
if (mergeCount >= mMaxAllowableMerges) {
break;
if (DEBUG) {
Log.d(TAG, "Merging: " + nextSection.firstAppItem.sectionName +
" to " + section.firstAppItem.sectionName +
" mergedNumRows: " + (sectionAppCount / mNumAppsPerRow));
}
mergeCount++;
}
}
}
@@ -213,7 +213,13 @@ public class AppsContainerView extends BaseContainerView implements DragSource,
new AppsContainerSearchEditTextView.OnBackKeyListener() {
@Override
public void onBackKey() {
hideSearchField(true, true);
// Only hide the search field if there is no query, or if there
// are no filtered results
String query = Utilities.trim(
mSearchBarEditView.getEditableText().toString());
if (query.isEmpty() || mApps.hasNoFilteredResults()) {
hideSearchField(true, true);
}
}
});
}
@@ -277,15 +283,17 @@ public class AppsContainerView extends BaseContainerView implements DragSource,
} else {
// If there are fixed bounds, then we update the padding to reflect the fixed bounds.
setPadding(mFixedBounds.left, mFixedBounds.top, getMeasuredWidth() - mFixedBounds.right,
mInsets.bottom);
mFixedBounds.bottom);
}
// Update the apps recycler view, inset it by the container inset as well
DeviceProfile grid = LauncherAppState.getInstance().getDynamicGrid().getDeviceProfile();
int startMargin = grid.isPhone() ? mContentMarginStart : 0;
int inset = mFixedBounds.isEmpty() ? mContainerInset : mFixedBoundsContainerInset;
if (isRtl) {
mAppsRecyclerView.setPadding(inset, inset, inset + mContentMarginStart, inset);
mAppsRecyclerView.setPadding(inset, inset, inset + startMargin, inset);
} else {
mAppsRecyclerView.setPadding(inset + mContentMarginStart, inset, inset, inset);
mAppsRecyclerView.setPadding(inset + startMargin, inset, inset, inset);
}
// Update the header bar
@@ -90,6 +90,7 @@ class AppsGridAdapter extends RecyclerView.Adapter<AppsGridAdapter.ViewHolder> {
return;
}
DeviceProfile grid = LauncherAppState.getInstance().getDynamicGrid().getDeviceProfile();
List<AlphabeticalAppsList.AdapterItem> items = mApps.getAdapterItems();
boolean hasDrawnPredictedAppDivider = false;
int childCount = parent.getChildCount();
@@ -104,8 +105,6 @@ class AppsGridAdapter extends RecyclerView.Adapter<AppsGridAdapter.ViewHolder> {
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();
@@ -113,7 +112,7 @@ class AppsGridAdapter extends RecyclerView.Adapter<AppsGridAdapter.ViewHolder> {
c.drawLine(left + iconInset, top, right - iconInset, top, mPredictedAppsDividerPaint);
hasDrawnPredictedAppDivider = true;
} else if (shouldDrawItemSection(holder, i, items)) {
} else if (grid.isPhone() && shouldDrawItemSection(holder, i, items)) {
// At this point, we only draw sections for each section break;
int viewTopOffset = (2 * child.getPaddingTop());
int pos = holder.getPosition();
@@ -132,7 +131,8 @@ class AppsGridAdapter extends RecyclerView.Adapter<AppsGridAdapter.ViewHolder> {
continue;
}
// Find the section code points
// Find the section name bounds
PointF sectionBounds = getAndCacheSectionBounds(sectionName);
// Calculate where to draw the section
@@ -59,11 +59,12 @@ public class BaseContainerView extends FrameLayout implements Insettable {
mFixedBounds.set(fixedBounds);
if (Launcher.DISABLE_ALL_APPS_SEARCH_INTEGRATION) {
mFixedBounds.top = mInsets.top;
mFixedBounds.bottom = getMeasuredHeight();
mFixedBounds.bottom = mInsets.bottom;
}
// To ensure that the child RecyclerView has the full width to handle touches right to
// the edge of the screen, we only apply the top and bottom padding to the bounds
mFixedBounds.inset(0, mFixedBoundsContainerInset);
mFixedBounds.top += mFixedBoundsContainerInset;
mFixedBounds.bottom += mFixedBoundsContainerInset;
onFixedBoundsUpdated();
}
// Post the updates since they can trigger a relayout, and this call can be triggered from
+7 -2
View File
@@ -350,6 +350,9 @@ public class Launcher extends Activity
private DeviceProfile mDeviceProfile;
// This is set to the view that launched the activity that navigated the user away from
// launcher. Since there is no callback for when the activity has finished launching, enable
// the press state and keep this reference to reset the press state when we return to launcher.
private BubbleTextView mWaitingForResume;
protected static HashMap<String, CustomAppWidget> sCustomAppWidgets =
@@ -1021,10 +1024,12 @@ public class Launcher extends Activity
if (mOnResumeState == State.WORKSPACE) {
showWorkspace(false);
} else if (mOnResumeState == State.APPS) {
boolean launchedFromApp = (mWaitingForResume != null);
// 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
// view after launching an app, as they may be depending on the UI to be static to
// switch to another app, otherwise, if it was
showAppsView(false /* animated */, false /* resetListToTop */,
false /* updatePredictedApps */);
!launchedFromApp /* updatePredictedApps */);
} else if (mOnResumeState == State.WIDGETS) {
showWidgetsView(false, false);
}
@@ -373,7 +373,7 @@ public class WidgetsContainerView extends BaseContainerView
} else {
// If there are fixed bounds, then we update the padding to reflect the fixed bounds.
setPadding(mFixedBounds.left, mFixedBounds.top, getMeasuredWidth() - mFixedBounds.right,
mInsets.bottom);
mFixedBounds.bottom);
}
}