Exposing some private methods to easily customize arrow popup

Change-Id: I6f6dc0604a616f26c0879732a0237767ddbfdccc
diff --git a/src/com/android/launcher3/AbstractFloatingView.java b/src/com/android/launcher3/AbstractFloatingView.java
index daf2032..4f03bf0 100644
--- a/src/com/android/launcher3/AbstractFloatingView.java
+++ b/src/com/android/launcher3/AbstractFloatingView.java
@@ -34,6 +34,7 @@
 
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
 import com.android.launcher3.util.TouchController;
+import com.android.launcher3.views.ActivityContext;
 import com.android.launcher3.views.BaseDragLayer;
 
 import java.lang.annotation.Retention;
@@ -151,7 +152,7 @@
         if (mIsOpen) {
             sendAccessibilityEvent(TYPE_VIEW_FOCUSED);
         }
-        BaseDraggingActivity.fromContext(getContext()).getDragLayer()
+        ActivityContext.lookupContext(getContext()).getDragLayer()
                 .sendAccessibilityEvent(TYPE_WINDOW_CONTENT_CHANGED);
     }
 
@@ -160,7 +161,7 @@
     }
 
     protected static <T extends AbstractFloatingView> T getOpenView(
-            BaseDraggingActivity activity, @FloatingViewType int type) {
+            ActivityContext activity, @FloatingViewType int type) {
         BaseDragLayer dragLayer = activity.getDragLayer();
         // Iterate in reverse order. AbstractFloatingView is added later to the dragLayer,
         // and will be one of the last views.
@@ -176,7 +177,7 @@
         return null;
     }
 
-    public static void closeOpenContainer(BaseDraggingActivity activity,
+    public static void closeOpenContainer(ActivityContext activity,
             @FloatingViewType int type) {
         AbstractFloatingView view = getOpenView(activity, type);
         if (view != null) {
@@ -184,7 +185,7 @@
         }
     }
 
-    public static void closeOpenViews(BaseDraggingActivity activity, boolean animate,
+    public static void closeOpenViews(ActivityContext activity, boolean animate,
             @FloatingViewType int type) {
         BaseDragLayer dragLayer = activity.getDragLayer();
         // Iterate in reverse order. AbstractFloatingView is added later to the dragLayer,
@@ -200,20 +201,20 @@
         }
     }
 
-    public static void closeAllOpenViews(BaseDraggingActivity activity, boolean animate) {
+    public static void closeAllOpenViews(ActivityContext activity, boolean animate) {
         closeOpenViews(activity, animate, TYPE_ALL);
         activity.finishAutoCancelActionMode();
     }
 
-    public static void closeAllOpenViews(BaseDraggingActivity activity) {
+    public static void closeAllOpenViews(ActivityContext activity) {
         closeAllOpenViews(activity, true);
     }
 
-    public static AbstractFloatingView getTopOpenView(BaseDraggingActivity activity) {
+    public static AbstractFloatingView getTopOpenView(ActivityContext activity) {
         return getTopOpenViewWithType(activity, TYPE_ALL);
     }
 
-    public static AbstractFloatingView getTopOpenViewWithType(BaseDraggingActivity activity,
+    public static AbstractFloatingView getTopOpenViewWithType(ActivityContext activity,
             @FloatingViewType int type) {
         return getOpenView(activity, type);
     }
diff --git a/src/com/android/launcher3/BaseActivity.java b/src/com/android/launcher3/BaseActivity.java
index a4b6f5b..eca280b 100644
--- a/src/com/android/launcher3/BaseActivity.java
+++ b/src/com/android/launcher3/BaseActivity.java
@@ -26,6 +26,7 @@
 import android.content.Intent;
 import android.content.res.Configuration;
 import android.support.annotation.IntDef;
+import android.view.ContextThemeWrapper;
 import android.view.View.AccessibilityDelegate;
 
 import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
@@ -116,13 +117,6 @@
         return Utilities.ATLEAST_NOUGAT && isInMultiWindowMode();
     }
 
-    public static BaseActivity fromContext(Context context) {
-        if (context instanceof BaseActivity) {
-            return (BaseActivity) context;
-        }
-        return ((BaseActivity) ((ContextWrapper) context).getBaseContext());
-    }
-
     public SystemUiController getSystemUiController() {
         if (mSystemUiController == null) {
             mSystemUiController = new SystemUiController(getWindow());
@@ -259,4 +253,14 @@
         writer.println(" mActivityFlags: " + mActivityFlags);
         writer.println(" mForceInvisible: " + mForceInvisible);
     }
+
+    public static <T extends BaseActivity> T fromContext(Context context) {
+        if (context instanceof BaseActivity) {
+            return (T) context;
+        } else if (context instanceof ContextThemeWrapper) {
+            return fromContext(((ContextWrapper) context).getBaseContext());
+        } else {
+            throw new IllegalArgumentException("Cannot find BaseActivity in parent tree");
+        }
+    }
 }
diff --git a/src/com/android/launcher3/BaseDraggingActivity.java b/src/com/android/launcher3/BaseDraggingActivity.java
index eec196e..d6635dc 100644
--- a/src/com/android/launcher3/BaseDraggingActivity.java
+++ b/src/com/android/launcher3/BaseDraggingActivity.java
@@ -18,8 +18,6 @@
 
 import android.app.ActivityOptions;
 import android.content.ActivityNotFoundException;
-import android.content.Context;
-import android.content.ContextWrapper;
 import android.content.Intent;
 import android.graphics.Rect;
 import android.os.Bundle;
@@ -28,23 +26,23 @@
 import android.os.UserHandle;
 import android.util.Log;
 import android.view.ActionMode;
-import android.view.Surface;
 import android.view.View;
 import android.widget.Toast;
 
 import com.android.launcher3.LauncherSettings.Favorites;
 import com.android.launcher3.badge.BadgeInfo;
 import com.android.launcher3.compat.LauncherAppsCompat;
+import com.android.launcher3.shortcuts.DeepShortcutManager;
 import com.android.launcher3.uioverrides.DisplayRotationListener;
 import com.android.launcher3.uioverrides.WallpaperColorInfo;
-import com.android.launcher3.shortcuts.DeepShortcutManager;
 import com.android.launcher3.views.BaseDragLayer;
+import com.android.launcher3.views.ActivityContext;
 
 /**
  * Extension of BaseActivity allowing support for drag-n-drop
  */
 public abstract class BaseDraggingActivity extends BaseActivity
-        implements WallpaperColorInfo.OnChangeListener {
+        implements WallpaperColorInfo.OnChangeListener, ActivityContext {
 
     private static final String TAG = "BaseDraggingActivity";
 
@@ -110,6 +108,7 @@
         mCurrentActionMode = null;
     }
 
+    @Override
     public boolean finishAutoCancelActionMode() {
         if (mCurrentActionMode != null && AUTO_CANCEL_ACTION_MODE == mCurrentActionMode.getTag()) {
             mCurrentActionMode.finish();
@@ -128,13 +127,6 @@
 
     public abstract void invalidateParent(ItemInfo info);
 
-    public static BaseDraggingActivity fromContext(Context context) {
-        if (context instanceof BaseDraggingActivity) {
-            return (BaseDraggingActivity) context;
-        }
-        return ((BaseDraggingActivity) ((ContextWrapper) context).getBaseContext());
-    }
-
     public Rect getViewBounds(View v) {
         int[] pos = new int[2];
         v.getLocationOnScreen(pos);
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 9f16857..7ab9768 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -2405,10 +2405,7 @@
     }
 
     public static Launcher getLauncher(Context context) {
-        if (context instanceof Launcher) {
-            return (Launcher) context;
-        }
-        return ((Launcher) ((ContextWrapper) context).getBaseContext());
+        return (Launcher) fromContext(context);
     }
 
     /**
diff --git a/src/com/android/launcher3/dragndrop/BaseItemDragListener.java b/src/com/android/launcher3/dragndrop/BaseItemDragListener.java
index 1e84b41..e204c63 100644
--- a/src/com/android/launcher3/dragndrop/BaseItemDragListener.java
+++ b/src/com/android/launcher3/dragndrop/BaseItemDragListener.java
@@ -106,6 +106,10 @@
     }
 
     protected boolean onDragStart(DragEvent event) {
+        return onDragStart(event, this);
+    }
+
+    protected boolean onDragStart(DragEvent event, DragOptions.PreDragCondition preDragCondition) {
         ClipDescription desc =  event.getClipDescription();
         if (desc == null || !desc.hasMimeType(getMimeType())) {
             Log.e(TAG, "Someone started a dragAndDrop before us.");
@@ -115,7 +119,7 @@
         Point downPos = new Point((int) event.getX(), (int) event.getY());
         DragOptions options = new DragOptions();
         options.systemDndStartPoint = downPos;
-        options.preDragCondition = this;
+        options.preDragCondition = preDragCondition;
 
         // We use drag event position as the screenPos for the preview image. Since mPreviewRect
         // already includes the view position relative to the drag event on the source window,
@@ -123,7 +127,7 @@
         // across windows, using drag position here give a good estimate for relative position
         // to source window.
         createDragHelper().startDrag(new Rect(mPreviewRect),
-                mPreviewBitmapWidth, mPreviewViewWidth, downPos,  this, options);
+                mPreviewBitmapWidth, mPreviewViewWidth, downPos, this, options);
         mDragStartTime = SystemClock.uptimeMillis();
         return true;
     }
diff --git a/src/com/android/launcher3/logging/LoggerUtils.java b/src/com/android/launcher3/logging/LoggerUtils.java
index 83593aa..1c4327c 100644
--- a/src/com/android/launcher3/logging/LoggerUtils.java
+++ b/src/com/android/launcher3/logging/LoggerUtils.java
@@ -144,7 +144,7 @@
     }
 
     public static Target newItemTarget(View v, InstantAppResolver instantAppResolver) {
-        return (v.getTag() instanceof ItemInfo)
+        return (v != null) && (v.getTag() instanceof ItemInfo)
                 ? newItemTarget((ItemInfo) v.getTag(), instantAppResolver)
                 : newTarget(Target.Type.ITEM);
     }
diff --git a/src/com/android/launcher3/popup/ArrowPopup.java b/src/com/android/launcher3/popup/ArrowPopup.java
index be666a6..e157482 100644
--- a/src/com/android/launcher3/popup/ArrowPopup.java
+++ b/src/com/android/launcher3/popup/ArrowPopup.java
@@ -36,8 +36,10 @@
 import android.view.ViewGroup;
 import android.view.ViewOutlineProvider;
 import android.view.animation.AccelerateDecelerateInterpolator;
+import android.widget.FrameLayout;
 
 import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.InsettableFrameLayout;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAnimUtils;
 import com.android.launcher3.R;
@@ -47,6 +49,7 @@
 import com.android.launcher3.dragndrop.DragLayer;
 import com.android.launcher3.graphics.TriangleShape;
 import com.android.launcher3.util.Themes;
+import com.android.launcher3.views.BaseDragLayer;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -134,7 +137,7 @@
     protected void reorderAndShow(int viewsToFlip) {
         setVisibility(View.INVISIBLE);
         mIsOpen = true;
-        mLauncher.getDragLayer().addView(this);
+        getPopupContainer().addView(this);
         orientAboutObject();
 
         boolean reverseOrder = mIsAboveIcon;
@@ -163,7 +166,7 @@
                 ? R.dimen.popup_arrow_horizontal_center_start
                 : R.dimen.popup_arrow_horizontal_center_end);
         final int halfArrowWidth = res.getDimensionPixelSize(R.dimen.popup_arrow_width) / 2;
-        mLauncher.getDragLayer().addView(mArrow);
+        getPopupContainer().addView(mArrow);
         DragLayer.LayoutParams arrowLp = (DragLayer.LayoutParams) mArrow.getLayoutParams();
         if (mIsLeftAligned) {
             mArrow.setX(getX() + arrowCenterOffset - halfArrowWidth);
@@ -179,7 +182,7 @@
             ShapeDrawable arrowDrawable = new ShapeDrawable(TriangleShape.create(
                     arrowLp.width, arrowLp.height, !mIsAboveIcon));
             Paint arrowPaint = arrowDrawable.getPaint();
-            arrowPaint.setColor(Themes.getAttrColor(mLauncher, R.attr.popupColorPrimary));
+            arrowPaint.setColor(Themes.getAttrColor(getContext(), R.attr.popupColorPrimary));
             // The corner path effect won't be reflected in the shadow, but shouldn't be noticeable.
             int radius = getResources().getDimensionPixelSize(R.dimen.popup_arrow_corner_radius);
             arrowPaint.setPathEffect(new CornerPathEffect(radius));
@@ -222,7 +225,7 @@
         int height = getMeasuredHeight() + extraVerticalSpace;
 
         getTargetObjectLocation(mTempRect);
-        DragLayer dragLayer = mLauncher.getDragLayer();
+        InsettableFrameLayout dragLayer = getPopupContainer();
         Rect insets = dragLayer.getInsets();
 
         // Align left (right in RTL) if there is room.
@@ -301,12 +304,11 @@
             return;
         }
 
-        DragLayer.LayoutParams lp = (DragLayer.LayoutParams) getLayoutParams();
-        DragLayer.LayoutParams arrowLp = (DragLayer.LayoutParams) mArrow.getLayoutParams();
+        FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams();
+        FrameLayout.LayoutParams arrowLp = (FrameLayout.LayoutParams) mArrow.getLayoutParams();
         if (mIsAboveIcon) {
             arrowLp.gravity = lp.gravity = Gravity.BOTTOM;
-            lp.bottomMargin =
-                    mLauncher.getDragLayer().getHeight() - y - getMeasuredHeight() - insets.top;
+            lp.bottomMargin = getPopupContainer().getHeight() - y - getMeasuredHeight() - insets.top;
             arrowLp.bottomMargin = lp.bottomMargin - arrowLp.height - mArrayOffset - insets.bottom;
         } else {
             arrowLp.gravity = lp.gravity = Gravity.TOP;
@@ -320,7 +322,7 @@
         super.onLayout(changed, l, t, r, b);
 
         // enforce contained is within screen
-        DragLayer dragLayer = mLauncher.getDragLayer();
+        ViewGroup dragLayer = getPopupContainer();
         if (getTranslationX() + l < 0 || getTranslationX() + r > dragLayer.getWidth()) {
             // If we are still off screen, center horizontally too.
             mGravity |= Gravity.CENTER_HORIZONTAL;
@@ -454,7 +456,11 @@
         }
         mIsOpen = false;
         mDeferContainerRemoval = false;
-        mLauncher.getDragLayer().removeView(this);
-        mLauncher.getDragLayer().removeView(mArrow);
+        getPopupContainer().removeView(this);
+        getPopupContainer().removeView(mArrow);
+    }
+
+    protected BaseDragLayer getPopupContainer() {
+        return mLauncher.getDragLayer();
     }
 }
diff --git a/src/com/android/launcher3/popup/PopupContainerWithArrow.java b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
index 172cf41..635e043 100644
--- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java
+++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
@@ -55,7 +55,6 @@
 import com.android.launcher3.accessibility.ShortcutMenuAccessibilityDelegate;
 import com.android.launcher3.badge.BadgeInfo;
 import com.android.launcher3.dragndrop.DragController;
-import com.android.launcher3.dragndrop.DragLayer;
 import com.android.launcher3.dragndrop.DragOptions;
 import com.android.launcher3.dragndrop.DragView;
 import com.android.launcher3.logging.LoggerUtils;
@@ -65,8 +64,10 @@
 import com.android.launcher3.shortcuts.DeepShortcutManager;
 import com.android.launcher3.shortcuts.DeepShortcutView;
 import com.android.launcher3.shortcuts.ShortcutDragPreviewProvider;
+import com.android.launcher3.touch.ItemClickHandler;
 import com.android.launcher3.touch.ItemLongClickListener;
 import com.android.launcher3.util.PackageUserKey;
+import com.android.launcher3.views.BaseDragLayer;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -146,10 +147,14 @@
                 command, mOriginalIcon, ContainerType.DEEPSHORTCUTS);
     }
 
+    public OnClickListener getItemClickListener() {
+        return ItemClickHandler.INSTANCE;
+    }
+
     @Override
     public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
         if (ev.getAction() == MotionEvent.ACTION_DOWN) {
-            DragLayer dl = mLauncher.getDragLayer();
+            BaseDragLayer dl = getPopupContainer();
             if (!dl.isEventOverView(this, ev)) {
                 mLauncher.getUserEventDispatcher().logActionTapOutside(
                         LoggerUtils.newContainerTarget(ContainerType.DEEPSHORTCUTS));
@@ -215,7 +220,7 @@
     }
 
     @TargetApi(Build.VERSION_CODES.P)
-    private void populateAndShow(final BubbleTextView originalIcon, final List<String> shortcutIds,
+    protected void populateAndShow(final BubbleTextView originalIcon, final List<String> shortcutIds,
             final List<NotificationKeyData> notificationKeys, List<SystemShortcut> systemShortcuts) {
         mNumNotifications = notificationKeys.size();
         mOriginalIcon = originalIcon;
@@ -293,7 +298,7 @@
 
     @Override
     protected void getTargetObjectLocation(Rect outPos) {
-        mLauncher.getDragLayer().getDescendantRectRelativeToSelf(mOriginalIcon, outPos);
+        getPopupContainer().getDescendantRectRelativeToSelf(mOriginalIcon, outPos);
         outPos.top += mOriginalIcon.getPaddingTop();
         outPos.left += mOriginalIcon.getPaddingLeft();
         outPos.right -= mOriginalIcon.getPaddingRight();
diff --git a/src/com/android/launcher3/shortcuts/DeepShortcutView.java b/src/com/android/launcher3/shortcuts/DeepShortcutView.java
index 9ad266b..7d0ea7b 100644
--- a/src/com/android/launcher3/shortcuts/DeepShortcutView.java
+++ b/src/com/android/launcher3/shortcuts/DeepShortcutView.java
@@ -121,7 +121,7 @@
         mBubbleText.setText(usingLongLabel ? longLabel : mDetail.getShortLabel());
 
         // TODO: Add the click handler to this view directly and not the child view.
-        mBubbleText.setOnClickListener(ItemClickHandler.INSTANCE);
+        mBubbleText.setOnClickListener(container.getItemClickListener());
         mBubbleText.setOnLongClickListener(container);
         mBubbleText.setOnTouchListener(container);
     }
diff --git a/src/com/android/launcher3/touch/ItemClickHandler.java b/src/com/android/launcher3/touch/ItemClickHandler.java
index f2f5592..97f836f 100644
--- a/src/com/android/launcher3/touch/ItemClickHandler.java
+++ b/src/com/android/launcher3/touch/ItemClickHandler.java
@@ -162,7 +162,7 @@
      *
      * @param v The view that was clicked. Must be a tagged with a {@link ShortcutInfo}.
      */
-    private static void onClickAppShortcut(View v, ShortcutInfo shortcut, Launcher launcher) {
+    public static void onClickAppShortcut(View v, ShortcutInfo shortcut, Launcher launcher) {
         if (shortcut.isDisabled()) {
             final int disabledFlags = shortcut.runtimeStatusFlags & ShortcutInfo.FLAG_DISABLED_MASK;
             if ((disabledFlags &
diff --git a/src/com/android/launcher3/views/ActivityContext.java b/src/com/android/launcher3/views/ActivityContext.java
new file mode 100644
index 0000000..04100af
--- /dev/null
+++ b/src/com/android/launcher3/views/ActivityContext.java
@@ -0,0 +1,43 @@
+/*
+ * 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.views;
+
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.view.ContextThemeWrapper;
+
+/**
+ * An interface to be used along with a context. This allows a generic class to depend on Context
+ * subclass instead of an Activity.
+ */
+public interface ActivityContext {
+
+    default boolean finishAutoCancelActionMode() {
+        return false;
+    }
+
+    BaseDragLayer getDragLayer();
+
+    static ActivityContext lookupContext(Context context) {
+        if (context instanceof ActivityContext) {
+            return (ActivityContext) context;
+        } else if (context instanceof ContextThemeWrapper) {
+            return lookupContext(((ContextWrapper) context).getBaseContext());
+        } else {
+            throw new IllegalArgumentException("Cannot find ActivityContext in parent tree");
+        }
+    }
+}
diff --git a/src/com/android/launcher3/views/BaseDragLayer.java b/src/com/android/launcher3/views/BaseDragLayer.java
index 8457b2b..e8a879f 100644
--- a/src/com/android/launcher3/views/BaseDragLayer.java
+++ b/src/com/android/launcher3/views/BaseDragLayer.java
@@ -28,8 +28,6 @@
 import android.widget.FrameLayout;
 
 import com.android.launcher3.AbstractFloatingView;
-import com.android.launcher3.BaseActivity;
-import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.InsettableFrameLayout;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.util.MultiValueAlpha;
@@ -41,7 +39,8 @@
 /**
  * A viewgroup with utility methods for drag-n-drop and touch interception
  */
-public abstract class BaseDragLayer<T extends BaseDraggingActivity> extends InsettableFrameLayout {
+public abstract class BaseDragLayer<T extends Context & ActivityContext>
+        extends InsettableFrameLayout {
 
     protected final int[] mTmpXY = new int[2];
     protected final Rect mHitRect = new Rect();
@@ -55,7 +54,7 @@
 
     public BaseDragLayer(Context context, AttributeSet attrs, int alphaChannelCount) {
         super(context, attrs);
-        mActivity = (T) BaseActivity.fromContext(context);
+        mActivity = (T) ActivityContext.lookupContext(context);
         mMultiValueAlpha = new MultiValueAlpha(this, alphaChannelCount);
     }
 
diff --git a/src/com/android/launcher3/views/OptionsPopupView.java b/src/com/android/launcher3/views/OptionsPopupView.java
index db4c492..5046639 100644
--- a/src/com/android/launcher3/views/OptionsPopupView.java
+++ b/src/com/android/launcher3/views/OptionsPopupView.java
@@ -94,7 +94,7 @@
         if (ev.getAction() != MotionEvent.ACTION_DOWN) {
             return false;
         }
-        if (mLauncher.getDragLayer().isEventOverView(this, ev)) {
+        if (getPopupContainer().isEventOverView(this, ev)) {
             return false;
         }
         close(true);