2015-03-10 16:28:47 -07:00
|
|
|
package com.android.launcher3;
|
|
|
|
|
|
|
|
|
|
import android.content.Context;
|
|
|
|
|
import android.content.res.Resources;
|
|
|
|
|
import android.graphics.Canvas;
|
|
|
|
|
import android.graphics.Paint;
|
|
|
|
|
import android.graphics.Rect;
|
|
|
|
|
import android.support.v7.widget.GridLayoutManager;
|
|
|
|
|
import android.support.v7.widget.RecyclerView;
|
|
|
|
|
import android.view.LayoutInflater;
|
|
|
|
|
import android.view.View;
|
|
|
|
|
import android.view.ViewGroup;
|
2015-03-13 11:14:16 -07:00
|
|
|
import android.widget.TextView;
|
2015-03-18 14:16:05 -07:00
|
|
|
|
|
|
|
|
import com.android.launcher3.util.Thunk;
|
2015-03-10 16:28:47 -07:00
|
|
|
|
2015-04-06 15:12:49 -07:00
|
|
|
import java.util.List;
|
|
|
|
|
|
2015-03-10 16:28:47 -07:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* The grid view adapter of all the apps.
|
|
|
|
|
*/
|
|
|
|
|
class AppsGridAdapter extends RecyclerView.Adapter<AppsGridAdapter.ViewHolder> {
|
|
|
|
|
|
|
|
|
|
public static final String TAG = "AppsGridAdapter";
|
|
|
|
|
|
|
|
|
|
private static final int SECTION_BREAK_VIEW_TYPE = 0;
|
|
|
|
|
private static final int ICON_VIEW_TYPE = 1;
|
2015-03-13 11:14:16 -07:00
|
|
|
private static final int EMPTY_VIEW_TYPE = 2;
|
2015-03-10 16:28:47 -07:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* ViewHolder for each icon.
|
|
|
|
|
*/
|
|
|
|
|
public static class ViewHolder extends RecyclerView.ViewHolder {
|
|
|
|
|
public View mContent;
|
2015-05-04 15:28:34 -07:00
|
|
|
public boolean mIsSectionHeader;
|
2015-03-13 11:14:16 -07:00
|
|
|
public boolean mIsEmptyRow;
|
2015-03-10 16:28:47 -07:00
|
|
|
|
2015-05-04 15:28:34 -07:00
|
|
|
public ViewHolder(View v, boolean isSectionHeader, boolean isEmptyRow) {
|
2015-03-10 16:28:47 -07:00
|
|
|
super(v);
|
|
|
|
|
mContent = v;
|
2015-05-04 15:28:34 -07:00
|
|
|
mIsSectionHeader = isSectionHeader;
|
2015-03-13 11:14:16 -07:00
|
|
|
mIsEmptyRow = isEmptyRow;
|
2015-03-10 16:28:47 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Helper class to size the grid items.
|
|
|
|
|
*/
|
|
|
|
|
public class GridSpanSizer extends GridLayoutManager.SpanSizeLookup {
|
|
|
|
|
@Override
|
|
|
|
|
public int getSpanSize(int position) {
|
2015-03-13 11:14:16 -07:00
|
|
|
if (mApps.hasNoFilteredResults()) {
|
|
|
|
|
// Empty view spans full width
|
|
|
|
|
return mAppsPerRow;
|
|
|
|
|
}
|
|
|
|
|
|
2015-04-06 15:12:49 -07:00
|
|
|
if (mApps.getAdapterItems().get(position).isSectionHeader) {
|
2015-03-13 11:14:16 -07:00
|
|
|
// Section break spans full width
|
2015-03-10 16:28:47 -07:00
|
|
|
return mAppsPerRow;
|
|
|
|
|
} else {
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Helper class to draw the section headers
|
|
|
|
|
*/
|
|
|
|
|
public class GridItemDecoration extends RecyclerView.ItemDecoration {
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
|
2015-04-06 15:12:49 -07:00
|
|
|
List<AlphabeticalAppsList.AdapterItem> items = mApps.getAdapterItems();
|
2015-03-10 16:28:47 -07:00
|
|
|
for (int i = 0; i < parent.getChildCount(); i++) {
|
|
|
|
|
View child = parent.getChildAt(i);
|
|
|
|
|
ViewHolder holder = (ViewHolder) parent.getChildViewHolder(child);
|
2015-05-04 15:28:34 -07:00
|
|
|
if (shouldDrawItemSection(holder, child, items)) {
|
|
|
|
|
// Draw at the parent
|
|
|
|
|
AlphabeticalAppsList.AdapterItem item =
|
|
|
|
|
items.get(holder.getPosition());
|
|
|
|
|
String section = item.sectionName;
|
|
|
|
|
mSectionTextPaint.getTextBounds(section, 0, section.length(),
|
|
|
|
|
mTmpBounds);
|
|
|
|
|
if (mIsRtl) {
|
|
|
|
|
int left = parent.getWidth() - mPaddingStart - mStartMargin;
|
|
|
|
|
c.drawText(section, left + (mStartMargin - mTmpBounds.width()) / 2,
|
|
|
|
|
child.getTop() + (2 * child.getPaddingTop()) +
|
|
|
|
|
mTmpBounds.height(), mSectionTextPaint);
|
|
|
|
|
} else {
|
|
|
|
|
int left = mPaddingStart;
|
|
|
|
|
c.drawText(section, left + (mStartMargin - mTmpBounds.width()) / 2,
|
|
|
|
|
child.getTop() + (2 * child.getPaddingTop()) +
|
|
|
|
|
mTmpBounds.height(), mSectionTextPaint);
|
2015-03-10 16:28:47 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
|
|
|
|
|
RecyclerView.State state) {
|
|
|
|
|
// Do nothing
|
|
|
|
|
}
|
2015-05-04 15:28:34 -07:00
|
|
|
|
|
|
|
|
private boolean shouldDrawItemSection(ViewHolder holder, View child,
|
|
|
|
|
List<AlphabeticalAppsList.AdapterItem> items) {
|
|
|
|
|
// Ensure item is not already removed
|
|
|
|
|
GridLayoutManager.LayoutParams lp = (GridLayoutManager.LayoutParams)
|
|
|
|
|
child.getLayoutParams();
|
|
|
|
|
if (lp.isItemRemoved()) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
// Ensure we have a valid holder
|
|
|
|
|
if (holder == null) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
// 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()) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
// Only draw the first item in the section (the first one after the section header)
|
|
|
|
|
return items.get(pos - 1).isSectionHeader && !items.get(pos).isSectionHeader;
|
|
|
|
|
}
|
2015-03-10 16:28:47 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private LayoutInflater mLayoutInflater;
|
2015-03-18 14:16:05 -07:00
|
|
|
@Thunk AlphabeticalAppsList mApps;
|
2015-04-08 10:27:49 -07:00
|
|
|
private GridLayoutManager mGridLayoutMgr;
|
2015-03-10 16:28:47 -07:00
|
|
|
private GridSpanSizer mGridSizer;
|
|
|
|
|
private GridItemDecoration mItemDecoration;
|
|
|
|
|
private View.OnTouchListener mTouchListener;
|
|
|
|
|
private View.OnClickListener mIconClickListener;
|
|
|
|
|
private View.OnLongClickListener mIconLongClickListener;
|
2015-03-18 14:16:05 -07:00
|
|
|
@Thunk int mAppsPerRow;
|
|
|
|
|
@Thunk boolean mIsRtl;
|
2015-03-13 11:14:16 -07:00
|
|
|
private String mEmptySearchText;
|
2015-03-10 16:28:47 -07:00
|
|
|
|
|
|
|
|
// Section drawing
|
2015-03-18 14:16:05 -07:00
|
|
|
@Thunk int mPaddingStart;
|
|
|
|
|
@Thunk int mStartMargin;
|
|
|
|
|
@Thunk Paint mSectionTextPaint;
|
|
|
|
|
@Thunk Rect mTmpBounds = new Rect();
|
2015-03-10 16:28:47 -07:00
|
|
|
|
|
|
|
|
|
|
|
|
|
public AppsGridAdapter(Context context, AlphabeticalAppsList apps, int appsPerRow,
|
|
|
|
|
View.OnTouchListener touchListener, View.OnClickListener iconClickListener,
|
|
|
|
|
View.OnLongClickListener iconLongClickListener) {
|
|
|
|
|
Resources res = context.getResources();
|
|
|
|
|
mApps = apps;
|
|
|
|
|
mAppsPerRow = appsPerRow;
|
|
|
|
|
mGridSizer = new GridSpanSizer();
|
2015-04-08 10:27:49 -07:00
|
|
|
mGridLayoutMgr = new GridLayoutManager(context, appsPerRow, GridLayoutManager.VERTICAL,
|
|
|
|
|
false);
|
|
|
|
|
mGridLayoutMgr.setSpanSizeLookup(mGridSizer);
|
2015-03-10 16:28:47 -07:00
|
|
|
mItemDecoration = new GridItemDecoration();
|
|
|
|
|
mLayoutInflater = LayoutInflater.from(context);
|
|
|
|
|
mTouchListener = touchListener;
|
|
|
|
|
mIconClickListener = iconClickListener;
|
|
|
|
|
mIconLongClickListener = iconLongClickListener;
|
|
|
|
|
mStartMargin = res.getDimensionPixelSize(R.dimen.apps_grid_view_start_margin);
|
2015-03-16 12:39:05 -07:00
|
|
|
mPaddingStart = res.getDimensionPixelSize(R.dimen.apps_container_inset);
|
2015-03-10 16:28:47 -07:00
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
2015-04-08 10:27:49 -07:00
|
|
|
/**
|
|
|
|
|
* Sets the number of apps per row.
|
|
|
|
|
*/
|
|
|
|
|
public void setNumAppsPerRow(int appsPerRow) {
|
|
|
|
|
mAppsPerRow = appsPerRow;
|
|
|
|
|
mGridLayoutMgr.setSpanCount(appsPerRow);
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-10 16:28:47 -07:00
|
|
|
/**
|
|
|
|
|
* Sets whether we are in RTL mode.
|
|
|
|
|
*/
|
|
|
|
|
public void setRtl(boolean rtl) {
|
|
|
|
|
mIsRtl = rtl;
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-13 11:14:16 -07:00
|
|
|
/**
|
|
|
|
|
* Sets the text to show when there are no apps.
|
|
|
|
|
*/
|
|
|
|
|
public void setEmptySearchText(String query) {
|
|
|
|
|
mEmptySearchText = query;
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-10 16:28:47 -07:00
|
|
|
/**
|
|
|
|
|
* Returns the grid layout manager.
|
|
|
|
|
*/
|
|
|
|
|
public GridLayoutManager getLayoutManager(Context context) {
|
2015-04-08 10:27:49 -07:00
|
|
|
return mGridLayoutMgr;
|
2015-03-10 16:28:47 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Returns the item decoration for the recycler view.
|
|
|
|
|
*/
|
|
|
|
|
public RecyclerView.ItemDecoration getItemDecoration() {
|
|
|
|
|
return mItemDecoration;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Returns the left padding for the recycler view.
|
|
|
|
|
*/
|
|
|
|
|
public int getContentMarginStart() {
|
|
|
|
|
return mStartMargin;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
|
|
|
|
switch (viewType) {
|
2015-03-13 11:14:16 -07:00
|
|
|
case EMPTY_VIEW_TYPE:
|
|
|
|
|
return new ViewHolder(mLayoutInflater.inflate(R.layout.apps_empty_view, parent,
|
|
|
|
|
false), false /* isSectionRow */, true /* isEmptyRow */);
|
2015-03-10 16:28:47 -07:00
|
|
|
case SECTION_BREAK_VIEW_TYPE:
|
2015-03-13 11:14:16 -07:00
|
|
|
return new ViewHolder(new View(parent.getContext()), true /* isSectionRow */,
|
|
|
|
|
false /* isEmptyRow */);
|
2015-03-10 16:28:47 -07:00
|
|
|
case ICON_VIEW_TYPE:
|
|
|
|
|
BubbleTextView icon = (BubbleTextView) mLayoutInflater.inflate(
|
|
|
|
|
R.layout.apps_grid_row_icon_view, parent, false);
|
|
|
|
|
icon.setOnTouchListener(mTouchListener);
|
|
|
|
|
icon.setOnClickListener(mIconClickListener);
|
|
|
|
|
icon.setOnLongClickListener(mIconLongClickListener);
|
|
|
|
|
icon.setFocusable(true);
|
2015-03-13 11:14:16 -07:00
|
|
|
return new ViewHolder(icon, false /* isSectionRow */, false /* isEmptyRow */);
|
2015-03-10 16:28:47 -07:00
|
|
|
default:
|
|
|
|
|
throw new RuntimeException("Unexpected view type");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void onBindViewHolder(ViewHolder holder, int position) {
|
2015-03-13 11:14:16 -07:00
|
|
|
switch (holder.getItemViewType()) {
|
|
|
|
|
case ICON_VIEW_TYPE:
|
2015-04-06 15:12:49 -07:00
|
|
|
AppInfo info = mApps.getAdapterItems().get(position).appInfo;
|
2015-03-13 11:14:16 -07:00
|
|
|
BubbleTextView icon = (BubbleTextView) holder.mContent;
|
|
|
|
|
icon.applyFromApplicationInfo(info);
|
|
|
|
|
break;
|
|
|
|
|
case EMPTY_VIEW_TYPE:
|
|
|
|
|
TextView emptyViewText = (TextView) holder.mContent.findViewById(R.id.empty_text);
|
|
|
|
|
emptyViewText.setText(mEmptySearchText);
|
|
|
|
|
break;
|
2015-03-10 16:28:47 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public int getItemCount() {
|
2015-03-13 11:14:16 -07:00
|
|
|
if (mApps.hasNoFilteredResults()) {
|
|
|
|
|
// For the empty view
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
2015-04-06 15:12:49 -07:00
|
|
|
return mApps.getAdapterItems().size();
|
2015-03-10 16:28:47 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public int getItemViewType(int position) {
|
2015-03-13 11:14:16 -07:00
|
|
|
if (mApps.hasNoFilteredResults()) {
|
|
|
|
|
return EMPTY_VIEW_TYPE;
|
2015-04-06 15:12:49 -07:00
|
|
|
} else if (mApps.getAdapterItems().get(position).isSectionHeader) {
|
2015-03-10 16:28:47 -07:00
|
|
|
return SECTION_BREAK_VIEW_TYPE;
|
|
|
|
|
}
|
|
|
|
|
return ICON_VIEW_TYPE;
|
|
|
|
|
}
|
|
|
|
|
}
|