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;
|
|
|
|
|
import android.view.LayoutInflater;
|
|
|
|
|
import android.view.View;
|
|
|
|
|
import android.view.ViewGroup;
|
2015-07-30 12:53:33 -07:00
|
|
|
import android.view.accessibility.AccessibilityEvent;
|
2016-04-28 17:39:03 -07:00
|
|
|
|
2023-01-12 10:31:24 -08:00
|
|
|
import androidx.annotation.Px;
|
2019-12-10 12:19:13 -08:00
|
|
|
import androidx.core.view.accessibility.AccessibilityEventCompat;
|
|
|
|
|
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
|
|
|
|
|
import androidx.core.view.accessibility.AccessibilityRecordCompat;
|
|
|
|
|
import androidx.recyclerview.widget.GridLayoutManager;
|
|
|
|
|
import androidx.recyclerview.widget.RecyclerView;
|
2022-10-21 20:42:34 +00:00
|
|
|
import androidx.recyclerview.widget.RecyclerView.Adapter;
|
2019-12-10 12:19:13 -08:00
|
|
|
|
2022-10-21 20:42:34 +00:00
|
|
|
import com.android.launcher3.util.ScrollableLayoutManager;
|
2022-01-14 23:15:47 -05:00
|
|
|
import com.android.launcher3.views.ActivityContext;
|
2017-02-21 16:43:56 -08:00
|
|
|
|
|
|
|
|
import java.util.List;
|
2022-12-02 00:31:43 +00:00
|
|
|
import java.util.concurrent.CopyOnWriteArrayList;
|
2015-04-06 15:12:49 -07:00
|
|
|
|
2015-03-10 16:28:47 -07:00
|
|
|
/**
|
|
|
|
|
* The grid view adapter of all the apps.
|
2022-01-14 23:15:47 -05:00
|
|
|
*
|
|
|
|
|
* @param <T> Type of context inflating all apps.
|
2015-03-10 16:28:47 -07:00
|
|
|
*/
|
2022-01-14 23:15:47 -05:00
|
|
|
public class AllAppsGridAdapter<T extends Context & ActivityContext> extends
|
2022-03-09 17:45:45 +00:00
|
|
|
BaseAllAppsAdapter<T> {
|
2015-03-10 16:28:47 -07:00
|
|
|
|
|
|
|
|
public static final String TAG = "AppsGridAdapter";
|
2022-09-23 17:08:51 -07:00
|
|
|
private final AppsGridLayoutManager mGridLayoutMgr;
|
2022-12-02 00:31:43 +00:00
|
|
|
private final CopyOnWriteArrayList<OnLayoutCompletedListener> mOnLayoutCompletedListeners =
|
|
|
|
|
new CopyOnWriteArrayList<>();
|
2022-11-08 03:11:56 +00:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Listener for {@link RecyclerView.LayoutManager#onLayoutCompleted(RecyclerView.State)}
|
|
|
|
|
*/
|
|
|
|
|
public interface OnLayoutCompletedListener {
|
|
|
|
|
void onLayoutCompleted();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Adds a {@link OnLayoutCompletedListener} to receive a callback when {@link
|
|
|
|
|
* RecyclerView.LayoutManager#onLayoutCompleted(RecyclerView.State)} is called
|
|
|
|
|
*/
|
|
|
|
|
public void addOnLayoutCompletedListener(OnLayoutCompletedListener listener) {
|
|
|
|
|
mOnLayoutCompletedListeners.add(listener);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Removes a {@link OnLayoutCompletedListener} to not receive a callback when {@link
|
|
|
|
|
* RecyclerView.LayoutManager#onLayoutCompleted(RecyclerView.State)} is called
|
|
|
|
|
*/
|
|
|
|
|
public void removeOnLayoutCompletedListener(OnLayoutCompletedListener listener) {
|
|
|
|
|
mOnLayoutCompletedListeners.remove(listener);
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-10 16:28:47 -07:00
|
|
|
|
2022-03-09 17:45:45 +00:00
|
|
|
public AllAppsGridAdapter(T activityContext, LayoutInflater inflater,
|
|
|
|
|
AlphabeticalAppsList apps, BaseAdapterProvider[] adapterProviders) {
|
|
|
|
|
super(activityContext, inflater, apps, adapterProviders);
|
|
|
|
|
mGridLayoutMgr = new AppsGridLayoutManager(mActivityContext);
|
2022-09-23 17:08:51 -07:00
|
|
|
mGridLayoutMgr.setSpanSizeLookup(new GridSpanSizer());
|
2022-03-09 17:45:45 +00:00
|
|
|
setAppsPerRow(activityContext.getDeviceProfile().numShownAllAppsColumns);
|
2015-03-10 16:28:47 -07:00
|
|
|
}
|
|
|
|
|
|
2020-08-15 09:40:26 -07:00
|
|
|
/**
|
2022-03-09 17:45:45 +00:00
|
|
|
* Returns the grid layout manager.
|
2020-08-15 09:40:26 -07:00
|
|
|
*/
|
2022-09-23 17:08:51 -07:00
|
|
|
public AppsGridLayoutManager getLayoutManager() {
|
2022-03-09 17:45:45 +00:00
|
|
|
return mGridLayoutMgr;
|
2020-08-15 09:40:26 -07:00
|
|
|
}
|
|
|
|
|
|
2022-09-23 17:08:51 -07:00
|
|
|
/** @return the column index that the given adapter index falls. */
|
|
|
|
|
public int getSpanIndex(int adapterIndex) {
|
|
|
|
|
AppsGridLayoutManager lm = getLayoutManager();
|
|
|
|
|
return lm.getSpanSizeLookup().getSpanIndex(adapterIndex, lm.getSpanCount());
|
|
|
|
|
}
|
|
|
|
|
|
2015-07-30 12:53:33 -07:00
|
|
|
/**
|
|
|
|
|
* A subclass of GridLayoutManager that overrides accessibility values during app search.
|
|
|
|
|
*/
|
2022-10-21 20:42:34 +00:00
|
|
|
public class AppsGridLayoutManager extends ScrollableLayoutManager {
|
2015-07-30 12:53:33 -07:00
|
|
|
|
|
|
|
|
public AppsGridLayoutManager(Context context) {
|
2022-10-21 20:42:34 +00:00
|
|
|
super(context);
|
2015-07-30 12:53:33 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@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()));
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-12 10:31:24 -08:00
|
|
|
/**
|
|
|
|
|
* We need to extend all apps' RecyclerView's bottom by 5% of view height to ensure extra
|
|
|
|
|
* roll(s) of app icons is rendered at the bottom, so that they can fill the bottom gap
|
|
|
|
|
* created during predictive back's scale animation from all apps to home.
|
|
|
|
|
*/
|
|
|
|
|
@Override
|
|
|
|
|
protected void calculateExtraLayoutSpace(RecyclerView.State state, int[] extraLayoutSpace) {
|
|
|
|
|
super.calculateExtraLayoutSpace(state, extraLayoutSpace);
|
|
|
|
|
@Px int extraSpacePx = (int) (getHeight()
|
|
|
|
|
* (1 - AllAppsTransitionController.SWIPE_ALL_APPS_TO_HOME_MIN_SCALE) / 2);
|
|
|
|
|
extraLayoutSpace[1] = Math.max(extraLayoutSpace[1], extraSpacePx);
|
|
|
|
|
}
|
|
|
|
|
|
2017-02-21 16:43:56 -08:00
|
|
|
/**
|
|
|
|
|
* 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();
|
2022-03-09 17:45:45 +00:00
|
|
|
adapterPosition = Math.max(adapterPosition, items.size() - 1);
|
2017-02-21 16:43:56 -08:00
|
|
|
int extraRows = 0;
|
2022-05-06 16:22:50 -07:00
|
|
|
for (int i = 0; i <= adapterPosition && i < items.size(); 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
|
|
|
}
|
2022-10-21 20:42:34 +00:00
|
|
|
|
2022-11-08 03:11:56 +00:00
|
|
|
@Override
|
|
|
|
|
public void onLayoutCompleted(RecyclerView.State state) {
|
|
|
|
|
super.onLayoutCompleted(state);
|
|
|
|
|
for (OnLayoutCompletedListener listener : mOnLayoutCompletedListeners) {
|
|
|
|
|
listener.onLayoutCompleted();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-21 20:42:34 +00:00
|
|
|
@Override
|
|
|
|
|
protected int incrementTotalHeight(Adapter adapter, int position, int heightUntilLastPos) {
|
|
|
|
|
AllAppsGridAdapter.AdapterItem item = mApps.getAdapterItems().get(position);
|
|
|
|
|
// only account for the first icon in the row since they are the same size within a row
|
|
|
|
|
return (isIconViewType(item.viewType) && item.rowAppIndex != 0)
|
|
|
|
|
? heightUntilLastPos
|
|
|
|
|
: (heightUntilLastPos + mCachedSizes.get(item.viewType));
|
|
|
|
|
}
|
2015-07-30 12:53:33 -07:00
|
|
|
}
|
|
|
|
|
|
2022-03-09 17:45:45 +00:00
|
|
|
@Override
|
|
|
|
|
public void setAppsPerRow(int appsPerRow) {
|
|
|
|
|
mAppsPerRow = appsPerRow;
|
|
|
|
|
int totalSpans = mAppsPerRow;
|
|
|
|
|
for (BaseAdapterProvider adapterProvider : mAdapterProviders) {
|
|
|
|
|
for (int itemPerRow : adapterProvider.getSupportedItemsPerRowArray()) {
|
|
|
|
|
if (totalSpans % itemPerRow != 0) {
|
|
|
|
|
totalSpans *= itemPerRow;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
mGridLayoutMgr.setSpanCount(totalSpans);
|
|
|
|
|
}
|
|
|
|
|
|
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) {
|
2021-03-05 14:59:13 -06:00
|
|
|
int totalSpans = mGridLayoutMgr.getSpanCount();
|
2022-12-19 09:57:12 -08:00
|
|
|
List<AdapterItem> items = mApps.getAdapterItems();
|
|
|
|
|
if (position >= items.size()) {
|
|
|
|
|
return totalSpans;
|
|
|
|
|
}
|
|
|
|
|
int viewType = items.get(position).viewType;
|
2020-09-20 11:28:18 -07:00
|
|
|
if (isIconViewType(viewType)) {
|
2021-03-05 14:59:13 -06:00
|
|
|
return totalSpans / mAppsPerRow;
|
2016-07-07 14:47:05 -07:00
|
|
|
} else {
|
2021-06-14 00:31:00 -05:00
|
|
|
BaseAdapterProvider adapterProvider = getAdapterProvider(viewType);
|
|
|
|
|
if (adapterProvider != null) {
|
|
|
|
|
return totalSpans / adapterProvider.getItemsPerRow(viewType, mAppsPerRow);
|
|
|
|
|
}
|
|
|
|
|
|
2017-11-24 16:40:03 +08:00
|
|
|
// Section breaks span the full width
|
2021-03-05 14:59:13 -06:00
|
|
|
return totalSpans;
|
2015-03-10 16:28:47 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|