summaryrefslogtreecommitdiff
path: root/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
diff options
context:
space:
mode:
Diffstat (limited to 'quickstep/src/com/android/launcher3/taskbar/TaskbarView.java')
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/TaskbarView.java380
1 files changed, 137 insertions, 243 deletions
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
index c6573a639c..59393d7b5b 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
@@ -15,76 +15,54 @@
*/
package com.android.launcher3.taskbar;
-import static android.view.View.MeasureSpec.EXACTLY;
-import static android.view.View.MeasureSpec.makeMeasureSpec;
-
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Rect;
-import android.graphics.RectF;
import android.util.AttributeSet;
-import android.view.DragEvent;
-import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
-import android.view.ViewConfiguration;
-import android.widget.LinearLayout;
+import android.widget.FrameLayout;
import androidx.annotation.LayoutRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.Insettable;
import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
import com.android.launcher3.folder.FolderIcon;
import com.android.launcher3.model.data.FolderInfo;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
+import com.android.launcher3.uioverrides.ApiWrapper;
import com.android.launcher3.views.ActivityContext;
/**
* Hosts the Taskbar content such as Hotseat and Recent Apps. Drawn on top of other apps.
*/
-public class TaskbarView extends LinearLayout implements FolderIcon.FolderIconParent, Insettable {
+public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconParent, Insettable {
- private final int mIconTouchSize;
- private final boolean mIsRtl;
- private final int mTouchSlop;
- private final RectF mTempDelegateBounds = new RectF();
- private final RectF mDelegateSlopBounds = new RectF();
private final int[] mTempOutLocation = new int[2];
+ private final Rect mIconLayoutBounds = new Rect();
+ private final int mIconTouchSize;
private final int mItemMarginLeftRight;
+ private final int mItemPadding;
private final TaskbarActivityContext mActivityContext;
- // Initialized in TaskbarController constructor.
+ // Initialized in init.
+ private TaskbarViewController.TaskbarViewCallbacks mControllerCallbacks;
private View.OnClickListener mIconClickListener;
private View.OnLongClickListener mIconLongClickListener;
- LinearLayout mSystemButtonContainer;
- LinearLayout mHotseatIconsContainer;
-
- // Delegate touches to the closest view if within mIconTouchSize.
- private boolean mDelegateTargeted;
- private View mDelegateView;
// Prevents dispatching touches to children if true
private boolean mTouchEnabled = true;
- private boolean mIsDraggingItem;
// Only non-null when the corresponding Folder is open.
private @Nullable FolderIcon mLeaveBehindFolderIcon;
- /** Provider of buttons added to taskbar in 3 button nav */
- private ButtonProvider mButtonProvider;
-
- private boolean mDisableRelayout;
- private boolean mAreHolesAllowed;
-
public TaskbarView(@NonNull Context context) {
this(context, null);
}
@@ -105,85 +83,80 @@ public class TaskbarView extends LinearLayout implements FolderIcon.FolderIconPa
Resources resources = getResources();
mIconTouchSize = resources.getDimensionPixelSize(R.dimen.taskbar_icon_touch_size);
- mItemMarginLeftRight = resources.getDimensionPixelSize(R.dimen.taskbar_icon_spacing);
-
- mIsRtl = Utilities.isRtl(resources);
- mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
- mSystemButtonContainer = findViewById(R.id.system_button_layout);
- mHotseatIconsContainer = findViewById(R.id.hotseat_icons_layout);
- }
- protected void construct(OnClickListener clickListener, OnLongClickListener longClickListener,
- ButtonProvider buttonProvider) {
- mIconClickListener = clickListener;
- mIconLongClickListener = longClickListener;
- mButtonProvider = buttonProvider;
+ int actualMargin = resources.getDimensionPixelSize(R.dimen.taskbar_icon_spacing);
+ int actualIconSize = mActivityContext.getDeviceProfile().iconSizePx;
- if (mActivityContext.canShowNavButtons()) {
- createNavButtons();
- } else {
- mSystemButtonContainer.setVisibility(GONE);
- }
+ // We layout the icons to be of mIconTouchSize in width and height
+ mItemMarginLeftRight = actualMargin - (mIconTouchSize - actualIconSize) / 2;
+ mItemPadding = (mIconTouchSize - actualIconSize) / 2;
- int numHotseatIcons = mActivityContext.getDeviceProfile().numShownHotseatIcons;
- updateHotseatItems(new ItemInfo[numHotseatIcons]);
+ // Needed to draw folder leave-behind when opening one.
+ setWillNotDraw(false);
}
- /**
- * Enables/disables empty icons in taskbar so that the layout matches with Launcher
- */
- public void setHolesAllowedInLayout(boolean areHolesAllowed) {
- if (mAreHolesAllowed != areHolesAllowed) {
- mAreHolesAllowed = areHolesAllowed;
- updateHotseatItemsVisibility();
- // TODO: Add animation
- }
+ protected void init(TaskbarViewController.TaskbarViewCallbacks callbacks) {
+ mControllerCallbacks = callbacks;
+ mIconClickListener = mControllerCallbacks.getIconOnClickListener();
+ mIconLongClickListener = mControllerCallbacks.getIconOnLongClickListener();
+
+ setOnLongClickListener(mControllerCallbacks.getBackgroundOnLongClickListener());
}
- private void setHolesAllowedInLayoutNoAnimation(boolean areHolesAllowed) {
- if (mAreHolesAllowed != areHolesAllowed) {
- mAreHolesAllowed = areHolesAllowed;
- updateHotseatItemsVisibility();
- onMeasure(makeMeasureSpec(getMeasuredWidth(), EXACTLY),
- makeMeasureSpec(getMeasuredHeight(), EXACTLY));
- onLayout(false, getLeft(), getTop(), getRight(), getBottom());
+ private void removeAndRecycle(View view) {
+ removeView(view);
+ view.setOnClickListener(null);
+ view.setOnLongClickListener(null);
+ if (!(view.getTag() instanceof FolderInfo)) {
+ mActivityContext.getViewCache().recycleView(view.getSourceLayoutResId(), view);
}
+ view.setTag(null);
}
/**
* Inflates/binds the Hotseat views to show in the Taskbar given their ItemInfos.
*/
protected void updateHotseatItems(ItemInfo[] hotseatItemInfos) {
+ int nextViewIndex = 0;
+ int numViewsAnimated = 0;
+
for (int i = 0; i < hotseatItemInfos.length; i++) {
- ItemInfo hotseatItemInfo = hotseatItemInfos[
- !mIsRtl ? i : hotseatItemInfos.length - i - 1];
- View hotseatView = mHotseatIconsContainer.getChildAt(i);
+ ItemInfo hotseatItemInfo = hotseatItemInfos[i];
+ if (hotseatItemInfo == null) {
+ continue;
+ }
// Replace any Hotseat views with the appropriate type if it's not already that type.
final int expectedLayoutResId;
boolean isFolder = false;
- boolean needsReinflate = false;
- if (hotseatItemInfo != null && hotseatItemInfo.isPredictedItem()) {
+ if (hotseatItemInfo.isPredictedItem()) {
expectedLayoutResId = R.layout.taskbar_predicted_app_icon;
} else if (hotseatItemInfo instanceof FolderInfo) {
expectedLayoutResId = R.layout.folder_icon;
isFolder = true;
- // Unlike for BubbleTextView, we can't reapply a new FolderInfo after inflation, so
- // if the info changes we need to reinflate. This should only happen if a new folder
- // is dragged to the position that another folder previously existed.
- needsReinflate = hotseatView != null && hotseatView.getTag() != hotseatItemInfo;
} else {
expectedLayoutResId = R.layout.taskbar_app_icon;
}
- if (hotseatView == null
- || hotseatView.getSourceLayoutResId() != expectedLayoutResId
- || needsReinflate) {
- mHotseatIconsContainer.removeView(hotseatView);
+
+ View hotseatView = null;
+ while (nextViewIndex < getChildCount()) {
+ hotseatView = getChildAt(nextViewIndex);
+
+ // see if the view can be reused
+ if ((hotseatView.getSourceLayoutResId() != expectedLayoutResId)
+ || (isFolder && (hotseatView.getTag() != hotseatItemInfo))) {
+ // Unlike for BubbleTextView, we can't reapply a new FolderInfo after inflation,
+ // so if the info changes we need to reinflate. This should only happen if a new
+ // folder is dragged to the position that another folder previously existed.
+ removeAndRecycle(hotseatView);
+ hotseatView = null;
+ } else {
+ // View found
+ break;
+ }
+ }
+
+ if (hotseatView == null) {
if (isFolder) {
FolderInfo folderInfo = (FolderInfo) hotseatItemInfo;
FolderIcon folderIcon = FolderIcon.inflateFolderAndIcon(expectedLayoutResId,
@@ -193,40 +166,68 @@ public class TaskbarView extends LinearLayout implements FolderIcon.FolderIconPa
} else {
hotseatView = inflate(expectedLayoutResId);
}
- int iconSize = mActivityContext.getDeviceProfile().iconSizePx;
- LayoutParams lp = new LayoutParams(iconSize, iconSize);
- lp.setMargins(mItemMarginLeftRight, 0, mItemMarginLeftRight, 0);
- mHotseatIconsContainer.addView(hotseatView, i, lp);
+ LayoutParams lp = new LayoutParams(mIconTouchSize, mIconTouchSize);
+ hotseatView.setPadding(mItemPadding, mItemPadding, mItemPadding, mItemPadding);
+ addView(hotseatView, nextViewIndex, lp);
}
// Apply the Hotseat ItemInfos, or hide the view if there is none for a given index.
if (hotseatView instanceof BubbleTextView
&& hotseatItemInfo instanceof WorkspaceItemInfo) {
- ((BubbleTextView) hotseatView).applyFromWorkspaceItem(
- (WorkspaceItemInfo) hotseatItemInfo);
- hotseatView.setOnClickListener(mIconClickListener);
- hotseatView.setOnLongClickListener(mIconLongClickListener);
- } else if (isFolder) {
- hotseatView.setOnClickListener(mIconClickListener);
- hotseatView.setOnLongClickListener(mIconLongClickListener);
- } else {
- hotseatView.setOnClickListener(null);
- hotseatView.setOnLongClickListener(null);
- hotseatView.setTag(null);
+ BubbleTextView btv = (BubbleTextView) hotseatView;
+ WorkspaceItemInfo workspaceInfo = (WorkspaceItemInfo) hotseatItemInfo;
+
+ boolean animate = btv.shouldAnimateIconChange((WorkspaceItemInfo) hotseatItemInfo);
+ btv.applyFromWorkspaceItem(workspaceInfo, animate, numViewsAnimated);
+ if (animate) {
+ numViewsAnimated++;
+ }
}
- updateHotseatItemVisibility(hotseatView);
+ setClickAndLongClickListenersForIcon(hotseatView);
+ nextViewIndex++;
+ }
+ // Remove remaining views
+ while (nextViewIndex < getChildCount()) {
+ removeAndRecycle(getChildAt(nextViewIndex));
}
}
- protected void updateHotseatItemsVisibility() {
- for (int i = mHotseatIconsContainer.getChildCount() - 1; i >= 0; i--) {
- updateHotseatItemVisibility(mHotseatIconsContainer.getChildAt(i));
- }
+ /**
+ * Sets OnClickListener and OnLongClickListener for the given view.
+ */
+ public void setClickAndLongClickListenersForIcon(View icon) {
+ icon.setOnClickListener(mIconClickListener);
+ icon.setOnLongClickListener(mIconLongClickListener);
}
- private void updateHotseatItemVisibility(View hotseatView) {
- hotseatView.setVisibility(
- hotseatView.getTag() != null ? VISIBLE : (mAreHolesAllowed ? INVISIBLE : GONE));
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ int count = getChildCount();
+ int spaceNeeded = count * (mItemMarginLeftRight * 2 + mIconTouchSize);
+ int navSpaceNeeded = ApiWrapper.getHotseatEndOffset(getContext());
+ boolean layoutRtl = isLayoutRtl();
+ int iconEnd = right - (right - left - spaceNeeded) / 2;
+ boolean needMoreSpaceForNav = layoutRtl ?
+ navSpaceNeeded > (iconEnd - spaceNeeded) :
+ iconEnd > (right - navSpaceNeeded);
+ if (needMoreSpaceForNav) {
+ int offset = layoutRtl ?
+ navSpaceNeeded - (iconEnd - spaceNeeded) :
+ (right - navSpaceNeeded) - iconEnd;
+ iconEnd += offset;
+ }
+ // Layout the children
+ mIconLayoutBounds.right = iconEnd;
+ mIconLayoutBounds.top = (bottom - top - mIconTouchSize) / 2;
+ mIconLayoutBounds.bottom = mIconLayoutBounds.top + mIconTouchSize;
+ for (int i = count; i > 0; i--) {
+ View child = getChildAt(i - 1);
+ iconEnd -= mItemMarginLeftRight;
+ int iconStart = iconEnd - mIconTouchSize;
+ child.layout(iconStart, mIconLayoutBounds.top, iconEnd, mIconLayoutBounds.bottom);
+ iconEnd = iconStart - mItemMarginLeftRight;
+ }
+ mIconLayoutBounds.left = iconEnd;
}
@Override
@@ -239,87 +240,27 @@ public class TaskbarView extends LinearLayout implements FolderIcon.FolderIconPa
@Override
public boolean onTouchEvent(MotionEvent event) {
- boolean handled = delegateTouchIfNecessary(event);
- return super.onTouchEvent(event) || handled;
- }
-
- public void setTouchesEnabled(boolean touchEnabled) {
- this.mTouchEnabled = touchEnabled;
- }
-
- /**
- * User touched the Taskbar background. Determine whether the touch is close enough to a view
- * that we should forward the touches to it.
- * @return Whether a delegate view was chosen and it handled the touch event.
- */
- private boolean delegateTouchIfNecessary(MotionEvent event) {
- final float x = event.getX();
- final float y = event.getY();
- if (mDelegateView == null && event.getAction() == MotionEvent.ACTION_DOWN) {
- View delegateView = findDelegateView(x, y);
- if (delegateView != null) {
- mDelegateTargeted = true;
- mDelegateView = delegateView;
- mDelegateSlopBounds.set(mTempDelegateBounds);
- mDelegateSlopBounds.inset(-mTouchSlop, -mTouchSlop);
- }
+ if (!mTouchEnabled) {
+ return true;
}
-
- boolean sendToDelegate = mDelegateTargeted;
- boolean inBounds = true;
- switch (event.getAction()) {
- case MotionEvent.ACTION_MOVE:
- inBounds = mDelegateSlopBounds.contains(x, y);
- break;
- case MotionEvent.ACTION_UP:
- case MotionEvent.ACTION_CANCEL:
- mDelegateTargeted = false;
- break;
+ if (mIconLayoutBounds.contains((int) event.getX(), (int) event.getY())) {
+ // Don't allow long pressing between icons.
+ return true;
}
-
- boolean handled = false;
- if (sendToDelegate) {
- if (inBounds) {
- // Offset event coordinates to be inside the target view
- event.setLocation(mDelegateView.getWidth() / 2f, mDelegateView.getHeight() / 2f);
- } else {
- // Offset event coordinates to be outside the target view (in case it does
- // something like tracking pressed state)
- event.setLocation(-mTouchSlop * 2, -mTouchSlop * 2);
- }
- handled = mDelegateView.dispatchTouchEvent(event);
- // Cleanup if this was the last event to send to the delegate.
- if (!mDelegateTargeted) {
- mDelegateView = null;
+ if (mControllerCallbacks.onTouchEvent(event)) {
+ int oldAction = event.getAction();
+ try {
+ event.setAction(MotionEvent.ACTION_CANCEL);
+ return super.onTouchEvent(event);
+ } finally {
+ event.setAction(oldAction);
}
}
- return handled;
+ return super.onTouchEvent(event);
}
- /**
- * Return an item whose touch bounds contain the given coordinates,
- * or null if no such item exists.
- *
- * Also sets {@link #mTempDelegateBounds} to be the touch bounds of the chosen delegate view.
- */
- private @Nullable View findDelegateView(float x, float y) {
- for (int i = 0; i < getChildCount(); i++) {
- View child = getChildAt(i);
- if (!child.isShown() || !child.isClickable()) {
- continue;
- }
- int childCenterX = child.getLeft() + child.getWidth() / 2;
- int childCenterY = child.getTop() + child.getHeight() / 2;
- mTempDelegateBounds.set(
- childCenterX - mIconTouchSize / 2f,
- childCenterY - mIconTouchSize / 2f,
- childCenterX + mIconTouchSize / 2f,
- childCenterY + mIconTouchSize / 2f);
- if (mTempDelegateBounds.contains(x, y)) {
- return child;
- }
- }
- return null;
+ public void setTouchesEnabled(boolean touchEnabled) {
+ this.mTouchEnabled = touchEnabled;
}
/**
@@ -328,69 +269,25 @@ public class TaskbarView extends LinearLayout implements FolderIcon.FolderIconPa
*/
public boolean isEventOverAnyItem(MotionEvent ev) {
getLocationOnScreen(mTempOutLocation);
- float xInOurCoordinates = ev.getX() - mTempOutLocation[0];
- float yInOurCoorindates = ev.getY() - mTempOutLocation[1];
- return findDelegateView(xInOurCoordinates, yInOurCoorindates) != null;
+ int xInOurCoordinates = (int) ev.getX() - mTempOutLocation[0];
+ int yInOurCoorindates = (int) ev.getY() - mTempOutLocation[1];
+ return isShown() && mIconLayoutBounds.contains(xInOurCoordinates, yInOurCoorindates);
}
- /**
- * Add back/home/recents buttons into a single ViewGroup that will be inserted at
- * {@param navButtonStartIndex}
- */
- private void createNavButtons() {
- LinearLayout.LayoutParams buttonParams = new LinearLayout.LayoutParams(
- mActivityContext.getDeviceProfile().iconSizePx,
- mActivityContext.getDeviceProfile().iconSizePx
- );
- buttonParams.gravity = Gravity.CENTER;
-
- mSystemButtonContainer.addView(mButtonProvider.getBack(), buttonParams);
- mSystemButtonContainer.addView(mButtonProvider.getHome(), buttonParams);
- mSystemButtonContainer.addView(mButtonProvider.getRecents(), buttonParams);
- }
-
- @Override
- public boolean onDragEvent(DragEvent event) {
- switch (event.getAction()) {
- case DragEvent.ACTION_DRAG_STARTED:
- mIsDraggingItem = true;
- AbstractFloatingView.closeAllOpenViews(mActivityContext);
- return true;
- case DragEvent.ACTION_DRAG_ENDED:
- mIsDraggingItem = false;
- break;
- }
- return super.onDragEvent(event);
- }
-
- public boolean isDraggingItem() {
- return mIsDraggingItem;
+ public Rect getIconLayoutBounds() {
+ return mIconLayoutBounds;
}
/**
- * @return The bounding box of where the hotseat elements are relative to this TaskbarView.
+ * Returns the app icons currently shown in the taskbar.
*/
- protected RectF getHotseatBounds() {
- RectF result;
- mDisableRelayout = true;
- boolean wereHolesAllowed = mAreHolesAllowed;
- setHolesAllowedInLayoutNoAnimation(true);
- result = new RectF(
- mHotseatIconsContainer.getLeft(),
- mHotseatIconsContainer.getTop(),
- mHotseatIconsContainer.getRight(),
- mHotseatIconsContainer.getBottom());
- setHolesAllowedInLayoutNoAnimation(wereHolesAllowed);
- mDisableRelayout = false;
-
- return result;
- }
-
- @Override
- public void requestLayout() {
- if (!mDisableRelayout) {
- super.requestLayout();
+ public View[] getIconViews() {
+ final int count = getChildCount();
+ View[] icons = new View[count];
+ for (int i = 0; i < count; i++) {
+ icons[i] = getChildAt(i);
}
+ return icons;
}
// FolderIconParent implemented methods.
@@ -421,7 +318,7 @@ public class TaskbarView extends LinearLayout implements FolderIcon.FolderIconPa
}
private View inflate(@LayoutRes int layoutResId) {
- return mActivityContext.getLayoutInflater().inflate(layoutResId, this, false);
+ return mActivityContext.getViewCache().getView(layoutResId, mActivityContext, this);
}
@Override
@@ -429,11 +326,8 @@ public class TaskbarView extends LinearLayout implements FolderIcon.FolderIconPa
// Ignore, we just implement Insettable to draw behind system insets.
}
- public void setIconsVisibility(boolean isVisible) {
- mHotseatIconsContainer.setVisibility(isVisible ? VISIBLE : INVISIBLE);
- }
-
public boolean areIconsVisible() {
- return mHotseatIconsContainer.getVisibility() == VISIBLE;
+ // Consider the overall visibility
+ return getVisibility() == VISIBLE;
}
}