2015-05-14 19:24:40 -07:00
|
|
|
/*
|
|
|
|
|
* 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.
|
|
|
|
|
*/
|
2015-05-22 11:12:27 -07:00
|
|
|
package com.android.launcher3.allapps;
|
2015-03-10 16:28:47 -07:00
|
|
|
|
|
|
|
|
import android.content.Context;
|
2015-07-10 12:38:30 -07:00
|
|
|
import android.content.Intent;
|
2015-03-10 16:28:47 -07:00
|
|
|
import android.content.res.Resources;
|
2017-06-06 15:41:21 -07:00
|
|
|
import android.support.animation.DynamicAnimation;
|
2017-04-20 12:07:38 -07:00
|
|
|
import android.support.animation.SpringAnimation;
|
2015-07-30 12:53:33 -07:00
|
|
|
import android.support.v4.view.accessibility.AccessibilityEventCompat;
|
2017-02-21 16:43:56 -08:00
|
|
|
import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
|
2016-04-28 17:39:03 -07:00
|
|
|
import android.support.v4.view.accessibility.AccessibilityRecordCompat;
|
2015-03-10 16:28:47 -07:00
|
|
|
import android.support.v7.widget.GridLayoutManager;
|
|
|
|
|
import android.support.v7.widget.RecyclerView;
|
2015-08-19 17:26:21 -07:00
|
|
|
import android.view.Gravity;
|
2015-03-10 16:28:47 -07:00
|
|
|
import android.view.LayoutInflater;
|
|
|
|
|
import android.view.View;
|
2016-05-09 20:43:21 -07:00
|
|
|
import android.view.View.OnFocusChangeListener;
|
2015-06-02 11:24:28 -07:00
|
|
|
import android.view.ViewConfiguration;
|
2015-03-10 16:28:47 -07:00
|
|
|
import android.view.ViewGroup;
|
2015-07-30 12:53:33 -07:00
|
|
|
import android.view.accessibility.AccessibilityEvent;
|
2015-03-13 11:14:16 -07:00
|
|
|
import android.widget.TextView;
|
2016-04-28 17:39:03 -07:00
|
|
|
|
2015-05-22 11:12:27 -07:00
|
|
|
import com.android.launcher3.AppInfo;
|
|
|
|
|
import com.android.launcher3.BubbleTextView;
|
2015-07-10 12:38:30 -07:00
|
|
|
import com.android.launcher3.Launcher;
|
2015-05-22 11:12:27 -07:00
|
|
|
import com.android.launcher3.R;
|
2017-06-06 15:41:21 -07:00
|
|
|
import com.android.launcher3.Utilities;
|
2017-02-21 16:43:56 -08:00
|
|
|
import com.android.launcher3.allapps.AlphabeticalAppsList.AdapterItem;
|
2017-04-20 12:07:38 -07:00
|
|
|
import com.android.launcher3.anim.SpringAnimationHandler;
|
2018-01-15 14:52:47 +00:00
|
|
|
import com.android.launcher3.compat.UserManagerCompat;
|
2017-04-20 12:07:38 -07:00
|
|
|
import com.android.launcher3.config.FeatureFlags;
|
2017-05-07 11:56:00 -07:00
|
|
|
import com.android.launcher3.util.PackageManagerHelper;
|
2017-02-21 16:43:56 -08:00
|
|
|
|
|
|
|
|
import java.util.List;
|
2015-04-06 15:12:49 -07:00
|
|
|
|
2015-03-10 16:28:47 -07:00
|
|
|
/**
|
|
|
|
|
* The grid view adapter of all the apps.
|
|
|
|
|
*/
|
2015-08-11 16:20:47 -07:00
|
|
|
public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.ViewHolder> {
|
2015-03-10 16:28:47 -07:00
|
|
|
|
|
|
|
|
public static final String TAG = "AppsGridAdapter";
|
|
|
|
|
|
2015-05-12 19:05:30 -07:00
|
|
|
// A normal icon
|
2016-07-07 14:47:05 -07:00
|
|
|
public static final int VIEW_TYPE_ICON = 1 << 1;
|
2015-07-06 17:14:51 -07:00
|
|
|
// A prediction icon
|
2016-07-07 14:47:05 -07:00
|
|
|
public static final int VIEW_TYPE_PREDICTION_ICON = 1 << 2;
|
2015-05-12 19:05:30 -07:00
|
|
|
// The message shown when there are no filtered results
|
2016-07-07 14:47:05 -07:00
|
|
|
public static final int VIEW_TYPE_EMPTY_SEARCH = 1 << 3;
|
2016-07-19 12:11:05 -07:00
|
|
|
// The message to continue to a market search when there are no filtered results
|
2016-07-07 14:47:05 -07:00
|
|
|
public static final int VIEW_TYPE_SEARCH_MARKET = 1 << 4;
|
|
|
|
|
|
|
|
|
|
// We use various dividers for various purposes. They share enough attributes to reuse layouts,
|
|
|
|
|
// but differ in enough attributes to require different view types
|
|
|
|
|
|
|
|
|
|
// A divider that separates the apps list and the search market button
|
2017-11-24 16:40:03 +08:00
|
|
|
public static final int VIEW_TYPE_ALL_APPS_DIVIDER = 1 << 5;
|
2016-07-07 14:47:05 -07:00
|
|
|
// The divider that separates prediction icons from the app list
|
2017-06-23 16:12:50 -07:00
|
|
|
public static final int VIEW_TYPE_PREDICTION_DIVIDER = 1 << 6;
|
2018-01-23 13:52:48 -08:00
|
|
|
public static final int VIEW_TYPE_WORK_TAB_FOOTER = 1 << 7;
|
2016-07-07 14:47:05 -07:00
|
|
|
|
|
|
|
|
// Common view type masks
|
2017-11-24 16:40:03 +08:00
|
|
|
public static final int VIEW_TYPE_MASK_DIVIDER = VIEW_TYPE_ALL_APPS_DIVIDER
|
2016-10-13 09:31:24 -07:00
|
|
|
| VIEW_TYPE_PREDICTION_DIVIDER;
|
2016-07-07 14:47:05 -07:00
|
|
|
public static final int VIEW_TYPE_MASK_ICON = VIEW_TYPE_ICON
|
|
|
|
|
| VIEW_TYPE_PREDICTION_ICON;
|
2017-04-20 12:07:38 -07:00
|
|
|
public static final int VIEW_TYPE_MASK_HAS_SPRINGS = VIEW_TYPE_MASK_ICON
|
|
|
|
|
| VIEW_TYPE_PREDICTION_DIVIDER;
|
2016-07-07 14:47:05 -07:00
|
|
|
|
2015-03-10 16:28:47 -07:00
|
|
|
|
2015-08-21 11:16:27 -07:00
|
|
|
public interface BindViewCallback {
|
2016-10-13 09:31:24 -07:00
|
|
|
void onBindView(ViewHolder holder);
|
2015-08-21 11:16:27 -07:00
|
|
|
}
|
|
|
|
|
|
2015-03-10 16:28:47 -07:00
|
|
|
/**
|
|
|
|
|
* ViewHolder for each icon.
|
|
|
|
|
*/
|
|
|
|
|
public static class ViewHolder extends RecyclerView.ViewHolder {
|
2017-04-20 12:07:38 -07:00
|
|
|
|
2015-05-12 19:05:30 -07:00
|
|
|
public ViewHolder(View v) {
|
2015-03-10 16:28:47 -07:00
|
|
|
super(v);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-07-30 12:53:33 -07:00
|
|
|
/**
|
|
|
|
|
* A subclass of GridLayoutManager that overrides accessibility values during app search.
|
|
|
|
|
*/
|
|
|
|
|
public class AppsGridLayoutManager extends GridLayoutManager {
|
|
|
|
|
|
|
|
|
|
public AppsGridLayoutManager(Context context) {
|
|
|
|
|
super(context, 1, GridLayoutManager.VERTICAL, false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
|
|
|
|
|
super.onInitializeAccessibilityEvent(event);
|
2015-07-10 12:38:30 -07:00
|
|
|
|
|
|
|
|
// Ensure that we only report the number apps for accessibility not including other
|
|
|
|
|
// adapter views
|
|
|
|
|
final AccessibilityRecordCompat record = AccessibilityEventCompat
|
|
|
|
|
.asRecord(event);
|
|
|
|
|
record.setItemCount(mApps.getNumFilteredApps());
|
2017-02-21 16:43:56 -08:00
|
|
|
record.setFromIndex(Math.max(0,
|
|
|
|
|
record.getFromIndex() - getRowsNotForAccessibility(record.getFromIndex())));
|
|
|
|
|
record.setToIndex(Math.max(0,
|
|
|
|
|
record.getToIndex() - getRowsNotForAccessibility(record.getToIndex())));
|
2015-07-30 12:53:33 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public int getRowCountForAccessibility(RecyclerView.Recycler recycler,
|
|
|
|
|
RecyclerView.State state) {
|
2017-02-21 16:43:56 -08:00
|
|
|
return super.getRowCountForAccessibility(recycler, state) -
|
|
|
|
|
getRowsNotForAccessibility(mApps.getAdapterItems().size() - 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void onInitializeAccessibilityNodeInfoForItem(RecyclerView.Recycler recycler,
|
|
|
|
|
RecyclerView.State state, View host, AccessibilityNodeInfoCompat info) {
|
|
|
|
|
super.onInitializeAccessibilityNodeInfoForItem(recycler, state, host, info);
|
|
|
|
|
|
|
|
|
|
ViewGroup.LayoutParams lp = host.getLayoutParams();
|
|
|
|
|
AccessibilityNodeInfoCompat.CollectionItemInfoCompat cic = info.getCollectionItemInfo();
|
|
|
|
|
if (!(lp instanceof LayoutParams) || (cic == null)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
LayoutParams glp = (LayoutParams) lp;
|
|
|
|
|
info.setCollectionItemInfo(AccessibilityNodeInfoCompat.CollectionItemInfoCompat.obtain(
|
|
|
|
|
cic.getRowIndex() - getRowsNotForAccessibility(glp.getViewAdapterPosition()),
|
|
|
|
|
cic.getRowSpan(),
|
|
|
|
|
cic.getColumnIndex(),
|
|
|
|
|
cic.getColumnSpan(),
|
|
|
|
|
cic.isHeading(),
|
|
|
|
|
cic.isSelected()));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Returns the number of rows before {@param adapterPosition}, including this position
|
|
|
|
|
* which should not be counted towards the collection info.
|
|
|
|
|
*/
|
|
|
|
|
private int getRowsNotForAccessibility(int adapterPosition) {
|
|
|
|
|
List<AdapterItem> items = mApps.getAdapterItems();
|
|
|
|
|
adapterPosition = Math.max(adapterPosition, mApps.getAdapterItems().size() - 1);
|
|
|
|
|
int extraRows = 0;
|
|
|
|
|
for (int i = 0; i <= adapterPosition; i++) {
|
2018-01-23 13:52:48 -08:00
|
|
|
if (!isViewType(items.get(i).viewType, VIEW_TYPE_MASK_ICON)) {
|
2017-02-21 16:43:56 -08:00
|
|
|
extraRows++;
|
|
|
|
|
}
|
2015-07-30 12:53:33 -07:00
|
|
|
}
|
2017-02-21 16:43:56 -08:00
|
|
|
return extraRows;
|
2015-07-30 12:53:33 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-10 16:28:47 -07:00
|
|
|
/**
|
|
|
|
|
* Helper class to size the grid items.
|
|
|
|
|
*/
|
|
|
|
|
public class GridSpanSizer extends GridLayoutManager.SpanSizeLookup {
|
2015-05-05 17:21:58 -07:00
|
|
|
|
|
|
|
|
public GridSpanSizer() {
|
|
|
|
|
super();
|
|
|
|
|
setSpanIndexCacheEnabled(true);
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-10 16:28:47 -07:00
|
|
|
@Override
|
|
|
|
|
public int getSpanSize(int position) {
|
2016-07-07 14:47:05 -07:00
|
|
|
if (isIconViewType(mApps.getAdapterItems().get(position).viewType)) {
|
|
|
|
|
return 1;
|
|
|
|
|
} else {
|
2017-11-24 16:40:03 +08:00
|
|
|
// Section breaks span the full width
|
|
|
|
|
return mAppsPerRow;
|
2015-03-10 16:28:47 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-03 11:35:59 -08:00
|
|
|
private final Launcher mLauncher;
|
|
|
|
|
private final LayoutInflater mLayoutInflater;
|
|
|
|
|
private final AlphabeticalAppsList mApps;
|
|
|
|
|
private final GridLayoutManager mGridLayoutMgr;
|
|
|
|
|
private final GridSpanSizer mGridSizer;
|
|
|
|
|
private final View.OnClickListener mIconClickListener;
|
|
|
|
|
private final View.OnLongClickListener mIconLongClickListener;
|
|
|
|
|
|
|
|
|
|
private int mAppsPerRow;
|
2016-03-03 13:52:22 -08:00
|
|
|
|
2015-08-21 11:16:27 -07:00
|
|
|
private BindViewCallback mBindViewCallback;
|
2016-05-09 20:43:21 -07:00
|
|
|
private OnFocusChangeListener mIconFocusListener;
|
2015-07-10 12:38:30 -07:00
|
|
|
|
|
|
|
|
// The text to show when there are no search results and no market search handler.
|
|
|
|
|
private String mEmptySearchMessage;
|
|
|
|
|
// The intent to send off to the market app, updated each time the search query changes.
|
|
|
|
|
private Intent mMarketSearchIntent;
|
2015-03-10 16:28:47 -07:00
|
|
|
|
2017-10-18 10:31:36 -07:00
|
|
|
private final SpringAnimationHandler<ViewHolder> mSpringAnimationHandler;
|
2017-04-20 12:07:38 -07:00
|
|
|
|
2016-07-07 14:47:05 -07:00
|
|
|
public AllAppsGridAdapter(Launcher launcher, AlphabeticalAppsList apps, View.OnClickListener
|
2017-10-18 10:31:36 -07:00
|
|
|
iconClickListener, View.OnLongClickListener iconLongClickListener, boolean springAnim) {
|
2015-07-10 12:38:30 -07:00
|
|
|
Resources res = launcher.getResources();
|
|
|
|
|
mLauncher = launcher;
|
2015-03-10 16:28:47 -07:00
|
|
|
mApps = apps;
|
2015-07-10 12:38:30 -07:00
|
|
|
mEmptySearchMessage = res.getString(R.string.all_apps_loading_message);
|
2015-03-10 16:28:47 -07:00
|
|
|
mGridSizer = new GridSpanSizer();
|
2015-07-10 12:38:30 -07:00
|
|
|
mGridLayoutMgr = new AppsGridLayoutManager(launcher);
|
2015-04-08 10:27:49 -07:00
|
|
|
mGridLayoutMgr.setSpanSizeLookup(mGridSizer);
|
2015-07-10 12:38:30 -07:00
|
|
|
mLayoutInflater = LayoutInflater.from(launcher);
|
2015-03-10 16:28:47 -07:00
|
|
|
mIconClickListener = iconClickListener;
|
|
|
|
|
mIconLongClickListener = iconLongClickListener;
|
2017-10-18 10:31:36 -07:00
|
|
|
if (FeatureFlags.LAUNCHER3_PHYSICS && springAnim) {
|
2017-06-06 15:41:21 -07:00
|
|
|
mSpringAnimationHandler = new SpringAnimationHandler<>(
|
|
|
|
|
SpringAnimationHandler.Y_DIRECTION, new AllAppsSpringAnimationFactory());
|
2017-10-18 10:31:36 -07:00
|
|
|
} else {
|
|
|
|
|
mSpringAnimationHandler = null;
|
2017-06-06 15:41:21 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public SpringAnimationHandler getSpringAnimationHandler() {
|
|
|
|
|
return mSpringAnimationHandler;
|
2016-07-07 14:47:05 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static boolean isDividerViewType(int viewType) {
|
|
|
|
|
return isViewType(viewType, VIEW_TYPE_MASK_DIVIDER);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static boolean isIconViewType(int viewType) {
|
|
|
|
|
return isViewType(viewType, VIEW_TYPE_MASK_ICON);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static boolean isViewType(int viewType, int viewTypeMask) {
|
|
|
|
|
return (viewType & viewTypeMask) != 0;
|
2015-03-10 16:28:47 -07:00
|
|
|
}
|
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-20 12:07:38 -07:00
|
|
|
public int getNumAppsPerRow() {
|
|
|
|
|
return mAppsPerRow;
|
|
|
|
|
}
|
|
|
|
|
|
2016-05-09 20:43:21 -07:00
|
|
|
public void setIconFocusListener(OnFocusChangeListener focusListener) {
|
|
|
|
|
mIconFocusListener = focusListener;
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-13 11:14:16 -07:00
|
|
|
/**
|
2015-07-10 12:38:30 -07:00
|
|
|
* Sets the last search query that was made, used to show when there are no results and to also
|
|
|
|
|
* seed the intent for searching the market.
|
2015-03-13 11:14:16 -07:00
|
|
|
*/
|
2015-07-10 12:38:30 -07:00
|
|
|
public void setLastSearchQuery(String query) {
|
|
|
|
|
Resources res = mLauncher.getResources();
|
2015-09-02 15:06:12 -07:00
|
|
|
mEmptySearchMessage = res.getString(R.string.all_apps_no_search_results, query);
|
2017-05-07 11:56:00 -07:00
|
|
|
mMarketSearchIntent = PackageManagerHelper.getMarketSearchIntent(mLauncher, query);
|
2015-03-13 11:14:16 -07:00
|
|
|
}
|
|
|
|
|
|
2015-08-21 11:16:27 -07:00
|
|
|
/**
|
|
|
|
|
* Sets the callback for when views are bound.
|
|
|
|
|
*/
|
|
|
|
|
public void setBindViewCallback(BindViewCallback cb) {
|
|
|
|
|
mBindViewCallback = cb;
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-10 16:28:47 -07:00
|
|
|
/**
|
|
|
|
|
* Returns the grid layout manager.
|
|
|
|
|
*/
|
2015-05-05 17:21:58 -07:00
|
|
|
public GridLayoutManager getLayoutManager() {
|
2015-04-08 10:27:49 -07:00
|
|
|
return mGridLayoutMgr;
|
2015-03-10 16:28:47 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
|
|
|
|
switch (viewType) {
|
2016-07-07 14:47:05 -07:00
|
|
|
case VIEW_TYPE_ICON:
|
2017-02-17 12:16:13 -08:00
|
|
|
case VIEW_TYPE_PREDICTION_ICON:
|
2015-07-06 17:14:51 -07:00
|
|
|
BubbleTextView icon = (BubbleTextView) mLayoutInflater.inflate(
|
2016-07-11 18:59:18 -07:00
|
|
|
R.layout.all_apps_icon, parent, false);
|
2015-07-06 17:14:51 -07:00
|
|
|
icon.setOnClickListener(mIconClickListener);
|
|
|
|
|
icon.setOnLongClickListener(mIconLongClickListener);
|
2017-06-06 15:41:21 -07:00
|
|
|
icon.setLongPressTimeout(ViewConfiguration.getLongPressTimeout());
|
2016-05-09 20:43:21 -07:00
|
|
|
icon.setOnFocusChangeListener(mIconFocusListener);
|
2016-07-18 17:18:02 -07:00
|
|
|
|
2017-06-20 10:58:36 -07:00
|
|
|
// Ensure the all apps icon height matches the workspace icons in portrait mode.
|
|
|
|
|
icon.getLayoutParams().height = mLauncher.getDeviceProfile().allAppsCellHeightPx;
|
2017-06-06 20:36:24 +00:00
|
|
|
return new ViewHolder(icon);
|
2016-07-07 14:47:05 -07:00
|
|
|
case VIEW_TYPE_EMPTY_SEARCH:
|
2017-06-06 20:36:24 +00:00
|
|
|
return new ViewHolder(mLayoutInflater.inflate(R.layout.all_apps_empty_search,
|
2015-07-10 12:38:30 -07:00
|
|
|
parent, false));
|
2016-07-07 14:47:05 -07:00
|
|
|
case VIEW_TYPE_SEARCH_MARKET:
|
2015-07-10 12:38:30 -07:00
|
|
|
View searchMarketView = mLayoutInflater.inflate(R.layout.all_apps_search_market,
|
|
|
|
|
parent, false);
|
|
|
|
|
searchMarketView.setOnClickListener(new View.OnClickListener() {
|
|
|
|
|
@Override
|
|
|
|
|
public void onClick(View v) {
|
2016-02-18 15:31:55 -08:00
|
|
|
mLauncher.startActivitySafely(v, mMarketSearchIntent, null);
|
2015-07-10 12:38:30 -07:00
|
|
|
}
|
|
|
|
|
});
|
2017-06-06 20:36:24 +00:00
|
|
|
return new ViewHolder(searchMarketView);
|
2016-07-07 14:47:05 -07:00
|
|
|
case VIEW_TYPE_PREDICTION_DIVIDER:
|
2017-11-24 16:40:03 +08:00
|
|
|
case VIEW_TYPE_ALL_APPS_DIVIDER:
|
2017-06-06 20:36:24 +00:00
|
|
|
return new ViewHolder(mLayoutInflater.inflate(
|
2016-07-07 14:47:05 -07:00
|
|
|
R.layout.all_apps_divider, parent, false));
|
2017-11-24 16:40:03 +08:00
|
|
|
case VIEW_TYPE_WORK_TAB_FOOTER:
|
|
|
|
|
View footer = mLayoutInflater.inflate(R.layout.work_tab_footer, parent, false);
|
|
|
|
|
return new ViewHolder(footer);
|
2015-03-10 16:28:47 -07:00
|
|
|
default:
|
|
|
|
|
throw new RuntimeException("Unexpected view type");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void onBindViewHolder(ViewHolder holder, int position) {
|
2017-06-06 20:36:24 +00:00
|
|
|
switch (holder.getItemViewType()) {
|
2017-02-17 12:16:13 -08:00
|
|
|
case VIEW_TYPE_ICON:
|
|
|
|
|
case VIEW_TYPE_PREDICTION_ICON:
|
2015-04-06 15:12:49 -07:00
|
|
|
AppInfo info = mApps.getAdapterItems().get(position).appInfo;
|
2016-10-13 09:31:24 -07:00
|
|
|
BubbleTextView icon = (BubbleTextView) holder.itemView;
|
2017-10-26 11:28:06 -07:00
|
|
|
icon.reset();
|
2015-03-13 11:14:16 -07:00
|
|
|
icon.applyFromApplicationInfo(info);
|
|
|
|
|
break;
|
2016-07-07 14:47:05 -07:00
|
|
|
case VIEW_TYPE_EMPTY_SEARCH:
|
2016-10-13 09:31:24 -07:00
|
|
|
TextView emptyViewText = (TextView) holder.itemView;
|
2015-07-10 12:38:30 -07:00
|
|
|
emptyViewText.setText(mEmptySearchMessage);
|
2015-08-19 17:26:21 -07:00
|
|
|
emptyViewText.setGravity(mApps.hasNoFilteredResults() ? Gravity.CENTER :
|
|
|
|
|
Gravity.START | Gravity.CENTER_VERTICAL);
|
2015-07-10 12:38:30 -07:00
|
|
|
break;
|
2016-07-07 14:47:05 -07:00
|
|
|
case VIEW_TYPE_SEARCH_MARKET:
|
2016-10-13 09:31:24 -07:00
|
|
|
TextView searchView = (TextView) holder.itemView;
|
2015-07-10 12:38:30 -07:00
|
|
|
if (mMarketSearchIntent != null) {
|
|
|
|
|
searchView.setVisibility(View.VISIBLE);
|
|
|
|
|
} else {
|
|
|
|
|
searchView.setVisibility(View.GONE);
|
|
|
|
|
}
|
2015-03-13 11:14:16 -07:00
|
|
|
break;
|
2017-11-24 16:40:03 +08:00
|
|
|
case VIEW_TYPE_ALL_APPS_DIVIDER:
|
2017-02-17 12:16:13 -08:00
|
|
|
// nothing to do
|
|
|
|
|
break;
|
2017-11-24 16:40:03 +08:00
|
|
|
case VIEW_TYPE_WORK_TAB_FOOTER:
|
2018-01-03 14:41:31 +00:00
|
|
|
WorkModeSwitch workModeToggle = holder.itemView.findViewById(R.id.work_mode_toggle);
|
|
|
|
|
workModeToggle.refresh();
|
2018-01-15 14:52:47 +00:00
|
|
|
TextView managedByLabel = holder.itemView.findViewById(R.id.managed_by_label);
|
|
|
|
|
boolean anyProfileQuietModeEnabled = UserManagerCompat.getInstance(
|
|
|
|
|
managedByLabel.getContext()).isAnyProfileQuietModeEnabled();
|
|
|
|
|
managedByLabel.setText(anyProfileQuietModeEnabled
|
|
|
|
|
? R.string.work_mode_off_label : R.string.work_mode_on_label);
|
2017-11-24 16:40:03 +08:00
|
|
|
break;
|
2015-03-10 16:28:47 -07:00
|
|
|
}
|
2015-08-21 11:16:27 -07:00
|
|
|
if (mBindViewCallback != null) {
|
|
|
|
|
mBindViewCallback.onBindView(holder);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-06-06 20:36:24 +00:00
|
|
|
@Override
|
|
|
|
|
public void onViewAttachedToWindow(ViewHolder holder) {
|
|
|
|
|
int type = holder.getItemViewType();
|
2017-10-18 10:31:36 -07:00
|
|
|
if (mSpringAnimationHandler != null && isViewType(type, VIEW_TYPE_MASK_HAS_SPRINGS)) {
|
2017-06-06 15:41:21 -07:00
|
|
|
mSpringAnimationHandler.add(holder.itemView, holder);
|
2017-06-06 20:36:24 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-20 12:07:38 -07:00
|
|
|
@Override
|
|
|
|
|
public void onViewDetachedFromWindow(ViewHolder holder) {
|
|
|
|
|
int type = holder.getItemViewType();
|
2017-10-18 10:31:36 -07:00
|
|
|
if (mSpringAnimationHandler != null && isViewType(type, VIEW_TYPE_MASK_HAS_SPRINGS)) {
|
2017-06-06 15:41:21 -07:00
|
|
|
mSpringAnimationHandler.remove(holder.itemView);
|
2017-04-20 12:07:38 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-08-21 11:16:27 -07:00
|
|
|
@Override
|
|
|
|
|
public boolean onFailedToRecycleView(ViewHolder holder) {
|
|
|
|
|
// Always recycle and we will reset the view when it is bound
|
|
|
|
|
return true;
|
2015-03-10 16:28:47 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public int getItemCount() {
|
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-05-12 19:05:30 -07:00
|
|
|
AlphabeticalAppsList.AdapterItem item = mApps.getAdapterItems().get(position);
|
|
|
|
|
return item.viewType;
|
2015-03-10 16:28:47 -07:00
|
|
|
}
|
2017-06-06 15:41:21 -07:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Helper class to set the SpringAnimation values for an item in the adapter.
|
|
|
|
|
*/
|
|
|
|
|
private class AllAppsSpringAnimationFactory
|
|
|
|
|
implements SpringAnimationHandler.AnimationFactory<ViewHolder> {
|
|
|
|
|
private static final float DEFAULT_MAX_VALUE_PX = 100;
|
|
|
|
|
private static final float DEFAULT_MIN_VALUE_PX = -DEFAULT_MAX_VALUE_PX;
|
|
|
|
|
|
|
|
|
|
// Damping ratio range is [0, 1]
|
|
|
|
|
private static final float SPRING_DAMPING_RATIO = 0.55f;
|
|
|
|
|
|
|
|
|
|
// Stiffness is a non-negative number.
|
|
|
|
|
private static final float MIN_SPRING_STIFFNESS = 580f;
|
|
|
|
|
private static final float MAX_SPRING_STIFFNESS = 900f;
|
|
|
|
|
|
|
|
|
|
// The amount by which each adjacent rows' stiffness will differ.
|
|
|
|
|
private static final float ROW_STIFFNESS_COEFFICIENT = 50f;
|
|
|
|
|
|
2017-09-05 11:32:13 -07:00
|
|
|
// The percentage by which we multiply each row to create the row factor.
|
|
|
|
|
private static final float ROW_PERCENTAGE = 0.3f;
|
|
|
|
|
|
2017-06-06 15:41:21 -07:00
|
|
|
@Override
|
|
|
|
|
public SpringAnimation initialize(ViewHolder vh) {
|
|
|
|
|
return SpringAnimationHandler.forView(vh.itemView, DynamicAnimation.TRANSLATION_Y, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param spring A new or recycled SpringAnimation.
|
|
|
|
|
* @param vh The ViewHolder that {@param spring} is related to.
|
|
|
|
|
*/
|
|
|
|
|
@Override
|
|
|
|
|
public void update(SpringAnimation spring, ViewHolder vh) {
|
|
|
|
|
int numPredictedApps = Math.min(mAppsPerRow, mApps.getPredictedApps().size());
|
|
|
|
|
int appPosition = getAppPosition(vh.getAdapterPosition(), numPredictedApps,
|
|
|
|
|
mAppsPerRow);
|
|
|
|
|
|
|
|
|
|
int col = appPosition % mAppsPerRow;
|
|
|
|
|
int row = appPosition / mAppsPerRow;
|
|
|
|
|
|
|
|
|
|
int numTotalRows = mApps.getNumAppRows() - 1; // zero-based count
|
|
|
|
|
if (row > (numTotalRows / 2)) {
|
|
|
|
|
// Mirror the rows so that the top row acts the same as the bottom row.
|
|
|
|
|
row = Math.abs(numTotalRows - row);
|
|
|
|
|
}
|
|
|
|
|
|
2017-08-23 13:19:24 -07:00
|
|
|
calculateSpringValues(spring, row, col);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void setDefaultValues(SpringAnimation spring) {
|
|
|
|
|
calculateSpringValues(spring, 0, mAppsPerRow / 2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* We manipulate the stiffness, min, and max values based on the items distance to the
|
|
|
|
|
* first row and the items distance to the center column to create the ^-shaped motion
|
|
|
|
|
* effect.
|
|
|
|
|
*/
|
|
|
|
|
private void calculateSpringValues(SpringAnimation spring, int row, int col) {
|
2017-09-05 11:32:13 -07:00
|
|
|
float rowFactor = (1 + row) * ROW_PERCENTAGE;
|
2017-06-06 15:41:21 -07:00
|
|
|
float colFactor = getColumnFactor(col, mAppsPerRow);
|
|
|
|
|
|
|
|
|
|
float minValue = DEFAULT_MIN_VALUE_PX * (rowFactor + colFactor);
|
|
|
|
|
float maxValue = DEFAULT_MAX_VALUE_PX * (rowFactor + colFactor);
|
|
|
|
|
|
|
|
|
|
float stiffness = Utilities.boundToRange(
|
|
|
|
|
MAX_SPRING_STIFFNESS - (row * ROW_STIFFNESS_COEFFICIENT),
|
|
|
|
|
MIN_SPRING_STIFFNESS,
|
|
|
|
|
MAX_SPRING_STIFFNESS);
|
|
|
|
|
|
|
|
|
|
spring.setMinValue(minValue)
|
|
|
|
|
.setMaxValue(maxValue)
|
|
|
|
|
.getSpring()
|
|
|
|
|
.setStiffness(stiffness)
|
|
|
|
|
.setDampingRatio(SPRING_DAMPING_RATIO);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @return The app position is the position of the app in the Adapter if we ignored all
|
|
|
|
|
* other view types.
|
|
|
|
|
*
|
|
|
|
|
* The first app is at position 0, and the first app each following row is at a
|
|
|
|
|
* position that is a multiple of {@param appsPerRow}.
|
|
|
|
|
*
|
|
|
|
|
* ie. If there are 5 apps per row, and there are two rows of apps:
|
|
|
|
|
* 0 1 2 3 4
|
|
|
|
|
* 5 6 7 8 9
|
|
|
|
|
*/
|
|
|
|
|
private int getAppPosition(int position, int numPredictedApps, int appsPerRow) {
|
2017-06-27 08:37:14 -07:00
|
|
|
if (position < numPredictedApps) {
|
|
|
|
|
// Predicted apps are first in the adapter.
|
|
|
|
|
return position;
|
2017-06-06 15:41:21 -07:00
|
|
|
}
|
|
|
|
|
|
2017-06-27 08:37:14 -07:00
|
|
|
// There is at most 1 divider view between the predicted apps and the alphabetical apps.
|
|
|
|
|
int numDividerViews = numPredictedApps == 0 ? 0 : 1;
|
|
|
|
|
|
|
|
|
|
// This offset takes into consideration an incomplete row of predicted apps.
|
|
|
|
|
int numPredictedAppsOffset = appsPerRow - numPredictedApps;
|
|
|
|
|
return position + numPredictedAppsOffset - numDividerViews;
|
2017-06-06 15:41:21 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Increase the column factor as the distance increases between the column and the center
|
|
|
|
|
* column(s).
|
|
|
|
|
*/
|
|
|
|
|
private float getColumnFactor(int col, int numCols) {
|
|
|
|
|
float centerColumn = numCols / 2;
|
|
|
|
|
int distanceToCenter = (int) Math.abs(col - centerColumn);
|
|
|
|
|
|
|
|
|
|
boolean evenNumberOfColumns = numCols % 2 == 0;
|
|
|
|
|
if (evenNumberOfColumns && col < centerColumn) {
|
|
|
|
|
distanceToCenter -= 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float factor = 0;
|
|
|
|
|
while (distanceToCenter > 0) {
|
|
|
|
|
if (distanceToCenter == 1) {
|
|
|
|
|
factor += 0.2f;
|
|
|
|
|
} else {
|
|
|
|
|
factor += 0.1f;
|
|
|
|
|
}
|
|
|
|
|
--distanceToCenter;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return factor;
|
|
|
|
|
}
|
|
|
|
|
}
|
2015-03-10 16:28:47 -07:00
|
|
|
}
|