Adding an interface to allow adding custom views in FloatingHeaderView

Bug: 109828640
Change-Id: I9bde5d4fab47eb3e5787bbb741b5b9051a15c0c2
This commit is contained in:
Sunny Goyal
2018-11-01 16:37:03 -07:00
parent 066ace1b88
commit dcf917b133
6 changed files with 351 additions and 103 deletions
@@ -19,28 +19,34 @@ import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Point;
import android.graphics.Rect;
import android.util.ArrayMap;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Interpolator;
import android.widget.LinearLayout;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.RecyclerView;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Insettable;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.anim.PropertySetter;
import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
import com.android.systemui.plugins.AllAppsRow;
import com.android.systemui.plugins.AllAppsRow.OnHeightUpdatedListener;
import com.android.systemui.plugins.PluginListener;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.RecyclerView;
public class FloatingHeaderView extends LinearLayout implements
ValueAnimator.AnimatorUpdateListener, PluginListener<AllAppsRow> {
ValueAnimator.AnimatorUpdateListener, PluginListener<AllAppsRow>, Insettable,
OnHeightUpdatedListener {
private final Rect mClip = new Rect(0, 0, Integer.MAX_VALUE, Integer.MAX_VALUE);
private final ValueAnimator mAnimator = ValueAnimator.ofInt(0, 0);
@@ -62,17 +68,18 @@ public class FloatingHeaderView extends LinearLayout implements
int current = -mCurrentRV.getCurrentScrollY();
moved(current);
apply();
applyVerticalMove();
}
};
private final int mHeaderTopPadding;
protected final Map<AllAppsRow, PluginHeaderRow> mPluginRows = new ArrayMap<>();
protected ViewGroup mTabLayout;
private AllAppsRecyclerView mMainRV;
private AllAppsRecyclerView mWorkRV;
private AllAppsRecyclerView mCurrentRV;
protected final Map<AllAppsRow, View> mPluginRows;
// Contains just the values of the above map so we can iterate without extracting a new list.
protected final List<View> mPluginRowViews;
private ViewGroup mParent;
private boolean mHeaderCollapsed;
private int mSnappedScrolledY;
@@ -85,20 +92,42 @@ public class FloatingHeaderView extends LinearLayout implements
protected int mMaxTranslation;
private boolean mMainRVActive = true;
private boolean mCollapsed = false;
// This is initialized once during inflation and stays constant after that. Fixed views
// cannot be added or removed dynamically.
private FloatingHeaderRow[] mFixedRows = FloatingHeaderRow.NO_ROWS;
// Array of all fixed rows and plugin rows. This is initialized every time a plugin is
// enabled or disabled, and represent the current set of all rows.
private FloatingHeaderRow[] mAllRows = FloatingHeaderRow.NO_ROWS;
public FloatingHeaderView(@NonNull Context context) {
this(context, null);
}
public FloatingHeaderView(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
mPluginRows = new HashMap<>();
mPluginRowViews = new ArrayList<>();
mHeaderTopPadding = context.getResources()
.getDimensionPixelSize(R.dimen.all_apps_header_top_padding);
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
mTabLayout = findViewById(R.id.tabs);
// Find all floating header rows.
ArrayList<FloatingHeaderRow> rows = new ArrayList<>();
int count = getChildCount();
for (int i = 0; i < count; i++) {
View child = getChildAt(i);
if (child instanceof FloatingHeaderRow) {
rows.add((FloatingHeaderRow) child);
}
}
mFixedRows = rows.toArray(new FloatingHeaderRow[rows.size()]);
mAllRows = mFixedRows;
}
@Override
@@ -114,50 +143,70 @@ public class FloatingHeaderView extends LinearLayout implements
PluginManagerWrapper.INSTANCE.get(getContext()).removePluginListener(this);
}
@Override
public void onPluginConnected(AllAppsRow allAppsRowPlugin, Context context) {
mPluginRows.put(allAppsRowPlugin, null);
setupPluginRows();
allAppsRowPlugin.setOnHeightUpdatedListener(this::onPluginRowHeightUpdated);
private void recreateAllRowsArray() {
int pluginCount = mPluginRows.size();
if (pluginCount == 0) {
mAllRows = mFixedRows;
} else {
int count = mFixedRows.length;
mAllRows = new FloatingHeaderRow[count + pluginCount];
for (int i = 0; i < count; i++) {
mAllRows[i] = mFixedRows[i];
}
for (PluginHeaderRow row : mPluginRows.values()) {
mAllRows[count] = row;
count++;
}
}
}
protected void onPluginRowHeightUpdated() {
@Override
public void onPluginConnected(AllAppsRow allAppsRowPlugin, Context context) {
PluginHeaderRow headerRow = new PluginHeaderRow(allAppsRowPlugin, this);
addView(headerRow.mView, indexOfChild(mTabLayout));
mPluginRows.put(allAppsRowPlugin, headerRow);
recreateAllRowsArray();
allAppsRowPlugin.setOnHeightUpdatedListener(this);
}
@Override
public void onHeightUpdated() {
int oldMaxHeight = mMaxTranslation;
updateExpectedHeight();
if (mMaxTranslation != oldMaxHeight) {
AllAppsContainerView parent = (AllAppsContainerView) getParent();
if (parent != null) {
parent.setupHeader();
}
}
}
@Override
public void onPluginDisconnected(AllAppsRow plugin) {
View pluginRowView = mPluginRows.get(plugin);
removeView(pluginRowView);
PluginHeaderRow row = mPluginRows.get(plugin);
removeView(row.mView);
mPluginRows.remove(plugin);
mPluginRowViews.remove(pluginRowView);
onPluginRowHeightUpdated();
recreateAllRowsArray();
onHeightUpdated();
}
public void setup(AllAppsContainerView.AdapterHolder[] mAH, boolean tabsHidden) {
for (FloatingHeaderRow row : mAllRows) {
row.setup(this, mAllRows, tabsHidden);
}
updateExpectedHeight();
mTabsHidden = 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();
setMainActive(mMainRVActive || mWorkRV == null);
setupPluginRows();
reset(false);
}
private void setupPluginRows() {
for (Map.Entry<AllAppsRow, View> rowPluginEntry : mPluginRows.entrySet()) {
if (rowPluginEntry.getValue() == null) {
View pluginRow = rowPluginEntry.getKey().setup(this);
addView(pluginRow, indexOfChild(mTabLayout));
rowPluginEntry.setValue(pluginRow);
mPluginRowViews.add(pluginRow);
}
}
for (View plugin : mPluginRowViews) {
plugin.setVisibility(mHeaderCollapsed ? GONE : VISIBLE);
}
}
private AllAppsRecyclerView setupRV(AllAppsRecyclerView old, AllAppsRecyclerView updated) {
if (old != updated && updated != null ) {
updated.addOnScrollListener(mOnScrollListener);
@@ -165,6 +214,16 @@ public class FloatingHeaderView extends LinearLayout implements
return updated;
}
private void updateExpectedHeight() {
mMaxTranslation = 0;
if (mCollapsed) {
return;
}
for (FloatingHeaderRow row : mAllRows) {
mMaxTranslation += row.getExpectedHeight();
}
}
public void setMainActive(boolean active) {
mCurrentRV = active ? mMainRV : mWorkRV;
mMainRVActive = active;
@@ -208,12 +267,21 @@ public class FloatingHeaderView extends LinearLayout implements
}
}
protected void applyScroll(int uncappedY, int currentY) { }
protected void apply() {
protected void applyVerticalMove() {
int uncappedTranslationY = mTranslationY;
mTranslationY = Math.max(mTranslationY, -mMaxTranslation);
applyScroll(uncappedTranslationY, mTranslationY);
if (mCollapsed || uncappedTranslationY < mTranslationY - mHeaderTopPadding) {
// we hide it completely if already capped (for opening search anim)
for (FloatingHeaderRow row : mAllRows) {
row.setVerticalScroll(0, true /* isScrolledOut */);
}
} else {
for (FloatingHeaderRow row : mAllRows) {
row.setVerticalScroll(uncappedTranslationY, false /* isScrolledOut */);
}
}
mTabLayout.setTranslationY(mTranslationY);
mClip.top = mMaxTranslation + mTranslationY;
// clipping on a draw might cause additional redraw
@@ -223,6 +291,16 @@ public class FloatingHeaderView extends LinearLayout implements
}
}
/**
* Hides all the floating rows
*/
public void setCollapsed(boolean collapse) {
if (mCollapsed == collapse) return;
mCollapsed = collapse;
onHeightUpdated();
}
public void reset(boolean animate) {
if (mAnimator.isStarted()) {
mAnimator.cancel();
@@ -234,7 +312,7 @@ public class FloatingHeaderView extends LinearLayout implements
mAnimator.start();
} else {
mTranslationY = 0;
apply();
applyVerticalMove();
}
mHeaderCollapsed = false;
mSnappedScrolledY = -mMaxTranslation;
@@ -248,7 +326,7 @@ public class FloatingHeaderView extends LinearLayout implements
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mTranslationY = (Integer) animation.getAnimatedValue();
apply();
applyVerticalMove();
}
@Override
@@ -287,8 +365,12 @@ public class FloatingHeaderView extends LinearLayout implements
public void setContentVisibility(boolean hasHeader, boolean hasContent, PropertySetter setter,
Interpolator fadeInterpolator) {
setter.setViewAlpha(this, hasContent ? 1 : 0, fadeInterpolator);
for (FloatingHeaderRow row : mAllRows) {
row.setContentVisibility(hasHeader, hasContent, setter, fadeInterpolator);
}
allowTouchForwarding(hasContent);
setter.setFloat(mTabLayout, ALPHA, hasContent ? 1 : 0, fadeInterpolator);
}
protected void allowTouchForwarding(boolean allow) {
@@ -296,6 +378,11 @@ public class FloatingHeaderView extends LinearLayout implements
}
public boolean hasVisibleContent() {
for (FloatingHeaderRow row : mAllRows) {
if (row.hasVisibleContent()) {
return true;
}
}
return false;
}
@@ -303,6 +390,23 @@ public class FloatingHeaderView extends LinearLayout implements
public boolean hasOverlappingRendering() {
return false;
}
@Override
public void setInsets(Rect insets) {
DeviceProfile grid = Launcher.getLauncher(getContext()).getDeviceProfile();
for (FloatingHeaderRow row : mAllRows) {
row.setInsets(insets, grid);
}
}
public <T extends FloatingHeaderRow> T findFixedRowByType(Class<T> type) {
for (FloatingHeaderRow row : mAllRows) {
if (row.getTypeClass() == type) {
return (T) row;
}
}
return null;
}
}