Adding AppsStore for handling various app updates

Change-Id: Ia2242ce583576ace0924ef7142793ba37f4adcb9
This commit is contained in:
Sunny Goyal
2018-01-23 15:40:50 -08:00
parent 0680895528
commit 426345bfc4
11 changed files with 257 additions and 461 deletions
+1 -42
View File
@@ -31,48 +31,7 @@
<include layout="@layout/all_apps_fast_scroller" />
<com.android.launcher3.allapps.FloatingHeaderView
android:id="@+id/all_apps_header"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="@dimen/all_apps_header_top_padding"
android:clipToPadding="false"
android:layout_below="@id/search_container_all_apps" >
<include layout="@layout/predictions_view" android:id="@+id/header_content" />
<com.android.launcher3.allapps.PersonalWorkSlidingTabStrip
android:id="@+id/tabs"
android:layout_width="match_parent"
android:layout_height="@dimen/all_apps_header_tab_height"
android:layout_marginLeft="@dimen/all_apps_tabs_side_padding"
android:layout_marginRight="@dimen/all_apps_tabs_side_padding"
android:layout_below="@id/header_content"
android:orientation="horizontal">
<Button
android:id="@+id/tab_personal"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="?android:attr/selectableItemBackground"
android:fontFamily="sans-serif-medium"
android:text="@string/all_apps_personal_tab"
android:textAllCaps="true"
android:textColor="@color/all_apps_tab_text"
android:textSize="14sp"/>
<Button
android:id="@+id/tab_work"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="?android:attr/selectableItemBackground"
android:fontFamily="sans-serif-medium"
android:text="@string/all_apps_work_tab"
android:textAllCaps="true"
android:textColor="@color/all_apps_work_tab_text"
android:textSize="14sp"/>
</com.android.launcher3.allapps.PersonalWorkSlidingTabStrip>
</com.android.launcher3.allapps.FloatingHeaderView>
<include layout="@layout/all_apps_floating_header" />
<!-- Note: we are reusing/repurposing a system attribute for search layout, because of a
platform bug, which prevents using custom attributes in <include> tag -->
+58
View File
@@ -0,0 +1,58 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2018 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.allapps.FloatingHeaderView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/all_apps_header"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/search_container_all_apps"
android:clipToPadding="false"
android:paddingTop="@dimen/all_apps_header_top_padding"
android:orientation="vertical" >
<com.android.launcher3.allapps.PersonalWorkSlidingTabStrip
android:id="@+id/tabs"
android:layout_width="match_parent"
android:layout_height="@dimen/all_apps_header_tab_height"
android:layout_marginLeft="@dimen/all_apps_tabs_side_padding"
android:layout_marginRight="@dimen/all_apps_tabs_side_padding"
android:orientation="horizontal">
<Button
android:id="@+id/tab_personal"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="?android:attr/selectableItemBackground"
android:fontFamily="sans-serif-medium"
android:text="@string/all_apps_personal_tab"
android:textAllCaps="true"
android:textColor="@color/all_apps_tab_text"
android:textSize="14sp" />
<Button
android:id="@+id/tab_work"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="?android:attr/selectableItemBackground"
android:fontFamily="sans-serif-medium"
android:text="@string/all_apps_work_tab"
android:textAllCaps="true"
android:textColor="@color/all_apps_work_tab_text"
android:textSize="14sp" />
</com.android.launcher3.allapps.PersonalWorkSlidingTabStrip>
</com.android.launcher3.allapps.FloatingHeaderView>
-19
View File
@@ -1,19 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2017 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.allapps.PredictionRowView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
@@ -55,13 +55,11 @@ import com.android.launcher3.dragndrop.DragController;
import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.keyboard.FocusedItemDecorator;
import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.ComponentKeyMapper;
import com.android.launcher3.util.ItemInfoMatcher;
import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.views.BottomUserEducationView;
import java.util.HashMap;
import java.util.List;
import java.util.Set;
@@ -76,6 +74,7 @@ public class AllAppsContainerView extends RelativeLayout implements DragSource,
private final ClickShadowView mTouchFeedbackView;
private final ItemInfoMatcher mPersonalMatcher = ItemInfoMatcher.ofUser(Process.myUserHandle());
private final ItemInfoMatcher mWorkMatcher = ItemInfoMatcher.not(mPersonalMatcher);
private final AllAppsStore mAllAppsStore = new AllAppsStore();
private SearchUiManager mSearchUiManager;
private View mSearchContainer;
@@ -92,8 +91,6 @@ public class AllAppsContainerView extends RelativeLayout implements DragSource,
private boolean mHasPredictions = false;
private boolean mSearchModeWhileUsingTabs = false;
private final HashMap<ComponentKey, AppInfo> mComponentToAppMap = new HashMap<>();
public AllAppsContainerView(Context context) {
this(context, null);
}
@@ -132,6 +129,10 @@ public class AllAppsContainerView extends RelativeLayout implements DragSource,
// TODO: Reimplement once fast scroller is fixed.
}
public AllAppsStore getAppsStore() {
return mAllAppsStore;
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
@@ -149,64 +150,29 @@ public class AllAppsContainerView extends RelativeLayout implements DragSource,
public void setApps(List<AppInfo> apps) {
boolean hasWorkProfileApp = hasWorkProfileApp(apps);
rebindAdapters(hasWorkProfileApp);
mComponentToAppMap.clear();
addOrUpdateApps(apps);
mAllAppsStore.setApps(apps);
}
/**
* Adds or updates existing apps in the list
*/
public void addOrUpdateApps(List<AppInfo> apps) {
for (AppInfo app : apps) {
mComponentToAppMap.put(app.toComponentKey(), app);
}
onAppsUpdated();
mSearchUiManager.refreshSearchResult();
mHeader.onAppsUpdated();
mAllAppsStore.addOrUpdateApps(apps);
}
/**
* Removes some apps from the list.
*/
public void removeApps(List<AppInfo> apps) {
for (AppInfo app : apps) {
mComponentToAppMap.remove(app.toComponentKey());
}
onAppsUpdated();
mSearchUiManager.refreshSearchResult();
}
private void onAppsUpdated() {
for (int i = 0; i < getNumOfAdapters(); i++) {
mAH[i].appsList.onAppsUpdated();
}
}
private int getNumOfAdapters() {
return mUsingTabs ? mAH.length : 1;
mAllAppsStore.removeApps(apps);
}
public void updatePromiseAppProgress(PromiseAppInfo app) {
for (int i = 0; i < mAH.length; i++) {
updatePromiseAppProgress(app, mAH[i].recyclerView);
}
if (isHeaderVisible()) {
updatePromiseAppProgress(app, mHeader.getPredictionRow());
}
}
private void updatePromiseAppProgress(PromiseAppInfo app, ViewGroup parent) {
if (parent == null) {
return;
}
int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
View child = parent.getChildAt(i);
if (child instanceof BubbleTextView && child.getTag() == app) {
BubbleTextView bubbleTextView = (BubbleTextView) child;
bubbleTextView.applyProgressLevel(app.level);
mAllAppsStore.updateAllIcons((child) -> {
if (child.getTag() == app) {
child.applyProgressLevel(app.level);
}
}
});
}
/**
@@ -358,34 +324,15 @@ public class AllAppsContainerView extends RelativeLayout implements DragSource,
}
public void updateIconBadges(Set<PackageUserKey> updatedBadges) {
final PackageUserKey packageUserKey = new PackageUserKey(null, null);
for (int j = 0; j < mAH.length; j++) {
updateIconBadges(updatedBadges, packageUserKey, mAH[j].recyclerView);
}
if (mHeader != null) {
updateIconBadges(updatedBadges, packageUserKey, mHeader.getPredictionRow());
}
}
private void updateIconBadges(Set<PackageUserKey> updatedBadges, PackageUserKey packageUserKey,
ViewGroup parent) {
if (parent == null) {
return;
}
final int n = parent.getChildCount();
for (int i = 0; i < n; i++) {
View child = parent.getChildAt(i);
if (child instanceof PredictionRowView) {
updateIconBadges(updatedBadges, packageUserKey, (PredictionRowView) child);
PackageUserKey tempKey = new PackageUserKey(null, null);
mAllAppsStore.updateAllIcons((child) -> {
if (child.getTag() instanceof ItemInfo) {
ItemInfo info = (ItemInfo) child.getTag();
if (tempKey.updateFromItemInfo(info) && updatedBadges.contains(tempKey)) {
child.applyBadgeState(info, true /* animate */);
}
}
if (!(child instanceof BubbleTextView) || !(child.getTag() instanceof ItemInfo)) {
continue;
}
ItemInfo info = (ItemInfo) child.getTag();
if (packageUserKey.updateFromItemInfo(info) && updatedBadges.contains(packageUserKey)) {
((BubbleTextView) child).applyBadgeState(info, true /* animate */);
}
}
});
}
public SpringAnimationHandler getSpringAnimationHandler() {
@@ -403,6 +350,9 @@ public class AllAppsContainerView extends RelativeLayout implements DragSource,
replaceRVContainer(showTabs);
mUsingTabs = showTabs;
mAllAppsStore.unregisterIconContainer(mAH[AdapterHolder.MAIN].recyclerView);
mAllAppsStore.unregisterIconContainer(mAH[AdapterHolder.WORK].recyclerView);
if (mUsingTabs) {
mAH[AdapterHolder.MAIN].setup(mViewPager.getChildAt(0), mPersonalMatcher);
mAH[AdapterHolder.WORK].setup(mViewPager.getChildAt(1), mWorkMatcher);
@@ -419,6 +369,9 @@ public class AllAppsContainerView extends RelativeLayout implements DragSource,
}
}
mAllAppsStore.registerIconContainer(mAH[AdapterHolder.MAIN].recyclerView);
mAllAppsStore.registerIconContainer(mAH[AdapterHolder.WORK].recyclerView);
applyTouchDelegate();
}
@@ -492,9 +445,6 @@ public class AllAppsContainerView extends RelativeLayout implements DragSource,
}
public void setPredictedApps(List<ComponentKeyMapper<AppInfo>> apps) {
if (isHeaderVisible()) {
mHeader.getPredictionRow().setPredictedApps(apps);
}
mAH[AdapterHolder.MAIN].appsList.setPredictedApps(apps);
boolean hasPredictions = !apps.isEmpty();
if (mHasPredictions != hasPredictions) {
@@ -506,7 +456,7 @@ public class AllAppsContainerView extends RelativeLayout implements DragSource,
}
public AppInfo findApp(ComponentKeyMapper<AppInfo> mapper) {
return mapper.getItem(mComponentToAppMap);
return mAllAppsStore.getApp(mapper);
}
public AlphabeticalAppsList getApps() {
@@ -526,9 +476,9 @@ public class AllAppsContainerView extends RelativeLayout implements DragSource,
return;
}
mHeader.setVisibility(View.VISIBLE);
mHeader.setup(mAH, mComponentToAppMap, mNumPredictedAppsPerRow);
mHeader.setup(mAH, mAH[AllAppsContainerView.AdapterHolder.WORK].recyclerView == null);
int padding = mHeader.getPredictionRow().getExpectedHeight();
int padding = mHeader.getMaxTranslation();
if (mHasPredictions && !mUsingTabs) {
padding += mHeader.getPaddingTop() + mHeader.getPaddingBottom();
}
@@ -582,14 +532,6 @@ public class AllAppsContainerView extends RelativeLayout implements DragSource,
}
}
public List<AppInfo> getPredictedApps() {
if (isHeaderVisible()) {
return mHeader.getPredictionRow().getPredictedApps();
} else {
return mAH[AdapterHolder.MAIN].appsList.getPredictedApps();
}
}
public boolean isHeaderVisible() {
return mHeader != null && mHeader.getVisibility() == View.VISIBLE;
}
@@ -604,7 +546,7 @@ public class AllAppsContainerView extends RelativeLayout implements DragSource,
public static final int MAIN = 0;
public static final int WORK = 1;
final AllAppsGridAdapter adapter;
public final AllAppsGridAdapter adapter;
final LinearLayoutManager layoutManager;
final SpringAnimationHandler animationHandler;
final AlphabeticalAppsList appsList;
@@ -614,7 +556,7 @@ public class AllAppsContainerView extends RelativeLayout implements DragSource,
boolean verticalFadingEdge;
AdapterHolder(boolean isWork) {
appsList = new AlphabeticalAppsList(mLauncher, mComponentToAppMap, isWork);
appsList = new AlphabeticalAppsList(mLauncher, mAllAppsStore, isWork);
adapter = new AllAppsGridAdapter(mLauncher, appsList, mLauncher,
AllAppsContainerView.this, true);
appsList.setAdapter(adapter);
@@ -649,11 +591,6 @@ public class AllAppsContainerView extends RelativeLayout implements DragSource,
? paddingTopForTabs : padding.top;
recyclerView.setPadding(padding.left, paddingTop, padding.right, padding.bottom);
}
if (isHeaderVisible()) {
PredictionRowView prv = mHeader.getPredictionRow();
prv.setPadding(padding.left, prv.getPaddingTop() , padding.right,
prv.getPaddingBottom());
}
}
void applyNumsPerRow() {
@@ -663,10 +600,6 @@ public class AllAppsContainerView extends RelativeLayout implements DragSource,
}
adapter.setNumAppsPerRow(mNumAppsPerRow);
appsList.setNumAppsPerRow(mNumAppsPerRow, mNumPredictedAppsPerRow);
if (isHeaderVisible()) {
mHeader.getPredictionRow()
.setNumAppsPerRow(mNumPredictedAppsPerRow);
}
}
}
@@ -0,0 +1,127 @@
/*
* Copyright (C) 2018 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.allapps;
import android.view.View;
import android.view.ViewGroup;
import com.android.launcher3.AppInfo;
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.ComponentKeyMapper;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
/**
* A utility class to maintain the collection of all apps.
*/
public class AllAppsStore {
private final HashMap<ComponentKey, AppInfo> mComponentToAppMap = new HashMap<>();
private final List<OnUpdateListener> mUpdateListeners = new ArrayList<>();
private final ArrayList<ViewGroup> mIconContainers = new ArrayList<>();
public Collection<AppInfo> getApps() {
return mComponentToAppMap.values();
}
/**
* Sets the current set of apps.
*/
public void setApps(List<AppInfo> apps) {
mComponentToAppMap.clear();
addOrUpdateApps(apps);
}
public AppInfo getApp(ComponentKey key) {
return mComponentToAppMap.get(key);
}
public AppInfo getApp(ComponentKeyMapper<AppInfo> mapper) {
return mapper.getItem(mComponentToAppMap);
}
/**
* Adds or updates existing apps in the list
*/
public void addOrUpdateApps(List<AppInfo> apps) {
for (AppInfo app : apps) {
mComponentToAppMap.put(app.toComponentKey(), app);
}
notifyUpdate();
}
/**
* Removes some apps from the list.
*/
public void removeApps(List<AppInfo> apps) {
for (AppInfo app : apps) {
mComponentToAppMap.remove(app.toComponentKey());
}
notifyUpdate();
}
private void notifyUpdate() {
int count = mUpdateListeners.size();
for (int i = 0; i < count; i++) {
mUpdateListeners.get(i).onAppsUpdated();
}
}
public void addUpdateListener(OnUpdateListener listener) {
mUpdateListeners.add(listener);
}
public void removeUpdateListener(OnUpdateListener listener) {
mUpdateListeners.remove(listener);
}
public void registerIconContainer(ViewGroup container) {
if (container != null) {
mIconContainers.add(container);
}
}
public void unregisterIconContainer(ViewGroup container) {
mIconContainers.remove(container);
}
public void updateAllIcons(IconAction action) {
for (int i = mIconContainers.size() - 1; i >= 0; i--) {
ViewGroup parent = mIconContainers.get(i);
int childCount = parent.getChildCount();
for (int j = 0; j < childCount; j++) {
View child = parent.getChildAt(j);
if (child instanceof BubbleTextView) {
action.apply((BubbleTextView) child);
}
}
}
}
public interface OnUpdateListener {
void onAppsUpdated();
}
public interface IconAction {
void apply(BubbleTextView icon);
}
}
@@ -44,7 +44,7 @@ import java.util.TreeMap;
/**
* The alphabetically sorted list of applications.
*/
public class AlphabeticalAppsList {
public class AlphabeticalAppsList implements AllAppsStore.OnUpdateListener {
public static final String TAG = "AlphabeticalAppsList";
private static final boolean DEBUG = false;
@@ -153,7 +153,7 @@ public class AlphabeticalAppsList {
// The set of apps from the system not including predictions
private final List<AppInfo> mApps = new ArrayList<>();
private final HashMap<ComponentKey, AppInfo> mComponentToAppMap;
private final AllAppsStore mAllAppsStore;
// The set of filtered apps with the current filter
private final List<AppInfo> mFilteredApps = new ArrayList<>();
@@ -179,16 +179,13 @@ public class AlphabeticalAppsList {
private int mNumAppRowsInAdapter;
private ItemInfoMatcher mItemFilter;
public AlphabeticalAppsList(
Context context,
HashMap<ComponentKey,
AppInfo> componentToAppMap,
boolean isWork) {
mComponentToAppMap = componentToAppMap;
public AlphabeticalAppsList(Context context, AllAppsStore appsStore, boolean isWork) {
mAllAppsStore = appsStore;
mLauncher = Launcher.getLauncher(context);
mIndexer = new AlphabeticIndexCompat(context);
mAppNameComparator = new AppInfoComparator(context);
mIsWork = isWork;
mAllAppsStore.addUpdateListener(this);
}
public void updateItemFilter(ItemInfoMatcher itemFilter) {
@@ -283,14 +280,14 @@ public class AlphabeticalAppsList {
}
private List<AppInfo> processPredictedAppComponents(List<ComponentKeyMapper<AppInfo>> components) {
if (mComponentToAppMap.isEmpty()) {
if (mAllAppsStore.getApps().isEmpty()) {
// Apps have not been bound yet.
return Collections.emptyList();
}
List<AppInfo> predictedApps = new ArrayList<>();
for (ComponentKeyMapper<AppInfo> mapper : components) {
AppInfo info = mapper.getItem(mComponentToAppMap);
AppInfo info = mAllAppsStore.getApp(mapper);
if (info != null) {
predictedApps.add(info);
} else {
@@ -359,11 +356,12 @@ public class AlphabeticalAppsList {
/**
* Updates internals when the set of apps are updated.
*/
void onAppsUpdated() {
@Override
public void onAppsUpdated() {
// Sort the list of apps
mApps.clear();
for (AppInfo app : mComponentToAppMap.values()) {
for (AppInfo app : mAllAppsStore.getApps()) {
if (mItemFilter == null || mItemFilter.matches(app, null) || hasFilter()) {
mApps.add(app);
}
@@ -580,7 +578,7 @@ public class AlphabeticalAppsList {
}
ArrayList<AppInfo> result = new ArrayList<>();
for (ComponentKey key : mSearchResults) {
AppInfo match = mComponentToAppMap.get(key);
AppInfo match = mAllAppsStore.getApp(key);
if (match != null) {
result.add(match);
}
@@ -15,7 +15,6 @@
*/
package com.android.launcher3.allapps;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Point;
@@ -27,18 +26,14 @@ import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import com.android.launcher3.AppInfo;
import com.android.launcher3.R;
import com.android.launcher3.util.ComponentKey;
import java.util.HashMap;
public class FloatingHeaderView extends RelativeLayout implements
public class FloatingHeaderView extends LinearLayout implements
ValueAnimator.AnimatorUpdateListener {
private final Rect mClip = new Rect(0, 0, Integer.MAX_VALUE, Integer.MAX_VALUE);
private final ValueAnimator mAnimator = ValueAnimator.ofInt(0, 0);
private final Point mTempOffset = new Point();
@@ -63,19 +58,18 @@ public class FloatingHeaderView extends RelativeLayout implements
}
};
private PredictionRowView mPredictionRow;
private ViewGroup mTabLayout;
private AllAppsRecyclerView mMainRV;
private AllAppsRecyclerView mWorkRV;
private AllAppsRecyclerView mCurrentRV;
private ViewGroup mParent;
private boolean mTabsHidden;
private boolean mHeaderCollapsed;
private int mMaxTranslation;
private int mSnappedScrolledY;
private int mTranslationY;
private boolean mForwardToRecyclerView;
protected int mMaxTranslation;
public FloatingHeaderView(@NonNull Context context) {
this(context, null);
}
@@ -88,17 +82,10 @@ public class FloatingHeaderView extends RelativeLayout implements
protected void onFinishInflate() {
super.onFinishInflate();
mTabLayout = findViewById(R.id.tabs);
mPredictionRow = findViewById(R.id.header_content);
}
public void setup(AllAppsContainerView.AdapterHolder[] mAH,
HashMap<ComponentKey, AppInfo> componentToAppMap, int numPredictedAppsPerRow) {
mTabsHidden = mAH[AllAppsContainerView.AdapterHolder.WORK].recyclerView == null;
mTabLayout.setVisibility(mTabsHidden ? View.GONE : View.VISIBLE);
mPredictionRow.setup(mAH[AllAppsContainerView.AdapterHolder.MAIN].adapter,
componentToAppMap, numPredictedAppsPerRow);
mPredictionRow.setShowDivider(mTabsHidden);
mMaxTranslation = mPredictionRow.getExpectedHeight();
public void setup(AllAppsContainerView.AdapterHolder[] mAH, boolean tabsHidden) {
mTabLayout.setVisibility(tabsHidden ? View.GONE : View.VISIBLE);
mMainRV = setupRV(mMainRV, mAH[AllAppsContainerView.AdapterHolder.MAIN].recyclerView);
mWorkRV = setupRV(mWorkRV, mAH[AllAppsContainerView.AdapterHolder.WORK].recyclerView);
mParent = (ViewGroup) mMainRV.getParent();
@@ -117,12 +104,12 @@ public class FloatingHeaderView extends RelativeLayout implements
mCurrentRV = active ? mMainRV : mWorkRV;
}
public PredictionRowView getPredictionRow() {
return mPredictionRow;
public int getMaxTranslation() {
return mMaxTranslation;
}
private boolean canSnapAt(int currentScrollY) {
return Math.abs(currentScrollY) <= mPredictionRow.getHeight();
return Math.abs(currentScrollY) <= mMaxTranslation;
}
private void moved(final int currentScrollY) {
@@ -149,16 +136,12 @@ public class FloatingHeaderView extends RelativeLayout implements
}
}
private void apply() {
protected void applyScroll(int uncappedY, int currentY) { }
protected void apply() {
int uncappedTranslationY = mTranslationY;
mTranslationY = Math.max(mTranslationY, -mMaxTranslation);
if (mTranslationY != uncappedTranslationY) {
// we hide it completely if already capped (for opening search anim)
mPredictionRow.setVisibility(View.INVISIBLE);
} else {
mPredictionRow.setVisibility(View.VISIBLE);
mPredictionRow.setTranslationY(uncappedTranslationY);
}
applyScroll(uncappedTranslationY, mTranslationY);
mTabLayout.setTranslationY(mTranslationY);
mClip.top = mMaxTranslation + mTranslationY;
// clipping on a draw might cause additional redraw
@@ -218,10 +201,6 @@ public class FloatingHeaderView extends RelativeLayout implements
p.x = getLeft() - mCurrentRV.getLeft() - mParent.getLeft();
p.y = getTop() - mCurrentRV.getTop() - mParent.getTop();
}
public void onAppsUpdated() {
mPredictionRow.onAppsUpdated();
}
}
@@ -1,245 +0,0 @@
/*
* Copyright (C) 2017 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.allapps;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.widget.LinearLayout;
import com.android.launcher3.AppInfo;
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.logging.UserEventDispatcher;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.ComponentKeyMapper;
import com.android.launcher3.util.Themes;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
public class PredictionRowView extends LinearLayout implements
UserEventDispatcher.LogContainerProvider {
private static final String TAG = "PredictionRowView";
private HashMap<ComponentKey, AppInfo> mComponentToAppMap;
private int mNumPredictedAppsPerRow;
// The set of predicted app component names
private final List<ComponentKeyMapper<AppInfo>> mPredictedAppComponents = new ArrayList<>();
// The set of predicted apps resolved from the component names and the current set of apps
private final ArrayList<AppInfo> mPredictedApps = new ArrayList<>();
private final Paint mPaint;
// This adapter is only used to create an identical item w/ same behavior as in the all apps RV
private AllAppsGridAdapter mAdapter;
private boolean mShowDivider;
public PredictionRowView(@NonNull Context context) {
this(context, null);
}
public PredictionRowView(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
setOrientation(LinearLayout.HORIZONTAL);
setWillNotDraw(false);
mPaint = new Paint();
mPaint.setColor(Themes.getAttrColor(context, android.R.attr.colorControlHighlight));
mPaint.setStrokeWidth(getResources().getDimensionPixelSize(R.dimen.all_apps_divider_height));
}
public void setup(AllAppsGridAdapter adapter, HashMap<ComponentKey, AppInfo> componentToAppMap,
int numPredictedAppsPerRow) {
mAdapter = adapter;
mComponentToAppMap = componentToAppMap;
mNumPredictedAppsPerRow = numPredictedAppsPerRow;
setVisibility(mPredictedAppComponents.isEmpty() ? View.GONE : View.VISIBLE);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(getExpectedHeight(),
MeasureSpec.EXACTLY));
}
public int getExpectedHeight() {
int height = 0;
if (!mPredictedAppComponents.isEmpty()) {
height += Launcher.getLauncher(getContext())
.getDeviceProfile().allAppsCellHeightPx;
height += getPaddingTop() + getPaddingBottom();
}
return height;
}
public void setShowDivider(boolean showDivider) {
mShowDivider = showDivider;
int paddingBottom = showDivider ? getResources()
.getDimensionPixelSize(R.dimen.all_apps_prediction_row_divider_height) : 0;
setPadding(getPaddingLeft(), getPaddingTop(), getPaddingRight(), paddingBottom);
}
/**
* Sets the number of apps per row.
*/
public void setNumAppsPerRow(int numPredictedAppsPerRow) {
if (mNumPredictedAppsPerRow != numPredictedAppsPerRow) {
mNumPredictedAppsPerRow = numPredictedAppsPerRow;
onPredictionsUpdated();
}
}
/**
* Returns the predicted apps.
*/
public List<AppInfo> getPredictedApps() {
return mPredictedApps;
}
/**
* Sets the current set of predicted apps.
*
* This can be called before we get the full set of applications, we should merge the results
* only in onPredictionsUpdated() which is idempotent.
*
* If the number of predicted apps is the same as the previous list of predicted apps,
* we can optimize by swapping them in place.
*/
public void setPredictedApps(List<ComponentKeyMapper<AppInfo>> apps) {
mPredictedAppComponents.clear();
mPredictedAppComponents.addAll(apps);
mPredictedApps.clear();
mPredictedApps.addAll(processPredictedAppComponents(mPredictedAppComponents));
onPredictionsUpdated();
}
private void onPredictionsUpdated() {
int childCountBefore = getChildCount();
if (getChildCount() != mNumPredictedAppsPerRow) {
while (getChildCount() > mNumPredictedAppsPerRow) {
removeViewAt(0);
}
while (getChildCount() < mNumPredictedAppsPerRow) {
AllAppsGridAdapter.ViewHolder holder = mAdapter
.onCreateViewHolder(this, AllAppsGridAdapter.VIEW_TYPE_ICON);
BubbleTextView icon = (BubbleTextView) holder.itemView;
LinearLayout.LayoutParams params =
new LayoutParams(0, icon.getLayoutParams().height);
params.weight = 1;
icon.setLayoutParams(params);
addView(icon);
}
}
for (int i = 0; i < getChildCount(); i++) {
BubbleTextView icon = (BubbleTextView) getChildAt(i);
icon.reset();
if (mPredictedApps.size() > i) {
icon.setVisibility(View.VISIBLE);
icon.applyFromApplicationInfo(mPredictedApps.get(i));
} else {
icon.setVisibility(View.INVISIBLE);
}
}
if (getChildCount() > 0 && childCountBefore == 0
|| getChildCount() == 0 && childCountBefore > 0) {
// setting up header to adjust the height
// only necessary if childcount switches from/to 0
Launcher.getLauncher(getContext()).getAppsView().setupHeader();
}
}
/**
* Refreshes the app icons in the row view, while preserving the same set of predictions.
*/
public void onAppsUpdated() {
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
if (!(child instanceof BubbleTextView)) {
continue;
}
if (i >= mPredictedApps.size()) {
break;
}
BubbleTextView icon = (BubbleTextView) getChildAt(i);
icon.reset();
icon.applyFromApplicationInfo(mPredictedApps.get(i));
}
}
private List<AppInfo> processPredictedAppComponents(
List<ComponentKeyMapper<AppInfo>> components) {
if (mComponentToAppMap.isEmpty()) {
// Apps have not been bound yet.
return Collections.emptyList();
}
List<AppInfo> predictedApps = new ArrayList<>();
for (ComponentKeyMapper<AppInfo> mapper : components) {
AppInfo info = mapper.getItem(mComponentToAppMap);
if (info != null) {
predictedApps.add(info);
} else {
if (FeatureFlags.IS_DOGFOOD_BUILD) {
Log.e(TAG, "Predicted app not found: " + mapper);
}
}
// Stop at the number of predicted apps
if (predictedApps.size() == mNumPredictedAppsPerRow) {
break;
}
}
return predictedApps;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mShowDivider) {
int side = getResources().getDimensionPixelSize(R.dimen.dynamic_grid_edge_margin);
int y = getHeight() - (getPaddingBottom() / 2);
int x1 = getPaddingLeft() + side;
int x2 = getWidth() - getPaddingRight() - side;
canvas.drawLine(x1, y, x2, y, mPaint);
}
}
@Override
public void fillInLogContainerData(View v, ItemInfo info, LauncherLogProto.Target target,
LauncherLogProto.Target targetParent) {
for (int i = 0; i < mPredictedApps.size(); i++) {
AppInfo appInfo = mPredictedApps.get(i);
if (appInfo == info) {
targetParent.containerType = LauncherLogProto.ContainerType.PREDICTION;
target.predictedRank = i;
break;
}
}
}
}
@@ -34,12 +34,6 @@ public interface SearchUiManager {
*/
@NonNull SpringAnimation getSpringForFling();
/**
* Notifies the search manager that the apps-list has changed and the search UI should be
* updated accordingly.
*/
void refreshSearchResult();
/**
* Notifies the search manager to close any active search session.
*/
@@ -37,6 +37,7 @@ import com.android.launcher3.ExtendedEditText;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.allapps.AllAppsContainerView;
import com.android.launcher3.allapps.AllAppsStore;
import com.android.launcher3.allapps.AlphabeticalAppsList;
import com.android.launcher3.allapps.SearchUiManager;
import com.android.launcher3.graphics.TintedDrawableSpan;
@@ -48,7 +49,8 @@ import java.util.ArrayList;
* Layout to contain the All-apps search UI.
*/
public class AppsSearchContainerLayout extends FrameLayout
implements SearchUiManager, AllAppsSearchBarController.Callbacks {
implements SearchUiManager, AllAppsSearchBarController.Callbacks,
AllAppsStore.OnUpdateListener {
private final Launcher mLauncher;
private final int mMinHeight;
@@ -110,6 +112,18 @@ public class AppsSearchContainerLayout extends FrameLayout
}
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
mLauncher.getAppsView().getAppsStore().addUpdateListener(this);
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
mLauncher.getAppsView().getAppsStore().removeUpdateListener(this);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (!mLauncher.getDeviceProfile().isVerticalBarLayout()) {
@@ -134,7 +148,7 @@ public class AppsSearchContainerLayout extends FrameLayout
}
@Override
public void refreshSearchResult() {
public void onAppsUpdated() {
mSearchBarController.refreshSearchResult();
}
@@ -18,8 +18,6 @@ package com.android.launcher3.util;
import android.support.annotation.Nullable;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class ComponentKeyMapper<T> {