Revert "Revert "Adding support for hiding widgets from the widget tray and reconfiguring widgets""

This reverts commit f502e5faecbd460ad3b8258168c5812cbaa34c4e.

Reason for revert: Reverting post-build

Change-Id: I09eb292dbbbe37ffaf3abc477aa2ddb5700093b8
diff --git a/src/com/android/launcher3/ButtonDropTarget.java b/src/com/android/launcher3/ButtonDropTarget.java
index 19ee0b8..c866880 100644
--- a/src/com/android/launcher3/ButtonDropTarget.java
+++ b/src/com/android/launcher3/ButtonDropTarget.java
@@ -108,6 +108,12 @@
         setContentDescription(mText);
     }
 
+    protected void updateText(int resId) {
+        setText(resId);
+        mText = getText();
+        setContentDescription(mText);
+    }
+
     protected void setDrawable(int resId) {
         // We do not set the drawable in the xml as that inflates two drawables corresponding to
         // drawableLeft and drawableStart.
@@ -236,9 +242,7 @@
 
     protected abstract boolean supportsDrop(ItemInfo info);
 
-    public boolean supportsAccessibilityDrop(ItemInfo info) {
-        return supportsDrop(info);
-    }
+    public abstract boolean supportsAccessibilityDrop(ItemInfo info, View view);
 
     @Override
     public boolean isDropEnabled() {
@@ -368,4 +372,6 @@
                 TextUtils.TruncateAt.END);
         return !mText.equals(displayedText);
     }
+
+    public abstract int getControlTypeForLogging();
 }
diff --git a/src/com/android/launcher3/DeleteDropTarget.java b/src/com/android/launcher3/DeleteDropTarget.java
index c12ea57..28d1129 100644
--- a/src/com/android/launcher3/DeleteDropTarget.java
+++ b/src/com/android/launcher3/DeleteDropTarget.java
@@ -24,6 +24,7 @@
 import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
 import com.android.launcher3.dragndrop.DragOptions;
 import com.android.launcher3.folder.Folder;
+import com.android.launcher3.userevent.nano.LauncherLogProto.ControlType;
 
 public class DeleteDropTarget extends ButtonDropTarget {
 
@@ -54,7 +55,7 @@
      * @return true for items that should have a "Remove" action in accessibility.
      */
     @Override
-    public boolean supportsAccessibilityDrop(ItemInfo info) {
+    public boolean supportsAccessibilityDrop(ItemInfo info, View view) {
         return (info instanceof ShortcutInfo)
                 || (info instanceof LauncherAppWidgetInfo)
                 || (info instanceof FolderInfo);
@@ -103,4 +104,9 @@
         mLauncher.getDragLayer()
                 .announceForAccessibility(getContext().getString(R.string.item_removed));
     }
+
+    @Override
+    public int getControlTypeForLogging() {
+        return ControlType.REMOVE_TARGET;
+    }
 }
diff --git a/src/com/android/launcher3/InfoDropTarget.java b/src/com/android/launcher3/InfoDropTarget.java
deleted file mode 100644
index e52fd76..0000000
--- a/src/com/android/launcher3/InfoDropTarget.java
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * Copyright (C) 2011 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;
-
-import android.content.ActivityNotFoundException;
-import android.content.ComponentName;
-import android.content.Context;
-import android.graphics.Rect;
-import android.os.Bundle;
-import android.provider.Settings;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.widget.Toast;
-
-import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
-import com.android.launcher3.compat.LauncherAppsCompat;
-import com.android.launcher3.util.Themes;
-
-public class InfoDropTarget extends UninstallDropTarget {
-
-    private static final String TAG = "InfoDropTarget";
-
-    public InfoDropTarget(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public InfoDropTarget(Context context, AttributeSet attrs, int defStyle) {
-        super(context, attrs, defStyle);
-    }
-
-    @Override
-    protected void setupUi() {
-        // Get the hover color
-        mHoverColor = Themes.getColorAccent(getContext());
-        setDrawable(R.drawable.ic_info_shadow);
-    }
-
-    @Override
-    protected ComponentName performDropAction(ItemInfo item) {
-        return performDropAction(mLauncher, item, null, null);
-    }
-
-    /**
-     * @return Whether the activity was started.
-     */
-    public static boolean startDetailsActivityForInfo(
-            ItemInfo info, Launcher launcher, Rect sourceBounds, Bundle opts) {
-        return performDropAction(launcher, info, sourceBounds, opts) != null;
-    }
-
-    /**
-     * Performs the drop action and returns the target component for the dragObject or null if
-     * the action was not performed.
-     */
-    private static ComponentName performDropAction(Context context, ItemInfo info,
-            Rect sourceBounds, Bundle opts) {
-        if (info instanceof PromiseAppInfo) {
-            PromiseAppInfo promiseAppInfo = (PromiseAppInfo) info;
-            context.startActivity(promiseAppInfo.getMarketIntent(context));
-            return null;
-        }
-        ComponentName componentName = null;
-        if (info instanceof AppInfo) {
-            componentName = ((AppInfo) info).componentName;
-        } else if (info instanceof ShortcutInfo) {
-            componentName = info.getTargetComponent();
-        } else if (info instanceof PendingAddItemInfo) {
-            componentName = ((PendingAddItemInfo) info).componentName;
-        } else if (info instanceof LauncherAppWidgetInfo) {
-            componentName = ((LauncherAppWidgetInfo) info).providerName;
-        }
-        if (componentName != null) {
-            try {
-                LauncherAppsCompat.getInstance(context)
-                        .showAppDetailsForProfile(componentName, info.user, sourceBounds, opts);
-                return componentName;
-            } catch (SecurityException | ActivityNotFoundException e) {
-                Toast.makeText(context, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
-                Log.e(TAG, "Unable to launch settings", e);
-            }
-        }
-        return null;
-    }
-
-    @Override
-    public int getAccessibilityAction() {
-        return LauncherAccessibilityDelegate.INFO;
-    }
-
-    @Override
-    protected boolean supportsDrop(ItemInfo info) {
-        // Only show the App Info drop target if developer settings are enabled.
-        boolean developmentSettingsEnabled = Settings.Global.getInt(
-                getContext().getContentResolver(),
-                Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0) == 1;
-        if (!developmentSettingsEnabled) {
-            return false;
-        }
-        return info.itemType != LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT &&
-                (info instanceof AppInfo ||
-                (info instanceof ShortcutInfo && !((ShortcutInfo) info).isPromise()) ||
-                (info instanceof LauncherAppWidgetInfo &&
-                        ((LauncherAppWidgetInfo) info).restoreStatus == 0) ||
-                info instanceof PendingAddItemInfo);
-    }
-}
diff --git a/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java b/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java
index c713992..80758c9 100644
--- a/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java
+++ b/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java
@@ -94,4 +94,12 @@
     public boolean isCustomWidget() {
         return provider.getClassName().startsWith(CLS_CUSTOM_WIDGET_PREFIX);
     }
+
+    public int getWidgetFeatures() {
+        if (Utilities.ATLEAST_P) {
+            return widgetFeatures;
+        } else {
+            return 0;
+        }
+    }
  }
diff --git a/src/com/android/launcher3/UninstallDropTarget.java b/src/com/android/launcher3/SecondaryDropTarget.java
similarity index 61%
rename from src/com/android/launcher3/UninstallDropTarget.java
rename to src/com/android/launcher3/SecondaryDropTarget.java
index 68a441a..024b4eb 100644
--- a/src/com/android/launcher3/UninstallDropTarget.java
+++ b/src/com/android/launcher3/SecondaryDropTarget.java
@@ -1,8 +1,19 @@
 package com.android.launcher3;
 
+import static android.appwidget.AppWidgetManager.INVALID_APPWIDGET_ID;
+import static android.appwidget.AppWidgetProviderInfo.WIDGET_FEATURE_RECONFIGURABLE;
+
 import static com.android.launcher3.ItemInfoWithIcon.FLAG_SYSTEM_MASK;
 import static com.android.launcher3.ItemInfoWithIcon.FLAG_SYSTEM_NO;
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP;
+import static com.android.launcher3.accessibility.LauncherAccessibilityDelegate.RECONFIGURE;
+import static com.android.launcher3.accessibility.LauncherAccessibilityDelegate.UNINSTALL;
+import static com.android.launcher3.userevent.nano.LauncherLogProto.ControlType.SETTINGS_BUTTON;
+import static com.android.launcher3.userevent.nano.LauncherLogProto.ControlType.UNINSTALL_TARGET;
 
+import android.appwidget.AppWidgetHostView;
+import android.appwidget.AppWidgetManager;
+import android.appwidget.AppWidgetProviderInfo;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -20,27 +31,33 @@
 import android.widget.Toast;
 
 import com.android.launcher3.Launcher.OnResumeCallback;
-import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
 import com.android.launcher3.compat.LauncherAppsCompat;
 import com.android.launcher3.dragndrop.DragOptions;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
+import com.android.launcher3.util.Themes;
 
 import java.net.URISyntaxException;
 
-public class UninstallDropTarget extends ButtonDropTarget implements OnAlarmListener {
+/**
+ * Drop target which provides a secondary option for an item.
+ *    For app targets: shows as uninstall
+ *    For configurable widgets: shows as setup
+ */
+public class SecondaryDropTarget extends ButtonDropTarget implements OnAlarmListener {
 
-    private static final String TAG = "UninstallDropTarget";
+    private static final String TAG = "SecondaryDropTarget";
 
     private static final long CACHE_EXPIRE_TIMEOUT = 5000;
     private final ArrayMap<UserHandle, Boolean> mUninstallDisabledCache = new ArrayMap<>(1);
 
     private final Alarm mCacheExpireAlarm;
 
-    public UninstallDropTarget(Context context, AttributeSet attrs) {
+    private int mCurrentAccessibilityAction = -1;
+    public SecondaryDropTarget(Context context, AttributeSet attrs) {
         this(context, attrs, 0);
     }
 
-    public UninstallDropTarget(Context context, AttributeSet attrs, int defStyle) {
+    public SecondaryDropTarget(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
 
         mCacheExpireAlarm = new Alarm();
@@ -50,13 +67,24 @@
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
-        setupUi();
+        setupUi(UNINSTALL);
     }
 
-    protected void setupUi() {
-        // Get the hover color
-        mHoverColor = getResources().getColor(R.color.uninstall_target_hover_tint);
-        setDrawable(R.drawable.ic_uninstall_shadow);
+    private void setupUi(int action) {
+        if (action == mCurrentAccessibilityAction) {
+            return;
+        }
+        mCurrentAccessibilityAction = action;
+
+        if (action == UNINSTALL) {
+            mHoverColor = getResources().getColor(R.color.uninstall_target_hover_tint);
+            setDrawable(R.drawable.ic_uninstall_shadow);
+            updateText(R.string.uninstall_drop_target_label);
+        } else {
+            mHoverColor = Themes.getColorAccent(getContext());
+            setDrawable(R.drawable.ic_setup_shadow);
+            updateText(R.string.gadget_setup_text);
+        }
     }
 
     @Override
@@ -66,11 +94,30 @@
 
     @Override
     public int getAccessibilityAction() {
-        return LauncherAccessibilityDelegate.UNINSTALL;
+        return mCurrentAccessibilityAction;
+    }
+
+    @Override
+    public int getControlTypeForLogging() {
+        return mCurrentAccessibilityAction == UNINSTALL ? UNINSTALL_TARGET : SETTINGS_BUTTON;
     }
 
     @Override
     protected boolean supportsDrop(ItemInfo info) {
+        return supportsAccessibilityDrop(info, getViewUnderDrag(info));
+    }
+
+    @Override
+    public boolean supportsAccessibilityDrop(ItemInfo info, View view) {
+        if (view instanceof AppWidgetHostView) {
+            if (getReconfigurableWidgetId(view) != INVALID_APPWIDGET_ID) {
+                setupUi(RECONFIGURE);
+                return true;
+            }
+            return false;
+        }
+
+        setupUi(UNINSTALL);
         Boolean uninstallDisabled = mUninstallDisabledCache.get(info.user);
         if (uninstallDisabled == null) {
             UserManager userManager =
@@ -126,7 +173,7 @@
 
     @Override
     public void completeDrop(final DragObject d) {
-        ComponentName target = performDropAction(d.dragInfo);
+        ComponentName target = performDropAction(getViewUnderDrag(d.dragInfo), d.dragInfo);
         if (d.dragSource instanceof DeferredOnComplete) {
             DeferredOnComplete deferred = (DeferredOnComplete) d.dragSource;
             if (target != null) {
@@ -138,11 +185,48 @@
         }
     }
 
+    private View getViewUnderDrag(ItemInfo info) {
+        if (info instanceof LauncherAppWidgetInfo && info.container == CONTAINER_DESKTOP &&
+                mLauncher.getWorkspace().getDragInfo() != null) {
+            return mLauncher.getWorkspace().getDragInfo().cell;
+        }
+        return null;
+    }
+
+    /**
+     * Verifies that the view is an reconfigurable widget and returns the corresponding widget Id,
+     * otherwise return {@code INVALID_APPWIDGET_ID}
+     */
+    private int getReconfigurableWidgetId(View view) {
+        if (!(view instanceof AppWidgetHostView)) {
+            return INVALID_APPWIDGET_ID;
+        }
+        AppWidgetHostView hostView = (AppWidgetHostView) view;
+        AppWidgetProviderInfo widgetInfo = hostView.getAppWidgetInfo();
+        if (widgetInfo == null || widgetInfo.configure == null) {
+            return INVALID_APPWIDGET_ID;
+        }
+        if ( (LauncherAppWidgetProviderInfo.fromProviderInfo(getContext(), widgetInfo)
+                .getWidgetFeatures() & WIDGET_FEATURE_RECONFIGURABLE) == 0) {
+            return INVALID_APPWIDGET_ID;
+        }
+        return hostView.getAppWidgetId();
+    }
+
     /**
      * Performs the drop action and returns the target component for the dragObject or null if
      * the action was not performed.
      */
-    protected ComponentName performDropAction(ItemInfo info) {
+    protected ComponentName performDropAction(View view, ItemInfo info) {
+        if (mCurrentAccessibilityAction == RECONFIGURE) {
+            int widgetId = getReconfigurableWidgetId(view);
+            if (widgetId != INVALID_APPWIDGET_ID) {
+                mLauncher.getAppWidgetHost().startConfigActivity(mLauncher, widgetId, -1);
+            }
+            return null;
+        }
+        // else: mCurrentAccessibilityAction == UNINSTALL
+
         ComponentName cn = getUninstallTarget(info);
         if (cn == null) {
             // System applications cannot be installed. For now, show a toast explaining that.
@@ -164,7 +248,7 @@
 
     @Override
     public void onAccessibilityDrop(View view, ItemInfo item) {
-        performDropAction(item);
+        performDropAction(view, item);
     }
 
     /**
@@ -203,7 +287,7 @@
                     .getApplicationInfo(mPackageName, PackageManager.MATCH_UNINSTALLED_PACKAGES,
                             mDragObject.dragInfo.user) == null) {
                 mDragObject.dragSource = mOriginal;
-                mOriginal.onDropCompleted(UninstallDropTarget.this, mDragObject, true);
+                mOriginal.onDropCompleted(SecondaryDropTarget.this, mDragObject, true);
             } else {
                 sendFailure();
             }
@@ -212,7 +296,7 @@
         public void sendFailure() {
             mDragObject.dragSource = mOriginal;
             mDragObject.cancelled = true;
-            mOriginal.onDropCompleted(UninstallDropTarget.this, mDragObject, false);
+            mOriginal.onDropCompleted(SecondaryDropTarget.this, mDragObject, false);
         }
     }
 }
diff --git a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
index 3b6fea9..2cd8b1d 100644
--- a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
@@ -47,8 +47,8 @@
     private static final String TAG = "LauncherAccessibilityDelegate";
 
     public static final int REMOVE = R.id.action_remove;
-    public static final int INFO = R.id.action_info;
     public static final int UNINSTALL = R.id.action_uninstall;
+    public static final int RECONFIGURE = R.id.action_reconfigure;
     protected static final int ADD_TO_WORKSPACE = R.id.action_add_to_workspace;
     protected static final int MOVE = R.id.action_move;
     protected static final int MOVE_TO_WORKSPACE = R.id.action_move_to_workspace;
@@ -77,10 +77,10 @@
 
         mActions.put(REMOVE, new AccessibilityAction(REMOVE,
                 launcher.getText(R.string.remove_drop_target_label)));
-        mActions.put(INFO, new AccessibilityAction(INFO,
-                launcher.getText(R.string.app_info_drop_target_label)));
         mActions.put(UNINSTALL, new AccessibilityAction(UNINSTALL,
                 launcher.getText(R.string.uninstall_drop_target_label)));
+        mActions.put(RECONFIGURE, new AccessibilityAction(RECONFIGURE,
+                launcher.getText(R.string.gadget_setup_text)));
         mActions.put(ADD_TO_WORKSPACE, new AccessibilityAction(ADD_TO_WORKSPACE,
                 launcher.getText(R.string.action_add_to_workspace)));
         mActions.put(MOVE, new AccessibilityAction(MOVE,
@@ -110,7 +110,7 @@
         }
 
         for (ButtonDropTarget target : mLauncher.getDropTargetBar().getDropTargets()) {
-            if (target.supportsAccessibilityDrop(item)) {
+            if (target.supportsAccessibilityDrop(item, host)) {
                 info.addAction(mActions.get(target.getAccessibilityAction()));
             }
         }
@@ -222,7 +222,8 @@
             return PopupContainerWithArrow.showForIcon((BubbleTextView) host) != null;
         } else {
             for (ButtonDropTarget dropTarget : mLauncher.getDropTargetBar().getDropTargets()) {
-                if (action == dropTarget.getAccessibilityAction()) {
+                if (dropTarget.supportsAccessibilityDrop(item, host) &&
+                        action == dropTarget.getAccessibilityAction()) {
                     dropTarget.onAccessibilityDrop(host, item);
                     return true;
                 }
diff --git a/src/com/android/launcher3/logging/LoggerUtils.java b/src/com/android/launcher3/logging/LoggerUtils.java
index c608a23..d68ac15 100644
--- a/src/com/android/launcher3/logging/LoggerUtils.java
+++ b/src/com/android/launcher3/logging/LoggerUtils.java
@@ -20,11 +20,8 @@
 import android.view.View;
 
 import com.android.launcher3.ButtonDropTarget;
-import com.android.launcher3.DeleteDropTarget;
-import com.android.launcher3.InfoDropTarget;
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.LauncherSettings;
-import com.android.launcher3.UninstallDropTarget;
 import com.android.launcher3.userevent.nano.LauncherLogExtensions.TargetExtension;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
 import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
@@ -165,12 +162,8 @@
             return newTarget(Target.Type.CONTAINER);
         }
         Target t = newTarget(Target.Type.CONTROL);
-        if (v instanceof InfoDropTarget) {
-            t.controlType = ControlType.APPINFO_TARGET;
-        } else if (v instanceof UninstallDropTarget) {
-            t.controlType = ControlType.UNINSTALL_TARGET;
-        } else if (v instanceof DeleteDropTarget) {
-            t.controlType = ControlType.REMOVE_TARGET;
+        if (v instanceof ButtonDropTarget) {
+            t.controlType = ((ButtonDropTarget) v).getControlTypeForLogging();
         }
         return t;
     }
diff --git a/src/com/android/launcher3/model/WidgetsModel.java b/src/com/android/launcher3/model/WidgetsModel.java
index 1ff0dac..9f8f263 100644
--- a/src/com/android/launcher3/model/WidgetsModel.java
+++ b/src/com/android/launcher3/model/WidgetsModel.java
@@ -1,6 +1,8 @@
 
 package com.android.launcher3.model;
 
+import static android.appwidget.AppWidgetProviderInfo.WIDGET_FEATURE_HIDE_FROM_PICKER;
+
 import android.appwidget.AppWidgetProviderInfo;
 import android.content.Context;
 import android.content.pm.PackageManager;
@@ -153,6 +155,11 @@
         // add and update.
         for (WidgetItem item : rawWidgetsShortcuts) {
             if (item.widgetInfo != null) {
+                if ((item.widgetInfo.getWidgetFeatures() & WIDGET_FEATURE_HIDE_FROM_PICKER) != 0) {
+                    // Widget is hidden from picker
+                    continue;
+                }
+
                 // Ensure that all widgets we show can be added on a workspace of this size
                 int minSpanX = Math.min(item.widgetInfo.spanX, item.widgetInfo.minSpanX);
                 int minSpanY = Math.min(item.widgetInfo.spanY, item.widgetInfo.minSpanY);
diff --git a/src/com/android/launcher3/popup/SystemShortcut.java b/src/com/android/launcher3/popup/SystemShortcut.java
index 32fd063..2cc8dfa 100644
--- a/src/com/android/launcher3/popup/SystemShortcut.java
+++ b/src/com/android/launcher3/popup/SystemShortcut.java
@@ -9,7 +9,6 @@
 import android.view.View;
 
 import com.android.launcher3.AbstractFloatingView;
-import com.android.launcher3.InfoDropTarget;
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
@@ -83,7 +82,8 @@
                 public void onClick(View view) {
                     Rect sourceBounds = launcher.getViewBounds(view);
                     Bundle opts = launcher.getActivityLaunchOptionsAsBundle(view, false);
-                    InfoDropTarget.startDetailsActivityForInfo(itemInfo, launcher, sourceBounds, opts);
+                    new PackageManagerHelper(launcher).startDetailsActivityForInfo(
+                            itemInfo, sourceBounds, opts);
                     launcher.getUserEventDispatcher().logActionOnControl(Action.Touch.TAP,
                             ControlType.APPINFO_TARGET, view);
                 }
diff --git a/src/com/android/launcher3/util/PackageManagerHelper.java b/src/com/android/launcher3/util/PackageManagerHelper.java
index 81df153..0b3b632 100644
--- a/src/com/android/launcher3/util/PackageManagerHelper.java
+++ b/src/com/android/launcher3/util/PackageManagerHelper.java
@@ -17,6 +17,8 @@
 package com.android.launcher3.util;
 
 import android.app.AppOpsManager;
+import android.content.ActivityNotFoundException;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
@@ -24,13 +26,23 @@
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ResolveInfo;
+import android.graphics.Rect;
 import android.net.Uri;
 import android.os.Build;
+import android.os.Bundle;
 import android.os.UserHandle;
 import android.text.TextUtils;
+import android.util.Log;
+import android.widget.Toast;
 
 import com.android.launcher3.AppInfo;
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAppWidgetInfo;
+import com.android.launcher3.PendingAddItemInfo;
+import com.android.launcher3.PromiseAppInfo;
 import com.android.launcher3.R;
+import com.android.launcher3.ShortcutInfo;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.compat.LauncherAppsCompat;
 
@@ -42,6 +54,8 @@
  */
 public class PackageManagerHelper {
 
+    private static final String TAG = "PackageManagerHelper";
+
     private final Context mContext;
     private final PackageManager mPm;
     private final LauncherAppsCompat mLauncherApps;
@@ -169,4 +183,35 @@
             throw new RuntimeException(e);
         }
     }
+
+
+    /**
+     * Starts the details activity for {@code info}
+     */
+    public void startDetailsActivityForInfo(ItemInfo info, Rect sourceBounds, Bundle opts) {
+        if (info instanceof PromiseAppInfo) {
+            PromiseAppInfo promiseAppInfo = (PromiseAppInfo) info;
+            mContext.startActivity(promiseAppInfo.getMarketIntent(mContext));
+            return;
+        }
+        ComponentName componentName = null;
+        if (info instanceof AppInfo) {
+            componentName = ((AppInfo) info).componentName;
+        } else if (info instanceof ShortcutInfo) {
+            componentName = info.getTargetComponent();
+        } else if (info instanceof PendingAddItemInfo) {
+            componentName = ((PendingAddItemInfo) info).componentName;
+        } else if (info instanceof LauncherAppWidgetInfo) {
+            componentName = ((LauncherAppWidgetInfo) info).providerName;
+        }
+        if (componentName != null) {
+            try {
+                mLauncherApps.showAppDetailsForProfile(
+                        componentName, info.user, sourceBounds, opts);
+            } catch (SecurityException | ActivityNotFoundException e) {
+                Toast.makeText(mContext, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
+                Log.e(TAG, "Unable to launch settings", e);
+            }
+        }
+    }
 }